Anbernic RG-DS AW87391 Speaker Amps
Subsystems (3)
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.
Chris Morgan (2):
ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier
ASoC: codecs: aw87390: Add Anbernic RG-DS amplifier driver
.../bindings/sound/awinic,aw87390.yaml | 34 +++- sound/soc/codecs/aw87390.c | 176 +++++++++++++++++- sound/soc/codecs/aw87390.h | 86 +++++++++ 3 files changed, 283 insertions(+), 13 deletions(-)
Signature · 1 line
2.43.0
PATCH 1/2 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
@@ -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
Re: [PATCH 1/2] ASoC: dt-bindings: aw87390: Add Anbernic RG-DS Amplifier
pw-bot: not-applicable
PATCH 2/2 ASoC: codecs: aw87390: Add Anbernic RG-DS amplifier driver
In reply to Chris MorganFrom: 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 | 176 +++++++++++++++++++++++++++++++++++-- sound/soc/codecs/aw87390.h | 86 ++++++++++++++++++ 2 files changed, 254 insertions(+), 8 deletions(-)
sound/soc/codecs/aw87390.c +168 -8
@@ -314,6 +314,46 @@ 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; +int ret; + +switch (event) { +case SND_SOC_DAPM_PRE_PMU : +if (!IS_ERR (aw87390->vdd_reg)) { +if (regulator_enable (aw87390->vdd_reg)) +dev_err (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_err (aw_dev->dev,"Failed to disable vdd\n" ); + } +break ; +default : +dev_err (aw_dev->dev,"%s: invalid event %d\n" , __func__, event); + ret = -EINVAL ; + } + +return ret; +} +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 +361,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 +387,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 +471,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 +496,10 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, structunsigned 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 +507,24 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, structreturn 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 +542,7 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, structstatic 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 +571,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 +610,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
kernel test robot
build botRe: [PATCH 2/2] ASoC: codecs: aw87390: Add Anbernic RG-DS amplifier driver
kernel test robot noticed the following build warnings:
Hi Chris,
kernel test robot noticed the following build warnings:
[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on linus/master v6.19-rc6 next-20260122] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Chris-Morgan/ASoC-dt-bindings-aw87390-Add-Anbernic-RG-DS-Amplifier/20260123-235216 base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20260123154731.130649-3-macroalpha82%40gmail.com patch subject: [PATCH 2/2] ASoC: codecs: aw87390: Add Anbernic RG-DS amplifier driver config: sparc64-randconfig-001-20260124 (https://download.01.org/0day-ci/archive/20260124/[email protected]/config) compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260124/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> sound/soc/codecs/aw87390.c:343:7: warning: variable 'ret' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
343 | case SND_SOC_DAPM_POST_PMD:
| ^~~~~~~~~~~~~~~~~~~~~
include/sound/soc-dapm.h:388:32: note: expanded from macro 'SND_SOC_DAPM_POST_PMD'
388 | #define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
| ^~~
sound/soc/codecs/aw87390.c:354:9: note: uninitialized use occurs here
354 | return ret;
| ^~~
sound/soc/codecs/aw87390.c:339:7: warning: variable 'ret' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
339 | case SND_SOC_DAPM_PRE_PMD:
| ^~~~~~~~~~~~~~~~~~~~
include/sound/soc-dapm.h:387:31: note: expanded from macro 'SND_SOC_DAPM_PRE_PMD'
387 | #define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
| ^~~
sound/soc/codecs/aw87390.c:354:9: note: uninitialized use occurs here
354 | return ret;
| ^~~
sound/soc/codecs/aw87390.c:332:7: warning: variable 'ret' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
332 | case SND_SOC_DAPM_POST_PMU:
| ^~~~~~~~~~~~~~~~~~~~~
include/sound/soc-dapm.h:386:32: note: expanded from macro 'SND_SOC_DAPM_POST_PMU'
386 | #define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
| ^~~
sound/soc/codecs/aw87390.c:354:9: note: uninitialized use occurs here
354 | return ret;
| ^~~
sound/soc/codecs/aw87390.c:326:7: warning: variable 'ret' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
326 | case SND_SOC_DAPM_PRE_PMU:
| ^~~~~~~~~~~~~~~~~~~~
include/sound/soc-dapm.h:385:31: note: expanded from macro 'SND_SOC_DAPM_PRE_PMU'
385 | #define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
| ^~~
sound/soc/codecs/aw87390.c:354:9: note: uninitialized use occurs here
354 | return ret;
| ^~~
sound/soc/codecs/aw87390.c:323:9: note: initialize the variable 'ret' to silence this warning
323 | int ret;
| ^
| = 04 warnings generated.
vim +/ret +343 sound/soc/codecs/aw87390.c
316
317 static int aw87391_rgds_drv_event(struct snd_soc_dapm_widget *w,
318 struct snd_kcontrol *kcontrol, int event)
319 {
320 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
321 struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
322 struct aw_device *aw_dev = aw87390->aw_pa;
323 int ret;
324
325 switch (event) {
326 case SND_SOC_DAPM_PRE_PMU:
327 if (!IS_ERR(aw87390->vdd_reg)) {
328 if (regulator_enable(aw87390->vdd_reg))
329 dev_err(aw_dev->dev, "Failed to enable vdd\n");
330 }
331 break;
332 case SND_SOC_DAPM_POST_PMU:
333 regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
334 AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP | 335 AW87391_REG_EN_2X | AW87391_EN_SPK | 336 AW87391_EN_PA | AW87391_REG_EN_CP | 337 AW87391_EN_SW); 338 break; 339 case SND_SOC_DAPM_PRE_PMD: 340 regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, 341 AW87390_POWER_DOWN_VALUE); 342 break; > 343 case SND_SOC_DAPM_POST_PMD:
344 if (!IS_ERR(aw87390->vdd_reg)) {
345 if (regulator_disable(aw87390->vdd_reg))
346 dev_err(aw_dev->dev, "Failed to disable vdd\n");
347 }
348 break;
349 default:
350 dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
351 ret = -EINVAL;
352 }
353
354 return ret;
355 }
356
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki