devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
@ 2014-11-19 18:52 Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 1/9] MAINTAINERS: Add QCOM audio ASoC maintainer Kenneth Westfield
                   ` (9 more replies)
  0 siblings, 10 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

This set of patches adds support for audio on the Qualcomm Technologies
ipq806x SOC.

The ipq806x SOC has audio-related hardware blocks in its low-power audio
subsystem (or LPASS).  One of the relevant blocks in the LPASS is its
low-power audio interface (or LPAIF).  This encapsulates the MI2S port,
which is what these drivers are configured to use.  The I2S pins are
connected to an external DAC/amp chip.  In addition, a single GPIO is
connected to the same DAC/amp, which gives the SOC enable/disable
control.

The specific drivers added are:
 - a machine driver that handles the board-specific pins
 - a native driver that handles hardware access to the LPAIF
 - a CPU DAI driver for controlling the LPAIF block
 - a PCM MI2S platform driver

Corresponding additions to the device tree for the ipq806x and its
documentation has also been added.  Also, as this is a new directory,
the MAINTAINERS file has been updated as well.

 - Ken

Kenneth Westfield (9):
  MAINTAINERS: Add QCOM audio ASoC maintainer
  ASoC: qcom: Add device tree binding docs
  ASoC: ipq806x: add native LPAIF driver
  ASoC: ipq806x: Add LPASS CPU DAI driver
  ASoC: ipq806x: Add I2S PCM platform driver
  ASoC: ipq806x: Add machine driver for IPQ806X SOC
  ASoC: qcom: Add ability to build QCOM drivers
  ASoC: Allow for building QCOM drivers
  ARM: dts: Model IPQ LPASS audio hardware

 .../bindings/sound/qcom,ipq806x-snd-card.txt       |  41 ++
 .../bindings/sound/qcom,lpass-cpu-dai.txt          |  20 +
 .../devicetree/bindings/sound/qcom,lpass-lpaif.txt |  21 +
 .../bindings/sound/qcom,lpass-pcm-mi2s.txt         |  12 +
 MAINTAINERS                                        |   7 +
 arch/arm/boot/dts/qcom-ipq8064.dtsi                |  33 ++
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/qcom/Kconfig                             |  43 ++
 sound/soc/qcom/Makefile                            |  11 +
 sound/soc/qcom/ipq806x.c                           | 221 ++++++++++
 sound/soc/qcom/lpass-cpu-dai.c                     | 307 +++++++++++++
 sound/soc/qcom/lpass-lpaif.c                       | 488 +++++++++++++++++++++
 sound/soc/qcom/lpass-lpaif.h                       | 181 ++++++++
 sound/soc/qcom/lpass-pcm-mi2s.c                    | 390 ++++++++++++++++
 sound/soc/qcom/lpass-pcm-mi2s.h                    |  40 ++
 16 files changed, 1817 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,ipq806x-snd-card.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-cpu-dai.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-lpaif.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt
 create mode 100644 sound/soc/qcom/Kconfig
 create mode 100644 sound/soc/qcom/Makefile
 create mode 100644 sound/soc/qcom/ipq806x.c
 create mode 100644 sound/soc/qcom/lpass-cpu-dai.c
 create mode 100644 sound/soc/qcom/lpass-lpaif.c
 create mode 100644 sound/soc/qcom/lpass-lpaif.h
 create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.c
 create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.h

-- 
1.8.2.1

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 1/9] MAINTAINERS: Add QCOM audio ASoC maintainer
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 2/9] ASoC: qcom: Add device tree binding docs Kenneth Westfield
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Change-Id: I3e33dcff96e85c7be790f9b84d7755858a52644e
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e4f712785554a691f7f31dba57b600102b32554e..fe9b336a3b91e641abc8cbaab3fdcf6a2696026a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5093,6 +5093,13 @@ F:	drivers/char/ipmi/
 F:	include/linux/ipmi*
 F:	include/uapi/linux/ipmi*
 
+QCOM AUDIO (ASoC) DRIVERS
+M:     Patrick Lai <plai@codeaurora.org>
+M:     Banajit Goswami <bgoswami@codeaurora.org>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:     Supported
+F:     sound/soc/qcom/
+
 IPS SCSI RAID DRIVER
 M:	Adaptec OEM Raid Solutions <aacraid@adaptec.com>
 L:	linux-scsi@vger.kernel.org
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 2/9] ASoC: qcom: Add device tree binding docs
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 1/9] MAINTAINERS: Add QCOM audio ASoC maintainer Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-25 21:26   ` Mark Brown
  2014-11-19 18:52 ` [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver Kenneth Westfield
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Add documentation to the sound directory of the
device-tree bindings for IPQ806x audio drivers.

Change-Id: I7dd08be2a68579675a6916ecb72b943dea0e352b
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 .../bindings/sound/qcom,ipq806x-snd-card.txt       | 41 ++++++++++++++++++++++
 .../bindings/sound/qcom,lpass-cpu-dai.txt          | 20 +++++++++++
 .../devicetree/bindings/sound/qcom,lpass-lpaif.txt | 21 +++++++++++
 .../bindings/sound/qcom,lpass-pcm-mi2s.txt         | 12 +++++++
 4 files changed, 94 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,ipq806x-snd-card.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-cpu-dai.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-lpaif.txt
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt

diff --git a/Documentation/devicetree/bindings/sound/qcom,ipq806x-snd-card.txt b/Documentation/devicetree/bindings/sound/qcom,ipq806x-snd-card.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1187a7f37be3820bbd5bdf4f87ca6d37dcbe1454
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,ipq806x-snd-card.txt
@@ -0,0 +1,41 @@
+* Qualcomm Technologies IPQ806x SoundCard
+
+This node models the Qualcomm Technologies IPQ806x LPASS Audio SoundCard,
+with a connection between the CPU MI2S DAI and the external DAC.
+
+Required properties:
+- compatible        : "qcom,ipq806x-snd-card"
+- qcom,model        : The user-visible name of this sound card
+- pinctrl-0         : The default state of the MI2S pins
+- pinctrl-names     : The name of the default state
+- dac-gpios         : GPIO specifier to the GPIO -> DAC SDMODE pin
+- clocks            : A list of clock specifiers in the following order:
+                        * AHBIX bus clock
+- clock-names       : A list of names in the following order:
+                        * ahbix_clk
+asoc-platform       : This is phandle list containing the references to platform device
+                      nodes that are used as part of the sound card dai-links.
+asoc-platform-names : This property contains list of platform names. The order of
+                      the platform names should match to that of the phandle order
+                      given in "asoc-platform".
+asoc-cpu            : This is phandle list containing the references to cpu dai device nodes
+                      that are used as part of the sound card dai-links.
+asoc-cpu-names      : This property contains list of cpu dai names. The order of the
+                      cpu dai names should match to that of the phandle order given
+                      in "asoc-cpu".
+
+Example:
+
+sound {
+	compatible = "qcom,ipq806x-snd-card";
+	model = "ipq806x-snd-card";
+	pinctrl-0 = <&mi2s_pins>;
+	pinctrl-names = "default";
+	dac-gpios = <&qcom_pinmux 25 0>;
+	clocks = <&lcc AHBIX_CLK>;
+	clock-names = "ahbix_clk";
+	asoc-platform = <&pcm0>;
+	asoc-platform-names = "lpass-pcm-mi2s";
+	asoc-cpu = <&dai_mi2s>;
+	asoc-cpu-names = "lpass-cpu-dai";
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu-dai.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu-dai.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8b700c9727fef9969af810376071d2e390625fac
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu-dai.txt
@@ -0,0 +1,20 @@
+* Qualcomm Technologies IPQ806x LPASS DAI
+
+This node models the Qualcomm Technologies IPQ806x LPASS I2S DAI port.
+
+Required properties:
+- compatible  : "qcom,lpass-cpu-dai"
+- clocks      : A list of clock specifiers in the following order:
+                        * MI2S OSR clock
+                        * MI2S Bit clock
+- clock-names : A list of names in the following order:
+                        * mi2s_osr_clk
+                        * mi2s_bit_clk
+
+Example:
+
+lpass-cpu-dai {
+	compatible = "qcom,lpass-cpu-dai";
+	clocks = <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
+	clock-names = "mi2s_osr_clk", "mi2s_bit_clk";
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-lpaif.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-lpaif.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d4feebb81aa9faebe2352a72e02a21dffc2ab331
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-lpaif.txt
@@ -0,0 +1,21 @@
+* Qualcomm Technologies IPQ806x LPASS driver
+
+This node models the Qualcomm Technologies IPQ806x LPASS Audio driver,
+which controls the various components within the subsystem.
+
+Required properties:
+- compatible      : "qcom,lpass-lpaif"
+- reg             : Address space for the LPASS subsystem registers
+- reg-names       : The name of the LPASS subsystem register address space
+- interrupts      : Phandle to the LPASS interrupt
+- interrupt-names : The names of the LPASS interrupt
+
+Example:
+
+lpass-lpaif"
+	compatible = "qcom,lpass-lpaif";
+	reg = <0x28100000 0x10000>;
+	reg-names = "lpass-lpaif-mem";
+	interrupts = <0 85 1>;
+	interrupt-names = "lpass-lpaif-irq";
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d2ff501d44f7b7aa790cdadc8ba75c6a8bf37ccd
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt
@@ -0,0 +1,12 @@
+* Qualcomm Technologies IPQ806x PCM audio interface
+
+This node models the Qualcomm Technologies IPQ806x PCM audio interface.
+
+Required properties:
+- compatible: "qcom,lpass-pcm-mi2s"
+
+Example:
+
+lpass-pcm-mi2s {
+	compatible = "qcom,lpass-pcm-mi2s";
+};
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 1/9] MAINTAINERS: Add QCOM audio ASoC maintainer Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 2/9] ASoC: qcom: Add device tree binding docs Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-20 12:32   ` [alsa-devel] " Lars-Peter Clausen
  2014-11-25 21:44   ` Mark Brown
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Add the native LPAIF driver for LPASS block in Qualcomm
Technologies SoCs.

Change-Id: I0f06f73a1267d7721209e58ce18e0d4897001141
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/qcom/lpass-lpaif.c | 488 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/lpass-lpaif.h | 181 ++++++++++++++++
 2 files changed, 669 insertions(+)
 create mode 100644 sound/soc/qcom/lpass-lpaif.c
 create mode 100644 sound/soc/qcom/lpass-lpaif.h

diff --git a/sound/soc/qcom/lpass-lpaif.c b/sound/soc/qcom/lpass-lpaif.c
new file mode 100644
index 0000000000000000000000000000000000000000..e62843fe9bc4c63c3c7c119a9f076085b16a56b3
--- /dev/null
+++ b/sound/soc/qcom/lpass-lpaif.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+#include "lpass-lpaif.h"
+
+#define DRV_NAME	"lpass-lpaif"
+#define DRV_VERSION	"1.0"
+
+struct lpaif_dai_baseinfo {
+	void __iomem *base;
+};
+
+struct lpaif_dai_drv {
+	unsigned char *buffer;
+	dma_addr_t buffer_phys;
+	int channels;
+	irqreturn_t (*callback)(int intrsrc, void *private_data);
+	void *private_data;
+	int in_use;
+	unsigned int buffer_len;
+	unsigned int period_len;
+	unsigned int master_mode;
+};
+
+static struct lpaif_dai_baseinfo lpaif_dai_info;
+static struct lpaif_dai_drv *lpaif_dai[LPAIF_MAX_CHANNELS];
+static spinlock_t lpaif_lock;
+static struct resource *lpaif_irq;
+
+static int lpaif_pcm_int_enable(uint8_t dma_ch)
+{
+	uint32_t intr_val;
+	uint32_t status_val;
+	unsigned long flags;
+
+	if (dma_ch >= LPAIF_MAX_CHANNELS) {
+		pr_err("%s: invalid DMA channel given: %hhu\n",
+				__func__, dma_ch);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	/* clear status before enabling interrupt */
+	status_val = readl(lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0));
+	status_val |= LPAIF_PER_CH(dma_ch);
+	writel(status_val, lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0));
+
+	intr_val = readl(lpaif_dai_info.base + LPAIF_IRQ_EN(0));
+	intr_val |= LPAIF_PER_CH(dma_ch);
+	writel(intr_val, lpaif_dai_info.base + LPAIF_IRQ_EN(0));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+
+	return 0;
+}
+
+static int lpaif_pcm_int_disable(uint8_t dma_ch)
+{
+	uint32_t intr_val;
+	unsigned long flags;
+
+	if (dma_ch >= LPAIF_MAX_CHANNELS) {
+		pr_err("%s: invalid DMA channel given: %hhu\n",
+				__func__, dma_ch);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	intr_val = readl(lpaif_dai_info.base + LPAIF_IRQ_EN(0));
+	intr_val &= ~LPAIF_PER_CH(dma_ch);
+	writel(intr_val, lpaif_dai_info.base + LPAIF_IRQ_EN(0));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+
+	return 0;
+}
+
+void lpaif_cfg_i2s_playback(uint8_t enable, uint32_t mode, uint32_t off)
+{
+	uint32_t cfg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+
+	if (enable)
+		cfg |= LPAIF_SPK_EN;
+	else
+		cfg &= ~LPAIF_SPK_EN;
+
+	cfg |= mode << LPAIF_SPK_MODE;
+	cfg &= ~LPAIF_WS;
+
+	writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+}
+
+int lpaif_cfg_mi2s_hwparams_bit_width(uint32_t bit_width, uint32_t off)
+{
+	int ret = 0;
+	uint32_t cfg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+	cfg &= ~LPAIF_BIT_MASK;
+
+	switch (bit_width) {
+	case SNDRV_PCM_FORMAT_S16:
+		cfg |= LPAIF_BIT_RATE16;
+		break;
+	case SNDRV_PCM_FORMAT_S24:
+		cfg |= LPAIF_BIT_RATE24;
+		break;
+	case SNDRV_PCM_FORMAT_S32:
+		cfg |= LPAIF_BIT_RATE32;
+		break;
+	default:
+		pr_err("%s: invalid bitwidth given: %u\n",
+				__func__, bit_width);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (!ret)
+		writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+
+	return ret;
+}
+
+int lpaif_cfg_mi2s_playback_hwparams_channels(uint32_t channels, uint32_t off,
+		uint32_t bit_width)
+{
+	int ret = 0;
+	uint32_t cfg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+	cfg &= ~LPAIF_SPK_MODE_MASK;
+
+	switch (channels) {
+	case 2:
+		cfg |= LPAIF_SPK_MODE_SD0;
+		break;
+	case 4:
+		cfg |= LPAIF_SPK_MODE_QUAD01;
+		break;
+	case 6:
+		cfg |= LPAIF_SPK_MODE_6CH;
+		break;
+	case 8:
+		cfg |= LPAIF_SPK_MODE_8CH;
+		break;
+	default:
+		pr_err("%s: invalid channels given: %u\n", __func__, channels);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (!ret)
+		writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+
+	return ret;
+}
+
+static int lpaif_dai_config_dma(uint32_t dma_ch)
+{
+	if (dma_ch >= LPAIF_MAX_CHANNELS) {
+		pr_err("%s: invalid DMA channel given: %u\n",
+				__func__, dma_ch);
+		return -EINVAL;
+	}
+
+	writel(lpaif_dai[dma_ch]->buffer_phys,
+			lpaif_dai_info.base + LPAIF_DMA_BASE(dma_ch));
+	writel(((lpaif_dai[dma_ch]->buffer_len >> 2) - 1),
+			lpaif_dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
+	writel(((lpaif_dai[dma_ch]->period_len >> 2) - 1),
+			lpaif_dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
+
+	return 0;
+}
+
+static int lpaif_dai_cfg_dma_ch(uint32_t dma_ch, uint32_t channels,
+		uint32_t bit_width)
+{
+	int ret = 0;
+	uint32_t cfg;
+	unsigned long flags;
+
+	if (dma_ch >= LPAIF_MAX_CHANNELS) {
+		pr_err("%s: invalid DMA channel given: %u\n",
+				__func__, dma_ch);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&lpaif_lock, flags);
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+	if ((dma_ch == LPAIF_MI2S_DMA_RD_CH) ||
+			(dma_ch == LPAIF_MI2S_DMA_WR_CH)) {
+		cfg |= LPAIF_DMACTL_AUDIO_INTF_MI2S;
+		cfg &= ~LPAIF_DMACTL_WPSCNT_MASK;
+
+		if ((bit_width == 16) && (channels == 2)) {
+			cfg |= LPAIF_DMACTL_WPSCNT_MONO;
+		} else if (((bit_width == 16) && (channels == 4)) ||
+				(((bit_width == 24) || (bit_width == 32)) &&
+				(channels == 2))) {
+			cfg |= LPAIF_DMACTL_WPSCNT_STEREO;
+		} else if ((bit_width == 16) && (channels == 6)) {
+			cfg |= LPAIF_DMACTL_WPSCNT_3CH;
+		} else if (((bit_width == 16) && (channels == 8)) ||
+				(((bit_width == 32) || (bit_width == 24)) &&
+				(channels == 4))) {
+			cfg |= LPAIF_DMACTL_WPSCNT_4CH;
+		} else if (((bit_width == 24) || (bit_width == 32)) &&
+				(channels == 6)) {
+			cfg |= LPAIF_DMACTL_WPSCNT_6CH;
+		} else if (((bit_width == 24) || (bit_width == 32)) &&
+				(channels == 8)) {
+			cfg |= LPAIF_DMACTL_WPSCNT_8CH;
+		} else {
+			pr_err("%s: invalid PCM config given: bw=%u, ch=%u\n",
+					__func__, bit_width, channels);
+			ret = -EINVAL;
+		}
+	}
+
+	if (!ret)
+		writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+	spin_unlock_irqrestore(&lpaif_lock, flags);
+	return ret;
+}
+
+int lpaif_cfg_dma(uint32_t dma_ch, struct lpaif_dai_dma_params *params,
+		uint32_t bit_width, bool enable_intr)
+{
+	int ret;
+	uint32_t cfg;
+
+	lpaif_dai[dma_ch]->buffer = params->buffer;
+	lpaif_dai[dma_ch]->buffer_phys = params->src_start;
+	lpaif_dai[dma_ch]->channels = params->channels;
+	lpaif_dai[dma_ch]->buffer_len = params->buffer_size;
+	lpaif_dai[dma_ch]->period_len = params->period_size;
+
+	ret = lpaif_dai_config_dma(dma_ch);
+	if (ret) {
+		pr_err("%s: error configuring DMA block: %d\n", __func__, ret);
+		return ret;
+	}
+
+	if (enable_intr)
+		lpaif_pcm_int_enable(dma_ch);
+
+	ret = lpaif_dai_cfg_dma_ch(dma_ch, params->channels, bit_width);
+	if (ret) {
+		pr_err("%s: error configuring DMA channel: %d\n",
+				__func__, ret);
+		lpaif_pcm_int_disable(dma_ch);
+		return ret;
+	}
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+	cfg |= LPAIF_DMACTL_FIFO_WM_8 | LPAIF_DMACTL_BURST_EN;
+	writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+	cfg |= LPAIF_DMACTL_ENABLE;
+	writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+	return 0;
+}
+
+int lpaif_dai_stop(uint32_t dma_ch)
+{
+	writel(0x0, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+	return 0;
+}
+
+uint8_t lpaif_dma_stop(uint8_t dma_ch)
+{
+	uint32_t cfg;
+
+	cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+	cfg &= ~LPAIF_DMACTL_ENABLE;
+	writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch));
+	return 0;
+}
+
+void lpaif_register_dma_irq_handler(int dma_ch,
+		irqreturn_t (*callback)(int intrsrc, void *private_data),
+		void *private_data)
+{
+	lpaif_dai[dma_ch]->callback = callback;
+	lpaif_dai[dma_ch]->private_data = private_data;
+}
+
+void lpaif_unregister_dma_irq_handler(int dma_ch)
+{
+	lpaif_dai[dma_ch]->callback = NULL;
+	lpaif_dai[dma_ch]->private_data = NULL;
+}
+
+/*
+ * Logic to find the dma channel from interrupt.
+ * In total we have 9 channels, each channel records the transcation
+ * status. Either one of ths 3 status will be recorded per transcation
+ * (PER_CH,UNDER_RUN,OVER_RUN)
+ */
+static int lpaif_dai_find_dma_channel(uint32_t intrsrc)
+{
+	uint32_t dma_channel = 0;
+
+	while (dma_channel < LPAIF_MAX_CHANNELS) {
+		if (intrsrc & LPAIF_PER_CH(dma_channel))
+			return dma_channel;
+
+		dma_channel++;
+	}
+
+	return -1;
+}
+
+/* ISR for handling LPAIF interrupts  */
+static irqreturn_t lpaif_dai_irq_handler(int irq, void *data)
+{
+	unsigned long flag;
+	uint32_t intrsrc;
+	uint32_t dma_ch;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock_irqsave(&lpaif_lock, flag);
+	intrsrc = readl(lpaif_dai_info.base + LPAIF_IRQ_STAT(0));
+	writel(intrsrc, lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0));
+	spin_unlock_irqrestore(&lpaif_lock, flag);
+
+	while (intrsrc) {
+		dma_ch = lpaif_dai_find_dma_channel(intrsrc);
+		if (dma_ch != -1) {
+			if (lpaif_dai[dma_ch]->callback) {
+
+				ret = lpaif_dai[dma_ch]->callback(intrsrc,
+					lpaif_dai[dma_ch]->private_data);
+			}
+			intrsrc &= ~LPAIF_PER_CH(dma_ch);
+		} else {
+			pr_err("%s: error getting channel\n", __func__);
+			break;
+		}
+	}
+	return ret;
+}
+
+static void lpaif_dai_ch_free(void)
+{
+	int i;
+
+	for (i = 0; i < LPAIF_MAX_CHANNELS; i++)
+		kfree(lpaif_dai[i]);
+}
+
+static int lpaif_dai_probe(struct platform_device *pdev)
+{
+	uint8_t i;
+	int32_t rc;
+	struct resource *lpa_res;
+	struct device *lpaif_device;
+
+	lpaif_device = &pdev->dev;
+
+	lpa_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+			"lpass-lpaif-mem");
+	if (!lpa_res) {
+		dev_err(&pdev->dev, "%s: error getting resource\n", __func__);
+		return -ENODEV;
+	}
+	lpaif_dai_info.base = ioremap(lpa_res->start,
+			(lpa_res->end - lpa_res->start));
+	if (!lpaif_dai_info.base) {
+		dev_err(&pdev->dev, "%s: error remapping resource\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	lpaif_irq = platform_get_resource_byname(
+			pdev, IORESOURCE_IRQ, "lpass-lpaif-irq");
+	if (!lpaif_irq) {
+		dev_err(&pdev->dev, "%s: failed get irq res\n", __func__);
+		rc = -ENODEV;
+		goto error;
+	}
+
+	rc = request_irq(lpaif_irq->start, lpaif_dai_irq_handler,
+			IRQF_TRIGGER_RISING, "lpass-lpaif-intr", NULL);
+
+	if (rc < 0) {
+		dev_err(&pdev->dev, "%s: irq resource request failed\n",
+				__func__);
+		goto error;
+	}
+
+	/*
+	 * Allocating memory for all the LPA_IF DMA channels
+	 */
+	for (i = 0; i < LPAIF_MAX_CHANNELS; i++) {
+		lpaif_dai[i] = kzalloc(sizeof(struct lpaif_dai_drv),
+				GFP_KERNEL);
+		if (!lpaif_dai[i]) {
+			rc = -ENOMEM;
+			goto error_irq;
+		}
+	}
+	spin_lock_init(&lpaif_lock);
+	return 0;
+
+error_irq:
+	free_irq(lpaif_irq->start, NULL);
+	lpaif_dai_ch_free();
+error:
+	iounmap(lpaif_dai_info.base);
+	return rc;
+}
+
+static int lpaif_dai_remove(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < LPAIF_MAX_CHANNELS; i++)
+		lpaif_dai_stop(i);
+	synchronize_irq(lpaif_irq->start);
+	free_irq(lpaif_irq->start, NULL);
+	iounmap(lpaif_dai_info.base);
+	lpaif_dai_ch_free();
+	return 0;
+}
+
+static const struct of_device_id lpaif_dai_dt_match[] = {
+	{.compatible = "qcom,lpass-lpaif"},
+	{}
+};
+
+static struct platform_driver lpass_lpaif_driver = {
+	.probe = lpaif_dai_probe,
+	.remove = lpaif_dai_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = lpaif_dai_dt_match,
+		},
+};
+module_platform_driver(lpass_lpaif_driver);
+
+MODULE_DESCRIPTION("QCOM LPASS LPAIF Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, lpaif_dai_dt_match);
+MODULE_VERSION(DRV_VERSION);
diff --git a/sound/soc/qcom/lpass-lpaif.h b/sound/soc/qcom/lpass-lpaif.h
new file mode 100644
index 0000000000000000000000000000000000000000..e10731bb2cef96e31ebf7a92b9ba3e8ee22e0360
--- /dev/null
+++ b/sound/soc/qcom/lpass-lpaif.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _LPASS_LPAIF_H
+#define _LPASS_LPAIF_H
+
+#define LPAIF_BANK_OFFSET			0x1000
+
+/* Audio DMA registers for DMA channel confuguration */
+#define LPAIF_DMA_CH_CTL_BASE			0x6000
+#define LPAIF_DMA_CH_INDEX(ch)			(LPAIF_BANK_OFFSET * ch)
+
+#define LPAIF_DMA_CTRL_ADDR(ch, addr)		(LPAIF_DMA_CH_CTL_BASE \
+						+ (LPAIF_DMA_CH_INDEX(ch) \
+						+ addr))
+
+#define LPAIF_DMA_CTL(x)			LPAIF_DMA_CTRL_ADDR(x, 0x00)
+#define LPAIF_BURST_EN				(1 << 11)
+#define LPAIF_WPSCNT_ONE			(0 << 8)
+#define LPAIF_WPSCNT_TWO			(1 << 8)
+#define LPAIF_WPSCNT_THREE			(2 << 8)
+#define LPAIF_WPSCNT_FOUR			(3 << 8)
+#define LPAIF_WPSCNT_SIX			(5 << 8)
+#define LPAIF_WPSCNT_EIGHT			(7 << 8)
+#define LPAIF_AUDIO_INTF_NONE			(0 << 4)
+#define LPAIF_AUDIO_INTF_CODEC			(1 << 4)
+#define LPAIF_AUDIO_INTF_PCM			(2 << 4)
+#define LPAIF_AUDIO_INTF_SEC_I2S		(3 << 4)
+#define LPAIF_AUDIO_INTF_MI2S			(4 << 4)
+#define LPAIF_AUDIO_INTF_HDMI			(5 << 4)
+#define LPAIF_AUDIO_INTF_MIXOUT			(6 << 4)
+#define LPAIF_AUDIO_INTF_LOOPBACK1		(7 << 4)
+#define LPAIF_AUDIO_INTF_LOOPBACK2		(8 << 4)
+#define LPAIF_FIFO_WATERMRK(x)			((x & 0x7) << 1)
+#define LPAIF_ENABLE				(1 << 0)
+
+#define LPAIF_DMA_BASE(x)			LPAIF_DMA_CTRL_ADDR(x, 0x04)
+#define LPAIF_BASE_ADDR				(0xFFFFFFFF << 4)
+
+#define	LPAIF_DMA_BUFF_LEN(x)			LPAIF_DMA_CTRL_ADDR(x, 0x08)
+#define LPAIF_DMA_CURR_ADDR(x)			LPAIF_DMA_CTRL_ADDR(x, 0x0c)
+#define	LPAIF_DMA_PER_LEN(x)			LPAIF_DMA_CTRL_ADDR(x, 0x10)
+#define	LPAIF_DMA_PER_CNT(x)			LPAIF_DMA_CTRL_ADDR(x, 0x14)
+#define	LPAIF_DMA_FRM(x)			LPAIF_DMA_CTRL_ADDR(x, 0x18)
+#define LPAIF_DMA_FRMCLR(x)			LPAIF_DMA_CTRL_ADDR(x, 0x1c)
+#define LPAIF_DMA_SET_BUFF_CNT(x)		LPAIF_DMA_CTRL_ADDR(x, 0x20)
+#define	LPAIF_DMA_SET_PER_CNT(x)		LPAIF_DMA_CTRL_ADDR(x, 0x24)
+
+#define LPAIF_MAX_CHANNELS			9
+
+#define LPAIF_CODEC_SPK				0x0
+#define LPAIF_CODEC_MIC				0x1
+#define LPAIF_SEC_SPK				0x2
+#define LPAIF_SEC_MIC				0x3
+#define LPAIF_MI2S				0x4
+
+#define LPAIF_LB				(1 << 15)
+#define LPAIF_SPK_EN				(1 << 14)
+
+#define LPAIF_SPK_MODE_MASK			0x3C00
+#define LPAIF_SPK_MODE				10
+#define LPAIF_SPK_MODE_NONE			(0 << 10)
+#define LPAIF_SPK_MODE_SD0			(1 << 10)
+#define LPAIF_SPK_MODE_SD1			(2 << 10)
+#define LPAIF_SPK_MODE_SD2			(3 << 10)
+#define LPAIF_SPK_MODE_SD3			(4 << 10)
+#define LPAIF_SPK_MODE_QUAD01			(5 << 10)
+#define LPAIF_SPK_MODE_QUAD23			(6 << 10)
+#define LPAIF_SPK_MODE_6CH			(7 << 10)
+#define LPAIF_SPK_MODE_8CH			(8 << 10)
+
+#define LPAIF_WS				(1 << 2)
+
+#define LPAIF_BIT_MASK				(0x3)
+#define LPAIF_BIT_RATE16			(0 << 0)
+#define LPAIF_BIT_RATE24			(1 << 0)
+#define LPAIF_BIT_RATE32			(2 << 0)
+
+#define LPAIF_MI2S_CTL_OFFSET(x)		(0x0010 + (0x4 * x))
+
+/* LPAIF INTERRUPT CTRL */
+
+#define LPAIF_DMA_IRQ_BASE			0x3000
+#define LPAIF_DMA_IRQ_INDEX(x)			(LPAIF_BANK_OFFSET * x)
+#define LPAIF_DMA_IRQ_ADDR(irq, addr)		(LPAIF_DMA_IRQ_BASE  \
+						+ LPAIF_DMA_IRQ_INDEX(irq) \
+						+ addr)
+
+#define LPAIF_IRQ_EN(x)				LPAIF_DMA_IRQ_ADDR(x, 0x00)
+#define LPAIF_IRQ_STAT(x)			LPAIF_DMA_IRQ_ADDR(x, 0x04)
+#define LPAIF_IRQ_RAW_STAT(x)			LPAIF_DMA_IRQ_ADDR(x, 0x08)
+#define LPAIF_IRQ_CLEAR(x)			LPAIF_DMA_IRQ_ADDR(x, 0x0c)
+#define LPAIF_IRQ_FORCE(x)			LPAIF_DMA_IRQ_ADDR(x, 0x10)
+#define LPAIF_PER_CH(x)				(1 << (3 * x))
+#define LPAIF_UNDER_CH(x)			(2 << (3 * x))
+#define LPAIF_ERR_CH(x)				(4 << (3 * x))
+
+/* DMA CTRL */
+
+#define LPAIF_DMACTL_BURST_EN			(1 << 11)
+#define LPAIF_DMACTL_WPSCNT_MASK		(0x700)
+#define LPAIF_DMACTL_WPSCNT_MONO		(0 << 8)
+#define LPAIF_DMACTL_WPSCNT_STEREO		(1 << 8)
+#define LPAIF_DMACTL_WPSCNT_STEREO_2CH		(0 << 8)
+#define LPAIF_DMACTL_WPSCNT_3CH			(2 << 8)
+#define LPAIF_DMACTL_WPSCNT_4CH			(3 << 8)
+#define LPAIF_DMACTL_WPSCNT_5CH			(4 << 8)
+#define LPAIF_DMACTL_WPSCNT_6CH			(5 << 8)
+#define LPAIF_DMACTL_WPSCNT_7CH			(6 << 8)
+#define LPAIF_DMACTL_WPSCNT_8CH			(7 << 8)
+
+#define LPAIF_DMACTL_AUDIO_INTF_MASK		(0xF0)
+#define LPAIF_DMACTL_AUDIO_INTF_NONE		(0 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_CODEC		(1 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_PCM		(2 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_SEC_I2S		(3 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_MI2S		(4 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_HDMI		(5 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_MIXOUT		(6 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_LB1		(7 << 4)
+#define LPAIF_DMACTL_AUDIO_INTF_LB2		(8 << 4)
+
+#define LPAIF_DMACTL_FIFO_WM_1			(0 << 1)
+#define LPAIF_DMACTL_FIFO_WM_2			(1 << 1)
+#define LPAIF_DMACTL_FIFO_WM_3			(2 << 1)
+#define LPAIF_DMACTL_FIFO_WM_4			(3 << 1)
+#define LPAIF_DMACTL_FIFO_WM_5			(4 << 1)
+#define LPAIF_DMACTL_FIFO_WM_6			(5 << 1)
+#define LPAIF_DMACTL_FIFO_WM_7			(6 << 1)
+#define LPAIF_DMACTL_FIFO_WM_8			(7 << 1)
+
+#define LPAIF_DMACTL_ENABLE			(1 << 0)
+
+enum lpaif_dma_intf_wr_ch {
+	LPAIF_MIN_DMA_WR_CH	= 5,
+	LPAIF_PCM0_DMA_WR_CH	= 5,
+	LPAIF_PCM1_DMA_WR_CH	= 6,
+	LPAIF_MI2S_DMA_WR_CH	= 6,
+	LPAIF_MAX_DMA_WR_CH	= 8,
+};
+
+enum lpaif_dma_intf_rd_ch {
+	LPAIF_MIN_DMA_RD_CH	= 0,
+	LPAIF_MI2S_DMA_RD_CH	= 0,
+	LPAIF_PCM0_DMA_RD_CH	= 1,
+	LPAIF_PCM1_DMA_RD_CH	= 2,
+	LPAIF_MAX_DMA_RD_CH	= 4,
+};
+
+struct lpaif_dai_dma_params {
+	u8 *buffer;
+	uint32_t src_start;
+	uint32_t bus_id;
+	int buffer_size;
+	int period_size;
+	int channels;
+};
+
+void lpaif_cfg_i2s_playback(uint8_t enable, uint32_t mode, uint32_t off);
+int lpaif_cfg_mi2s_hwparams_bit_width(uint32_t bit_width, uint32_t off);
+int lpaif_cfg_mi2s_playback_hwparams_channels(uint32_t channels,
+		uint32_t off, uint32_t bit_width);
+int lpaif_cfg_dma(uint32_t dma_ch, struct lpaif_dai_dma_params *params,
+		uint32_t bit_width, bool enable_intr);
+int lpaif_dai_stop(uint32_t dma_ch);
+uint8_t lpaif_dma_stop(uint8_t dma_ch);
+void lpaif_register_dma_irq_handler(int dma_ch,
+		irqreturn_t (*callback)(int intr_src, void *private_data),
+		void *private_data);
+void lpaif_unregister_dma_irq_handler(int dma_ch);
+#endif /* _LPASS_LPAIF_H */
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (2 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-19 21:17   ` Pierre-Louis Bossart
                     ` (3 more replies)
  2014-11-19 18:52 ` [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver Kenneth Westfield
                   ` (5 subsequent siblings)
  9 siblings, 4 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Add the CPU DAI driver for the QCOM LPASS SOC.

Change-Id: I64ac4407dd32bb9a3066d4b7427292002eaf5d14
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/qcom/lpass-cpu-dai.c | 307 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 307 insertions(+)
 create mode 100644 sound/soc/qcom/lpass-cpu-dai.c

diff --git a/sound/soc/qcom/lpass-cpu-dai.c b/sound/soc/qcom/lpass-cpu-dai.c
new file mode 100644
index 0000000000000000000000000000000000000000..0693e761e01fd416f52684977bcfaf1579dc259c
--- /dev/null
+++ b/sound/soc/qcom/lpass-cpu-dai.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "lpass-lpaif.h"
+#include "lpass-pcm-mi2s.h"
+
+#define DRV_NAME	"lpass-cpu-dai"
+#define DRV_VERSION	"1.0"
+
+#define LPASS_INVALID	(-1)
+
+struct mi2s_hw_params {
+	uint8_t channels;
+	uint32_t freq;
+	uint8_t bit_width;
+};
+
+static struct clk *lpaif_mi2s_bit_clk;
+static struct clk *lpaif_mi2s_osr_clk;
+static struct mi2s_hw_params mi2s_params;
+
+/*
+ * Returns the OSR to BIT clock divider that would be used with the given
+ * stream characteristics.
+ */
+static uint32_t lpass_cpu_get_bit_div(uint32_t samp_freq, uint32_t bit_width,
+							uint32_t channels)
+{
+	if (channels == 8) {
+		if (bit_width == 24 &&
+			((samp_freq != 176400) &&
+			(samp_freq != 192000)))
+			return 2;
+		return 1;
+	} else if (channels == 6) {
+		if ((bit_width == 32 && ((samp_freq == 176400) ||
+			(samp_freq == 192000))) || (bit_width == 24 &&
+			(samp_freq == 192000)))
+			return 1;
+		else
+			return 2;
+	}
+
+	switch (samp_freq) {
+	case 8000:
+	case 16000:
+	case 32000:
+	case 64000:
+		if (bit_width == 16)
+			return 4;
+		break;
+	case 48000:
+		if (bit_width == 24)
+			return 4;
+		break;
+	case 96000:
+	case 192000:
+		return 4;
+	case 176400:
+		return 2;
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	default:
+		pr_err("%s: Invalid PCM format given\n", __func__);
+		return LPASS_INVALID;
+	}
+
+	return 4;
+}
+
+static int lpass_cpu_mi2s_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params,
+					struct snd_soc_dai *dai)
+{
+	uint32_t ret = 0;
+	uint32_t bit_act;
+	uint16_t bit_div;
+	uint32_t bit_width = params_format(params);
+	uint32_t channels = params_channels(params);
+	uint32_t rate = params_rate(params);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+	struct mi2s_hw_params curr_params;
+
+	bit_act = snd_pcm_format_width(bit_width);
+	if (bit_act == LPASS_INVALID) {
+		dev_err(dai->dev, "%s: Invalid bit width given\n", __func__);
+		return -EINVAL;
+	}
+
+	prtd->pcm_stream_info.bit_width = bit_act;
+	curr_params.freq = rate;
+	curr_params.channels = channels;
+	curr_params.bit_width = bit_act;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* disable SPKR to make sure it will start in sane state */
+		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
+
+		/*
+		 * Set channel info, it will take effect only if SPKR is
+		 * enabled
+		 */
+		ret = lpaif_cfg_mi2s_playback_hwparams_channels(channels,
+						LPAIF_MI2S, bit_act);
+	} else {
+		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Make sure our channel setting was success */
+	if (ret) {
+		/* shutdown would be called where we restore stuff */
+		dev_err(dai->dev, "%s: Channel setting unsuccessful\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	mi2s_params.freq = rate;
+	mi2s_params.channels = channels;
+	mi2s_params.bit_width = bit_act;
+
+	ret = lpaif_cfg_mi2s_hwparams_bit_width(bit_width, LPAIF_MI2S);
+	if (ret) {
+		dev_err(dai->dev, "%s: Could not set bit width in HW\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	bit_div = lpass_cpu_get_bit_div(rate, bit_act, channels);
+	if (bit_div == LPASS_INVALID) {
+		dev_err(dai->dev, "%s: Could not get bit clk divider\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(lpaif_mi2s_osr_clk,
+		(rate * bit_act * channels * bit_div));
+	if (ret) {
+		dev_err(dai->dev, "%s: error in setting mi2s osr clk\n",
+				__func__);
+		return ret;
+	}
+	ret = clk_prepare_enable(lpaif_mi2s_osr_clk);
+	if (ret) {
+		dev_err(dai->dev, "%s: error in enabling mi2s osr clk\n",
+				__func__);
+		return ret;
+	}
+	prtd->lpaif_clk.is_osr_clk_enabled = 1;
+
+	ret = clk_set_rate(lpaif_mi2s_bit_clk, rate * bit_act * channels);
+	if (ret) {
+		dev_err(dai->dev, "%s: error in setting mi2s bit clk\n",
+				__func__);
+		return ret;
+	}
+	ret = clk_prepare_enable(lpaif_mi2s_bit_clk);
+	if (ret) {
+		dev_err(dai->dev, "%s: error in enabling mi2s bit clk\n",
+				__func__);
+		return ret;
+	}
+	prtd->lpaif_clk.is_bit_clk_enabled = 1;
+
+	return 0;
+}
+
+static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int lpass_cpu_mi2s_startup(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+
+	lpaif_mi2s_osr_clk = clk_get(dai->dev, "mi2s_osr_clk");
+	if (IS_ERR(lpaif_mi2s_osr_clk)) {
+		dev_err(dai->dev, "%s: Error in getting mi2s_osr_clk\n",
+				__func__);
+		return PTR_ERR(lpaif_mi2s_osr_clk);
+	}
+
+	lpaif_mi2s_bit_clk = clk_get(dai->dev, "mi2s_bit_clk");
+	if (IS_ERR(lpaif_mi2s_bit_clk)) {
+		dev_err(dai->dev, "%s: Error in getting mi2s_bit_clk\n",
+				__func__);
+		return PTR_ERR(lpaif_mi2s_bit_clk);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
+	} else {
+		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void lpass_cpu_mi2s_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+
+	if (prtd->lpaif_clk.is_osr_clk_enabled)
+		clk_disable_unprepare(lpaif_mi2s_osr_clk);
+	clk_put(lpaif_mi2s_osr_clk);
+
+	if (prtd->lpaif_clk.is_bit_clk_enabled)
+		clk_disable_unprepare(lpaif_mi2s_bit_clk);
+	clk_put(lpaif_mi2s_bit_clk);
+}
+
+static int lpass_cpu_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	return 0;
+}
+
+static struct snd_soc_dai_ops lpass_cpu_mi2s_ops = {
+	.startup	= lpass_cpu_mi2s_startup,
+	.prepare	= lpass_cpu_mi2s_prepare,
+	.hw_params	= lpass_cpu_mi2s_hw_params,
+	.shutdown	= lpass_cpu_mi2s_shutdown,
+	.set_fmt	= lpass_cpu_mi2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver lpass_cpu_dais[] = {
+	{
+		.playback = {
+			.rates		= SNDRV_PCM_RATE_8000_192000,
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rate_min	= 8000,
+			.rate_max	= 192000,
+		},
+		.ops    = &lpass_cpu_mi2s_ops,
+		.name = "lpass-mi2s-dai"
+	},
+};
+
+static const struct snd_soc_component_driver lpass_cpu_component = {
+	.name           = DRV_NAME,
+};
+
+static int lpass_cpu_dai_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = snd_soc_register_component(&pdev->dev, &lpass_cpu_component,
+			lpass_cpu_dais, ARRAY_SIZE(lpass_cpu_dais));
+	if (ret)
+		dev_err(&pdev->dev, "%s: error registering soc dais\n",
+				__func__);
+
+	return ret;
+}
+
+static int lpass_cpu_dai_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id lpass_cpu_dai_dt_match[] = {
+	{.compatible = "qcom,lpass-cpu-dai"},
+	{}
+};
+
+static struct platform_driver lpass_cpu_dai_driver = {
+	.probe	= lpass_cpu_dai_probe,
+	.remove	= lpass_cpu_dai_remove,
+	.driver	= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = lpass_cpu_dai_dt_match,
+	},
+};
+module_platform_driver(lpass_cpu_dai_driver);
+
+MODULE_DESCRIPTION("QCOM LPASS CPU DAI DRIVER");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, lpass_cpu_dai_dt_match);
+MODULE_VERSION(DRV_VERSION);
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (3 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-19 21:10   ` [alsa-devel] " Pierre-Louis Bossart
  2014-11-25 22:01   ` Mark Brown
  2014-11-19 18:52 ` [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC Kenneth Westfield
                   ` (4 subsequent siblings)
  9 siblings, 2 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Add PCM platform driver for the LPASS I2S port.

Change-Id: If6516fb615b16551817fd9248c1c704fe521fb32
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/qcom/lpass-pcm-mi2s.c | 390 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/lpass-pcm-mi2s.h |  40 +++++
 2 files changed, 430 insertions(+)
 create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.c
 create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.h

diff --git a/sound/soc/qcom/lpass-pcm-mi2s.c b/sound/soc/qcom/lpass-pcm-mi2s.c
new file mode 100644
index 0000000000000000000000000000000000000000..110b088d156c6d06cbe054920fc63fd064a0ac45
--- /dev/null
+++ b/sound/soc/qcom/lpass-pcm-mi2s.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/dma-mapping.h>
+#include <sound/soc.h>
+#include "lpass-lpaif.h"
+#include "lpass-pcm-mi2s.h"
+
+#define DRV_NAME	"lpass-pcm-mi2s"
+#define DRV_VERSION	"1.0"
+
+/* MI2S Hw params */
+#define LPASS_MI2S_NO_OF_PERIODS	(65)
+#define LPASS_MI2S_PERIOD_BYTES_MIN	(8064)
+#define LPASS_MI2S_BUFF_SIZE		(LPASS_MI2S_PERIOD_BYTES_MIN * \
+					LPASS_MI2S_NO_OF_PERIODS)
+
+static struct snd_pcm_hardware lpass_pcm_hardware_playback = {
+	.info			=	SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_PAUSE |
+					SNDRV_PCM_INFO_RESUME,
+	.formats		=	SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+	.rates			=	SNDRV_PCM_RATE_8000_192000,
+	.rate_min		=	8000,
+	.rate_max		=	192000,
+	.channels_min		=	2,
+	.channels_max		=	8,
+	.buffer_bytes_max	=	LPASS_MI2S_BUFF_SIZE,
+	.period_bytes_max	=	(LPASS_MI2S_BUFF_SIZE) / 2,
+	.period_bytes_min	=	LPASS_MI2S_PERIOD_BYTES_MIN,
+	.periods_min		=	LPASS_MI2S_NO_OF_PERIODS,
+	.periods_max		=	LPASS_MI2S_NO_OF_PERIODS,
+	.fifo_size		=	0,
+};
+
+static int lpass_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		size = lpass_pcm_hardware_playback.buffer_bytes_max;
+	} else {
+		pr_err("%s: Invalid stream direction\n", __func__);
+		return -EINVAL;
+	}
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+					&buf->addr, GFP_KERNEL);
+	if (!buf->area) {
+		pr_err("%s: Could not allocate DMA buffer\n", __func__);
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	return 0;
+}
+
+static void lpass_pcm_free_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+
+	substream = pcm->streams[stream].substream;
+	buf = &substream->dma_buffer;
+	if (buf->area) {
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+					buf->area, buf->addr);
+	}
+	buf->area = NULL;
+}
+
+static irqreturn_t lpass_pcm_mi2s_irq(int intrsrc, void *data)
+{
+	int dma_ch;
+	uint32_t ret = IRQ_NONE;
+	uint32_t has_xrun, pending;
+
+	struct snd_pcm_substream *substream = data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+
+	if (prtd) {
+		dma_ch = prtd->lpaif_info.dma_ch;
+	} else {
+		pr_debug("%s: received interrupt w/o runtime\n", __func__);
+		return IRQ_NONE;
+	}
+
+	pending = intrsrc & (LPAIF_UNDER_CH(dma_ch) | LPAIF_PER_CH(dma_ch) |
+				LPAIF_ERR_CH(dma_ch));
+
+	has_xrun = pending & LPAIF_UNDER_CH(dma_ch);
+
+	if (unlikely(has_xrun) && substream->runtime &&
+			snd_pcm_running(substream)) {
+		pr_debug("%s: xrun warning\n", __func__);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		pending &= ~LPAIF_UNDER_CH(dma_ch);
+		ret = IRQ_HANDLED;
+	}
+
+	if (pending & LPAIF_PER_CH(dma_ch)) {
+		if (++prtd->pcm_stream_info.period_index >= runtime->periods)
+			prtd->pcm_stream_info.period_index = 0;
+		snd_pcm_period_elapsed(substream);
+		pending &= ~LPAIF_PER_CH(dma_ch);
+		ret = IRQ_HANDLED;
+	}
+
+	if (pending & LPAIF_UNDER_CH(dma_ch)) {
+		snd_pcm_period_elapsed(substream);
+		pr_debug("%s: xrun warning\n", __func__);
+		ret = IRQ_HANDLED;
+	}
+
+	if (pending & LPAIF_ERR_CH(dma_ch)) {
+		pr_debug("%s: Bus access warning\n", __func__);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t lpass_pcm_mi2s_pointer(
+				struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+	snd_pcm_uframes_t offset;
+
+	offset = prtd->pcm_stream_info.period_index * runtime->period_size;
+
+	return offset >= (runtime->buffer_size) ? 0 : offset;
+}
+
+static int lpass_pcm_mi2s_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return dma_mmap_coherent(substream->pcm->card->dev, vma,
+		runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+}
+
+static int lpass_pcm_mi2s_prepare(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpaif_dai_dma_params dma_params;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+
+	if (!prtd) {
+		pr_err("%s: Error in getting runtime data\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * This is the case for under\over-run, we have already
+	 * configured the DMA registers for this stream
+	 */
+	if (prtd->pcm_stream_info.pcm_prepare_start)
+		return 0;
+
+	lpaif_dma_stop(prtd->lpaif_info.dma_ch);
+	prtd->pcm_stream_info.pcm_prepare_start = 1;
+	prtd->lpaif_info.lpa_if_dma_start = 0;
+
+	memset(&dma_params, 0, sizeof(dma_params));
+	dma_params.src_start = runtime->dma_addr;
+	dma_params.buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	dma_params.period_size = snd_pcm_lib_period_bytes(substream);
+	dma_params.channels = runtime->channels;
+	ret = lpaif_cfg_dma(prtd->lpaif_info.dma_ch, &dma_params,
+					prtd->pcm_stream_info.bit_width,
+					1 /*enable intr*/);
+	if (ret) {
+		pr_err("%s: Error in configuring DMA\n", __func__);
+		return -EINVAL;
+	}
+
+	lpaif_register_dma_irq_handler(prtd->lpaif_info.dma_ch,
+					lpass_pcm_mi2s_irq, substream);
+
+	return 0;
+}
+
+static int lpass_pcm_mi2s_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+
+	if (prtd) {
+		lpaif_dai_stop(prtd->lpaif_info.dma_ch);
+		lpaif_unregister_dma_irq_handler(prtd->lpaif_info.dma_ch);
+		kfree(prtd);
+	}
+
+	return 0;
+}
+
+static int lpass_pcm_mi2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			lpaif_cfg_i2s_playback(1, 0, LPAIF_MI2S);
+		} else {
+			pr_err("%s: Invalid stream direction\n", __func__);
+			ret = -EINVAL;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
+		} else {
+			pr_err("%s: Invalid stream direction\n", __func__);
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: Invalid trigger command given\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int lpass_pcm_mi2s_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lpass_runtime_data_t *prtd = runtime->private_data;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	prtd->pcm_stream_info.pcm_prepare_start = 0;
+	prtd->pcm_stream_info.period_index = 0;
+	return 0;
+}
+
+static int lpass_pcm_mi2s_open(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct lpass_runtime_data_t *prtd;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		runtime->dma_bytes =
+			lpass_pcm_hardware_playback.buffer_bytes_max;
+		snd_soc_set_runtime_hwparams(substream,
+				&lpass_pcm_hardware_playback);
+	} else {
+		pr_err("%s: Invalid stream direction\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+			SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		pr_err("%s: snd_pcm_hw_constraint_integer failed\n", __func__);
+		return -EINVAL;
+	}
+
+	prtd = kzalloc(sizeof(struct lpass_runtime_data_t), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	prtd->pcm_stream_info.pcm_prepare_start = 0;
+	prtd->lpaif_clk.is_bit_clk_enabled = 0;
+	prtd->lpaif_clk.is_osr_clk_enabled = 0;
+	prtd->lpaif_info.dma_ch = LPAIF_MI2S_DMA_RD_CH;
+
+	prtd->pcm_stream_info.substream = substream;
+	runtime->private_data = prtd;
+
+	return 0;
+}
+
+static struct snd_pcm_ops lpass_asoc_pcm_mi2s_ops = {
+	.open		= lpass_pcm_mi2s_open,
+	.hw_params	= lpass_pcm_mi2s_hw_params,
+	.trigger	= lpass_pcm_mi2s_trigger,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.close		= lpass_pcm_mi2s_close,
+	.prepare	= lpass_pcm_mi2s_prepare,
+	.mmap		= lpass_pcm_mi2s_mmap,
+	.pointer	= lpass_pcm_mi2s_pointer,
+};
+
+static void lpass_asoc_pcm_mi2s_free(struct snd_pcm *pcm)
+{
+	lpass_pcm_free_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int lpass_asoc_pcm_mi2s_new(struct snd_soc_pcm_runtime *prtd)
+{
+	struct snd_card *card = prtd->card->snd_card;
+	struct snd_pcm *pcm = prtd->pcm;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &card->dev->coherent_dma_mask;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ret = lpass_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_PLAYBACK);
+	} else {
+		pr_err("%s: Invalid stream direction\n", __func__);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct snd_soc_platform_driver lpass_asoc_pcm_mi2s_platform = {
+	.ops		= &lpass_asoc_pcm_mi2s_ops,
+	.pcm_new	= lpass_asoc_pcm_mi2s_new,
+	.pcm_free	= lpass_asoc_pcm_mi2s_free,
+};
+
+static int lpass_pcm_mi2s_driver_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = snd_soc_register_platform(&pdev->dev,
+			&lpass_asoc_pcm_mi2s_platform);
+	if (ret)
+		dev_err(&pdev->dev, "%s: Failed to register pcm device: %d\n",
+				__func__, ret);
+
+	return ret;
+}
+
+static int lpass_pcm_mi2s_driver_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id lpass_pcm_mi2s_dt_match[] = {
+	{ .compatible = "qcom,lpass-pcm-mi2s", },
+	{}
+};
+
+static struct platform_driver lpass_pcm_mi2s_driver = {
+	.driver	= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = lpass_pcm_mi2s_dt_match,
+	},
+	.probe	= lpass_pcm_mi2s_driver_probe,
+	.remove	= lpass_pcm_mi2s_driver_remove,
+};
+module_platform_driver(lpass_pcm_mi2s_driver);
+
+MODULE_DESCRIPTION("LPASS PCM MI2S Platform Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, lpass_pcm_mi2s_dt_match);
+MODULE_VERSION(DRV_VERSION);
diff --git a/sound/soc/qcom/lpass-pcm-mi2s.h b/sound/soc/qcom/lpass-pcm-mi2s.h
new file mode 100644
index 0000000000000000000000000000000000000000..edd43c4d419d1999babe20d4e6f58c77df38c6e1
--- /dev/null
+++ b/sound/soc/qcom/lpass-pcm-mi2s.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 _LPASS_PCM_MI2S_H
+#define _LPAS_PCM_MI2S_H
+
+struct lpass_pcm_stream_t {
+	uint8_t pcm_prepare_start;
+	uint32_t period_index;
+	struct snd_pcm_substream *substream;
+	uint32_t bit_width;
+};
+
+struct lpass_lpaif_t {
+	uint8_t lpa_if_dma_start;
+	uint8_t dma_ch;
+};
+
+struct lpass_lpaif_clk_t {
+	uint8_t is_bit_clk_enabled;
+	uint8_t is_osr_clk_enabled;
+};
+
+struct lpass_runtime_data_t {
+	struct lpass_pcm_stream_t pcm_stream_info;
+	struct lpass_lpaif_t lpaif_info;
+	struct lpass_lpaif_clk_t lpaif_clk;
+};
+
+#endif /* _LPASS_PCM_MI2S_H */
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (4 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-25 22:03   ` Mark Brown
  2014-11-19 18:52 ` [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers Kenneth Westfield
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Add machine driver for the IPQ806X LPASS SOC.

Change-Id: Ica26398fafd3098cdd12dcf45ebccec2ad820002
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/qcom/ipq806x.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 221 insertions(+)
 create mode 100644 sound/soc/qcom/ipq806x.c

diff --git a/sound/soc/qcom/ipq806x.c b/sound/soc/qcom/ipq806x.c
new file mode 100644
index 0000000000000000000000000000000000000000..e973bd71fa7fe117e73b1b562ec2758b3379e98f
--- /dev/null
+++ b/sound/soc/qcom/ipq806x.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/clk.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME	"ipq806x-asoc-snd"
+#define DRV_VERSION	"1.0"
+
+struct ipq806x_asoc_mach_data {
+	struct clk *ahbix_clk;
+	struct gpio_desc *dac_desc;
+};
+
+static struct snd_soc_dai_link ipq_snd_dai[] = {
+	/* Front end DAI Links */
+	{
+		.name		= "IPQ806x Media1",
+		.stream_name	= "MultiMedia1",
+		.cpu_dai_name	= "lpass-cpu-dai",
+		.platform_name	= "lpass-pcm-mi2s",
+		.codec_dai_name	= "snd-soc-dummy-dai",
+		.codec_name	= "snd-soc-dummy",
+	},
+};
+
+
+static int ipq806x_populate_dai_link_component_of_node(
+		struct snd_soc_card *card)
+{
+	int i, index;
+	int ret = 0;
+	struct device *cdev = card->dev;
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *np;
+
+	if (!cdev) {
+		pr_err("%s: Sound card device memory NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+			continue;
+
+		/* populate platform_of_node for snd card dai links */
+		if (dai_link[i].platform_name &&
+				!dai_link[i].platform_of_node) {
+			index = of_property_match_string(cdev->of_node,
+					"asoc-platform-names",
+					dai_link[i].platform_name);
+			if (index < 0) {
+				pr_err("%s: No match found for platform name: %s\n",
+						__func__,
+						dai_link[i].platform_name);
+				ret = index;
+				goto err;
+			}
+			np = of_parse_phandle(cdev->of_node, "asoc-platform",
+					index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+						__func__,
+						dai_link[i].platform_name,
+						index);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].platform_of_node = np;
+			dai_link[i].platform_name = NULL;
+		}
+
+		/* populate cpu_of_node for snd card dai links */
+		if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+			index = of_property_match_string(cdev->of_node,
+					"asoc-cpu-names",
+					dai_link[i].cpu_dai_name);
+			if (index >= 0) {
+				np = of_parse_phandle(cdev->of_node,
+						"asoc-cpu", index);
+				if (!np) {
+					pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+						__func__,
+						dai_link[i].cpu_dai_name);
+					ret = -ENODEV;
+					goto err;
+				}
+				dai_link[i].cpu_of_node = np;
+				dai_link[i].cpu_dai_name = NULL;
+			}
+		}
+	}
+
+err:
+	return ret;
+}
+
+static struct snd_soc_card snd_soc_card_ipq = {
+	.name = DRV_NAME,
+};
+
+static int ipq806x_asoc_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_card_ipq;
+	struct ipq806x_asoc_mach_data *pdata;
+	int ret;
+
+	pdata = devm_kzalloc(&pdev->dev,
+			sizeof(struct ipq806x_asoc_mach_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+				ret);
+		goto err;
+	}
+	card->dai_link	= ipq_snd_dai;
+	card->num_links	= ARRAY_SIZE(ipq_snd_dai);
+
+	ret = ipq806x_populate_dai_link_component_of_node(card);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+
+	pdata->dac_desc = devm_gpiod_get(&pdev->dev, "dac");
+	if (IS_ERR(pdata->dac_desc)) {
+		pr_err("unable to get dac-gpio\n");
+		ret = PTR_ERR(pdata->dac_desc);
+		goto err;
+	}
+	gpiod_direction_output(pdata->dac_desc, 0);
+	gpiod_set_value(pdata->dac_desc, 1);
+
+	pdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix_clk");
+	if (IS_ERR(pdata->ahbix_clk)) {
+		dev_err(&pdev->dev, "%s: Error in getting ahbix_clk\n",
+				__func__);
+		ret = PTR_ERR(pdata->ahbix_clk);
+		goto err;
+	}
+
+	clk_set_rate(pdata->ahbix_clk, 131072);
+	ret = clk_prepare_enable(pdata->ahbix_clk);
+	if (IS_ERR_VALUE(ret)) {
+		dev_err(&pdev->dev, "%s: Error in enabling ahbix_clk\n",
+				__func__);
+		goto err;
+	}
+
+	ret = snd_soc_register_card(card);
+	if (ret == -EPROBE_DEFER) {
+		goto err_clock;
+	} else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+				ret);
+		goto err_clock;
+	}
+
+	return 0;
+
+err_clock:
+	clk_disable_unprepare(pdata->ahbix_clk);
+
+err:
+	return ret;
+}
+
+static int ipq806x_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct ipq806x_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+	clk_disable_unprepare(pdata->ahbix_clk);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static const struct of_device_id ipq806x_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,ipq806x-snd-card", },
+	{},
+};
+
+static struct platform_driver ipq806x_asoc_machine_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = ipq806x_asoc_machine_of_match,
+	},
+	.probe = ipq806x_asoc_machine_probe,
+	.remove = ipq806x_asoc_machine_remove,
+};
+module_platform_driver(ipq806x_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC IPQ806x Machine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, ipq806x_asoc_machine_of_match);
+MODULE_VERSION(DRV_VERSION);
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (5 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-25 22:07   ` Mark Brown
  2014-11-19 18:52 ` [PATCH 8/9] ASoC: Allow for building " Kenneth Westfield
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Now all drivers are in place, allow them to build.

Change-Id: I16b9c4c2796bc1cf86aecae5fc500e685906fa8f
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/qcom/Kconfig  | 43 +++++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/Makefile | 11 +++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 sound/soc/qcom/Kconfig
 create mode 100644 sound/soc/qcom/Makefile

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..00cbdf8b29e68d32154a854a8bbfe2ac13b8b985
--- /dev/null
+++ b/sound/soc/qcom/Kconfig
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+config SND_SOC_CPU_DAI
+	bool
+	depends on SND_SOC_QCOM
+
+config SND_SOC_IPQ806X
+	bool "SoC Audio support for IPQ806x based platforms"
+	depends on SND_SOC_QCOM || ARCH_QCOM
+	select SND_SOC_PCM_I2S
+	select SND_SOC_CPU_DAI
+	select SND_SOC_LPAIF
+	help
+          Support for Qualcomm Technologies LPASS audio block in IPQ806X SOC-based systems.
+          Say Y if you want to use audio devices that will run on these boards.
+
+config SND_SOC_LPAIF
+	bool
+	depends on SND_SOC_QCOM
+
+config SND_SOC_PCM_I2S
+	bool
+	depends on SND_SOC_QCOM
+
+config SND_SOC_QCOM
+	bool "SoC Audio support for QCOM platforms"
+	help
+          Support for audio in Qualcomm Technologies SOC-based systems.
+          Say Y if you want to use audio devices that will run on IPQ806x and similar SOCs.
+
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0a74177df75f7aa5f7fbaab35da9b92689fa3d6a
--- /dev/null
+++ b/sound/soc/qcom/Makefile
@@ -0,0 +1,11 @@
+#QCOM SOUNDCARDS
+
+snd-soc-ipq806x-objs := ipq806x.o
+snd-soc-lpass-pcm-mi2s-objs := lpass-pcm-mi2s.o
+snd-soc-lpass-cpu-dai-objs := lpass-cpu-dai.o
+snd-soc-lpass-lpaif-objs := lpass-lpaif.o
+
+obj-$(CONFIG_SND_SOC_IPQ806X) += snd-soc-ipq806x.o
+obj-$(CONFIG_SND_SOC_PCM_I2S) += snd-soc-lpass-pcm-mi2s.o
+obj-$(CONFIG_SND_SOC_CPU_DAI) += snd-soc-lpass-cpu-dai.o
+obj-$(CONFIG_SND_SOC_LPAIF) += snd-soc-lpass-lpaif.o
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 8/9] ASoC: Allow for building QCOM drivers
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (6 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-19 18:52 ` [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware Kenneth Westfield
  2014-11-19 20:16 ` [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kumar Gala
  9 siblings, 0 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Allow for the QCOM LPASS drivers to build.

Change-Id: Ic870d8e9487cebe634c8bd2f1c7263c27237febc
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 sound/soc/Kconfig  | 1 +
 sound/soc/Makefile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7d5d6444a83737ffa3c01acbe1925de619e0390a..b98eebf0e30bba19ec24acc7f0e6ab155e5e602b 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/intel/Kconfig"
 source "sound/soc/mxs/Kconfig"
 source "sound/soc/pxa/Kconfig"
+source "sound/soc/qcom/Kconfig"
 source "sound/soc/rockchip/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/sh/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 865e090c80616024a552da5e33d2796b9be1dbbd..3d78a8d874dc98dd6219b33e3f2c100ef0334bfc 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= pxa/
+obj-$(CONFIG_SND_SOC)	+= qcom/
 obj-$(CONFIG_SND_SOC)	+= rockchip/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= sh/
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (7 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 8/9] ASoC: Allow for building " Kenneth Westfield
@ 2014-11-19 18:52 ` Kenneth Westfield
  2014-11-19 22:54   ` Courtney Cavin
  2014-11-25 22:08   ` Mark Brown
  2014-11-19 20:16 ` [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kumar Gala
  9 siblings, 2 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-19 18:52 UTC (permalink / raw)
  To: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List
  Cc: Kenneth Westfield, Mark Brown, Liam Girdwood, Takashi Iwai,
	Rob Herring, Greg KH, David Brown, Bryan Huntsman,
	Banajit Goswami, Patrick Lai

From: Kenneth Westfield <kwestfie@codeaurora.org>

Model the LPASS audio hardware for the IPQ806X.

Change-Id: Ide1aa0d09c23d4496aa9c40e3c9878a968261f11
Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
---
 arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
index 63b2146f563b541e4994697af5ee1bbb41a4abd1..0e5b3b625f0442964aa7fbbc993c6c818fe99041 100644
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -2,6 +2,7 @@
 
 #include "skeleton.dtsi"
 #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
+#include <dt-bindings/clock/qcom,lcc-ipq806x.h>
 #include <dt-bindings/soc/qcom,gsbi.h>
 
 / {
@@ -66,6 +67,38 @@
 		ranges;
 		compatible = "simple-bus";
 
+		sound {
+			compatible = "qcom,ipq806x-snd-card";
+			status = "disabled";
+			clocks = <&lcc AHBIX_CLK>;
+			clock-names = "ahbix_clk";
+			asoc-platform = <&pcm0>;
+			asoc-platform-names = "lpass-pcm-mi2s";
+			asoc-cpu = <&dai_mi2s>;
+			asoc-cpu-names = "lpass-cpu-dai";
+		};
+
+		lpass-lpaif {
+			compatible = "qcom,lpass-lpaif";
+			status = "disabled";
+			reg = <0x28100000 0x10000>;
+			reg-names = "lpass-lpaif-mem";
+			interrupts = <0 85 1>;
+			interrupt-names = "lpass-lpaif-irq";
+		};
+
+		dai_mi2s: lpass-cpu-dai {
+			compatible = "qcom,lpass-cpu-dai";
+			status = "disabled";
+			clocks = <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
+			clock-names = "mi2s_osr_clk", "mi2s_bit_clk";
+		};
+
+		pcm0: lpass-pcm-mi2s {
+			compatible = "qcom,lpass-pcm-mi2s";
+			status = "disabled";
+		};
+
 		qcom_pinmux: pinmux@800000 {
 			compatible = "qcom,ipq8064-pinctrl";
 			reg = <0x800000 0x4000>;
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
  2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
                   ` (8 preceding siblings ...)
  2014-11-19 18:52 ` [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware Kenneth Westfield
@ 2014-11-19 20:16 ` Kumar Gala
  2014-11-20  9:51   ` Mark Brown
  2014-11-21 20:24   ` [alsa-devel] " Kenneth Westfield
  9 siblings, 2 replies; 31+ messages in thread
From: Kumar Gala @ 2014-11-19 20:16 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Mark Brown, Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH,
	David Brown, Bryan Huntsman, Banajit Goswami, Patrick Lai


On Nov 19, 2014, at 12:52 PM, Kenneth Westfield <kwestfie@codeaurora.org> wrote:

> From: Kenneth Westfield <kwestfie@codeaurora.org>
> 
> This set of patches adds support for audio on the Qualcomm Technologies
> ipq806x SOC.
> 
> The ipq806x SOC has audio-related hardware blocks in its low-power audio
> subsystem (or LPASS).  One of the relevant blocks in the LPASS is its
> low-power audio interface (or LPAIF).  This encapsulates the MI2S port,
> which is what these drivers are configured to use.  The I2S pins are
> connected to an external DAC/amp chip.  In addition, a single GPIO is
> connected to the same DAC/amp, which gives the SOC enable/disable
> control.
> 
> The specific drivers added are:
> - a machine driver that handles the board-specific pins
> - a native driver that handles hardware access to the LPAIF
> - a CPU DAI driver for controlling the LPAIF block
> - a PCM MI2S platform driver
> 
> Corresponding additions to the device tree for the ipq806x and its
> documentation has also been added.  Also, as this is a new directory,
> the MAINTAINERS file has been updated as well.
> 
> - Ken

Some general comments, you shouldn’t have gerrit Change-Id’s in commit messages, the Kconfig/Makefile changes should be part of the patch that adds the code associated with them.

- k

-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver
  2014-11-19 18:52 ` [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver Kenneth Westfield
@ 2014-11-19 21:10   ` Pierre-Louis Bossart
  2014-11-25 22:01   ` Mark Brown
  1 sibling, 0 replies; 31+ messages in thread
From: Pierre-Louis Bossart @ 2014-11-19 21:10 UTC (permalink / raw)
  To: Kenneth Westfield, ALSA Mailing List, Device Tree Mailing List,
	MSM Mailing List
  Cc: Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On 11/19/14, 12:52 PM, Kenneth Westfield wrote:
> From: Kenneth Westfield <kwestfie@codeaurora.org>
>
> Add PCM platform driver for the LPASS I2S port.
>
> Change-Id: If6516fb615b16551817fd9248c1c704fe521fb32
> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
> ---
>   sound/soc/qcom/lpass-pcm-mi2s.c | 390 ++++++++++++++++++++++++++++++++++++++++
>   sound/soc/qcom/lpass-pcm-mi2s.h |  40 +++++
>   2 files changed, 430 insertions(+)
>   create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.c
>   create mode 100644 sound/soc/qcom/lpass-pcm-mi2s.h
>
> diff --git a/sound/soc/qcom/lpass-pcm-mi2s.c b/sound/soc/qcom/lpass-pcm-mi2s.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..110b088d156c6d06cbe054920fc63fd064a0ac45
> --- /dev/null
> +++ b/sound/soc/qcom/lpass-pcm-mi2s.c
> @@ -0,0 +1,390 @@
> +/*
> + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * 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/dma-mapping.h>
> +#include <sound/soc.h>
> +#include "lpass-lpaif.h"
> +#include "lpass-pcm-mi2s.h"
> +
> +#define DRV_NAME	"lpass-pcm-mi2s"
> +#define DRV_VERSION	"1.0"
> +
> +/* MI2S Hw params */
> +#define LPASS_MI2S_NO_OF_PERIODS	(65)
> +#define LPASS_MI2S_PERIOD_BYTES_MIN	(8064)
> +#define LPASS_MI2S_BUFF_SIZE		(LPASS_MI2S_PERIOD_BYTES_MIN * \
> +					LPASS_MI2S_NO_OF_PERIODS)
> +
> +static struct snd_pcm_hardware lpass_pcm_hardware_playback = {
> +	.info			=	SNDRV_PCM_INFO_MMAP |
> +					SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +					SNDRV_PCM_INFO_MMAP_VALID |
> +					SNDRV_PCM_INFO_INTERLEAVED |
> +					SNDRV_PCM_INFO_PAUSE |
> +					SNDRV_PCM_INFO_RESUME,
> +	.formats		=	SNDRV_PCM_FMTBIT_S16 |
> +					SNDRV_PCM_FMTBIT_S24 |
> +					SNDRV_PCM_FMTBIT_S32,
> +	.rates			=	SNDRV_PCM_RATE_8000_192000,
> +	.rate_min		=	8000,
> +	.rate_max		=	192000,
> +	.channels_min		=	2,
> +	.channels_max		=	8,
> +	.buffer_bytes_max	=	LPASS_MI2S_BUFF_SIZE,
> +	.period_bytes_max	=	(LPASS_MI2S_BUFF_SIZE) / 2,
> +	.period_bytes_min	=	LPASS_MI2S_PERIOD_BYTES_MIN,
> +	.periods_min		=	LPASS_MI2S_NO_OF_PERIODS,
> +	.periods_max		=	LPASS_MI2S_NO_OF_PERIODS,

Did you really mean 65 periods min and max? .periods_min is typically 
1..4, 16 in rare cases.

> +	.fifo_size		=	0,
> +};
> +
> +static int lpass_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
> +{
> +	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
> +	struct snd_dma_buffer *buf = &substream->dma_buffer;
> +	size_t size;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		size = lpass_pcm_hardware_playback.buffer_bytes_max;
> +	} else {
> +		pr_err("%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	buf->dev.type = SNDRV_DMA_TYPE_DEV;
> +	buf->dev.dev = pcm->card->dev;
> +	buf->private_data = NULL;
> +	buf->area = dma_alloc_coherent(pcm->card->dev, size,
> +					&buf->addr, GFP_KERNEL);
> +	if (!buf->area) {
> +		pr_err("%s: Could not allocate DMA buffer\n", __func__);
> +		return -ENOMEM;
> +	}
> +	buf->bytes = size;
> +
> +	return 0;
> +}
> +
> +static void lpass_pcm_free_dma_buffer(struct snd_pcm *pcm, int stream)
> +{
> +	struct snd_pcm_substream *substream;
> +	struct snd_dma_buffer *buf;
> +
> +	substream = pcm->streams[stream].substream;
> +	buf = &substream->dma_buffer;
> +	if (buf->area) {
> +		dma_free_coherent(pcm->card->dev, buf->bytes,
> +					buf->area, buf->addr);
> +	}
> +	buf->area = NULL;
> +}
> +
> +static irqreturn_t lpass_pcm_mi2s_irq(int intrsrc, void *data)
> +{
> +	int dma_ch;
> +	uint32_t ret = IRQ_NONE;
> +	uint32_t has_xrun, pending;
> +
> +	struct snd_pcm_substream *substream = data;
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	if (prtd) {
> +		dma_ch = prtd->lpaif_info.dma_ch;
> +	} else {
> +		pr_debug("%s: received interrupt w/o runtime\n", __func__);
> +		return IRQ_NONE;
> +	}
> +
> +	pending = intrsrc & (LPAIF_UNDER_CH(dma_ch) | LPAIF_PER_CH(dma_ch) |
> +				LPAIF_ERR_CH(dma_ch));
> +
> +	has_xrun = pending & LPAIF_UNDER_CH(dma_ch);
> +
> +	if (unlikely(has_xrun) && substream->runtime &&
> +			snd_pcm_running(substream)) {
> +		pr_debug("%s: xrun warning\n", __func__);
> +		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
> +		pending &= ~LPAIF_UNDER_CH(dma_ch);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	if (pending & LPAIF_PER_CH(dma_ch)) {
> +		if (++prtd->pcm_stream_info.period_index >= runtime->periods)
> +			prtd->pcm_stream_info.period_index = 0;
> +		snd_pcm_period_elapsed(substream);
> +		pending &= ~LPAIF_PER_CH(dma_ch);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	if (pending & LPAIF_UNDER_CH(dma_ch)) {
> +		snd_pcm_period_elapsed(substream);
> +		pr_debug("%s: xrun warning\n", __func__);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	if (pending & LPAIF_ERR_CH(dma_ch)) {
> +		pr_debug("%s: Bus access warning\n", __func__);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t lpass_pcm_mi2s_pointer(
> +				struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +	snd_pcm_uframes_t offset;
> +
> +	offset = prtd->pcm_stream_info.period_index * runtime->period_size;
> +
> +	return offset >= (runtime->buffer_size) ? 0 : offset;
> +}
> +
> +static int lpass_pcm_mi2s_mmap(struct snd_pcm_substream *substream,
> +				struct vm_area_struct *vma)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +
> +	return dma_mmap_coherent(substream->pcm->card->dev, vma,
> +		runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
> +}
> +
> +static int lpass_pcm_mi2s_prepare(struct snd_pcm_substream *substream)
> +{
> +	int ret;
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpaif_dai_dma_params dma_params;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	if (!prtd) {
> +		pr_err("%s: Error in getting runtime data\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * This is the case for under\over-run, we have already
> +	 * configured the DMA registers for this stream
> +	 */
> +	if (prtd->pcm_stream_info.pcm_prepare_start)
> +		return 0;
> +
> +	lpaif_dma_stop(prtd->lpaif_info.dma_ch);
> +	prtd->pcm_stream_info.pcm_prepare_start = 1;
> +	prtd->lpaif_info.lpa_if_dma_start = 0;
> +
> +	memset(&dma_params, 0, sizeof(dma_params));
> +	dma_params.src_start = runtime->dma_addr;
> +	dma_params.buffer_size = snd_pcm_lib_buffer_bytes(substream);
> +	dma_params.period_size = snd_pcm_lib_period_bytes(substream);
> +	dma_params.channels = runtime->channels;
> +	ret = lpaif_cfg_dma(prtd->lpaif_info.dma_ch, &dma_params,
> +					prtd->pcm_stream_info.bit_width,
> +					1 /*enable intr*/);
> +	if (ret) {
> +		pr_err("%s: Error in configuring DMA\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	lpaif_register_dma_irq_handler(prtd->lpaif_info.dma_ch,
> +					lpass_pcm_mi2s_irq, substream);
> +
> +	return 0;
> +}
> +
> +static int lpass_pcm_mi2s_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	if (prtd) {
> +		lpaif_dai_stop(prtd->lpaif_info.dma_ch);
> +		lpaif_unregister_dma_irq_handler(prtd->lpaif_info.dma_ch);
> +		kfree(prtd);
> +	}
> +
> +	return 0;
> +}
> +
> +static int lpass_pcm_mi2s_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +			lpaif_cfg_i2s_playback(1, 0, LPAIF_MI2S);
> +		} else {
> +			pr_err("%s: Invalid stream direction\n", __func__);
> +			ret = -EINVAL;
> +		}
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +			lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
> +		} else {
> +			pr_err("%s: Invalid stream direction\n", __func__);
> +			ret = -EINVAL;
> +		}
> +		break;
> +	default:
> +		pr_err("%s: Invalid trigger command given\n", __func__);
> +		ret = -EINVAL;
> +		break;
> +	}
> +	return ret;
> +}
> +
> +static int lpass_pcm_mi2s_hw_params(struct snd_pcm_substream *substream,
> +					struct snd_pcm_hw_params *params)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> +	prtd->pcm_stream_info.pcm_prepare_start = 0;
> +	prtd->pcm_stream_info.period_index = 0;
> +	return 0;
> +}
> +
> +static int lpass_pcm_mi2s_open(struct snd_pcm_substream *substream)
> +{
> +	int ret;
> +	struct lpass_runtime_data_t *prtd;
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		runtime->dma_bytes =
> +			lpass_pcm_hardware_playback.buffer_bytes_max;
> +		snd_soc_set_runtime_hwparams(substream,
> +				&lpass_pcm_hardware_playback);
> +	} else {
> +		pr_err("%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime,
> +			SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0) {
> +		pr_err("%s: snd_pcm_hw_constraint_integer failed\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	prtd = kzalloc(sizeof(struct lpass_runtime_data_t), GFP_KERNEL);
> +	if (prtd == NULL)
> +		return -ENOMEM;
> +
> +	prtd->pcm_stream_info.pcm_prepare_start = 0;
> +	prtd->lpaif_clk.is_bit_clk_enabled = 0;
> +	prtd->lpaif_clk.is_osr_clk_enabled = 0;
> +	prtd->lpaif_info.dma_ch = LPAIF_MI2S_DMA_RD_CH;
> +
> +	prtd->pcm_stream_info.substream = substream;
> +	runtime->private_data = prtd;
> +
> +	return 0;
> +}
> +
> +static struct snd_pcm_ops lpass_asoc_pcm_mi2s_ops = {
> +	.open		= lpass_pcm_mi2s_open,
> +	.hw_params	= lpass_pcm_mi2s_hw_params,
> +	.trigger	= lpass_pcm_mi2s_trigger,
> +	.ioctl		= snd_pcm_lib_ioctl,
> +	.close		= lpass_pcm_mi2s_close,
> +	.prepare	= lpass_pcm_mi2s_prepare,
> +	.mmap		= lpass_pcm_mi2s_mmap,
> +	.pointer	= lpass_pcm_mi2s_pointer,
> +};
> +
> +static void lpass_asoc_pcm_mi2s_free(struct snd_pcm *pcm)
> +{
> +	lpass_pcm_free_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
> +}
> +
> +static int lpass_asoc_pcm_mi2s_new(struct snd_soc_pcm_runtime *prtd)
> +{
> +	struct snd_card *card = prtd->card->snd_card;
> +	struct snd_pcm *pcm = prtd->pcm;
> +	int ret = 0;
> +
> +	if (!card->dev->coherent_dma_mask)
> +		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +	if (!card->dev->dma_mask)
> +		card->dev->dma_mask = &card->dev->coherent_dma_mask;
> +
> +	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
> +		ret = lpass_pcm_preallocate_dma_buffer(pcm,
> +				SNDRV_PCM_STREAM_PLAYBACK);
> +	} else {
> +		pr_err("%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static struct snd_soc_platform_driver lpass_asoc_pcm_mi2s_platform = {
> +	.ops		= &lpass_asoc_pcm_mi2s_ops,
> +	.pcm_new	= lpass_asoc_pcm_mi2s_new,
> +	.pcm_free	= lpass_asoc_pcm_mi2s_free,
> +};
> +
> +static int lpass_pcm_mi2s_driver_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = snd_soc_register_platform(&pdev->dev,
> +			&lpass_asoc_pcm_mi2s_platform);
> +	if (ret)
> +		dev_err(&pdev->dev, "%s: Failed to register pcm device: %d\n",
> +				__func__, ret);
> +
> +	return ret;
> +}
> +
> +static int lpass_pcm_mi2s_driver_remove(struct platform_device *pdev)
> +{
> +	snd_soc_unregister_platform(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id lpass_pcm_mi2s_dt_match[] = {
> +	{ .compatible = "qcom,lpass-pcm-mi2s", },
> +	{}
> +};
> +
> +static struct platform_driver lpass_pcm_mi2s_driver = {
> +	.driver	= {
> +		.name	= DRV_NAME,
> +		.owner	= THIS_MODULE,
> +		.of_match_table = lpass_pcm_mi2s_dt_match,
> +	},
> +	.probe	= lpass_pcm_mi2s_driver_probe,
> +	.remove	= lpass_pcm_mi2s_driver_remove,
> +};
> +module_platform_driver(lpass_pcm_mi2s_driver);
> +
> +MODULE_DESCRIPTION("LPASS PCM MI2S Platform Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_DEVICE_TABLE(of, lpass_pcm_mi2s_dt_match);
> +MODULE_VERSION(DRV_VERSION);
> diff --git a/sound/soc/qcom/lpass-pcm-mi2s.h b/sound/soc/qcom/lpass-pcm-mi2s.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..edd43c4d419d1999babe20d4e6f58c77df38c6e1
> --- /dev/null
> +++ b/sound/soc/qcom/lpass-pcm-mi2s.h
> @@ -0,0 +1,40 @@
> +/*
> + * Copyright (c) 2014 The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * 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 _LPASS_PCM_MI2S_H
> +#define _LPAS_PCM_MI2S_H
> +
> +struct lpass_pcm_stream_t {
> +	uint8_t pcm_prepare_start;
> +	uint32_t period_index;
> +	struct snd_pcm_substream *substream;
> +	uint32_t bit_width;
> +};
> +
> +struct lpass_lpaif_t {
> +	uint8_t lpa_if_dma_start;
> +	uint8_t dma_ch;
> +};
> +
> +struct lpass_lpaif_clk_t {
> +	uint8_t is_bit_clk_enabled;
> +	uint8_t is_osr_clk_enabled;
> +};
> +
> +struct lpass_runtime_data_t {
> +	struct lpass_pcm_stream_t pcm_stream_info;
> +	struct lpass_lpaif_t lpaif_info;
> +	struct lpass_lpaif_clk_t lpaif_clk;
> +};
> +
> +#endif /* _LPASS_PCM_MI2S_H */
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
@ 2014-11-19 21:17   ` Pierre-Louis Bossart
  2014-11-21 20:23     ` [alsa-devel] " Kenneth Westfield
  2014-11-20  0:20   ` Courtney Cavin
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 31+ messages in thread
From: Pierre-Louis Bossart @ 2014-11-19 21:17 UTC (permalink / raw)
  To: Kenneth Westfield, ALSA Mailing List, Device Tree Mailing List,
	MSM Mailing List
  Cc: Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On 11/19/14, 12:52 PM, Kenneth Westfield wrote:
> From: Kenneth Westfield <kwestfie@codeaurora.org>
>
> Add the CPU DAI driver for the QCOM LPASS SOC.
>
> Change-Id: I64ac4407dd32bb9a3066d4b7427292002eaf5d14
> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
> ---
>   sound/soc/qcom/lpass-cpu-dai.c | 307 +++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 307 insertions(+)
>   create mode 100644 sound/soc/qcom/lpass-cpu-dai.c
>
> diff --git a/sound/soc/qcom/lpass-cpu-dai.c b/sound/soc/qcom/lpass-cpu-dai.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..0693e761e01fd416f52684977bcfaf1579dc259c
> --- /dev/null
> +++ b/sound/soc/qcom/lpass-cpu-dai.c
> @@ -0,0 +1,307 @@
> +/*
> + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * 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/clk.h>
> +#include <sound/soc.h>
> +#include <sound/pcm_params.h>
> +#include "lpass-lpaif.h"
> +#include "lpass-pcm-mi2s.h"
> +
> +#define DRV_NAME	"lpass-cpu-dai"
> +#define DRV_VERSION	"1.0"
> +
> +#define LPASS_INVALID	(-1)
> +
> +struct mi2s_hw_params {
> +	uint8_t channels;
> +	uint32_t freq;
> +	uint8_t bit_width;
> +};
> +
> +static struct clk *lpaif_mi2s_bit_clk;
> +static struct clk *lpaif_mi2s_osr_clk;
> +static struct mi2s_hw_params mi2s_params;
> +
> +/*
> + * Returns the OSR to BIT clock divider that would be used with the given
> + * stream characteristics.
> + */
> +static uint32_t lpass_cpu_get_bit_div(uint32_t samp_freq, uint32_t bit_width,
> +							uint32_t channels)
> +{
> +	if (channels == 8) {
> +		if (bit_width == 24 &&
> +			((samp_freq != 176400) &&
> +			(samp_freq != 192000)))
> +			return 2;
> +		return 1;
> +	} else if (channels == 6) {
> +		if ((bit_width == 32 && ((samp_freq == 176400) ||
> +			(samp_freq == 192000))) || (bit_width == 24 &&
> +			(samp_freq == 192000)))
> +			return 1;
> +		else
> +			return 2;
> +	}
> +
> +	switch (samp_freq) {
> +	case 8000:
> +	case 16000:
> +	case 32000:
> +	case 64000:
> +		if (bit_width == 16)
> +			return 4;

Does this test have any value? the behavior is identical in all cases.

> +		break;
> +	case 48000:
> +		if (bit_width == 24)
> +			return 4;

same.

> +		break;
> +	case 96000:
> +	case 192000:
> +		return 4;
> +	case 176400:
> +		return 2;
> +	case 11025:
> +	case 22050:
> +	case 44100:
> +	case 88200:
> +	default:
> +		pr_err("%s: Invalid PCM format given\n", __func__);
> +		return LPASS_INVALID;
> +	}
> +
> +	return 4;
> +}
> +
> +static int lpass_cpu_mi2s_hw_params(struct snd_pcm_substream *substream,
> +					struct snd_pcm_hw_params *params,
> +					struct snd_soc_dai *dai)
> +{
> +	uint32_t ret = 0;
> +	uint32_t bit_act;
> +	uint16_t bit_div;
> +	uint32_t bit_width = params_format(params);
> +	uint32_t channels = params_channels(params);
> +	uint32_t rate = params_rate(params);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +	struct mi2s_hw_params curr_params;
> +
> +	bit_act = snd_pcm_format_width(bit_width);
> +	if (bit_act == LPASS_INVALID) {
> +		dev_err(dai->dev, "%s: Invalid bit width given\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	prtd->pcm_stream_info.bit_width = bit_act;
> +	curr_params.freq = rate;
> +	curr_params.channels = channels;
> +	curr_params.bit_width = bit_act;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		/* disable SPKR to make sure it will start in sane state */
> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
> +
> +		/*
> +		 * Set channel info, it will take effect only if SPKR is
> +		 * enabled
> +		 */
> +		ret = lpaif_cfg_mi2s_playback_hwparams_channels(channels,
> +						LPAIF_MI2S, bit_act);
> +	} else {
> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* Make sure our channel setting was success */
> +	if (ret) {
> +		/* shutdown would be called where we restore stuff */
> +		dev_err(dai->dev, "%s: Channel setting unsuccessful\n",
> +				__func__);
> +		return -EINVAL;
> +	}
> +
> +	mi2s_params.freq = rate;
> +	mi2s_params.channels = channels;
> +	mi2s_params.bit_width = bit_act;
> +
> +	ret = lpaif_cfg_mi2s_hwparams_bit_width(bit_width, LPAIF_MI2S);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: Could not set bit width in HW\n",
> +				__func__);
> +		return -EINVAL;
> +	}
> +
> +	bit_div = lpass_cpu_get_bit_div(rate, bit_act, channels);
> +	if (bit_div == LPASS_INVALID) {
> +		dev_err(dai->dev, "%s: Could not get bit clk divider\n",
> +				__func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_set_rate(lpaif_mi2s_osr_clk,
> +		(rate * bit_act * channels * bit_div));
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in setting mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(lpaif_mi2s_osr_clk);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in enabling mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	prtd->lpaif_clk.is_osr_clk_enabled = 1;
> +
> +	ret = clk_set_rate(lpaif_mi2s_bit_clk, rate * bit_act * channels);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in setting mi2s bit clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(lpaif_mi2s_bit_clk);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in enabling mi2s bit clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	prtd->lpaif_clk.is_bit_clk_enabled = 1;
> +
> +	return 0;
> +}
> +
> +static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}
> +
> +static int lpass_cpu_mi2s_startup(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +
> +	lpaif_mi2s_osr_clk = clk_get(dai->dev, "mi2s_osr_clk");
> +	if (IS_ERR(lpaif_mi2s_osr_clk)) {
> +		dev_err(dai->dev, "%s: Error in getting mi2s_osr_clk\n",
> +				__func__);
> +		return PTR_ERR(lpaif_mi2s_osr_clk);
> +	}
> +
> +	lpaif_mi2s_bit_clk = clk_get(dai->dev, "mi2s_bit_clk");
> +	if (IS_ERR(lpaif_mi2s_bit_clk)) {
> +		dev_err(dai->dev, "%s: Error in getting mi2s_bit_clk\n",
> +				__func__);
> +		return PTR_ERR(lpaif_mi2s_bit_clk);
> +	}
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
> +	} else {
> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void lpass_cpu_mi2s_shutdown(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	if (prtd->lpaif_clk.is_osr_clk_enabled)
> +		clk_disable_unprepare(lpaif_mi2s_osr_clk);
> +	clk_put(lpaif_mi2s_osr_clk);
> +
> +	if (prtd->lpaif_clk.is_bit_clk_enabled)
> +		clk_disable_unprepare(lpaif_mi2s_bit_clk);
> +	clk_put(lpaif_mi2s_bit_clk);
> +}
> +
> +static int lpass_cpu_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> +	return 0;
> +}
> +
> +static struct snd_soc_dai_ops lpass_cpu_mi2s_ops = {
> +	.startup	= lpass_cpu_mi2s_startup,
> +	.prepare	= lpass_cpu_mi2s_prepare,
> +	.hw_params	= lpass_cpu_mi2s_hw_params,
> +	.shutdown	= lpass_cpu_mi2s_shutdown,
> +	.set_fmt	= lpass_cpu_mi2s_set_fmt,
> +};
> +
> +static struct snd_soc_dai_driver lpass_cpu_dais[] = {
> +	{
> +		.playback = {
> +			.rates		= SNDRV_PCM_RATE_8000_192000,
> +			.formats	= SNDRV_PCM_FMTBIT_S16 |
> +					SNDRV_PCM_FMTBIT_S24 |
> +					SNDRV_PCM_FMTBIT_S32,
> +			.channels_min	= 2,
> +			.channels_max	= 2,
> +			.rate_min	= 8000,
> +			.rate_max	= 192000,
> +		},
> +		.ops    = &lpass_cpu_mi2s_ops,
> +		.name = "lpass-mi2s-dai"
> +	},
> +};
> +
> +static const struct snd_soc_component_driver lpass_cpu_component = {
> +	.name           = DRV_NAME,
> +};
> +
> +static int lpass_cpu_dai_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = snd_soc_register_component(&pdev->dev, &lpass_cpu_component,
> +			lpass_cpu_dais, ARRAY_SIZE(lpass_cpu_dais));
> +	if (ret)
> +		dev_err(&pdev->dev, "%s: error registering soc dais\n",
> +				__func__);
> +
> +	return ret;
> +}
> +
> +static int lpass_cpu_dai_remove(struct platform_device *pdev)
> +{
> +	snd_soc_unregister_component(&pdev->dev);
> +	return 0;
> +}
> +
> +static const struct of_device_id lpass_cpu_dai_dt_match[] = {
> +	{.compatible = "qcom,lpass-cpu-dai"},
> +	{}
> +};
> +
> +static struct platform_driver lpass_cpu_dai_driver = {
> +	.probe	= lpass_cpu_dai_probe,
> +	.remove	= lpass_cpu_dai_remove,
> +	.driver	= {
> +		.name	= DRV_NAME,
> +		.owner	= THIS_MODULE,
> +		.of_match_table = lpass_cpu_dai_dt_match,
> +	},
> +};
> +module_platform_driver(lpass_cpu_dai_driver);
> +
> +MODULE_DESCRIPTION("QCOM LPASS CPU DAI DRIVER");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_DEVICE_TABLE(of, lpass_cpu_dai_dt_match);
> +MODULE_VERSION(DRV_VERSION);
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware
  2014-11-19 18:52 ` [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware Kenneth Westfield
@ 2014-11-19 22:54   ` Courtney Cavin
  2014-11-21 20:17     ` [alsa-devel] " Kenneth Westfield
  2014-11-25 22:08   ` Mark Brown
  1 sibling, 1 reply; 31+ messages in thread
From: Courtney Cavin @ 2014-11-19 22:54 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Mark Brown, Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH,
	David Brown, Bryan Huntsman, Banajit Goswami, Patrick Lai

On Wed, Nov 19, 2014 at 07:52:49PM +0100, Kenneth Westfield wrote:
> From: Kenneth Westfield <kwestfie@codeaurora.org>
> 
> Model the LPASS audio hardware for the IPQ806X.
> 
> Change-Id: Ide1aa0d09c23d4496aa9c40e3c9878a968261f11

As Kumar mentioned, please exclude this.

> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>

Typically, the order of these SoB should match some sort of chain of delivery:
 - The first should be the author of the patch
 - The last should match the email source (you) <-- doesn't seem to be the case

> ---
>  arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> index 63b2146f563b541e4994697af5ee1bbb41a4abd1..0e5b3b625f0442964aa7fbbc993c6c818fe99041 100644
> --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
> @@ -2,6 +2,7 @@
>  
>  #include "skeleton.dtsi"
>  #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
> +#include <dt-bindings/clock/qcom,lcc-ipq806x.h>

Neither this file nor an associated clock controller driver exists in
mainline.  Is there some other series this depends on?

-Courtney

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
  2014-11-19 21:17   ` Pierre-Louis Bossart
@ 2014-11-20  0:20   ` Courtney Cavin
  2014-11-20 12:36   ` [alsa-devel] " Lars-Peter Clausen
  2014-11-25 21:53   ` Mark Brown
  3 siblings, 0 replies; 31+ messages in thread
From: Courtney Cavin @ 2014-11-20  0:20 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Mark Brown, Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH,
	David Brown, Bryan Huntsman, Banajit Goswami, Patrick Lai

On Wed, Nov 19, 2014 at 07:52:44PM +0100, Kenneth Westfield wrote:
> From: Kenneth Westfield <kwestfie@codeaurora.org>
> 
> Add the CPU DAI driver for the QCOM LPASS SOC.
> 
> Change-Id: I64ac4407dd32bb9a3066d4b7427292002eaf5d14
> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
> ---
[...]
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <sound/soc.h>
> +#include <sound/pcm_params.h>
> +#include "lpass-lpaif.h"
> +#include "lpass-pcm-mi2s.h"

This header and the associated structures are not added until 5/9:
"ASoC: ipq806x: Add I2S PCM platform driver"...

> +
> +#define DRV_NAME	"lpass-cpu-dai"
> +#define DRV_VERSION	"1.0"
> +
> +#define LPASS_INVALID	(-1)
> +
> +struct mi2s_hw_params {
> +	uint8_t channels;
> +	uint32_t freq;
> +	uint8_t bit_width;
> +};

This struct, the static global instance of it below ('mi2s_params'), and
the additional use of it in lpass_cpu_mi2s_hw_params() ('curr_params')
are only ever written, never read.

> +
> +static struct clk *lpaif_mi2s_bit_clk;
> +static struct clk *lpaif_mi2s_osr_clk;

It would seem more logical to me to put these in allocated private driver
data for the DAI, managed from (struct snd_soc_dai_driver).probe/remove.

> +static struct mi2s_hw_params mi2s_params;
[...]
> +static int lpass_cpu_mi2s_hw_params(struct snd_pcm_substream *substream,
> +					struct snd_pcm_hw_params *params,
> +					struct snd_soc_dai *dai)
> +{
[...]
> +	ret = clk_set_rate(lpaif_mi2s_osr_clk,
> +		(rate * bit_act * channels * bit_div));
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in setting mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(lpaif_mi2s_osr_clk);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in enabling mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	prtd->lpaif_clk.is_osr_clk_enabled = 1;
> +
> +	ret = clk_set_rate(lpaif_mi2s_bit_clk, rate * bit_act * channels);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in setting mi2s bit clk\n",
> +				__func__);
> +		return ret;

clk_disable_unprepare(lpaif_mi2s_osr_clk)?

> +	}
> +	ret = clk_prepare_enable(lpaif_mi2s_bit_clk);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in enabling mi2s bit clk\n",
> +				__func__);
> +		return ret;

clk_disable_unprepare(lpaif_mi2s_bit_clk)?
clk_disable_unprepare(lpaif_mi2s_osr_clk)?

> +	}
> +	prtd->lpaif_clk.is_bit_clk_enabled = 1;
> +
> +	return 0;
> +}
> +
> +static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}

Isn't this ((struct snd_soc_dai_ops).prepare) optional?

> +
> +static int lpass_cpu_mi2s_startup(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +
> +	lpaif_mi2s_osr_clk = clk_get(dai->dev, "mi2s_osr_clk");
> +	if (IS_ERR(lpaif_mi2s_osr_clk)) {
> +		dev_err(dai->dev, "%s: Error in getting mi2s_osr_clk\n",
> +				__func__);
> +		return PTR_ERR(lpaif_mi2s_osr_clk);
> +	}
> +
> +	lpaif_mi2s_bit_clk = clk_get(dai->dev, "mi2s_bit_clk");
> +	if (IS_ERR(lpaif_mi2s_bit_clk)) {
> +		dev_err(dai->dev, "%s: Error in getting mi2s_bit_clk\n",
> +				__func__);
> +		return PTR_ERR(lpaif_mi2s_bit_clk);

clk_put(lpaif_mi2s_osr_clk)?

> +	}
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
> +	} else {
> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
clk_put(lpaif_mi2s_bit_clk)?
clk_put(lpaif_mi2s_osr_clk)?
> +	}
> +
> +	return 0;
> +}
> +
> +static void lpass_cpu_mi2s_shutdown(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	if (prtd->lpaif_clk.is_osr_clk_enabled)
> +		clk_disable_unprepare(lpaif_mi2s_osr_clk);

This behavior is a bit odd.  If you clk_prepare_enable() the clocks in
.hw_params, shouldn't you clk_disable_unprepare() in .hw_free?  Then you
wouldn't need these booleans, or the associated lpaif_clk struct.

> +	clk_put(lpaif_mi2s_osr_clk);
> +
> +	if (prtd->lpaif_clk.is_bit_clk_enabled)
> +		clk_disable_unprepare(lpaif_mi2s_bit_clk);
> +	clk_put(lpaif_mi2s_bit_clk);
> +}

-Courtney

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
  2014-11-19 20:16 ` [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kumar Gala
@ 2014-11-20  9:51   ` Mark Brown
  2014-11-21 20:24   ` [alsa-devel] " Kenneth Westfield
  1 sibling, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-20  9:51 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Kenneth Westfield, ALSA Mailing List, Device Tree Mailing List,
	MSM Mailing List, Liam Girdwood, Takashi Iwai, Rob Herring,
	Greg KH, David Brown, Bryan Huntsman, Banajit Goswami,
	Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 533 bytes --]

On Wed, Nov 19, 2014 at 02:16:10PM -0600, Kumar Gala wrote:

Kumar, as I keep saying please fix your mailer to word wrap within
paragraphs.

> Some general comments, you shouldn’t have gerrit Change-Id’s in commit

Indeed.

> messages, the Kconfig/Makefile changes should be part of the patch
> that adds the code associated with them.

No, it's totally fine for a large new driver like this that's split over
many commits to add the Kconfig and Makefile at the end - there is no
value in trying to split them up.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver
  2014-11-19 18:52 ` [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver Kenneth Westfield
@ 2014-11-20 12:32   ` Lars-Peter Clausen
  2014-11-21 20:19     ` Kenneth Westfield
  2014-11-25 21:44   ` Mark Brown
  1 sibling, 1 reply; 31+ messages in thread
From: Lars-Peter Clausen @ 2014-11-20 12:32 UTC (permalink / raw)
  To: Kenneth Westfield, ALSA Mailing List, Device Tree Mailing List,
	MSM Mailing List
  Cc: Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On 11/19/2014 07:52 PM, Kenneth Westfield wrote:
> From: Kenneth Westfield <kwestfie@codeaurora.org>
>
> Add the native LPAIF driver for LPASS block in Qualcomm
> Technologies SoCs.
>
> Change-Id: I0f06f73a1267d7721209e58ce18e0d4897001141
> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
> ---
>   sound/soc/qcom/lpass-lpaif.c | 488 +++++++++++++++++++++++++++++++++++++++++++
>   sound/soc/qcom/lpass-lpaif.h | 181 ++++++++++++++++
>   2 files changed, 669 insertions(+)
>   create mode 100644 sound/soc/qcom/lpass-lpaif.c
>   create mode 100644 sound/soc/qcom/lpass-lpaif.h
>
> diff --git a/sound/soc/qcom/lpass-lpaif.c b/sound/soc/qcom/lpass-lpaif.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..e62843fe9bc4c63c3c7c119a9f076085b16a56b3
> --- /dev/null
> +++ b/sound/soc/qcom/lpass-lpaif.c
> @@ -0,0 +1,488 @@
> +/*
> + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * 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/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/clk.h>
> +#include <linux/types.h>
> +#include <sound/soc.h>
> +#include "lpass-lpaif.h"
> +
> +#define DRV_NAME	"lpass-lpaif"
> +#define DRV_VERSION	"1.0"
> +
> +struct lpaif_dai_baseinfo {
> +	void __iomem *base;
> +};
> +
> +struct lpaif_dai_drv {
> +	unsigned char *buffer;
> +	dma_addr_t buffer_phys;
> +	int channels;
> +	irqreturn_t (*callback)(int intrsrc, void *private_data);
> +	void *private_data;
> +	int in_use;
> +	unsigned int buffer_len;
> +	unsigned int period_len;
> +	unsigned int master_mode;
> +};
> +
> +static struct lpaif_dai_baseinfo lpaif_dai_info;
> +static struct lpaif_dai_drv *lpaif_dai[LPAIF_MAX_CHANNELS];
> +static spinlock_t lpaif_lock;
> +static struct resource *lpaif_irq;

Please don't use global state for device drivers. Make the state device 
instance specific.

> +
[...]
> +
> +static int lpaif_dai_probe(struct platform_device *pdev)
> +{
> +	uint8_t i;
> +	int32_t rc;
> +	struct resource *lpa_res;
> +	struct device *lpaif_device;
> +
> +	lpaif_device = &pdev->dev;
> +
> +	lpa_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +			"lpass-lpaif-mem");
> +	if (!lpa_res) {
> +		dev_err(&pdev->dev, "%s: error getting resource\n", __func__);
> +		return -ENODEV;
> +	}
> +	lpaif_dai_info.base = ioremap(lpa_res->start,
> +			(lpa_res->end - lpa_res->start));

It's probably better to use devm_ioremap_resource here.

> +	if (!lpaif_dai_info.base) {
> +		dev_err(&pdev->dev, "%s: error remapping resource\n",
> +				__func__);
> +		return -ENOMEM;
> +	}
> +
> +	lpaif_irq = platform_get_resource_byname(
> +			pdev, IORESOURCE_IRQ, "lpass-lpaif-irq");

platform_get_irq_byname

> +	if (!lpaif_irq) {
> +		dev_err(&pdev->dev, "%s: failed get irq res\n", __func__);
> +		rc = -ENODEV;
> +		goto error;
> +	}
> +
> +	rc = request_irq(lpaif_irq->start, lpaif_dai_irq_handler,
> +			IRQF_TRIGGER_RISING, "lpass-lpaif-intr", NULL);
> +
> +	if (rc < 0) {
> +		dev_err(&pdev->dev, "%s: irq resource request failed\n",
> +				__func__);
> +		goto error;
> +	}
> +
> +	/*
> +	 * Allocating memory for all the LPA_IF DMA channels
> +	 */
> +	for (i = 0; i < LPAIF_MAX_CHANNELS; i++) {
> +		lpaif_dai[i] = kzalloc(sizeof(struct lpaif_dai_drv),
> +				GFP_KERNEL);
> +		if (!lpaif_dai[i]) {
> +			rc = -ENOMEM;
> +			goto error_irq;
> +		}
> +	}
> +	spin_lock_init(&lpaif_lock);


This needs to be initialized before you request the interrupt as the 
interrupt handler is using the spinlock.

> +	return 0;
> +
> +error_irq:
> +	free_irq(lpaif_irq->start, NULL);
> +	lpaif_dai_ch_free();
> +error:
> +	iounmap(lpaif_dai_info.base);
> +	return rc;
> +}
> +
> +static int lpaif_dai_remove(struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < LPAIF_MAX_CHANNELS; i++)
> +		lpaif_dai_stop(i);
> +	synchronize_irq(lpaif_irq->start);

free_irq does a synchronize_irq, not need to call it manually.

> +	free_irq(lpaif_irq->start, NULL);
> +	iounmap(lpaif_dai_info.base);
> +	lpaif_dai_ch_free();
> +	return 0;
> +}
> +
[..]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
  2014-11-19 21:17   ` Pierre-Louis Bossart
  2014-11-20  0:20   ` Courtney Cavin
@ 2014-11-20 12:36   ` Lars-Peter Clausen
  2014-11-25 21:53   ` Mark Brown
  3 siblings, 0 replies; 31+ messages in thread
From: Lars-Peter Clausen @ 2014-11-20 12:36 UTC (permalink / raw)
  To: Kenneth Westfield, ALSA Mailing List, Device Tree Mailing List,
	MSM Mailing List
  Cc: Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On 11/19/2014 07:52 PM, Kenneth Westfield wrote:
[...]
> +static struct clk *lpaif_mi2s_bit_clk;
> +static struct clk *lpaif_mi2s_osr_clk;
> +static struct mi2s_hw_params mi2s_params;

Again, no global state please.
[...]
> +static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}
[...]
> +static int lpass_cpu_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> +	return 0;
> +}

There is no need to implement empty dummy functions the core handles it just 
fine if the callbacks are NULL.

> +
[...]
> +
> +static struct snd_soc_dai_driver lpass_cpu_dais[] = {
> +	{
> +		.playback = {
> +			.rates		= SNDRV_PCM_RATE_8000_192000,
> +			.formats	= SNDRV_PCM_FMTBIT_S16 |
> +					SNDRV_PCM_FMTBIT_S24 |
> +					SNDRV_PCM_FMTBIT_S32,
> +			.channels_min	= 2,
> +			.channels_max	= 2,
> +			.rate_min	= 8000,
> +			.rate_max	= 192000,
> +		},
> +		.ops    = &lpass_cpu_mi2s_ops,
> +		.name = "lpass-mi2s-dai"
> +	},
> +};


If there is only one DAI no need to make this a array.

> +
> +static const struct snd_soc_component_driver lpass_cpu_component = {
> +	.name           = DRV_NAME,
> +};
> +
> +static int lpass_cpu_dai_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = snd_soc_register_component(&pdev->dev, &lpass_cpu_component,
> +			lpass_cpu_dais, ARRAY_SIZE(lpass_cpu_dais));

devm_

> +	if (ret)
> +		dev_err(&pdev->dev, "%s: error registering soc dais\n",
> +				__func__);
> +
> +	return ret;
> +}
[...]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware
  2014-11-19 22:54   ` Courtney Cavin
@ 2014-11-21 20:17     ` Kenneth Westfield
  0 siblings, 0 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-21 20:17 UTC (permalink / raw)
  To: Courtney Cavin
  Cc: Device Tree Mailing List, ALSA Mailing List, Banajit Goswami,
	Takashi Iwai, MSM Mailing List, Patrick Lai, Liam Girdwood,
	Rob Herring, Bryan Huntsman, Mark Brown, Greg KH, David Brown

On Wed, November 19, 2014 2:54 pm, Courtney Cavin wrote:

> On Wed, Nov 19, 2014 at 07:52:49PM +0100, Kenneth Westfield wrote:
>> From: Kenneth Westfield <kwestfie@codeaurora.org>
>>
>> Model the LPASS audio hardware for the IPQ806X.
>>
>> Change-Id: Ide1aa0d09c23d4496aa9c40e3c9878a968261f11
>
> As Kumar mentioned, please exclude this.
>
>> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
>> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
>
> Typically, the order of these SoB should match some sort of chain of delivery:
>  - The first should be the author of the patch
>  - The last should match the email source (you) <-- doesn't seem to be the case
>
>> ---
>>  arch/arm/boot/dts/qcom-ipq8064.dtsi | 33 +++++++++++++++++++++++++++++++++
>>  1 file changed, 33 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
>> index 63b2146f563b541e4994697af5ee1bbb41a4abd1..0e5b3b625f0442964aa7fbbc993c6c818fe99041 100644
>> --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
>> +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
>> @@ -2,6 +2,7 @@
>>
>>  #include "skeleton.dtsi"
>>  #include <dt-bindings/clock/qcom,gcc-ipq806x.h>
>> +#include <dt-bindings/clock/qcom,lcc-ipq806x.h>
>
> Neither this file nor an associated clock controller driver exists in
> mainline.  Is there some other series this depends on?
>
> -Courtney
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>

Courtney

Thank you for your comments.  I will separately address each comment shortly.

-- 
Kenneth Westfield
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver
  2014-11-20 12:32   ` [alsa-devel] " Lars-Peter Clausen
@ 2014-11-21 20:19     ` Kenneth Westfield
  0 siblings, 0 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-21 20:19 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On Thu, November 20, 2014 4:32 am, Lars-Peter Clausen wrote:

> On 11/19/2014 07:52 PM, Kenneth Westfield wrote:
>> From: Kenneth Westfield <kwestfie@codeaurora.org>
>>
>> Add the native LPAIF driver for LPASS block in Qualcomm
>> Technologies SoCs.
>>
>> Change-Id: I0f06f73a1267d7721209e58ce18e0d4897001141
>> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
>> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
>> ---
>>   sound/soc/qcom/lpass-lpaif.c | 488 +++++++++++++++++++++++++++++++++++++++++++
>>   sound/soc/qcom/lpass-lpaif.h | 181 ++++++++++++++++
>>   2 files changed, 669 insertions(+)
>>   create mode 100644 sound/soc/qcom/lpass-lpaif.c
>>   create mode 100644 sound/soc/qcom/lpass-lpaif.h
>>
>> diff --git a/sound/soc/qcom/lpass-lpaif.c b/sound/soc/qcom/lpass-lpaif.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..e62843fe9bc4c63c3c7c119a9f076085b16a56b3
>> --- /dev/null
>> +++ b/sound/soc/qcom/lpass-lpaif.c
>> @@ -0,0 +1,488 @@
>> +/*
>> + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * 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/platform_device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/clk.h>
>> +#include <linux/types.h>
>> +#include <sound/soc.h>
>> +#include "lpass-lpaif.h"
>> +
>> +#define DRV_NAME	"lpass-lpaif"
>> +#define DRV_VERSION	"1.0"
>> +
>> +struct lpaif_dai_baseinfo {
>> +	void __iomem *base;
>> +};
>> +
>> +struct lpaif_dai_drv {
>> +	unsigned char *buffer;
>> +	dma_addr_t buffer_phys;
>> +	int channels;
>> +	irqreturn_t (*callback)(int intrsrc, void *private_data);
>> +	void *private_data;
>> +	int in_use;
>> +	unsigned int buffer_len;
>> +	unsigned int period_len;
>> +	unsigned int master_mode;
>> +};
>> +
>> +static struct lpaif_dai_baseinfo lpaif_dai_info;
>> +static struct lpaif_dai_drv *lpaif_dai[LPAIF_MAX_CHANNELS];
>> +static spinlock_t lpaif_lock;
>> +static struct resource *lpaif_irq;
>
> Please don't use global state for device drivers. Make the state device
> instance specific.
>
>> +
> [...]
>> +
>> +static int lpaif_dai_probe(struct platform_device *pdev)
>> +{
>> +	uint8_t i;
>> +	int32_t rc;
>> +	struct resource *lpa_res;
>> +	struct device *lpaif_device;
>> +
>> +	lpaif_device = &pdev->dev;
>> +
>> +	lpa_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +			"lpass-lpaif-mem");
>> +	if (!lpa_res) {
>> +		dev_err(&pdev->dev, "%s: error getting resource\n", __func__);
>> +		return -ENODEV;
>> +	}
>> +	lpaif_dai_info.base = ioremap(lpa_res->start,
>> +			(lpa_res->end - lpa_res->start));
>
> It's probably better to use devm_ioremap_resource here.
>
>> +	if (!lpaif_dai_info.base) {
>> +		dev_err(&pdev->dev, "%s: error remapping resource\n",
>> +				__func__);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	lpaif_irq = platform_get_resource_byname(
>> +			pdev, IORESOURCE_IRQ, "lpass-lpaif-irq");
>
> platform_get_irq_byname
>
>> +	if (!lpaif_irq) {
>> +		dev_err(&pdev->dev, "%s: failed get irq res\n", __func__);
>> +		rc = -ENODEV;
>> +		goto error;
>> +	}
>> +
>> +	rc = request_irq(lpaif_irq->start, lpaif_dai_irq_handler,
>> +			IRQF_TRIGGER_RISING, "lpass-lpaif-intr", NULL);
>> +
>> +	if (rc < 0) {
>> +		dev_err(&pdev->dev, "%s: irq resource request failed\n",
>> +				__func__);
>> +		goto error;
>> +	}
>> +
>> +	/*
>> +	 * Allocating memory for all the LPA_IF DMA channels
>> +	 */
>> +	for (i = 0; i < LPAIF_MAX_CHANNELS; i++) {
>> +		lpaif_dai[i] = kzalloc(sizeof(struct lpaif_dai_drv),
>> +				GFP_KERNEL);
>> +		if (!lpaif_dai[i]) {
>> +			rc = -ENOMEM;
>> +			goto error_irq;
>> +		}
>> +	}
>> +	spin_lock_init(&lpaif_lock);
>
>
> This needs to be initialized before you request the interrupt as the
> interrupt handler is using the spinlock.
>
>> +	return 0;
>> +
>> +error_irq:
>> +	free_irq(lpaif_irq->start, NULL);
>> +	lpaif_dai_ch_free();
>> +error:
>> +	iounmap(lpaif_dai_info.base);
>> +	return rc;
>> +}
>> +
>> +static int lpaif_dai_remove(struct platform_device *pdev)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < LPAIF_MAX_CHANNELS; i++)
>> +		lpaif_dai_stop(i);
>> +	synchronize_irq(lpaif_irq->start);
>
> free_irq does a synchronize_irq, not need to call it manually.
>
>> +	free_irq(lpaif_irq->start, NULL);
>> +	iounmap(lpaif_dai_info.base);
>> +	lpaif_dai_ch_free();
>> +	return 0;
>> +}
>> +
> [..]
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>

Lars

Thank you for your comments.  I will separately address each comment shortly.

-- 
Kenneth Westfield
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 21:17   ` Pierre-Louis Bossart
@ 2014-11-21 20:23     ` Kenneth Westfield
  0 siblings, 0 replies; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-21 20:23 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Banajit Goswami, Takashi Iwai, Greg KH, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Mark Brown,
	David Brown

On Wed, November 19, 2014 1:17 pm, Pierre-Louis Bossart wrote:

> On 11/19/14, 12:52 PM, Kenneth Westfield wrote:
>> From: Kenneth Westfield <kwestfie@codeaurora.org>
>>
>> Add the CPU DAI driver for the QCOM LPASS SOC.
>>
>> Change-Id: I64ac4407dd32bb9a3066d4b7427292002eaf5d14
>> Signed-off-by: Kenneth Westfield <kwestfie@codeaurora.org>
>> Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
>> ---
>>   sound/soc/qcom/lpass-cpu-dai.c | 307 +++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 307 insertions(+)
>>   create mode 100644 sound/soc/qcom/lpass-cpu-dai.c
>>
>> diff --git a/sound/soc/qcom/lpass-cpu-dai.c b/sound/soc/qcom/lpass-cpu-dai.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..0693e761e01fd416f52684977bcfaf1579dc259c
>> --- /dev/null
>> +++ b/sound/soc/qcom/lpass-cpu-dai.c
>> @@ -0,0 +1,307 @@
>> +/*
>> + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * 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/clk.h>
>> +#include <sound/soc.h>
>> +#include <sound/pcm_params.h>
>> +#include "lpass-lpaif.h"
>> +#include "lpass-pcm-mi2s.h"
>> +
>> +#define DRV_NAME	"lpass-cpu-dai"
>> +#define DRV_VERSION	"1.0"
>> +
>> +#define LPASS_INVALID	(-1)
>> +
>> +struct mi2s_hw_params {
>> +	uint8_t channels;
>> +	uint32_t freq;
>> +	uint8_t bit_width;
>> +};
>> +
>> +static struct clk *lpaif_mi2s_bit_clk;
>> +static struct clk *lpaif_mi2s_osr_clk;
>> +static struct mi2s_hw_params mi2s_params;
>> +
>> +/*
>> + * Returns the OSR to BIT clock divider that would be used with the given
>> + * stream characteristics.
>> + */
>> +static uint32_t lpass_cpu_get_bit_div(uint32_t samp_freq, uint32_t bit_width,
>> +							uint32_t channels)
>> +{
>> +	if (channels == 8) {
>> +		if (bit_width == 24 &&
>> +			((samp_freq != 176400) &&
>> +			(samp_freq != 192000)))
>> +			return 2;
>> +		return 1;
>> +	} else if (channels == 6) {
>> +		if ((bit_width == 32 && ((samp_freq == 176400) ||
>> +			(samp_freq == 192000))) || (bit_width == 24 &&
>> +			(samp_freq == 192000)))
>> +			return 1;
>> +		else
>> +			return 2;
>> +	}
>> +
>> +	switch (samp_freq) {
>> +	case 8000:
>> +	case 16000:
>> +	case 32000:
>> +	case 64000:
>> +		if (bit_width == 16)
>> +			return 4;
>
> Does this test have any value? the behavior is identical in all cases.
>
>> +		break;
>> +	case 48000:
>> +		if (bit_width == 24)
>> +			return 4;
>
> same.
>
>> +		break;
>> +	case 96000:
>> +	case 192000:
>> +		return 4;
>> +	case 176400:
>> +		return 2;
>> +	case 11025:
>> +	case 22050:
>> +	case 44100:
>> +	case 88200:
>> +	default:
>> +		pr_err("%s: Invalid PCM format given\n", __func__);
>> +		return LPASS_INVALID;
>> +	}
>> +
>> +	return 4;
>> +}
>> +
>> +static int lpass_cpu_mi2s_hw_params(struct snd_pcm_substream *substream,
>> +					struct snd_pcm_hw_params *params,
>> +					struct snd_soc_dai *dai)
>> +{
>> +	uint32_t ret = 0;
>> +	uint32_t bit_act;
>> +	uint16_t bit_div;
>> +	uint32_t bit_width = params_format(params);
>> +	uint32_t channels = params_channels(params);
>> +	uint32_t rate = params_rate(params);
>> +	struct snd_pcm_runtime *runtime = substream->runtime;
>> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
>> +	struct mi2s_hw_params curr_params;
>> +
>> +	bit_act = snd_pcm_format_width(bit_width);
>> +	if (bit_act == LPASS_INVALID) {
>> +		dev_err(dai->dev, "%s: Invalid bit width given\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	prtd->pcm_stream_info.bit_width = bit_act;
>> +	curr_params.freq = rate;
>> +	curr_params.channels = channels;
>> +	curr_params.bit_width = bit_act;
>> +
>> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
>> +		/* disable SPKR to make sure it will start in sane state */
>> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
>> +
>> +		/*
>> +		 * Set channel info, it will take effect only if SPKR is
>> +		 * enabled
>> +		 */
>> +		ret = lpaif_cfg_mi2s_playback_hwparams_channels(channels,
>> +						LPAIF_MI2S, bit_act);
>> +	} else {
>> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Make sure our channel setting was success */
>> +	if (ret) {
>> +		/* shutdown would be called where we restore stuff */
>> +		dev_err(dai->dev, "%s: Channel setting unsuccessful\n",
>> +				__func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	mi2s_params.freq = rate;
>> +	mi2s_params.channels = channels;
>> +	mi2s_params.bit_width = bit_act;
>> +
>> +	ret = lpaif_cfg_mi2s_hwparams_bit_width(bit_width, LPAIF_MI2S);
>> +	if (ret) {
>> +		dev_err(dai->dev, "%s: Could not set bit width in HW\n",
>> +				__func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	bit_div = lpass_cpu_get_bit_div(rate, bit_act, channels);
>> +	if (bit_div == LPASS_INVALID) {
>> +		dev_err(dai->dev, "%s: Could not get bit clk divider\n",
>> +				__func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = clk_set_rate(lpaif_mi2s_osr_clk,
>> +		(rate * bit_act * channels * bit_div));
>> +	if (ret) {
>> +		dev_err(dai->dev, "%s: error in setting mi2s osr clk\n",
>> +				__func__);
>> +		return ret;
>> +	}
>> +	ret = clk_prepare_enable(lpaif_mi2s_osr_clk);
>> +	if (ret) {
>> +		dev_err(dai->dev, "%s: error in enabling mi2s osr clk\n",
>> +				__func__);
>> +		return ret;
>> +	}
>> +	prtd->lpaif_clk.is_osr_clk_enabled = 1;
>> +
>> +	ret = clk_set_rate(lpaif_mi2s_bit_clk, rate * bit_act * channels);
>> +	if (ret) {
>> +		dev_err(dai->dev, "%s: error in setting mi2s bit clk\n",
>> +				__func__);
>> +		return ret;
>> +	}
>> +	ret = clk_prepare_enable(lpaif_mi2s_bit_clk);
>> +	if (ret) {
>> +		dev_err(dai->dev, "%s: error in enabling mi2s bit clk\n",
>> +				__func__);
>> +		return ret;
>> +	}
>> +	prtd->lpaif_clk.is_bit_clk_enabled = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
>> +					struct snd_soc_dai *dai)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int lpass_cpu_mi2s_startup(struct snd_pcm_substream *substream,
>> +					struct snd_soc_dai *dai)
>> +{
>> +
>> +	lpaif_mi2s_osr_clk = clk_get(dai->dev, "mi2s_osr_clk");
>> +	if (IS_ERR(lpaif_mi2s_osr_clk)) {
>> +		dev_err(dai->dev, "%s: Error in getting mi2s_osr_clk\n",
>> +				__func__);
>> +		return PTR_ERR(lpaif_mi2s_osr_clk);
>> +	}
>> +
>> +	lpaif_mi2s_bit_clk = clk_get(dai->dev, "mi2s_bit_clk");
>> +	if (IS_ERR(lpaif_mi2s_bit_clk)) {
>> +		dev_err(dai->dev, "%s: Error in getting mi2s_bit_clk\n",
>> +				__func__);
>> +		return PTR_ERR(lpaif_mi2s_bit_clk);
>> +	}
>> +
>> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
>> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);
>> +	} else {
>> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void lpass_cpu_mi2s_shutdown(struct snd_pcm_substream *substream,
>> +					struct snd_soc_dai *dai)
>> +{
>> +	struct snd_pcm_runtime *runtime = substream->runtime;
>> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
>> +
>> +	if (prtd->lpaif_clk.is_osr_clk_enabled)
>> +		clk_disable_unprepare(lpaif_mi2s_osr_clk);
>> +	clk_put(lpaif_mi2s_osr_clk);
>> +
>> +	if (prtd->lpaif_clk.is_bit_clk_enabled)
>> +		clk_disable_unprepare(lpaif_mi2s_bit_clk);
>> +	clk_put(lpaif_mi2s_bit_clk);
>> +}
>> +
>> +static int lpass_cpu_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
>> +{
>> +	return 0;
>> +}
>> +
>> +static struct snd_soc_dai_ops lpass_cpu_mi2s_ops = {
>> +	.startup	= lpass_cpu_mi2s_startup,
>> +	.prepare	= lpass_cpu_mi2s_prepare,
>> +	.hw_params	= lpass_cpu_mi2s_hw_params,
>> +	.shutdown	= lpass_cpu_mi2s_shutdown,
>> +	.set_fmt	= lpass_cpu_mi2s_set_fmt,
>> +};
>> +
>> +static struct snd_soc_dai_driver lpass_cpu_dais[] = {
>> +	{
>> +		.playback = {
>> +			.rates		= SNDRV_PCM_RATE_8000_192000,
>> +			.formats	= SNDRV_PCM_FMTBIT_S16 |
>> +					SNDRV_PCM_FMTBIT_S24 |
>> +					SNDRV_PCM_FMTBIT_S32,
>> +			.channels_min	= 2,
>> +			.channels_max	= 2,
>> +			.rate_min	= 8000,
>> +			.rate_max	= 192000,
>> +		},
>> +		.ops    = &lpass_cpu_mi2s_ops,
>> +		.name = "lpass-mi2s-dai"
>> +	},
>> +};
>> +
>> +static const struct snd_soc_component_driver lpass_cpu_component = {
>> +	.name           = DRV_NAME,
>> +};
>> +
>> +static int lpass_cpu_dai_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +
>> +	ret = snd_soc_register_component(&pdev->dev, &lpass_cpu_component,
>> +			lpass_cpu_dais, ARRAY_SIZE(lpass_cpu_dais));
>> +	if (ret)
>> +		dev_err(&pdev->dev, "%s: error registering soc dais\n",
>> +				__func__);
>> +
>> +	return ret;
>> +}
>> +
>> +static int lpass_cpu_dai_remove(struct platform_device *pdev)
>> +{
>> +	snd_soc_unregister_component(&pdev->dev);
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id lpass_cpu_dai_dt_match[] = {
>> +	{.compatible = "qcom,lpass-cpu-dai"},
>> +	{}
>> +};
>> +
>> +static struct platform_driver lpass_cpu_dai_driver = {
>> +	.probe	= lpass_cpu_dai_probe,
>> +	.remove	= lpass_cpu_dai_remove,
>> +	.driver	= {
>> +		.name	= DRV_NAME,
>> +		.owner	= THIS_MODULE,
>> +		.of_match_table = lpass_cpu_dai_dt_match,
>> +	},
>> +};
>> +module_platform_driver(lpass_cpu_dai_driver);
>> +
>> +MODULE_DESCRIPTION("QCOM LPASS CPU DAI DRIVER");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DRV_NAME);
>> +MODULE_DEVICE_TABLE(of, lpass_cpu_dai_dt_match);
>> +MODULE_VERSION(DRV_VERSION);
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

Thank you for your comments.  I will separately address each comment shortly.

-- 
Kenneth Westfield
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
  2014-11-19 20:16 ` [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kumar Gala
  2014-11-20  9:51   ` Mark Brown
@ 2014-11-21 20:24   ` Kenneth Westfield
  2014-11-24 18:52     ` Mark Brown
  1 sibling, 1 reply; 31+ messages in thread
From: Kenneth Westfield @ 2014-11-21 20:24 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Device Tree Mailing List, ALSA Mailing List, Banajit Goswami,
	Takashi Iwai, MSM Mailing List, Patrick Lai, Liam Girdwood,
	Rob Herring, Bryan Huntsman, Mark Brown, Greg KH, David Brown

On Wed, November 19, 2014 12:16 pm, Kumar Gala wrote:
>
> On Nov 19, 2014, at 12:52 PM, Kenneth Westfield <kwestfie@codeaurora.org> wrote:
>
>> From: Kenneth Westfield <kwestfie@codeaurora.org>
>>
>> This set of patches adds support for audio on the Qualcomm Technologies
>> ipq806x SOC.
>>
>> The ipq806x SOC has audio-related hardware blocks in its low-power audio
>> subsystem (or LPASS).  One of the relevant blocks in the LPASS is its
>> low-power audio interface (or LPAIF).  This encapsulates the MI2S port,
>> which is what these drivers are configured to use.  The I2S pins are
>> connected to an external DAC/amp chip.  In addition, a single GPIO is
>> connected to the same DAC/amp, which gives the SOC enable/disable
>> control.
>>
>> The specific drivers added are:
>> - a machine driver that handles the board-specific pins
>> - a native driver that handles hardware access to the LPAIF
>> - a CPU DAI driver for controlling the LPAIF block
>> - a PCM MI2S platform driver
>>
>> Corresponding additions to the device tree for the ipq806x and its
>> documentation has also been added.  Also, as this is a new directory,
>> the MAINTAINERS file has been updated as well.
>>
>> - Ken
>
> Some general comments, you shouldn’t have gerrit Change-Id’s in commit messages, the
> Kconfig/Makefile changes should be part of the patch that adds the code associated with them.
>
> - k
>
> --
> Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>

Thank you for your comments.  I will separately address each comment shortly.

-- 
Kenneth Westfield
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC
  2014-11-21 20:24   ` [alsa-devel] " Kenneth Westfield
@ 2014-11-24 18:52     ` Mark Brown
  0 siblings, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-24 18:52 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: Kumar Gala, Device Tree Mailing List, ALSA Mailing List,
	Banajit Goswami, Takashi Iwai, MSM Mailing List, Patrick Lai,
	Liam Girdwood, Rob Herring, Bryan Huntsman, Greg KH, David Brown

[-- Attachment #1: Type: text/plain, Size: 615 bytes --]

On Fri, Nov 21, 2014 at 12:24:33PM -0800, Kenneth Westfield wrote:

> Thank you for your comments.  I will separately address each comment shortly.

Please stop sending mails like this for every mail people send you -
there's no content in them, it's just adding to the volume of mail
people have to read.  If you're making some specific comment that's
obviously fine but just saying effectively "I'm going to send you more
mail in future" isn't adding much.

Please also when you do reply to mails delete unneeded context from the
mail, this makes it much easier for people to find any new content you
have added.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 2/9] ASoC: qcom: Add device tree binding docs
  2014-11-19 18:52 ` [PATCH 2/9] ASoC: qcom: Add device tree binding docs Kenneth Westfield
@ 2014-11-25 21:26   ` Mark Brown
  0 siblings, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 21:26 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 3369 bytes --]

On Wed, Nov 19, 2014 at 10:52:42AM -0800, Kenneth Westfield wrote:

> +* Qualcomm Technologies IPQ806x SoundCard
> +
> +This node models the Qualcomm Technologies IPQ806x LPASS Audio SoundCard,
> +with a connection between the CPU MI2S DAI and the external DAC.

> +Required properties:
> +- compatible        : "qcom,ipq806x-snd-card"
> +- qcom,model        : The user-visible name of this sound card
> +- pinctrl-0         : The default state of the MI2S pins
> +- pinctrl-names     : The name of the default state

Why is a sound card doing pin control?  I would expect that the
component devices would do their own pin control.  Also if you have
named pin control states the set of valid names should be specified.

> +- dac-gpios         : GPIO specifier to the GPIO -> DAC SDMODE pin

Simiarly why is a sound card controlling the DAC GPIOs, is this not part
of the CODEC?

> +- clocks            : A list of clock specifiers in the following order:
> +                        * AHBIX bus clock
> +- clock-names       : A list of names in the following order:
> +                        * ahbix_clk

Again I'd really expect any devices on the AHB to be controlling the AHB
related clocks rather than a sound card doing it.

> +asoc-platform       : This is phandle list containing the references to platform device
> +                      nodes that are used as part of the sound card dai-links.
> +asoc-platform-names : This property contains list of platform names. The order of
> +                      the platform names should match to that of the phandle order
> +                      given in "asoc-platform".

The device tree bindings should be OS neutral but ASoC is a Linux thing.
This needs to be written in terms of the hardware it's describing.

> +Required properties:
> +- compatible  : "qcom,lpass-cpu-dai"
> +- clocks      : A list of clock specifiers in the following order:
> +                        * MI2S OSR clock
> +                        * MI2S Bit clock
> +- clock-names : A list of names in the following order:
> +                        * mi2s_osr_clk
> +                        * mi2s_bit_clk

If there are names (which is good) why is the ordering important?  The
whole point in having a mandatory list of names is to remove the
ordering and completeness requirements.

> +Required properties:
> +- compatible      : "qcom,lpass-lpaif"
> +- reg             : Address space for the LPASS subsystem registers
> +- reg-names       : The name of the LPASS subsystem register address space
> +- interrupts      : Phandle to the LPASS interrupt
> +- interrupt-names : The names of the LPASS interrupt

Again you need to document the valid names.

> index 0000000000000000000000000000000000000000..d2ff501d44f7b7aa790cdadc8ba75c6a8bf37ccd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-pcm-mi2s.txt
> @@ -0,0 +1,12 @@
> +* Qualcomm Technologies IPQ806x PCM audio interface
> +
> +This node models the Qualcomm Technologies IPQ806x PCM audio interface.
> +
> +Required properties:
> +- compatible: "qcom,lpass-pcm-mi2s"
> +
> +Example:
> +
> +lpass-pcm-mi2s {
> +	compatible = "qcom,lpass-pcm-mi2s";
> +};

This doesn't appear to describe hardware - there are no register
addresses or anything.  I'd guess this is most likely part of another
hardware block and should be handled by the driver for that device.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver
  2014-11-19 18:52 ` [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver Kenneth Westfield
  2014-11-20 12:32   ` [alsa-devel] " Lars-Peter Clausen
@ 2014-11-25 21:44   ` Mark Brown
  1 sibling, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 21:44 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 3991 bytes --]

On Wed, Nov 19, 2014 at 10:52:43AM -0800, Kenneth Westfield wrote:

> +#define DRV_NAME	"lpass-lpaif"
> +#define DRV_VERSION	"1.0"

Don't add versions like this, the kernel is already more than adequately
versioned and nobody is ever going to bother to update it.

> +static int lpaif_pcm_int_enable(uint8_t dma_ch)
> +{
> +	uint32_t intr_val;
> +	uint32_t status_val;
> +	unsigned long flags;
> +
> +	if (dma_ch >= LPAIF_MAX_CHANNELS) {
> +		pr_err("%s: invalid DMA channel given: %hhu\n",
> +				__func__, dma_ch);

dev_err().

> +void lpaif_cfg_i2s_playback(uint8_t enable, uint32_t mode, uint32_t off)
> +{

The kernel types for fixed size unsigned integers are u8, u32 and so on.

> +		if ((bit_width == 16) && (channels == 2)) {
> +			cfg |= LPAIF_DMACTL_WPSCNT_MONO;
> +		} else if (((bit_width == 16) && (channels == 4)) ||

switch statements please, it's both more legible and more extensible.

> +void lpaif_register_dma_irq_handler(int dma_ch,
> +		irqreturn_t (*callback)(int intrsrc, void *private_data),
> +		void *private_data)
> +{
> +	lpaif_dai[dma_ch]->callback = callback;
> +	lpaif_dai[dma_ch]->private_data = private_data;
> +}
> +
> +void lpaif_unregister_dma_irq_handler(int dma_ch)
> +{
> +	lpaif_dai[dma_ch]->callback = NULL;
> +	lpaif_dai[dma_ch]->private_data = NULL;
> +}

What is this doing?  Linux already has a perfectly good interface for
requesting and releasing interrupts...

> +static int lpaif_dai_find_dma_channel(uint32_t intrsrc)
> +{
> +	uint32_t dma_channel = 0;
> +
> +	while (dma_channel < LPAIF_MAX_CHANNELS) {
> +		if (intrsrc & LPAIF_PER_CH(dma_channel))
> +			return dma_channel;
> +
> +		dma_channel++;
> +	}

A comment explaining why we can't just map directly might be helpful
here.

> +
> +	return -1;

Real error codes please.

> +static irqreturn_t lpaif_dai_irq_handler(int irq, void *data)
> +{
> +	unsigned long flag;
> +	uint32_t intrsrc;
> +	uint32_t dma_ch;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	spin_lock_irqsave(&lpaif_lock, flag);
> +	intrsrc = readl(lpaif_dai_info.base + LPAIF_IRQ_STAT(0));
> +	writel(intrsrc, lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0));
> +	spin_unlock_irqrestore(&lpaif_lock, flag);

This appears to be unconditionally acknowleding all interrupts, even
those we don't understand.  This is bad practice - we should at least be
logging an error and ideally returning IRQ_NONE if we don't understand
the interrupt and letting the core error handling work for us.  For
example it looks like this will happily and silently acknowledge both
error and underrun interrupts from the hardware.

It's also not at all obvious why we're taking this spinlock in the
interrupt handler, that's *extremely* unusual.  What is being protected
- the code needs to make that clear?

> +	while (intrsrc) {
> +		dma_ch = lpaif_dai_find_dma_channel(intrsrc);
> +		if (dma_ch != -1) {
> +			if (lpaif_dai[dma_ch]->callback) {
> +
> +				ret = lpaif_dai[dma_ch]->callback(intrsrc,
> +					lpaif_dai[dma_ch]->private_data);
> +			}
> +			intrsrc &= ~LPAIF_PER_CH(dma_ch);
> +		} else {
> +			pr_err("%s: error getting channel\n", __func__);
> +			break;
> +		}
> +	}
> +	return ret;
> +}

This all looks like a very simple demux of a single register - don't we
have a generic irqchip that can handle it?  It looks like that's what
you're trying to implement here, each DMA channel could be requesting
the three interrupts it has separately rather than open coding an
interrupt controller here.  

I'm not actually immediately seeing one right now but it'd be simple to
add (or regmap-irq could do it if we used a regmap, though it assumes
threading and isn't a great fit).

> +static struct platform_driver lpass_lpaif_driver = {
> +	.probe = lpaif_dai_probe,
> +	.remove = lpaif_dai_remove,
> +	.driver = {
> +		.name = DRV_NAME,
> +		.owner = THIS_MODULE,

No need for .owner on platform devices any more.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver
  2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
                     ` (2 preceding siblings ...)
  2014-11-20 12:36   ` [alsa-devel] " Lars-Peter Clausen
@ 2014-11-25 21:53   ` Mark Brown
  3 siblings, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 21:53 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 3201 bytes --]

On Wed, Nov 19, 2014 at 10:52:44AM -0800, Kenneth Westfield wrote:

> +	if (channels == 8) {
> +		if (bit_width == 24 &&
> +			((samp_freq != 176400) &&
> +			(samp_freq != 192000)))
> +			return 2;
> +		return 1;

Coding style - there's more brackets than are needed coupled with some
strange indentation (eg, the second samp_freq line being indented to the
outside bracket when it's still within that bracket).  Use of switch
statements would probably help, at least on channels.

> +static int lpass_cpu_mi2s_hw_params(struct snd_pcm_substream *substream,
> +					struct snd_pcm_hw_params *params,
> +					struct snd_soc_dai *dai)
> +{
> +	uint32_t ret = 0;
> +	uint32_t bit_act;
> +	uint16_t bit_div;
> +	uint32_t bit_width = params_format(params);
> +	uint32_t channels = params_channels(params);
> +	uint32_t rate = params_rate(params);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +	struct mi2s_hw_params curr_params;
> +
> +	bit_act = snd_pcm_format_width(bit_width);
> +	if (bit_act == LPASS_INVALID) {

snd_pcm_format_width() returns an error code on error, LPASS_INVALID is
not an error code.  Check the return value for error codes...

> +		dev_err(dai->dev, "%s: Invalid bit width given\n", __func__);
> +		return -EINVAL;

...and just return them.

> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		/* disable SPKR to make sure it will start in sane state */
> +		lpaif_cfg_i2s_playback(0, 0, LPAIF_MI2S);

Shouldn't we be doing this on probe and/or resume if it's required?

> +
> +		/*
> +		 * Set channel info, it will take effect only if SPKR is
> +		 * enabled
> +		 */
> +		ret = lpaif_cfg_mi2s_playback_hwparams_channels(channels,
> +						LPAIF_MI2S, bit_act);
> +	} else {
> +		dev_err(dai->dev, "%s: Invalid stream direction\n", __func__);
> +		return -EINVAL;
> +	}

If the device only supports playback no need to have any conditional
code here, the core will prevent capture being started.

> +	ret = clk_set_rate(lpaif_mi2s_osr_clk,
> +		(rate * bit_act * channels * bit_div));
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in setting mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(lpaif_mi2s_osr_clk);
> +	if (ret) {
> +		dev_err(dai->dev, "%s: error in enabling mi2s osr clk\n",
> +				__func__);
> +		return ret;
> +	}
> +	prtd->lpaif_clk.is_osr_clk_enabled = 1;

Coding style, more blank lines between blocks here.  Also not clear why
we're tracking if the clock is enabled when we do it unconditonally.

> +static int lpass_cpu_mi2s_prepare(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}

Remove empty functions.

> +static int lpass_cpu_mi2s_startup(struct snd_pcm_substream *substream,
> +					struct snd_soc_dai *dai)
> +{
> +
> +	lpaif_mi2s_osr_clk = clk_get(dai->dev, "mi2s_osr_clk");
> +	if (IS_ERR(lpaif_mi2s_osr_clk)) {
> +		dev_err(dai->dev, "%s: Error in getting mi2s_osr_clk\n",
> +				__func__);
> +		return PTR_ERR(lpaif_mi2s_osr_clk);
> +	}

No, request resources in probe().  That way deferred probe works and we
don't get mysterious errors at runtime if things go wrong.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver
  2014-11-19 18:52 ` [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver Kenneth Westfield
  2014-11-19 21:10   ` [alsa-devel] " Pierre-Louis Bossart
@ 2014-11-25 22:01   ` Mark Brown
  1 sibling, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 22:01 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 1888 bytes --]

On Wed, Nov 19, 2014 at 10:52:45AM -0800, Kenneth Westfield wrote:

> +	.periods_min		=	LPASS_MI2S_NO_OF_PERIODS,
> +	.periods_max		=	LPASS_MI2S_NO_OF_PERIODS,

As Pierre said this is really odd - it appears to be just a single
possible value.

> +static irqreturn_t lpass_pcm_mi2s_irq(int intrsrc, void *data)
> +{
> +	int dma_ch;
> +	uint32_t ret = IRQ_NONE;
> +	uint32_t has_xrun, pending;
> +
> +	struct snd_pcm_substream *substream = data;
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;

Here we rely on runtime.

> +
> +	if (prtd) {
> +		dma_ch = prtd->lpaif_info.dma_ch;
> +	} else {
> +		pr_debug("%s: received interrupt w/o runtime\n", __func__);
> +		return IRQ_NONE;
> +	}

Here we print (as a debug message, not a dev_err()) an error saying we
lack a runtime (actually it's private data).

> +	if (unlikely(has_xrun) && substream->runtime &&
> +			snd_pcm_running(substream)) {

Here we check if runtime (which we already dereferenced) is non-NULL.

> +	if (pending & LPAIF_UNDER_CH(dma_ch)) {
> +		snd_pcm_period_elapsed(substream);
> +		pr_debug("%s: xrun warning\n", __func__);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	if (pending & LPAIF_ERR_CH(dma_ch)) {
> +		pr_debug("%s: Bus access warning\n", __func__);
> +		ret = IRQ_HANDLED;
> +	}

These errors should be logged as such.

> +static int lpass_pcm_mi2s_hw_params(struct snd_pcm_substream *substream,
> +					struct snd_pcm_hw_params *params)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct lpass_runtime_data_t *prtd = runtime->private_data;
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> +	prtd->pcm_stream_info.pcm_prepare_start = 0;
> +	prtd->pcm_stream_info.period_index = 0;
> +	return 0;
> +}

This appears to ignore the params passed so hw_params() seems like the
wrong place - open()?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC
  2014-11-19 18:52 ` [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC Kenneth Westfield
@ 2014-11-25 22:03   ` Mark Brown
  0 siblings, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 22:03 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 626 bytes --]

On Wed, Nov 19, 2014 at 10:52:46AM -0800, Kenneth Westfield wrote:

> +static struct snd_soc_dai_link ipq_snd_dai[] = {
> +	/* Front end DAI Links */
> +	{
> +		.name		= "IPQ806x Media1",
> +		.stream_name	= "MultiMedia1",
> +		.cpu_dai_name	= "lpass-cpu-dai",
> +		.platform_name	= "lpass-pcm-mi2s",
> +		.codec_dai_name	= "snd-soc-dummy-dai",
> +		.codec_name	= "snd-soc-dummy",
> +	},
> +};

OK, so the CODEC here is actually a dumb DAC with a GPIO enable?  You
should write a driver for this CODEC that imposes the constraints the
device has and manages the GPIO, then it looks like you should be able
to use simple-card.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers
  2014-11-19 18:52 ` [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers Kenneth Westfield
@ 2014-11-25 22:07   ` Mark Brown
  2014-11-27  1:26     ` Bryan Huntsman
  0 siblings, 1 reply; 31+ messages in thread
From: Mark Brown @ 2014-11-25 22:07 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 1955 bytes --]

On Wed, Nov 19, 2014 at 10:52:47AM -0800, Kenneth Westfield wrote:

> +++ b/sound/soc/qcom/Kconfig
> @@ -0,0 +1,43 @@
> +#
> +# Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.

Is there some kind of copyright assignment on file or something?  It's
weird to have someone else without obvious authorship own the
copyright...

> +# Permission to use, copy, modify, and/or distribute this software for any
> +# purpose with or without fee is hereby granted, provided that the above
> +# copyright notice and this permission notice appear in all copies.
> +#
> +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Please just license the file under the GPL, all the drivers that get
built from here are GPLed - I can't see any reason for this to have an
odd license.

> +config SND_SOC_QCOM
> +	bool "SoC Audio support for QCOM platforms"
> +	help
> +          Support for audio in Qualcomm Technologies SOC-based systems.
> +          Say Y if you want to use audio devices that will run on IPQ806x and similar SOCs.
> +

I'd expect this to be the first thing in the file not the last.  Why are
things in here bools, shouldn't they be tristates?

> index 0000000000000000000000000000000000000000..0a74177df75f7aa5f7fbaab35da9b92689fa3d6a
> --- /dev/null
> +++ b/sound/soc/qcom/Makefile
> @@ -0,0 +1,11 @@
> +#QCOM SOUNDCARDS
> +

This appears to be CPU drivers as well as sound cards (as you'd expect
for machine drivers) so the comment isn't quite right - just remove it.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware
  2014-11-19 18:52 ` [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware Kenneth Westfield
  2014-11-19 22:54   ` Courtney Cavin
@ 2014-11-25 22:08   ` Mark Brown
  1 sibling, 0 replies; 31+ messages in thread
From: Mark Brown @ 2014-11-25 22:08 UTC (permalink / raw)
  To: Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Bryan Huntsman, Banajit Goswami, Patrick Lai

[-- Attachment #1: Type: text/plain, Size: 1157 bytes --]

On Wed, Nov 19, 2014 at 10:52:49AM -0800, Kenneth Westfield wrote:

> --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
> +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi

> +		sound {
> +			compatible = "qcom,ipq806x-snd-card";
> +			status = "disabled";
> +			clocks = <&lcc AHBIX_CLK>;
> +			clock-names = "ahbix_clk";
> +			asoc-platform = <&pcm0>;
> +			asoc-platform-names = "lpass-pcm-mi2s";
> +			asoc-cpu = <&dai_mi2s>;
> +			asoc-cpu-names = "lpass-cpu-dai";
> +		};

This appears to be part of the board but...

> +		lpass-lpaif {
> +			compatible = "qcom,lpass-lpaif";
> +			status = "disabled";
> +			reg = <0x28100000 0x10000>;
> +			reg-names = "lpass-lpaif-mem";
> +			interrupts = <0 85 1>;
> +			interrupt-names = "lpass-lpaif-irq";
> +		};
> +
> +		dai_mi2s: lpass-cpu-dai {
> +			compatible = "qcom,lpass-cpu-dai";
> +			status = "disabled";
> +			clocks = <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
> +			clock-names = "mi2s_osr_clk", "mi2s_bit_clk";
> +		};
> +
> +		pcm0: lpass-pcm-mi2s {
> +			compatible = "qcom,lpass-pcm-mi2s";
> +			status = "disabled";
> +		};
> +

...these are all part of the SoC.  I'd expect these to go into separate
files.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers
  2014-11-25 22:07   ` Mark Brown
@ 2014-11-27  1:26     ` Bryan Huntsman
  0 siblings, 0 replies; 31+ messages in thread
From: Bryan Huntsman @ 2014-11-27  1:26 UTC (permalink / raw)
  To: Mark Brown, Kenneth Westfield
  Cc: ALSA Mailing List, Device Tree Mailing List, MSM Mailing List,
	Liam Girdwood, Takashi Iwai, Rob Herring, Greg KH, David Brown,
	Banajit Goswami, Patrick Lai

On 11/25/2014 02:07 PM, Mark Brown wrote:
> On Wed, Nov 19, 2014 at 10:52:47AM -0800, Kenneth Westfield wrote:
> 
>> +++ b/sound/soc/qcom/Kconfig
>> @@ -0,0 +1,43 @@
...
>> +# Permission to use, copy, modify, and/or distribute this software for any
>> +# purpose with or without fee is hereby granted, provided that the above
>> +# copyright notice and this permission notice appear in all copies.
>> +#
>> +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
>> +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
>> +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
>> +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
>> +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
>> +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
>> +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> 
> Please just license the file under the GPL, all the drivers that get
> built from here are GPLed - I can't see any reason for this to have an
> odd license.

Why is there even a license blurb here?  Just drop it.

- Bryan


-- 
Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a
Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2014-11-27  1:26 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-11-19 18:52 [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kenneth Westfield
2014-11-19 18:52 ` [PATCH 1/9] MAINTAINERS: Add QCOM audio ASoC maintainer Kenneth Westfield
2014-11-19 18:52 ` [PATCH 2/9] ASoC: qcom: Add device tree binding docs Kenneth Westfield
2014-11-25 21:26   ` Mark Brown
2014-11-19 18:52 ` [PATCH 3/9] ASoC: ipq806x: add native LPAIF driver Kenneth Westfield
2014-11-20 12:32   ` [alsa-devel] " Lars-Peter Clausen
2014-11-21 20:19     ` Kenneth Westfield
2014-11-25 21:44   ` Mark Brown
2014-11-19 18:52 ` [PATCH 4/9] ASoC: ipq806x: Add LPASS CPU DAI driver Kenneth Westfield
2014-11-19 21:17   ` Pierre-Louis Bossart
2014-11-21 20:23     ` [alsa-devel] " Kenneth Westfield
2014-11-20  0:20   ` Courtney Cavin
2014-11-20 12:36   ` [alsa-devel] " Lars-Peter Clausen
2014-11-25 21:53   ` Mark Brown
2014-11-19 18:52 ` [PATCH 5/9] ASoC: ipq806x: Add I2S PCM platform driver Kenneth Westfield
2014-11-19 21:10   ` [alsa-devel] " Pierre-Louis Bossart
2014-11-25 22:01   ` Mark Brown
2014-11-19 18:52 ` [PATCH 6/9] ASoC: ipq806x: Add machine driver for IPQ806X SOC Kenneth Westfield
2014-11-25 22:03   ` Mark Brown
2014-11-19 18:52 ` [PATCH 7/9] ASoC: qcom: Add ability to build QCOM drivers Kenneth Westfield
2014-11-25 22:07   ` Mark Brown
2014-11-27  1:26     ` Bryan Huntsman
2014-11-19 18:52 ` [PATCH 8/9] ASoC: Allow for building " Kenneth Westfield
2014-11-19 18:52 ` [PATCH 9/9] ARM: dts: Model IPQ LPASS audio hardware Kenneth Westfield
2014-11-19 22:54   ` Courtney Cavin
2014-11-21 20:17     ` [alsa-devel] " Kenneth Westfield
2014-11-25 22:08   ` Mark Brown
2014-11-19 20:16 ` [PATCH 0/9] ASoC: QCOM: Add support for ipq806x SOC Kumar Gala
2014-11-20  9:51   ` Mark Brown
2014-11-21 20:24   ` [alsa-devel] " Kenneth Westfield
2014-11-24 18:52     ` Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).