Skip to contentSkip to conversation

Anbernic RG-DS AW87391 Speaker Amps

PATCH Applied Chris Morgan 3 participants 6 messages Jan 28 – Feb 22

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v2 cover Anbernic RG-DS AW87391 Speaker Amps

From: Chris Morgan <[email protected]>

Add support for the Anbernic RG-DS Speaker Amplifiers. The Anbernic RG-DS uses two AW87391 ICs at 0x58 and 0x5B on i2c2. However, the manufacturer did not provide a firmware file, only a sequence of register writes to each device to enable and disable them.

Add support for this *specific* configuration in the AW87390 driver. Since we are relying on a device specific sequence I am using a device specific compatible string. This driver does not currently support the aw87391 for any other device as I have none to test with valid firmware. Attempts to create firmware with the AwinicSCPv4 have not been successful.

Changes since V1:
- Correct an uninitalized ret value error identified by kernel test robot. [1]
https://lore.kernel.org/oe-kbuild-all/[email protected]/
- Added devicetree binding for the Anbernic RG-DS. This is based on the following commit that has since been accepted. [2]

[1] https://lore.kernel.org/oe-kbuild-all/[email protected]/
[2] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit?id=9e3f8ae040009f66367b2ba1081b7e313b39aeff

Chris Morgan (3):
ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier
ASoC: codecs: aw87390: Add Anbernic RG-DS amp driver
arm64: dts: rockchip: add Awinic aw87391 for Anbernic RG-DS

 .../bindings/sound/awinic,aw87390.yaml        |  34 +++-
 .../dts/rockchip/rk3568-anbernic-rg-ds.dts    |  44 ++++-
 sound/soc/codecs/aw87390.c                    | 175 +++++++++++++++++-
 sound/soc/codecs/aw87390.h                    |  86 +++++++++
 4 files changed, 322 insertions(+), 17 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

PATCH v2 1/3 ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier

From: Chris Morgan <[email protected]>

Add a binding for the Anbernic RG-DS Amplifier, which is an Awinic aw87391 audio amplifier. This manufacturer did not provide firmware so we have to use a list of init commands instead, requiring device specific functionality rather than generic aw87391 functionality.

---
 .../bindings/sound/awinic,aw87390.yaml        | 34 ++++++++++++++++---
 1 file changed, 29 insertions(+), 5 deletions(-)
Documentation/devicetree/bindings/sound/awinic,aw87390.yaml +29 -5
index ba9d8767c5d5..9c1baae767c4 100644
@@ -15,12 +15,15 @@ description:
   sound quallity, which is a new high efficiency, low
   noise, constant large volume, 6th Smart K audio amplifier.
 
-allOf:
-  - $ref: dai-common.yaml#
-
 properties:
   compatible:
-    const: awinic,aw87390
+    oneOf:
+      - enum:
+          - awinic,aw87390
+      - items:
+          - enum:
+              - anbernic,rgds-amp
+          - const: awinic,aw87391
 
   reg:
     maxItems: 1
@@ -40,10 +43,31 @@ required:
   - compatible
   - reg
   - "#sound-dai-cells"
-  - awinic,audio-channel
 
 unevaluatedProperties: false
 
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - awinic,aw87390
+    then:
+      required:
+        - awinic,audio-channel
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - anbernic,rgds-amp
+    then:
+      properties:
+        vdd-supply: true
+
 examples:
   - |
     i2c {
Signature · 1 line
2.43.0
#2 lore raw

PATCH v2 2/3 ASoC: codecs: aw87390: Add Anbernic RG-DS amp driver

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add support for Anbernic's RG-DS audio amplifiers, powered by Awinic AW87391 amplifier ICs. These chips typically require an init sequence provided by firmware, but the manufacturer did not provide firmware in this case. As a result we had to hard-code the init sequence and use a device specific binding (rather than a binding just for the aw87391).

---
 sound/soc/codecs/aw87390.c | 175 +++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/aw87390.h |  86 ++++++++++++++++++
 2 files changed, 253 insertions(+), 8 deletions(-)
sound/soc/codecs/aw87390.c +167 -8
index d7fd865c349f..613daccca3af 100644
@@ -314,6 +314,45 @@ static int aw87390_drv_event(struct snd_soc_dapm_widget *w,
 	return ret;
 }
 
+static int aw87391_rgds_drv_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+	struct aw_device *aw_dev = aw87390->aw_pa;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (!IS_ERR(aw87390->vdd_reg)) {
+			if (regulator_enable(aw87390->vdd_reg))
+				dev_warn(aw_dev->dev, "Failed to enable vdd\n");
+	}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+			     AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+			     AW87391_REG_EN_2X | AW87391_EN_SPK |
+			     AW87391_EN_PA | AW87391_REG_EN_CP |
+			     AW87391_EN_SW);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG,
+			     AW87390_POWER_DOWN_VALUE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (!IS_ERR(aw87390->vdd_reg)) {
+			if (regulator_disable(aw87390->vdd_reg))
+				dev_warn(aw_dev->dev, "Failed to disable vdd\n");
+	}
+		break;
+	default:
+		dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
 	SND_SOC_DAPM_INPUT("IN"),
 	SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
@@ -321,6 +360,14 @@ static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
 	SND_SOC_DAPM_OUTPUT("OUT"),
 };
 
+static const struct snd_soc_dapm_widget aw87391_rgds_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("IN"),
+	SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87391_rgds_drv_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
 static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
 	{ "SPK PA", NULL, "IN" },
 	{ "OUT", NULL, "SPK PA" },
@@ -339,6 +386,80 @@ static int aw87390_codec_probe(struct snd_soc_component *component)
 	return 0;
 }
 
+/*
+ * Firmware typically is used to load the sequence of init commands,
+ * however for the Anbernic RG-DS we don't have a firmware file just
+ * a list of registers and values. Most of these values are undocumented
+ * in the AW87391 datasheet.
+ */
+static void aw87391_rgds_codec_init(struct aw87390 *aw87390)
+{
+	struct aw_device *aw_dev = aw87390->aw_pa;
+
+	/* Undocumented command per datasheet. */
+	regmap_write(aw_dev->regmap, 0x64, 0x3a);
+
+	/* Bits 7:4 are undocumented but provided by manufacturer. */
+	regmap_write(aw_dev->regmap, AW87391_CP_REG,
+		     (5 << 4) | AW87391_REG_CP_OVP_8_50V);
+
+	regmap_write(aw_dev->regmap, AW87391_AGCPO_REG,
+		     AW87391_AK1_S_016 | AW87391_AGC2PO_MW(500));
+
+	regmap_write(aw_dev->regmap, AW87391_AGC2PA_REG,
+		     AW87391_RK_S_20_48 | AW87391_AK2_S_41 | AW87391_AK2F_S_41);
+
+	/* Undocumented commands per datasheet. */
+	regmap_write(aw_dev->regmap, 0x5d, 0x00);
+	regmap_write(aw_dev->regmap, 0x5e, 0xb4);
+	regmap_write(aw_dev->regmap, 0x5f, 0x30);
+	regmap_write(aw_dev->regmap, 0x60, 0x39);
+	regmap_write(aw_dev->regmap, 0x61, 0x10);
+	regmap_write(aw_dev->regmap, 0x62, 0x03);
+	regmap_write(aw_dev->regmap, 0x63, 0x7d);
+	regmap_write(aw_dev->regmap, 0x65, 0xa0);
+	regmap_write(aw_dev->regmap, 0x66, 0x21);
+	regmap_write(aw_dev->regmap, 0x67, 0x41);
+	regmap_write(aw_dev->regmap, 0x68, 0x3b);
+	regmap_write(aw_dev->regmap, 0x6e, 0x00);
+	regmap_write(aw_dev->regmap, 0x6f, 0x00);
+	regmap_write(aw_dev->regmap, 0x70, 0x00);
+	regmap_write(aw_dev->regmap, 0x71, 0x00);
+	regmap_write(aw_dev->regmap, 0x72, 0x34);
+	regmap_write(aw_dev->regmap, 0x73, 0x06);
+	regmap_write(aw_dev->regmap, 0x74, 0x10);
+	regmap_write(aw_dev->regmap, 0x75, 0x00);
+	regmap_write(aw_dev->regmap, 0x7a, 0x00);
+	regmap_write(aw_dev->regmap, 0x7b, 0x00);
+	regmap_write(aw_dev->regmap, 0x7c, 0x00);
+	regmap_write(aw_dev->regmap, 0x7d, 0x00);
+
+	regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_12DB);
+	regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+		     AW87391_EN_PA | AW87391_REG_EN_CP | AW87391_EN_SW);
+	regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+		     AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+		     AW87391_REG_EN_2X | AW87391_EN_SPK | AW87391_EN_PA |
+		     AW87391_REG_EN_CP | AW87391_EN_SW);
+	regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_15DB);
+}
+
+static int aw87391_rgds_codec_probe(struct snd_soc_component *component)
+{
+	struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+
+	aw87390->vdd_reg = devm_regulator_get_optional(aw87390->aw_pa->dev,
+						       "vdd");
+	if (IS_ERR(aw87390->vdd_reg) && PTR_ERR(aw87390->vdd_reg) != -ENODEV)
+		return dev_err_probe(aw87390->aw_pa->dev,
+				     PTR_ERR(aw87390->vdd_reg),
+				     "Could not get vdd regulator\n");
+
+	aw87391_rgds_codec_init(aw87390);
+
+	return 0;
+}
+
 static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
 	.probe = aw87390_codec_probe,
 	.dapm_widgets = aw87390_dapm_widgets,
@@ -349,6 +470,14 @@ static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
 	.num_controls = ARRAY_SIZE(aw87390_controls),
 };
 
+static const struct snd_soc_component_driver soc_codec_dev_anbernic_rgds = {
+	.probe = aw87391_rgds_codec_probe,
+	.dapm_widgets = aw87391_rgds_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(aw87391_rgds_dapm_widgets),
+	.dapm_routes = aw87390_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+};
+
 static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
 {
 	struct aw_device *aw_dev = aw87390->aw_pa;
@@ -366,6 +495,10 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
 	unsigned int chip_id;
 	int ret;
 
+	aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+	if (!aw_dev)
+		return -ENOMEM;
+
 	/* read chip id */
 	ret = regmap_read(regmap, AW87390_ID_REG, &chip_id);
 	if (ret) {
@@ -373,22 +506,24 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
 		return ret;
 	}
 
-	if (chip_id != AW87390_CHIP_ID) {
+	switch (chip_id) {
+	case AW87390_CHIP_ID:
+		aw_dev->chip_id = AW87390_CHIP_ID;
+		break;
+	case AW87391_CHIP_ID:
+		aw_dev->chip_id = AW87391_CHIP_ID;
+		break;
+	default:
 		dev_err(&i2c->dev, "unsupported device\n");
 		return -ENXIO;
 	}
 
 	dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
 
-	aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
-	if (!aw_dev)
-		return -ENOMEM;
-
 	aw87390->aw_pa = aw_dev;
 	aw_dev->i2c = i2c;
 	aw_dev->regmap = regmap;
 	aw_dev->dev = &i2c->dev;
-	aw_dev->chip_id = AW87390_CHIP_ID;
 	aw_dev->acf = NULL;
 	aw_dev->prof_info.prof_desc = NULL;
 	aw_dev->prof_info.count = 0;
@@ -406,6 +541,7 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
 static int aw87390_i2c_probe(struct i2c_client *i2c)
 {
 	struct aw87390 *aw87390;
+	const struct snd_soc_component_driver *priv;
 	int ret;
 
 	ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
@@ -434,16 +570,38 @@ static int aw87390_i2c_probe(struct i2c_client *i2c)
 	if (ret)
 		return ret;
 
-	ret = devm_snd_soc_register_component(&i2c->dev,
-				&soc_codec_dev_aw87390, NULL, 0);
+	switch (aw87390->aw_pa->chip_id) {
+	case AW87390_CHIP_ID:
+		ret = devm_snd_soc_register_component(&i2c->dev,
+					&soc_codec_dev_aw87390, NULL, 0);
+		break;
+	case AW87391_CHIP_ID:
+		priv = of_device_get_match_data(&i2c->dev);
+		if (!priv)
+			return dev_err_probe(&i2c->dev, -EINVAL,
+					     "aw87391 not currently supported\n");
+		ret = devm_snd_soc_register_component(&i2c->dev, priv, NULL, 0);
+		break;
+	default:
+		return -ENXIO;
+	}
+
 	if (ret)
 		dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
 
 	return ret;
 }
 
+static const struct of_device_id aw87390_of_match[] = {
+	{ .compatible = "awinic,aw87390" },
+	{ .compatible = "anbernic,rgds-amp", .data = &soc_codec_dev_anbernic_rgds },
+	{},
+};
+MODULE_DEVICE_TABLE(of, aw87390_of_match);
+
 static const struct i2c_device_id aw87390_i2c_id[] = {
 	{ AW87390_I2C_NAME },
+	{ AW87391_I2C_NAME },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
@@ -451,6 +609,7 @@ MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
 static struct i2c_driver aw87390_i2c_driver = {
 	.driver = {
 		.name = AW87390_I2C_NAME,
+		.of_match_table = of_match_ptr(aw87390_of_match),
 	},
 	.probe = aw87390_i2c_probe,
 	.id_table = aw87390_i2c_id,
sound/soc/codecs/aw87390.h +86 -0

104 lines not on this page · raw message

Signature · 1 line
2.43.0
#3 lore raw

PATCH v2 3/3 arm64: dts: rockchip: add Awinic aw87391 for Anbernic RG-DS

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add support for the two Awinic aw87391 audio amplifiers used in the Anbernic RG-DS. These amplifiers require a specific init sequence to start which is usually provided by a firmware file, but in our case the manufacturer only provided the sequence. As a result, we hard-code a device specific compatible.

Additionally, add support for the VDD regulator used to power both amplifiers. Note that the amps can accept and respond to i2c commands even without regulator power (perhaps due to a secondary power source) but cannot play audio.

---
 .../dts/rockchip/rk3568-anbernic-rg-ds.dts    | 44 +++++++++++++++++--
 1 file changed, 40 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts +40 -4
index 6ac1fe0d3c98..8d906ab02c5f 100644
@@ -354,6 +354,7 @@ sound {
 		compatible = "simple-audio-card";
 		pinctrl-0 = <&hp_det>;
 		pinctrl-names = "default";
+		simple-audio-card,aux-devs = <&aw87391_pa_l>, <&aw87391_pa_r>;
 		simple-audio-card,format = "i2s";
 		simple-audio-card,hp-det-gpios = <&gpio4 RK_PC2 GPIO_ACTIVE_HIGH>;
 		simple-audio-card,mclk-fs = <256>;
@@ -363,8 +364,10 @@ sound {
 			"MICL", "Mic Jack",
 			"Headphones", "HPOL",
 			"Headphones", "HPOR",
-			"Internal Speakers", "HPOL",
-			"Internal Speakers", "HPOR";
+			"Internal Speakers", "Left Amp OUT",
+			"Internal Speakers", "Right Amp OUT",
+			"Left Amp IN", "HPOL",
+			"Right Amp IN", "HPOR";
 		simple-audio-card,widgets =
 			"Microphone", "Mic Jack",
 			"Headphone", "Headphones",
@@ -468,6 +471,18 @@ vcc_wifi: regulator-vcc-wifi {
 		regulator-max-microvolt = <3300000>;
 		regulator-name = "vcc_wifi";
 	};
+
+	vdd_amp: regulator-vcc-amp {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio4 RK_PC3 GPIO_ACTIVE_HIGH>;
+		pinctrl-0 = <&vdd_amp_h>;
+		pinctrl-names = "default";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		vin-supply = <&vccio_acodec>;
+		regulator-name = "vdd_amp";
+	};
 };
 
 &cpu0 {
@@ -840,8 +855,22 @@ &i2c2 {
 	pinctrl-names = "default";
 	status = "okay";
 
-	/* awinic,aw87391 at 0x58 */
-	/* awinic,aw87391 at 0x5b */
+	aw87391_pa_l: audio-codec@58 {
+		compatible = "anbernic,rgds-amp", "awinic,aw87391";
+		reg = <0x58>;
+		vdd-supply = <&vdd_amp>;
+		#sound-dai-cells = <0>;
+		sound-name-prefix = "Left Amp";
+	};
+
+	aw87391_pa_r: audio-codec@5b {
+		compatible = "anbernic,rgds-amp", "awinic,aw87391";
+		reg = <0x5b>;
+		vdd-supply = <&vdd_amp>;
+		#sound-dai-cells = <0>;
+		sound-name-prefix = "Right Amp";
+	};
+
 	/* invensense,icm42607p at 0x68 */
 };
 
@@ -1014,6 +1043,13 @@ touch1_irq: touch1-irq {
 		};
 	};
 
+	vdd-amp {
+		vdd_amp_h: vdd-amp-h {
+			rockchip,pins =
+				<4 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	vcc-lcd {
 		vdd_lcd0_h: vdd-lcd0-h {
 			rockchip,pins =
Signature · 1 line
2.43.0
maintainer
#4 lore raw

Re: (subset) [PATCH V2 0/3] Anbernic RG-DS AW87391 Speaker Amps

In reply to Chris Morgan
Quoted 5 lines · Chris Morgan

Add support for the Anbernic RG-DS Speaker Amplifiers. The Anbernic RG-DS uses two AW87391 ICs at 0x58 and 0x5B on i2c2. However, the manufacturer did not provide a firmware file, only a sequence of register writes to each device to enable and disable them.

Quoted 8 lines

Add support for this *specific* configuration in the AW87390 driver. Since we are relying on a device specific sequence I am using a device specific compatible string. This driver does not currently support the aw87391 for any other device as I have none to test with valid firmware. Attempts to create firmware with the AwinicSCPv4 have not been successful.

[...]

Applied to

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

Thanks!

[1/3] ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier

      commit: c26d6cdade6c2a96049f24fac64a8e3734188703
[2/3] ASoC: codecs: aw87390: Add Anbernic RG-DS amp driver
      commit: a145cfd0ffe7bd7d61ce25839cec737c449b0d2c

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

maintainer Applied
#5 lore raw

Re: (subset) [PATCH V2 0/3] Anbernic RG-DS AW87391 Speaker Amps

In reply to Chris Morgan
Quoted 5 lines · Chris Morgan

Add support for the Anbernic RG-DS Speaker Amplifiers. The Anbernic RG-DS uses two AW87391 ICs at 0x58 and 0x5B on i2c2. However, the manufacturer did not provide a firmware file, only a sequence of register writes to each device to enable and disable them.

Quoted 8 lines

Add support for this *specific* configuration in the AW87390 driver. Since we are relying on a device specific sequence I am using a device specific compatible string. This driver does not currently support the aw87391 for any other device as I have none to test with valid firmware. Attempts to create firmware with the AwinicSCPv4 have not been successful.

[...]

Applied, thanks!

[3/3] arm64: dts: rockchip: add Awinic aw87391 for Anbernic RG-DS

      commit: 1ee69b9cdcd1b838e514520fb4103ca77acd068a

Best regards,

Signature · 1 line
Heiko Stuebner <[email protected]>