[PATCH V2 0/5] ASoC: codecs: Add aw87390 amplifier driver
From: Weidong Wang wangweidong.a@awinic.com
The awinic aw87390 is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier.
Add a DT schema for describing awinic aw87390 audio amplifiers. They are controlled using I2C.
v1 -> v2 : Added properties in awinic,aw88395 file Remove fade-enable because the functionality is duplicated in the aw88395 driver Modify dev_err_probe usage Delete unused header files Change the i2c driver name Modify the way a function returns a value
Weidong Wang (5): ASoC: dt-bindings: Adds properties to "awinic,aw88395" ASoC: dt-bindings: Add schema for "awinic,aw87390" ASoC: codecs: Add code for bin parsing compatible with aw87390 ASoC: codecs: Change the aw88261 variable name and i2c driver name ASoC: codecs: Add aw87390 amplifier driver
.../bindings/sound/awinic,aw87390.yaml | 58 +++ .../bindings/sound/awinic,aw88395.yaml | 19 + sound/soc/codecs/Kconfig | 15 +- sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw87390.c | 462 ++++++++++++++++++ sound/soc/codecs/aw87390.h | 85 ++++ sound/soc/codecs/aw88261.c | 23 +- sound/soc/codecs/aw88261.h | 2 +- sound/soc/codecs/aw88395/aw88395.c | 9 +- sound/soc/codecs/aw88395/aw88395.h | 2 +- sound/soc/codecs/aw88395/aw88395_device.c | 47 +- sound/soc/codecs/aw88395/aw88395_device.h | 6 +- sound/soc/codecs/aw88395/aw88395_lib.c | 23 +- sound/soc/codecs/aw88395/aw88395_reg.h | 1 + 14 files changed, 685 insertions(+), 69 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw87390.yaml create mode 100644 sound/soc/codecs/aw87390.c create mode 100644 sound/soc/codecs/aw87390.h
base-commit: 0bb80ecc33a8fb5a682236443c1e740d5c917d1d
From: Weidong Wang wangweidong.a@awinic.com
Adds properties to "awinic,aw88395" to make files more complete
Signed-off-by: Weidong Wang wangweidong.a@awinic.com --- .../bindings/sound/awinic,aw88395.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 4051c2538caf..4965aa4a5370 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -32,11 +32,28 @@ properties: reset-gpios: maxItems: 1
+ awinic,audio-channel: + description: + It is used to distinguish multiple PA devices, so that different + configurations can be loaded to different PA devices + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + awinic,sync-flag: + description: + Flag bit used to keep the phase synchronized in the case of multiple PA + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 1 + required: - compatible - reg - '#sound-dai-cells' - reset-gpios + - awinic,audio-channel + - awinic,sync-flag
unevaluatedProperties: false
@@ -51,5 +68,7 @@ examples: reg = <0x34>; #sound-dai-cells = <0>; reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + awinic,audio-channel = <0>; + awinic,sync-flag = <0>; }; };
On Tue, Sep 12, 2023 at 02:58:48PM +0800, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
The subject is still pretty generic. Ideally, we'd never have the same subject twice. I'd do something like this:
ASoC: dt-bindings: awinic,aw88395: Add properties for multiple PA support
Adds properties to "awinic,aw88395" to make files more complete
Complete in what way? multiple PAs, right. It's not really clear to me though how these properties enable support for multiple PAs.
Signed-off-by: Weidong Wang wangweidong.a@awinic.com
.../bindings/sound/awinic,aw88395.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 4051c2538caf..4965aa4a5370 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -32,11 +32,28 @@ properties: reset-gpios: maxItems: 1
- awinic,audio-channel:
- description:
It is used to distinguish multiple PA devices, so that different
configurations can be loaded to different PA devices
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 7
- awinic,sync-flag:
- description:
Flag bit used to keep the phase synchronized in the case of multiple PA
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 1
required:
- compatible
- reg
- '#sound-dai-cells'
- reset-gpios
- awinic,audio-channel
- awinic,sync-flag
unevaluatedProperties: false
@@ -51,5 +68,7 @@ examples: reg = <0x34>; #sound-dai-cells = <0>; reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
awinic,audio-channel = <0>;
};awinic,sync-flag = <0>; };
-- 2.41.0
Thank you very much for your advice, and I will modify it in the next patch.
On Tue, Sep 12, 2023 at 11:21:22PM -0500, robh@kernel.org wrote:
On Tue, Sep 12, 2023 at 02:58:48PM +0800, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
The subject is still pretty generic. Ideally, we'd never have the same subject twice. I'd do something like this:
ASoC: dt-bindings: awinic,aw88395: Add properties for multiple PA support
Thank you very much. I will modify according to your suggestion in the next patch
Adds properties to "awinic,aw88395" to make files more complete
Complete in what way? multiple PAs, right. It's not really clear to me though how these properties enable support for multiple PAs.
Thank you very much. I will change the following content in the next patch: Add two properties, the "awinic,audio-channel" property and the "awinic,sync-flag". The "awinic,audio-channel" is used to make different PA load different configurations, the "awinic,sync-flag" is used to synchronize the phases of multiple PA. These two properties will be read by the corresponding driver, so that the multi-PA to achieve better playback effect.
Signed-off-by: Weidong Wang wangweidong.a@awinic.com
.../bindings/sound/awinic,aw88395.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 4051c2538caf..4965aa4a5370 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -32,11 +32,28 @@ properties: reset-gpios: maxItems: 1
- awinic,audio-channel:
- description:
It is used to distinguish multiple PA devices, so that different
configurations can be loaded to different PA devices
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 7
- awinic,sync-flag:
- description:
Flag bit used to keep the phase synchronized in the case of multiple PA
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 1
required:
- compatible
- reg
- '#sound-dai-cells'
- reset-gpios
- awinic,audio-channel
- awinic,sync-flag
unevaluatedProperties: false
@@ -51,5 +68,7 @@ examples: reg = <0x34>; #sound-dai-cells = <0>; reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
awinic,audio-channel = <0>;
};awinic,sync-flag = <0>; };
-- 2.41.0
On 12/09/2023 08:58, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
Adds properties to "awinic,aw88395" to make files more complete
Signed-off-by: Weidong Wang wangweidong.a@awinic.com
.../bindings/sound/awinic,aw88395.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 4051c2538caf..4965aa4a5370 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -32,11 +32,28 @@ properties: reset-gpios: maxItems: 1
- awinic,audio-channel:
- description:
It is used to distinguish multiple PA devices, so that different
configurations can be loaded to different PA devices
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 7
- awinic,sync-flag:
- description:
Flag bit used to keep the phase synchronized in the case of multiple PA
- $ref: /schemas/types.yaml#/definitions/uint32
Looks like bool, not uint32. If you made it uint32 for some future purpose, then the name "flag" is misleading and anyway what would be the third option here for sync?
Best regards, Krzysztof
Thank you very much for your review, but I have a question that I would like to consult
On 13/09/2023 09:03, krzysztof.kozlowski@linaro.org wrote:
On 12/09/2023 08:58, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
Adds properties to "awinic,aw88395" to make files more complete
Signed-off-by: Weidong Wang wangweidong.a@awinic.com
.../bindings/sound/awinic,aw88395.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 4051c2538caf..4965aa4a5370 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -32,11 +32,28 @@ properties: reset-gpios: maxItems: 1
- awinic,audio-channel:
- description:
It is used to distinguish multiple PA devices, so that different
configurations can be loaded to different PA devices
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 0
- maximum: 7
- awinic,sync-flag:
- description:
Flag bit used to keep the phase synchronized in the case of multiple PA
- $ref: /schemas/types.yaml#/definitions/uint32
Looks like bool, not uint32. If you made it uint32 for some future purpose, then the name "flag" is misleading and anyway what would be the third option here for sync?
Thank you very much. Can I Change it to "$ref: /schemas/types.yaml#/definitions/bool"?
Best regards, Weidong Wang
From: Weidong Wang wangweidong.a@awinic.com
Add a DT schema for describing awinic aw87390 audio amplifiers. They are controlled using I2C.
Signed-off-by: Weidong Wang wangweidong.a@awinic.com --- .../bindings/sound/awinic,aw87390.yaml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml new file mode 100644 index 000000000000..ba9d8767c5d5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw87390.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic Aw87390 Audio Amplifier + +maintainers: + - Weidong Wang wangweidong.a@awinic.com + +description: + The awinic aw87390 is specifically designed to improve + the musical output dynamic range, enhance the overall + 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 + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + awinic,audio-channel: + description: + It is used to distinguish multiple PA devices, so that different + configurations can be loaded to different PA devices + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + +required: + - compatible + - reg + - "#sound-dai-cells" + - awinic,audio-channel + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@58 { + compatible = "awinic,aw87390"; + reg = <0x58>; + #sound-dai-cells = <0>; + awinic,audio-channel = <0>; + }; + };
On Tue, 12 Sep 2023 14:58:49 +0800, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
Add a DT schema for describing awinic aw87390 audio amplifiers. They are controlled using I2C.
Signed-off-by: Weidong Wang wangweidong.a@awinic.com
.../bindings/sound/awinic,aw87390.yaml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
Reviewed-by: Rob Herring robh@kernel.org
From: Weidong Wang wangweidong.a@awinic.com
Add aw87390 compatible code to the aw88395_lib.c file so that it can parse aw87390's bin file and function return method
Signed-off-by: Weidong Wang wangweidong.a@awinic.com --- sound/soc/codecs/aw88395/aw88395.c | 9 ++--- sound/soc/codecs/aw88395/aw88395.h | 2 +- sound/soc/codecs/aw88395/aw88395_device.c | 47 ++++++----------------- sound/soc/codecs/aw88395/aw88395_device.h | 6 +-- sound/soc/codecs/aw88395/aw88395_lib.c | 23 ++++++----- sound/soc/codecs/aw88395/aw88395_reg.h | 1 + 6 files changed, 33 insertions(+), 55 deletions(-)
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index 9dcd75dd799a..77227c8f01f6 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -175,9 +175,8 @@ static int aw88395_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); - const char *prof_name; - char *name; - int count; + char *prof_name, *name; + int count, ret;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -196,8 +195,8 @@ static int aw88395_profile_info(struct snd_kcontrol *kcontrol, name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item;
- prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count); - if (!prof_name) { + ret = aw88395_dev_get_prof_name(aw88395->aw_pa, count, &prof_name); + if (ret) { strscpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); return 0; diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h index 8036ba27f68d..c2a4f0cb8cd5 100644 --- a/sound/soc/codecs/aw88395/aw88395.h +++ b/sound/soc/codecs/aw88395/aw88395.h @@ -16,7 +16,7 @@
#define AW88395_DSP_16_DATA_MASK (0x0000ffff)
-#define AW88395_I2C_NAME "aw88395_smartpa" +#define AW88395_I2C_NAME "aw88395"
#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000) diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c index 33eda3741464..fd1f67d5f22f 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.c +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -297,9 +297,6 @@ static void aw_dev_fade_in(struct aw_device *aw_dev) int fade_step = aw_dev->fade_step; int i;
- if (!aw_dev->fade_en) - return; - if (fade_step == 0 || aw_dev->fade_in_time == 0) { aw_dev_set_volume(aw_dev, fade_in_vol); return; @@ -320,9 +317,6 @@ static void aw_dev_fade_out(struct aw_device *aw_dev) int fade_step = aw_dev->fade_step; int i;
- if (!aw_dev->fade_en) - return; - if (fade_step == 0 || aw_dev->fade_out_time == 0) { aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); return; @@ -1062,10 +1056,6 @@ static int aw_dev_update_reg_container(struct aw_device *aw_dev, aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); }
- /* keep min volume */ - if (aw_dev->fade_en) - aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); - aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg);
return ret; @@ -1306,7 +1296,9 @@ int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool forc return -EPERM; }
- prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index); + ret = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) + return ret;
dev_dbg(aw_dev->dev, "start update %s", prof_name);
@@ -1594,37 +1586,19 @@ static void aw88395_parse_channel_dt(struct aw_device *aw_dev) u32 channel_value; int ret;
- ret = of_property_read_u32(np, "sound-channel", &channel_value); + ret = of_property_read_u32(np, "awinic,audio-channel", &channel_value); if (ret) { dev_dbg(aw_dev->dev, - "read sound-channel failed,use default 0"); + "read audio-channel failed,use default 0"); aw_dev->channel = AW88395_DEV_DEFAULT_CH; return; }
- dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + dev_dbg(aw_dev->dev, "read audio-channel value is: %d", channel_value); aw_dev->channel = channel_value; }
-static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev) -{ - struct device_node *np = aw_dev->dev->of_node; - u32 fade_en; - int ret; - - ret = of_property_read_u32(np, "fade-enable", &fade_en); - if (ret) { - dev_dbg(aw_dev->dev, - "read fade-enable failed, close fade_in_out"); - fade_en = AW88395_FADE_IN_OUT_DEFAULT; - } - - dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); - - aw_dev->fade_en = fade_en; -} - static int aw_dev_init(struct aw_device *aw_dev) { aw_dev->chip_id = AW88395_CHIP_ID; @@ -1639,7 +1613,6 @@ static int aw_dev_init(struct aw_device *aw_dev) aw_dev->fade_step = AW88395_VOLUME_STEP_DB; aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE; aw88395_parse_channel_dt(aw_dev); - aw88395_parse_fade_enable_dt(aw_dev);
return 0; } @@ -1673,7 +1646,7 @@ int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index) } EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index);
-char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index) +int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) { struct aw_prof_info *prof_info = &aw_dev->prof_info; struct aw_prof_desc *prof_desc; @@ -1681,12 +1654,14 @@ char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index) if ((index >= aw_dev->prof_info.count) || (index < 0)) { dev_err(aw_dev->dev, "index[%d] overflow count[%d]", index, aw_dev->prof_info.count); - return NULL; + return -EINVAL; }
prof_desc = &aw_dev->prof_info.prof_desc[index];
- return prof_info->prof_name_list[prof_desc->id]; + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; } EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name);
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h index caf730753167..791c8c106557 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.h +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -141,6 +141,7 @@ struct aw_device { unsigned char prof_cur; unsigned char prof_index; unsigned char dsp_crc_st; + unsigned char dsp_cfg; u16 chip_id;
unsigned int channel; @@ -151,9 +152,6 @@ struct aw_device { struct regmap *regmap; char *acf;
- u32 fade_en; - unsigned char dsp_cfg; - u32 dsp_fw_len; u32 dsp_cfg_len; u8 platform; @@ -183,7 +181,7 @@ int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool forc void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol); int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, struct aw_prof_desc **prof_desc); -char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index); +int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index); int aw88395_dev_get_profile_index(struct aw_device *aw_dev); int aw88395_dev_get_profile_count(struct aw_device *aw_dev); diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 8ee1baa03269..9d561ae27feb 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -455,9 +455,11 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, goto parse_bin_failed; }
- if (aw_bin->header_info[0].valid_data_len % 4) { - dev_err(aw_dev->dev, "bin data len get error!"); - goto parse_bin_failed; + if (aw_dev->chip_id == AW88261_CHIP_ID) { + if (aw_bin->header_info[0].valid_data_len % 4) { + dev_err(aw_dev->dev, "bin data len get error!"); + goto parse_bin_failed; + } }
prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = @@ -579,9 +581,9 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, }
static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, - struct aw_all_prof_info all_prof_info) + struct aw_all_prof_info *all_prof_info) { - struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; struct aw_prof_info *prof_info = &aw_dev->prof_info; int num = 0; int i; @@ -621,9 +623,9 @@ static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, }
static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, - struct aw_all_prof_info all_prof_info) + struct aw_all_prof_info *all_prof_info) { - struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; struct aw_prof_info *prof_info = &aw_dev->prof_info; struct aw_sec_data_desc *sec_desc; int num = 0; @@ -701,12 +703,13 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
switch (aw_dev->chip_id) { case AW88395_CHIP_ID: - ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + ret = aw88395_dev_cfg_get_valid_prof(aw_dev, all_prof_info); if (ret < 0) goto exit; break; case AW88261_CHIP_ID: - ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + case AW87390_CHIP_ID: + ret = aw88261_dev_cfg_get_valid_prof(aw_dev, all_prof_info); if (ret < 0) goto exit; break; @@ -799,6 +802,7 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain ret = 0; break; case AW88261_CHIP_ID: + case AW87390_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && @@ -839,6 +843,7 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, ret = 0; break; case AW88261_CHIP_ID: + case AW87390_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h index e7a7c02efaf3..d0a273387313 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -97,6 +97,7 @@ enum aw88395_id { AW88395_CHIP_ID = 0x2049, AW88261_CHIP_ID = 0x2113, + AW87390_CHIP_ID = 0x76, };
#define AW88395_REG_MAX (0x7D)
From: Weidong Wang wangweidong.a@awinic.com
Change "sound-channel" to "awinic,audio-channel" Change "aw88261_smartpa" to "aw88261"
Signed-off-by: Weidong Wang wangweidong.a@awinic.com --- sound/soc/codecs/aw88261.c | 23 ++++++++++++----------- sound/soc/codecs/aw88261.h | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a697b5006b45..d01c3ea1bb7d 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -477,7 +477,7 @@ static int aw88261_dev_reg_update(struct aw88261 *aw88261, return ret; }
-static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index) +static int aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) { struct aw_prof_info *prof_info = &aw_dev->prof_info; struct aw_prof_desc *prof_desc; @@ -485,12 +485,14 @@ static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index) if ((index >= aw_dev->prof_info.count) || (index < 0)) { dev_err(aw_dev->dev, "index[%d] overflow count[%d]", index, aw_dev->prof_info.count); - return NULL; + return -EINVAL; }
prof_desc = &aw_dev->prof_info.prof_desc[index];
- return prof_info->prof_name_list[prof_desc->id]; + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; }
static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index, @@ -515,8 +517,8 @@ static int aw88261_dev_fw_update(struct aw88261 *aw88261) char *prof_name; int ret;
- prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index); - if (!prof_name) { + ret = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) { dev_err(aw_dev->dev, "get prof name failed"); return -EINVAL; } @@ -818,9 +820,8 @@ static int aw88261_profile_info(struct snd_kcontrol *kcontrol, { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); - const char *prof_name; - char *name; - int count; + char *prof_name, *name; + int count, ret;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -839,8 +840,8 @@ static int aw88261_profile_info(struct snd_kcontrol *kcontrol, name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item;
- prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count); - if (!prof_name) { + ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name); + if (ret) { strscpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); return 0; @@ -1189,7 +1190,7 @@ static void aw88261_parse_channel_dt(struct aw88261 *aw88261) u32 channel_value = AW88261_DEV_DEFAULT_CH; u32 sync_enable = false;
- of_property_read_u32(np, "sound-channel", &channel_value); + of_property_read_u32(np, "awinic,audio-channel", &channel_value); of_property_read_u32(np, "sync-flag", &sync_enable);
aw_dev->channel = channel_value; diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 4f3dbf438510..bd0841fa9b77 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -370,7 +370,7 @@ #define AW88261_START_RETRIES (5) #define AW88261_START_WORK_DELAY_MS (0)
-#define AW88261_I2C_NAME "aw88261_smartpa" +#define AW88261_I2C_NAME "aw88261"
#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000)
On 12/09/2023 08:58, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
Change "sound-channel" to "awinic,audio-channel" Change "aw88261_smartpa" to "aw88261"
- prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count);
- if (!prof_name) {
- ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
- if (ret) { strscpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); return 0;
@@ -1189,7 +1190,7 @@ static void aw88261_parse_channel_dt(struct aw88261 *aw88261) u32 channel_value = AW88261_DEV_DEFAULT_CH; u32 sync_enable = false;
- of_property_read_u32(np, "sound-channel", &channel_value);
- of_property_read_u32(np, "awinic,audio-channel", &channel_value); of_property_read_u32(np, "sync-flag", &sync_enable);
What about this?
Best regards, Krzysztof
Thank you very much for your review.
On 13/09/2023 09:03, krzysztof.kozlowski@linaro.org wrote:
On 12/09/2023 08:58, wangweidong.a@awinic.com wrote:
From: Weidong Wang wangweidong.a@awinic.com
Change "sound-channel" to "awinic,audio-channel" Change "aw88261_smartpa" to "aw88261"
I'll add "This change is for consistency with yaml properties" to the commit message.
- prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count);
- if (!prof_name) {
- ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
- if (ret) { strscpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); return 0;
@@ -1189,7 +1190,7 @@ static void aw88261_parse_channel_dt(struct aw88261 *aw88261) u32 channel_value = AW88261_DEV_DEFAULT_CH; u32 sync_enable = false;
- of_property_read_u32(np, "sound-channel", &channel_value);
- of_property_read_u32(np, "awinic,audio-channel", &channel_value); of_property_read_u32(np, "sync-flag", &sync_enable);
What about this?
This change is to be consistent with the name in the yaml file
Best regards, Weidong Wang
From: Weidong Wang wangweidong.a@awinic.com
Add i2c and amplifier registration for aw87390 and their associated operation functions.
Signed-off-by: Weidong Wang wangweidong.a@awinic.com --- sound/soc/codecs/Kconfig | 15 +- sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw87390.c | 462 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/aw87390.h | 85 +++++++ 4 files changed, 562 insertions(+), 2 deletions(-) create mode 100644 sound/soc/codecs/aw87390.c create mode 100644 sound/soc/codecs/aw87390.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f1e1dbc509f6..8c101919259f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ALC5632 imply SND_SOC_AUDIO_IIO_AUX imply SND_SOC_AW8738 + imply SND_SOC_AW87390 imply SND_SOC_AW88395 imply SND_SOC_AW88261 imply SND_SOC_BT_SCO @@ -638,12 +639,12 @@ config SND_SOC_AW8738 operation mode using the Awinic-specific one-wire pulse control.
config SND_SOC_AW88395_LIB + select CRC8 tristate
config SND_SOC_AW88395 tristate "Soc Audio for awinic aw88395" depends on I2C - select CRC8 select CRC32 select REGMAP_I2C select GPIOLIB @@ -657,7 +658,6 @@ config SND_SOC_AW88395 config SND_SOC_AW88261 tristate "Soc Audio for awinic aw88261" depends on I2C - select CRC8 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB @@ -668,6 +668,17 @@ config SND_SOC_AW88261 boost converter can be adjusted smartly according to the input amplitude.
+config SND_SOC_AW87390 + tristate "Soc Audio for awinic aw87390" + depends on I2C + select REGMAP_I2C + select SND_SOC_AW88395_LIB + help + The awinic aw87390 is specifically designed to improve + the musical output dynamic range, enhance the overall + sound quallity, which is a new high efficiency, low + noise, constant large volume, 6th Smart K audio amplifier. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a87e56938ce5..60df97b67c01 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -47,6 +47,7 @@ snd-soc-ak5558-objs := ak5558.o snd-soc-arizona-objs := arizona.o arizona-jack.o snd-soc-audio-iio-aux-objs := audio-iio-aux.o snd-soc-aw8738-objs := aw8738.o +snd-soc-aw87390-objs := aw87390.o snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o @@ -434,6 +435,7 @@ obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o +obj-$(CONFIG_SND_SOC_AW87390) += snd-soc-aw87390.o obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c new file mode 100644 index 000000000000..8efae3b73eea --- /dev/null +++ b/sound/soc/codecs/aw87390.c @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw87390.c -- AW87390 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Weidong Wang wangweidong.a@awinic.com +// + +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw87390.h" +#include "aw88395/aw88395_data_type.h" +#include "aw88395/aw88395_device.h" + +static const struct regmap_config aw87390_remap_config = { + .val_bits = 8, + .reg_bits = 8, + .max_register = AW87390_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int aw87390_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int i, ret; + + if (!data) { + dev_err(aw_dev->dev, "data is NULL\n"); + return -EINVAL; + } + + for (i = 0; i < len; i = i + 2) { + if (data[i] == AW87390_DELAY_REG_ADDR) { + usleep_range(data[i + 1] * AW87390_REG_DELAY_TIME, + data[i + 1] * AW87390_REG_DELAY_TIME + 10); + continue; + } + ret = regmap_write(aw_dev->regmap, data[i], data[i + 1]); + if (ret) + return ret; + } + + return 0; +} + +static int aw87390_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]\n", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; +} + +static int aw87390_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw87390_dev_fw_update(struct aw_device *aw_dev) +{ + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + ret = aw87390_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) { + dev_err(aw_dev->dev, "get prof name failed\n"); + return -EINVAL; + } + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw87390_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) { + dev_err(aw_dev->dev, "aw87390_dev_get_prof_data failed\n"); + return ret; + } + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw87390_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed\n"); + return ret; + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static int aw87390_power_off(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status == AW87390_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off\n"); + return 0; + } + + ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE); + if (ret) + return ret; + aw_dev->status = AW87390_DEV_PW_OFF; + + return ret; +} + +static int aw87390_power_on(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status == AW87390_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on\n"); + return 0; + } + + if (!aw_dev->fw_status) { + dev_err(aw_dev->dev, "fw not load\n"); + return -EINVAL; + } + + ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE); + if (ret) + return ret; + + ret = aw87390_dev_fw_update(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "%s load profile failed\n", __func__); + return ret; + } + aw_dev->status = AW87390_DEV_PW_ON; + + return ret; +} + +static int aw87390_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + + if (aw_dev->prof_index == index) + return -EPERM; + + aw_dev->prof_index = index; + + return 0; +} + +static int aw87390_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); + char *prof_name, *name; + int count, ret; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw87390->aw_pa->prof_info.count; + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + ret = aw87390_dev_get_prof_name(aw87390->aw_pa, count, &prof_name); + if (ret) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw87390_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw87390->aw_pa->prof_index; + + return 0; +} + +static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); + int ret; + + mutex_lock(&aw87390->lock); + ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change\n"); + mutex_unlock(&aw87390->lock); + return 0; + } + + if (aw87390->aw_pa->status == AW87390_DEV_PW_ON) { + aw87390_power_off(aw87390->aw_pa); + aw87390_power_on(aw87390->aw_pa); + } + + mutex_unlock(&aw87390->lock); + + return 1; +} + +static const struct snd_kcontrol_new aw87390_controls[] = { + AW87390_PROFILE_EXT("AW87390 Profile Set", aw87390_profile_info, + aw87390_profile_get, aw87390_profile_set), +}; + +static int aw87390_request_firmware_file(struct aw87390 *aw87390) +{ + const struct firmware *cont = NULL; + int ret; + + aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW87390_ACF_FILE, aw87390->aw_pa->dev); + if (ret) + return dev_err_probe(aw87390->aw_pa->dev, ret, + "load [%s] failed!\n", AW87390_ACF_FILE); + + dev_dbg(aw87390->aw_pa->dev, "loaded %s - size: %zu\n", + AW87390_ACF_FILE, cont ? cont->size : 0); + + aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw87390->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + + aw87390->aw_cfg->len = (int)cont->size; + memcpy(aw87390->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) { + dev_err(aw87390->aw_pa->dev, "load [%s] failed !\n", AW87390_ACF_FILE); + return ret; + } + + mutex_lock(&aw87390->lock); + + ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) + dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); + + mutex_unlock(&aw87390->lock); + + return ret; +} + +static int aw87390_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: + ret = aw87390_power_on(aw_dev); + break; + case SND_SOC_DAPM_POST_PMD: + ret = aw87390_power_off(aw_dev); + 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, + SND_SOC_DAPM_PRE_PMU | 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" }, +}; + +static int aw87390_codec_probe(struct snd_soc_component *component) +{ + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component); + int ret; + + ret = aw87390_request_firmware_file(aw87390); + if (ret) + return dev_err_probe(aw87390->aw_pa->dev, ret, + "aw87390_request_firmware_file failed\n"); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_aw87390 = { + .probe = aw87390_codec_probe, + .dapm_widgets = aw87390_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw87390_dapm_widgets), + .dapm_routes = aw87390_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes), + .controls = aw87390_controls, + .num_controls = ARRAY_SIZE(aw87390_controls), +}; + +static void aw87390_parse_channel_dt(struct aw87390 *aw87390) +{ + struct aw_device *aw_dev = aw87390->aw_pa; + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value = AW87390_DEV_DEFAULT_CH; + + of_property_read_u32(np, "awinic,audio-channel", &channel_value); + + aw_dev->channel = channel_value; +} + +static int aw87390_init(struct aw87390 **aw87390, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + /* read chip id */ + ret = regmap_read(regmap, AW87390_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret); + return ret; + } + + if (chip_id != AW87390_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device\n"); + return -ENXIO; + } + + dev_info(&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; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = AW87390_DEV_DEFAULT_CH; + aw_dev->fw_status = AW87390_DEV_FW_FAILED; + aw_dev->prof_index = AW87390_INIT_PROFILE; + aw_dev->status = AW87390_DEV_PW_OFF; + + aw87390_parse_channel_dt(*aw87390); + + return ret; +} + +static int aw87390_i2c_probe(struct i2c_client *i2c) +{ + struct aw87390 *aw87390; + int ret; + + ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n"); + + aw87390 = devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL); + if (!aw87390) + return -ENOMEM; + + mutex_init(&aw87390->lock); + + i2c_set_clientdata(i2c, aw87390); + + aw87390->regmap = devm_regmap_init_i2c(i2c, &aw87390_remap_config); + if (IS_ERR(aw87390->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(aw87390->regmap), + "failed to init regmap\n"); + + /* aw pa init */ + ret = aw87390_init(&aw87390, i2c, aw87390->regmap); + if (ret) + return ret; + + ret = regmap_write(aw87390->regmap, AW87390_ID_REG, AW87390_SOFT_RESET_VALUE); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw87390, NULL, 0); + if (ret) + dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id aw87390_i2c_id[] = { + { AW87390_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); + +static struct i2c_driver aw87390_i2c_driver = { + .driver = { + .name = AW87390_I2C_NAME, + }, + .probe = aw87390_i2c_probe, + .id_table = aw87390_i2c_id, +}; +module_i2c_driver(aw87390_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW87390 PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h new file mode 100644 index 000000000000..54c268c18e72 --- /dev/null +++ b/sound/soc/codecs/aw87390.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw87390.h -- aw87390 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Weidong Wang wangweidong.a@awinic.com +// + +#ifndef __AW87390_H__ +#define __AW87390_H__ + +#define AW87390_ID_REG (0x00) +#define AW87390_SYSCTRL_REG (0x01) +#define AW87390_MDCTRL_REG (0x02) +#define AW87390_CPOVP_REG (0x03) +#define AW87390_CPP_REG (0x04) +#define AW87390_PAG_REG (0x05) +#define AW87390_AGC3P_REG (0x06) +#define AW87390_AGC3PA_REG (0x07) +#define AW87390_AGC2P_REG (0x08) +#define AW87390_AGC2PA_REG (0x09) +#define AW87390_AGC1PA_REG (0x0A) +#define AW87390_SYSST_REG (0x59) +#define AW87390_SYSINT_REG (0x60) +#define AW87390_DFT_SYSCTRL_REG (0x61) +#define AW87390_DFT_MDCTRL_REG (0x62) +#define AW87390_DFT_CPADP_REG (0x63) +#define AW87390_DFT_AGCPA_REG (0x64) +#define AW87390_DFT_POFR_REG (0x65) +#define AW87390_DFT_OC_REG (0x66) +#define AW87390_DFT_ADP1_REG (0x67) +#define AW87390_DFT_REF_REG (0x68) +#define AW87390_DFT_LDO_REG (0x69) +#define AW87390_ADP1_REG (0x70) +#define AW87390_ADP2_REG (0x71) +#define AW87390_NG1_REG (0x72) +#define AW87390_NG2_REG (0x73) +#define AW87390_NG3_REG (0x74) +#define AW87390_CP_REG (0x75) +#define AW87390_AB_REG (0x76) +#define AW87390_TEST_REG (0x77) +#define AW87390_ENCR_REG (0x78) +#define AW87390_DELAY_REG_ADDR (0xFE) + +#define AW87390_SOFT_RESET_VALUE (0xAA) +#define AW87390_POWER_DOWN_VALUE (0x00) +#define AW87390_REG_MAX (0xFF) +#define AW87390_DEV_DEFAULT_CH (0) +#define AW87390_INIT_PROFILE (0) +#define AW87390_REG_DELAY_TIME (1000) +#define AW87390_I2C_NAME "aw87390" +#define AW87390_ACF_FILE "aw87390_acf.bin" + +#define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum aw87390_id { + AW87390_CHIP_ID = 0x76, +}; + +enum { + AW87390_DEV_FW_FAILED = 0, + AW87390_DEV_FW_OK, +}; + +enum { + AW87390_DEV_PW_OFF = 0, + AW87390_DEV_PW_ON, +}; + +struct aw87390 { + struct aw_device *aw_pa; + struct mutex lock; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif
participants (3)
-
Krzysztof Kozlowski
-
Rob Herring
-
wangweidong.a@awinic.com