alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes
@ 2020-04-25 18:46 Stephan Gerhold
  2020-04-25 18:46 ` [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable Stephan Gerhold
  2020-05-05 12:17 ` [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes Mark Brown
  0 siblings, 2 replies; 4+ messages in thread
From: Stephan Gerhold @ 2020-04-25 18:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Banajit Goswami,
	Stephan Gerhold, Patrick Lai, Liam Girdwood, Rob Herring,
	Srinivas Kandagatla, ~postmarketos/upstreaming,
	Kenneth Westfield

The lpass-cpu driver now allows configuring the MI2S SD lines
by defining subnodes for one of the DAIs.

Document this in the device tree bindings.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
Changes in v2:
  - Clarify number of entries for qcom,playback/capture-sd-lines
  - Suggest more generic node names (dai@...) for children DAI device nodes

v1: https://lore.kernel.org/alsa-devel/20200406135608.126171-1-stephan@gerhold.net/
---
 .../bindings/sound/qcom,lpass-cpu.txt         | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt
index 21c648328be9..32c2cdb3d32f 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt
@@ -30,6 +30,8 @@ Required properties:
 - reg			: Must contain an address for each entry in reg-names.
 - reg-names		: A list which must include the following entries:
 				* "lpass-lpaif"
+- #address-cells	: Must be 1
+- #size-cells		: Must be 0
 
 
 
@@ -37,6 +39,20 @@ Optional properties:
 
 - qcom,adsp		: Phandle for the audio DSP node
 
+By default, the driver uses up to 4 MI2S SD lines, for a total of 8 channels.
+The SD lines to use can be configured by adding subnodes for each of the DAIs.
+
+Required properties for each DAI (represented by a subnode):
+- reg			: Must be one of the DAI IDs
+			  (usually part of dt-bindings header)
+- qcom,playback-sd-lines: List of serial data lines to use for playback
+			  Each SD line should be represented by a number from 0-3.
+- qcom,capture-sd-lines	: List of serial data lines to use for capture
+			  Each SD line should be represented by a number from 0-3.
+
+Note that adding a subnode changes the default to "no lines configured",
+so both playback and capture lines should be configured when a subnode is added.
+
 Example:
 
 lpass@28100000 {
@@ -51,4 +67,13 @@ lpass@28100000 {
 	reg = <0x28100000 0x10000>;
 	reg-names = "lpass-lpaif";
 	qcom,adsp = <&adsp>;
+
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	/* Optional to set different MI2S SD lines */
+	dai@3 {
+		reg = <MI2S_QUATERNARY>;
+		qcom,playback-sd-lines = <0 1>;
+	};
 };
-- 
2.26.2


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

* [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable
  2020-04-25 18:46 [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes Stephan Gerhold
@ 2020-04-25 18:46 ` Stephan Gerhold
  2020-05-06 11:17   ` Srinivas Kandagatla
  2020-05-05 12:17 ` [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes Mark Brown
  1 sibling, 1 reply; 4+ messages in thread
From: Stephan Gerhold @ 2020-04-25 18:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Banajit Goswami,
	Stephan Gerhold, Patrick Lai, Liam Girdwood, Rob Herring,
	Srinivas Kandagatla, ~postmarketos/upstreaming,
	Kenneth Westfield

The LPASS hardware allows configuring the MI2S SD lines to use
when playing/recording audio. However, at the moment the lpass-cpu
driver has SD0 hard-coded for mono/stereo (or additional fixed
SD lines for more channels).

For weird reasons there seems to be hardware that uses one of the
other SD lines for mono/stereo. For example, some Samsung devices
use an external Speaker amplifier connected to Quaternary MI2S.
For some reason, the SD line for audio playback was connected to
SD1 rather than SD0. (I have no idea why...)
At the moment, the lpass-cpu driver cannot be configured to work
for the Speaker on these devices.

The q6afe driver already allows configuring the MI2S SD lines
through the "qcom,sd-lines" device tree property, but this works
only when routing audio through the ADSP.

This commit adds a very similar configuration for the lpass-cpu driver.
It is now possible to add additional subnodes to the lpass device in
the device tree, to configure the SD lines for playback and/or capture.
E.g. for the Samsung devices mentioned above:

&lpass {
	dai@3 {
		reg = <MI2S_QUATERNARY>;
		qcom,playback-sd-lines = <1>;
	};
};

qcom,playback/capture-sd-lines takes a list of SD lines (0-3)
in the same format as the q6afe driver. (The difference here is that
q6afe has separate DAIs for playback/capture, while lpass-cpu has one
for both...)

For backwards compatibility with older device trees, the lpass-cpu driver
defaults to LPAIF_I2SCTL_MODE_8CH if the subnode for a DAI is missing.
This is equivalent to the previous behavior: Up to 8 channels can be
configured, and SD0/QUAT01 will be chosen when setting up a stream
with fewer channels.

This allows the speaker to work on Samsung MSM8916 devices
that use an external speaker amplifier.

Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
Changes in v2:
  - Suggest generic node name (dai@...) for subnodes in example above.

v1: https://lore.kernel.org/alsa-devel/20200406135608.126171-2-stephan@gerhold.net/
---
 sound/soc/qcom/lpass-cpu.c       | 196 +++++++++++++++++++++++--------
 sound/soc/qcom/lpass-lpaif-reg.h |  30 ++---
 sound/soc/qcom/lpass.h           |   4 +
 3 files changed, 166 insertions(+), 64 deletions(-)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index dbce7e92baf3..de2b6d60ce6a 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -19,6 +19,16 @@
 #include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
+#define LPASS_CPU_MAX_MI2S_LINES	4
+#define LPASS_CPU_I2S_SD0_MASK		BIT(0)
+#define LPASS_CPU_I2S_SD1_MASK		BIT(1)
+#define LPASS_CPU_I2S_SD2_MASK		BIT(2)
+#define LPASS_CPU_I2S_SD3_MASK		BIT(3)
+#define LPASS_CPU_I2S_SD0_1_MASK	GENMASK(1, 0)
+#define LPASS_CPU_I2S_SD2_3_MASK	GENMASK(3, 2)
+#define LPASS_CPU_I2S_SD0_1_2_MASK	GENMASK(2, 0)
+#define LPASS_CPU_I2S_SD0_1_2_3_MASK	GENMASK(3, 0)
+
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 		unsigned int freq, int dir)
 {
@@ -72,6 +82,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
 	unsigned int rate = params_rate(params);
+	unsigned int mode;
 	unsigned int regval;
 	int bitwidth, ret;
 
@@ -99,60 +110,84 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		switch (channels) {
-		case 1:
-			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-			regval |= LPAIF_I2SCTL_SPKMONO_MONO;
-			break;
-		case 2:
-			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-			break;
-		case 4:
-			regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
-			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-			break;
-		case 6:
-			regval |= LPAIF_I2SCTL_SPKMODE_6CH;
-			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		mode = drvdata->mi2s_playback_sd_mode[dai->driver->id];
+	else
+		mode = drvdata->mi2s_capture_sd_mode[dai->driver->id];
+
+	if (!mode) {
+		dev_err(dai->dev, "no line is assigned\n");
+		return -EINVAL;
+	}
+
+	switch (channels) {
+	case 1:
+	case 2:
+		switch (mode) {
+		case LPAIF_I2SCTL_MODE_QUAD01:
+		case LPAIF_I2SCTL_MODE_6CH:
+		case LPAIF_I2SCTL_MODE_8CH:
+			mode = LPAIF_I2SCTL_MODE_SD0;
 			break;
-		case 8:
-			regval |= LPAIF_I2SCTL_SPKMODE_8CH;
-			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		case LPAIF_I2SCTL_MODE_QUAD23:
+			mode = LPAIF_I2SCTL_MODE_SD2;
 			break;
-		default:
-			dev_err(dai->dev, "invalid channels given: %u\n",
-				channels);
+		}
+
+		break;
+	case 4:
+		if (mode < LPAIF_I2SCTL_MODE_QUAD01) {
+			dev_err(dai->dev, "cannot configure 4 channels with mode %d\n",
+				mode);
 			return -EINVAL;
 		}
-	} else {
-		switch (channels) {
-		case 1:
-			regval |= LPAIF_I2SCTL_MICMODE_SD0;
-			regval |= LPAIF_I2SCTL_MICMONO_MONO;
-			break;
-		case 2:
-			regval |= LPAIF_I2SCTL_MICMODE_SD0;
-			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
-			break;
-		case 4:
-			regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
-			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
-			break;
-		case 6:
-			regval |= LPAIF_I2SCTL_MICMODE_6CH;
-			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+
+		switch (mode) {
+		case LPAIF_I2SCTL_MODE_6CH:
+		case LPAIF_I2SCTL_MODE_8CH:
+			mode = LPAIF_I2SCTL_MODE_QUAD01;
 			break;
-		case 8:
-			regval |= LPAIF_I2SCTL_MICMODE_8CH;
-			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+		}
+		break;
+	case 6:
+		if (mode < LPAIF_I2SCTL_MODE_6CH) {
+			dev_err(dai->dev, "cannot configure 6 channels with mode %d\n",
+				mode);
+			return -EINVAL;
+		}
+
+		switch (mode) {
+		case LPAIF_I2SCTL_MODE_8CH:
+			mode = LPAIF_I2SCTL_MODE_6CH;
 			break;
-		default:
-			dev_err(dai->dev, "invalid channels given: %u\n",
-				channels);
+		}
+		break;
+	case 8:
+		if (mode < LPAIF_I2SCTL_MODE_8CH) {
+			dev_err(dai->dev, "cannot configure 8 channels with mode %d\n",
+				mode);
 			return -EINVAL;
 		}
+		break;
+	default:
+		dev_err(dai->dev, "invalid channels given: %u\n", channels);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		regval |= LPAIF_I2SCTL_SPKMODE(mode);
+
+		if (channels >= 2)
+			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		else
+			regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+	} else {
+		regval |= LPAIF_I2SCTL_MICMODE(mode);
+
+		if (channels >= 2)
+			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+		else
+			regval |= LPAIF_I2SCTL_MICMONO_MONO;
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
@@ -413,6 +448,73 @@ static struct regmap_config lpass_cpu_regmap_config = {
 	.cache_type = REGCACHE_FLAT,
 };
 
+static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
+						struct device_node *node,
+						const char *name)
+{
+	unsigned int lines[LPASS_CPU_MAX_MI2S_LINES];
+	unsigned int sd_line_mask = 0;
+	int num_lines, i;
+
+	num_lines = of_property_read_variable_u32_array(node, name, lines, 0,
+							LPASS_CPU_MAX_MI2S_LINES);
+	if (num_lines < 0)
+		return LPAIF_I2SCTL_MODE_NONE;
+
+	for (i = 0; i < num_lines; i++)
+		sd_line_mask |= BIT(lines[i]);
+
+	switch (sd_line_mask) {
+	case LPASS_CPU_I2S_SD0_MASK:
+		return LPAIF_I2SCTL_MODE_SD0;
+	case LPASS_CPU_I2S_SD1_MASK:
+		return LPAIF_I2SCTL_MODE_SD1;
+	case LPASS_CPU_I2S_SD2_MASK:
+		return LPAIF_I2SCTL_MODE_SD2;
+	case LPASS_CPU_I2S_SD3_MASK:
+		return LPAIF_I2SCTL_MODE_SD3;
+	case LPASS_CPU_I2S_SD0_1_MASK:
+		return LPAIF_I2SCTL_MODE_QUAD01;
+	case LPASS_CPU_I2S_SD2_3_MASK:
+		return LPAIF_I2SCTL_MODE_QUAD23;
+	case LPASS_CPU_I2S_SD0_1_2_MASK:
+		return LPAIF_I2SCTL_MODE_6CH;
+	case LPASS_CPU_I2S_SD0_1_2_3_MASK:
+		return LPAIF_I2SCTL_MODE_8CH;
+	default:
+		dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask);
+		return LPAIF_I2SCTL_MODE_NONE;
+	}
+}
+
+static void of_lpass_cpu_parse_dai_data(struct device *dev,
+					struct lpass_data *data)
+{
+	struct device_node *node;
+	int ret, id;
+
+	/* Allow all channels by default for backwards compatibility */
+	for (id = 0; id < data->variant->num_dai; id++) {
+		data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+		data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+	}
+
+	for_each_child_of_node(dev->of_node, node) {
+		ret = of_property_read_u32(node, "reg", &id);
+		if (ret || id < 0 || id >= data->variant->num_dai) {
+			dev_err(dev, "valid dai id not found: %d\n", ret);
+			continue;
+		}
+
+		data->mi2s_playback_sd_mode[id] =
+			of_lpass_cpu_parse_sd_lines(dev, node,
+						    "qcom,playback-sd-lines");
+		data->mi2s_capture_sd_mode[id] =
+			of_lpass_cpu_parse_sd_lines(dev, node,
+						    "qcom,capture-sd-lines");
+	}
+}
+
 int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata;
@@ -442,6 +544,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 	drvdata->variant = (struct lpass_variant *)match->data;
 	variant = drvdata->variant;
 
+	of_lpass_cpu_parse_dai_data(dev, drvdata);
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
 
 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index 3d74ae123e9d..72a3e2f69572 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -22,17 +22,19 @@
 #define LPAIF_I2SCTL_SPKEN_DISABLE	(0 << LPAIF_I2SCTL_SPKEN_SHIFT)
 #define LPAIF_I2SCTL_SPKEN_ENABLE	(1 << LPAIF_I2SCTL_SPKEN_SHIFT)
 
+#define LPAIF_I2SCTL_MODE_NONE		0
+#define LPAIF_I2SCTL_MODE_SD0		1
+#define LPAIF_I2SCTL_MODE_SD1		2
+#define LPAIF_I2SCTL_MODE_SD2		3
+#define LPAIF_I2SCTL_MODE_SD3		4
+#define LPAIF_I2SCTL_MODE_QUAD01	5
+#define LPAIF_I2SCTL_MODE_QUAD23	6
+#define LPAIF_I2SCTL_MODE_6CH		7
+#define LPAIF_I2SCTL_MODE_8CH		8
+
 #define LPAIF_I2SCTL_SPKMODE_MASK	0x3C00
 #define LPAIF_I2SCTL_SPKMODE_SHIFT	10
-#define LPAIF_I2SCTL_SPKMODE_NONE	(0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD0	(1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD1	(2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD2	(3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD3	(4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD01	(5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD23	(6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_6CH	(7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_8CH	(8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE(mode)	((mode) << LPAIF_I2SCTL_SPKMODE_SHIFT)
 
 #define LPAIF_I2SCTL_SPKMONO_MASK	0x0200
 #define LPAIF_I2SCTL_SPKMONO_SHIFT	9
@@ -46,15 +48,7 @@
 
 #define LPAIF_I2SCTL_MICMODE_MASK	GENMASK(7, 4)
 #define LPAIF_I2SCTL_MICMODE_SHIFT	4
-#define LPAIF_I2SCTL_MICMODE_NONE	(0 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD0	(1 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD1	(2 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD2	(3 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD3	(4 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_QUAD01	(5 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_QUAD23	(6 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_6CH	(7 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_8CH	(8 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE(mode)	((mode) << LPAIF_I2SCTL_MICMODE_SHIFT)
 
 #define LPAIF_I2SCTL_MIMONO_MASK	GENMASK(3, 3)
 #define LPAIF_I2SCTL_MICMONO_SHIFT	3
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 17113d380dcc..bd19ec57c73d 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -29,6 +29,10 @@ struct lpass_data {
 	/* MI2S bit clock (derived from system clock by a divider */
 	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
 
+	/* MI2S SD lines to use for playback/capture */
+	unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
+	unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
+
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *lpaif;
 
-- 
2.26.2


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

* Re: [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes
  2020-04-25 18:46 [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes Stephan Gerhold
  2020-04-25 18:46 ` [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable Stephan Gerhold
@ 2020-05-05 12:17 ` Mark Brown
  1 sibling, 0 replies; 4+ messages in thread
From: Mark Brown @ 2020-05-05 12:17 UTC (permalink / raw)
  To: Stephan Gerhold
  Cc: Mark Rutland, devicetree, Kenneth Westfield, Banajit Goswami,
	Patrick Lai, alsa-devel, Liam Girdwood, Rob Herring,
	Srinivas Kandagatla, ~postmarketos/upstreaming

On Sat, 25 Apr 2020 20:46:56 +0200, Stephan Gerhold wrote:
> The lpass-cpu driver now allows configuring the MI2S SD lines
> by defining subnodes for one of the DAIs.
> 
> Document this in the device tree bindings.
> 
> Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.8

Thanks!

[1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes
      commit: d5797ede0818b24252f79497e1c7e1245c328f6b
[2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable
      commit: 4ff028f6c1087bcaf1ee970d4ef43730ed0aaa8c

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

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

* Re: [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable
  2020-04-25 18:46 ` [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable Stephan Gerhold
@ 2020-05-06 11:17   ` Srinivas Kandagatla
  0 siblings, 0 replies; 4+ messages in thread
From: Srinivas Kandagatla @ 2020-05-06 11:17 UTC (permalink / raw)
  To: Stephan Gerhold, Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Banajit Goswami,
	Patrick Lai, Liam Girdwood, Rob Herring,
	~postmarketos/upstreaming, Kenneth Westfield

Thanks Stephan for doing this,

On 25/04/2020 19:46, Stephan Gerhold wrote:
> The LPASS hardware allows configuring the MI2S SD lines to use
> when playing/recording audio. However, at the moment the lpass-cpu
> driver has SD0 hard-coded for mono/stereo (or additional fixed
> SD lines for more channels).
> 
> For weird reasons there seems to be hardware that uses one of the
> other SD lines for mono/stereo. For example, some Samsung devices
> use an external Speaker amplifier connected to Quaternary MI2S.
> For some reason, the SD line for audio playback was connected to
> SD1 rather than SD0. (I have no idea why...)
> At the moment, the lpass-cpu driver cannot be configured to work
> for the Speaker on these devices.
> 
> The q6afe driver already allows configuring the MI2S SD lines
> through the "qcom,sd-lines" device tree property, but this works
> only when routing audio through the ADSP.
> 
> This commit adds a very similar configuration for the lpass-cpu driver.
> It is now possible to add additional subnodes to the lpass device in
> the device tree, to configure the SD lines for playback and/or capture.
> E.g. for the Samsung devices mentioned above:
> 
> &lpass {
> 	dai@3 {
> 		reg = <MI2S_QUATERNARY>;
> 		qcom,playback-sd-lines = <1>;
> 	};
> };
> 
> qcom,playback/capture-sd-lines takes a list of SD lines (0-3)
> in the same format as the q6afe driver. (The difference here is that
> q6afe has separate DAIs for playback/capture, while lpass-cpu has one
> for both...)
> 
> For backwards compatibility with older device trees, the lpass-cpu driver
> defaults to LPAIF_I2SCTL_MODE_8CH if the subnode for a DAI is missing.
> This is equivalent to the previous behavior: Up to 8 channels can be
> configured, and SD0/QUAT01 will be chosen when setting up a stream
> with fewer channels.
> 
> This allows the speaker to work on Samsung MSM8916 devices
> that use an external speaker amplifier.
> 
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
> ---
> Changes in v2:
>    - Suggest generic node name (dai@...) for subnodes in example above.
> 
> v1: https://lore.kernel.org/alsa-devel/20200406135608.126171-2-stephan@gerhold.net/
> ---
>   sound/soc/qcom/lpass-cpu.c       | 196 +++++++++++++++++++++++--------
>   sound/soc/qcom/lpass-lpaif-reg.h |  30 ++---
>   sound/soc/qcom/lpass.h           |   4 +
>   3 files changed, 166 insertions(+), 64 deletions(-)
> 
LGTM,

Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>


> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index dbce7e92baf3..de2b6d60ce6a 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -19,6 +19,16 @@
>   #include "lpass-lpaif-reg.h"
>   #include "lpass.h"
>   
> +#define LPASS_CPU_MAX_MI2S_LINES	4
> +#define LPASS_CPU_I2S_SD0_MASK		BIT(0)
> +#define LPASS_CPU_I2S_SD1_MASK		BIT(1)
> +#define LPASS_CPU_I2S_SD2_MASK		BIT(2)
> +#define LPASS_CPU_I2S_SD3_MASK		BIT(3)
> +#define LPASS_CPU_I2S_SD0_1_MASK	GENMASK(1, 0)
> +#define LPASS_CPU_I2S_SD2_3_MASK	GENMASK(3, 2)
> +#define LPASS_CPU_I2S_SD0_1_2_MASK	GENMASK(2, 0)
> +#define LPASS_CPU_I2S_SD0_1_2_3_MASK	GENMASK(3, 0)
> +
>   static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
>   		unsigned int freq, int dir)
>   {
> @@ -72,6 +82,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
>   	snd_pcm_format_t format = params_format(params);
>   	unsigned int channels = params_channels(params);
>   	unsigned int rate = params_rate(params);
> +	unsigned int mode;
>   	unsigned int regval;
>   	int bitwidth, ret;
>   
> @@ -99,60 +110,84 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
>   		return -EINVAL;
>   	}
>   
> -	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> -		switch (channels) {
> -		case 1:
> -			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
> -			regval |= LPAIF_I2SCTL_SPKMONO_MONO;
> -			break;
> -		case 2:
> -			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
> -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
> -			break;
> -		case 4:
> -			regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
> -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
> -			break;
> -		case 6:
> -			regval |= LPAIF_I2SCTL_SPKMODE_6CH;
> -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		mode = drvdata->mi2s_playback_sd_mode[dai->driver->id];
> +	else
> +		mode = drvdata->mi2s_capture_sd_mode[dai->driver->id];
> +
> +	if (!mode) {
> +		dev_err(dai->dev, "no line is assigned\n");
> +		return -EINVAL;
> +	}
> +
> +	switch (channels) {
> +	case 1:
> +	case 2:
> +		switch (mode) {
> +		case LPAIF_I2SCTL_MODE_QUAD01:
> +		case LPAIF_I2SCTL_MODE_6CH:
> +		case LPAIF_I2SCTL_MODE_8CH:
> +			mode = LPAIF_I2SCTL_MODE_SD0;
>   			break;
> -		case 8:
> -			regval |= LPAIF_I2SCTL_SPKMODE_8CH;
> -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
> +		case LPAIF_I2SCTL_MODE_QUAD23:
> +			mode = LPAIF_I2SCTL_MODE_SD2;
>   			break;
> -		default:
> -			dev_err(dai->dev, "invalid channels given: %u\n",
> -				channels);
> +		}
> +
> +		break;
> +	case 4:
> +		if (mode < LPAIF_I2SCTL_MODE_QUAD01) {
> +			dev_err(dai->dev, "cannot configure 4 channels with mode %d\n",
> +				mode);
>   			return -EINVAL;
>   		}
> -	} else {
> -		switch (channels) {
> -		case 1:
> -			regval |= LPAIF_I2SCTL_MICMODE_SD0;
> -			regval |= LPAIF_I2SCTL_MICMONO_MONO;
> -			break;
> -		case 2:
> -			regval |= LPAIF_I2SCTL_MICMODE_SD0;
> -			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
> -			break;
> -		case 4:
> -			regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
> -			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
> -			break;
> -		case 6:
> -			regval |= LPAIF_I2SCTL_MICMODE_6CH;
> -			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
> +
> +		switch (mode) {
> +		case LPAIF_I2SCTL_MODE_6CH:
> +		case LPAIF_I2SCTL_MODE_8CH:
> +			mode = LPAIF_I2SCTL_MODE_QUAD01;
>   			break;
> -		case 8:
> -			regval |= LPAIF_I2SCTL_MICMODE_8CH;
> -			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
> +		}
> +		break;
> +	case 6:
> +		if (mode < LPAIF_I2SCTL_MODE_6CH) {
> +			dev_err(dai->dev, "cannot configure 6 channels with mode %d\n",
> +				mode);
> +			return -EINVAL;
> +		}
> +
> +		switch (mode) {
> +		case LPAIF_I2SCTL_MODE_8CH:
> +			mode = LPAIF_I2SCTL_MODE_6CH;
>   			break;
> -		default:
> -			dev_err(dai->dev, "invalid channels given: %u\n",
> -				channels);
> +		}
> +		break;
> +	case 8:
> +		if (mode < LPAIF_I2SCTL_MODE_8CH) {
> +			dev_err(dai->dev, "cannot configure 8 channels with mode %d\n",
> +				mode);
>   			return -EINVAL;
>   		}
> +		break;
> +	default:
> +		dev_err(dai->dev, "invalid channels given: %u\n", channels);
> +		return -EINVAL;
> +	}
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		regval |= LPAIF_I2SCTL_SPKMODE(mode);
> +
> +		if (channels >= 2)
> +			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
> +		else
> +			regval |= LPAIF_I2SCTL_SPKMONO_MONO;
> +	} else {
> +		regval |= LPAIF_I2SCTL_MICMODE(mode);
> +
> +		if (channels >= 2)
> +			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
> +		else
> +			regval |= LPAIF_I2SCTL_MICMONO_MONO;
>   	}
>   
>   	ret = regmap_write(drvdata->lpaif_map,
> @@ -413,6 +448,73 @@ static struct regmap_config lpass_cpu_regmap_config = {
>   	.cache_type = REGCACHE_FLAT,
>   };
>   
> +static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
> +						struct device_node *node,
> +						const char *name)
> +{
> +	unsigned int lines[LPASS_CPU_MAX_MI2S_LINES];
> +	unsigned int sd_line_mask = 0;
> +	int num_lines, i;
> +
> +	num_lines = of_property_read_variable_u32_array(node, name, lines, 0,
> +							LPASS_CPU_MAX_MI2S_LINES);
> +	if (num_lines < 0)
> +		return LPAIF_I2SCTL_MODE_NONE;
> +
> +	for (i = 0; i < num_lines; i++)
> +		sd_line_mask |= BIT(lines[i]);
> +
> +	switch (sd_line_mask) {
> +	case LPASS_CPU_I2S_SD0_MASK:
> +		return LPAIF_I2SCTL_MODE_SD0;
> +	case LPASS_CPU_I2S_SD1_MASK:
> +		return LPAIF_I2SCTL_MODE_SD1;
> +	case LPASS_CPU_I2S_SD2_MASK:
> +		return LPAIF_I2SCTL_MODE_SD2;
> +	case LPASS_CPU_I2S_SD3_MASK:
> +		return LPAIF_I2SCTL_MODE_SD3;
> +	case LPASS_CPU_I2S_SD0_1_MASK:
> +		return LPAIF_I2SCTL_MODE_QUAD01;
> +	case LPASS_CPU_I2S_SD2_3_MASK:
> +		return LPAIF_I2SCTL_MODE_QUAD23;
> +	case LPASS_CPU_I2S_SD0_1_2_MASK:
> +		return LPAIF_I2SCTL_MODE_6CH;
> +	case LPASS_CPU_I2S_SD0_1_2_3_MASK:
> +		return LPAIF_I2SCTL_MODE_8CH;
> +	default:
> +		dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask);
> +		return LPAIF_I2SCTL_MODE_NONE;
> +	}
> +}
> +
> +static void of_lpass_cpu_parse_dai_data(struct device *dev,
> +					struct lpass_data *data)
> +{
> +	struct device_node *node;
> +	int ret, id;
> +
> +	/* Allow all channels by default for backwards compatibility */
> +	for (id = 0; id < data->variant->num_dai; id++) {
> +		data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
> +		data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
> +	}
> +
> +	for_each_child_of_node(dev->of_node, node) {
> +		ret = of_property_read_u32(node, "reg", &id);
> +		if (ret || id < 0 || id >= data->variant->num_dai) {
> +			dev_err(dev, "valid dai id not found: %d\n", ret);
> +			continue;
> +		}
> +
> +		data->mi2s_playback_sd_mode[id] =
> +			of_lpass_cpu_parse_sd_lines(dev, node,
> +						    "qcom,playback-sd-lines");
> +		data->mi2s_capture_sd_mode[id] =
> +			of_lpass_cpu_parse_sd_lines(dev, node,
> +						    "qcom,capture-sd-lines");
> +	}
> +}
> +
>   int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>   {
>   	struct lpass_data *drvdata;
> @@ -442,6 +544,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>   	drvdata->variant = (struct lpass_variant *)match->data;
>   	variant = drvdata->variant;
>   
> +	of_lpass_cpu_parse_dai_data(dev, drvdata);
> +
>   	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
>   
>   	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
> diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
> index 3d74ae123e9d..72a3e2f69572 100644
> --- a/sound/soc/qcom/lpass-lpaif-reg.h
> +++ b/sound/soc/qcom/lpass-lpaif-reg.h
> @@ -22,17 +22,19 @@
>   #define LPAIF_I2SCTL_SPKEN_DISABLE	(0 << LPAIF_I2SCTL_SPKEN_SHIFT)
>   #define LPAIF_I2SCTL_SPKEN_ENABLE	(1 << LPAIF_I2SCTL_SPKEN_SHIFT)
>   
> +#define LPAIF_I2SCTL_MODE_NONE		0
> +#define LPAIF_I2SCTL_MODE_SD0		1
> +#define LPAIF_I2SCTL_MODE_SD1		2
> +#define LPAIF_I2SCTL_MODE_SD2		3
> +#define LPAIF_I2SCTL_MODE_SD3		4
> +#define LPAIF_I2SCTL_MODE_QUAD01	5
> +#define LPAIF_I2SCTL_MODE_QUAD23	6
> +#define LPAIF_I2SCTL_MODE_6CH		7
> +#define LPAIF_I2SCTL_MODE_8CH		8
> +
>   #define LPAIF_I2SCTL_SPKMODE_MASK	0x3C00
>   #define LPAIF_I2SCTL_SPKMODE_SHIFT	10
> -#define LPAIF_I2SCTL_SPKMODE_NONE	(0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_SD0	(1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_SD1	(2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_SD2	(3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_SD3	(4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_QUAD01	(5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_QUAD23	(6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_6CH	(7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> -#define LPAIF_I2SCTL_SPKMODE_8CH	(8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
> +#define LPAIF_I2SCTL_SPKMODE(mode)	((mode) << LPAIF_I2SCTL_SPKMODE_SHIFT)
>   
>   #define LPAIF_I2SCTL_SPKMONO_MASK	0x0200
>   #define LPAIF_I2SCTL_SPKMONO_SHIFT	9
> @@ -46,15 +48,7 @@
>   
>   #define LPAIF_I2SCTL_MICMODE_MASK	GENMASK(7, 4)
>   #define LPAIF_I2SCTL_MICMODE_SHIFT	4
> -#define LPAIF_I2SCTL_MICMODE_NONE	(0 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_SD0	(1 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_SD1	(2 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_SD2	(3 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_SD3	(4 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_QUAD01	(5 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_QUAD23	(6 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_6CH	(7 << LPAIF_I2SCTL_MICMODE_SHIFT)
> -#define LPAIF_I2SCTL_MICMODE_8CH	(8 << LPAIF_I2SCTL_MICMODE_SHIFT)
> +#define LPAIF_I2SCTL_MICMODE(mode)	((mode) << LPAIF_I2SCTL_MICMODE_SHIFT)
>   
>   #define LPAIF_I2SCTL_MIMONO_MASK	GENMASK(3, 3)
>   #define LPAIF_I2SCTL_MICMONO_SHIFT	3
> diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
> index 17113d380dcc..bd19ec57c73d 100644
> --- a/sound/soc/qcom/lpass.h
> +++ b/sound/soc/qcom/lpass.h
> @@ -29,6 +29,10 @@ struct lpass_data {
>   	/* MI2S bit clock (derived from system clock by a divider */
>   	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
>   
> +	/* MI2S SD lines to use for playback/capture */
> +	unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
> +	unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
> +
>   	/* low-power audio interface (LPAIF) registers */
>   	void __iomem *lpaif;
>   
> 

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

end of thread, other threads:[~2020-05-06 11:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-25 18:46 [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes Stephan Gerhold
2020-04-25 18:46 ` [PATCH v2 2/2] ASoC: qcom: lpass-cpu: Make I2S SD lines configurable Stephan Gerhold
2020-05-06 11:17   ` Srinivas Kandagatla
2020-05-05 12:17 ` [PATCH v2 1/2] dt-bindings: sound: lpass-cpu: Document DAI subnodes 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).