Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: Add NTP8918 and NTP8835 codecs support

Merge series from Igor Prusov <ivprusov@salutedevices.com>:

This series adds support for two NeoFidelity amplifiers. For both
amplifiers vendor provides software for equalizer and filters
configuration, which generates firmware files with registers values.
Since in both cases those files have same encoding, a common helper
module is added to get firmware via request_firmware() API and set
registers values.

+1201
+73
Documentation/devicetree/bindings/sound/neofidelity,ntp8835.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/sound/neofidelity,ntp8835.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: NeoFidelity NTP8835/NTP8835C Amplifiers 8 + 9 + maintainers: 10 + - Igor Prusov <ivprusov@salutedevices.com> 11 + 12 + description: | 13 + The NTP8835 is a single chip full digital audio amplifier 14 + including power stages for stereo amplifier systems. 15 + NTP8835 is integrated with versatile digital audio signal 16 + processing functions, high-performance, high-fidelity fully 17 + digital PWM modulator and two high-power full-bridge MOSFET 18 + power stages. NTP8835C has identical programming interface, 19 + but has different output signal characteristics. 20 + 21 + allOf: 22 + - $ref: dai-common.yaml# 23 + 24 + properties: 25 + compatible: 26 + enum: 27 + - neofidelity,ntp8835 28 + - neofidelity,ntp8835c 29 + 30 + reg: 31 + enum: 32 + - 0x2a 33 + - 0x2b 34 + - 0x2c 35 + - 0x2d 36 + 37 + reset-gpios: 38 + maxItems: 1 39 + 40 + '#sound-dai-cells': 41 + const: 0 42 + 43 + clocks: 44 + maxItems: 4 45 + 46 + clock-names: 47 + items: 48 + - const: wck 49 + - const: bck 50 + - const: scl 51 + - const: mclk 52 + 53 + required: 54 + - compatible 55 + - reg 56 + 57 + unevaluatedProperties: false 58 + 59 + examples: 60 + - | 61 + #include <dt-bindings/gpio/gpio.h> 62 + i2c { 63 + #address-cells = <1>; 64 + #size-cells = <0>; 65 + audio-codec@2b { 66 + compatible = "neofidelity,ntp8835"; 67 + #sound-dai-cells = <0>; 68 + reg = <0x2b>; 69 + reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; 70 + clocks = <&clkc 551>, <&clkc 552>, <&clkc 553>, <&clkc 554>; 71 + clock-names = "wck", "bck", "scl", "mclk"; 72 + }; 73 + };
+70
Documentation/devicetree/bindings/sound/neofidelity,ntp8918.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/sound/neofidelity,ntp8918.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: NeoFidelity NTP8918 Amplifier 8 + 9 + maintainers: 10 + - Igor Prusov <ivprusov@salutedevices.com> 11 + 12 + description: 13 + The NTP8918 is a single chip full digital audio amplifier 14 + including power stage for stereo amplifier system. 15 + The NTP8918 is integrated with versatile digital audio signal 16 + processing functions, high-performance, high-fidelity fully 17 + digital PWM modulator and two high-power full-bridge MOSFET 18 + power stages. 19 + 20 + allOf: 21 + - $ref: dai-common.yaml# 22 + 23 + properties: 24 + compatible: 25 + enum: 26 + - neofidelity,ntp8918 27 + 28 + reg: 29 + enum: 30 + - 0x2a 31 + - 0x2b 32 + - 0x2c 33 + - 0x2d 34 + 35 + reset-gpios: 36 + maxItems: 1 37 + 38 + '#sound-dai-cells': 39 + const: 0 40 + 41 + clocks: 42 + maxItems: 3 43 + 44 + clock-names: 45 + items: 46 + - const: wck 47 + - const: scl 48 + - const: bck 49 + 50 + required: 51 + - compatible 52 + - reg 53 + 54 + unevaluatedProperties: false 55 + 56 + examples: 57 + - | 58 + #include <dt-bindings/gpio/gpio.h> 59 + i2c { 60 + #address-cells = <1>; 61 + #size-cells = <0>; 62 + audio-codec@2a { 63 + compatible = "neofidelity,ntp8918"; 64 + #sound-dai-cells = <0>; 65 + reg = <0x2a>; 66 + clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>; 67 + clock-names = "wck", "scl", "bck"; 68 + reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; 69 + }; 70 + };
+2
Documentation/devicetree/bindings/vendor-prefixes.yaml
··· 1013 1013 description: Shanghai Neardi Technology Co., Ltd. 1014 1014 "^nec,.*": 1015 1015 description: NEC LCD Technologies, Ltd. 1016 + "^neofidelity,.*": 1017 + description: Neofidelity Inc. 1016 1018 "^neonode,.*": 1017 1019 description: Neonode Inc. 1018 1020 "^netgear,.*":
+13
sound/soc/codecs/Kconfig
··· 2565 2565 tristate 2566 2566 depends on I2C 2567 2567 2568 + config SND_SOC_NTPFW 2569 + tristate 2570 + 2571 + config SND_SOC_NTP8918 2572 + select SND_SOC_NTPFW 2573 + tristate "NeoFidelity NTP8918 amplifier" 2574 + depends on I2C 2575 + 2576 + config SND_SOC_NTP8835 2577 + select SND_SOC_NTPFW 2578 + tristate "NeoFidelity NTP8835 and NTP8835C amplifiers" 2579 + depends on I2C 2580 + 2568 2581 config SND_SOC_TPA6130A2 2569 2582 tristate "Texas Instruments TPA6130A2 headphone amplifier" 2570 2583 depends on I2C
+6
sound/soc/codecs/Makefile
··· 189 189 snd-soc-nau8822-y := nau8822.o 190 190 snd-soc-nau8824-y := nau8824.o 191 191 snd-soc-nau8825-y := nau8825.o 192 + snd-soc-ntp8835-y := ntp8835.o 193 + snd-soc-ntp8918-y := ntp8918.o 194 + snd-soc-ntpfw-y := ntpfw.o 192 195 snd-soc-hdmi-codec-y := hdmi-codec.o 193 196 snd-soc-pcm1681-y := pcm1681.o 194 197 snd-soc-pcm1789-codec-y := pcm1789.o ··· 594 591 obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o 595 592 obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o 596 593 obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o 594 + obj-$(CONFIG_SND_SOC_NTP8835) += snd-soc-ntp8835.o 595 + obj-$(CONFIG_SND_SOC_NTP8918) += snd-soc-ntp8918.o 596 + obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.o 597 597 obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o 598 598 obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o 599 599 obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
+480
sound/soc/codecs/ntp8835.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Driver for the NTP8835/NTP8835C Audio Amplifiers 4 + * 5 + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. 6 + * 7 + * Author: Igor Prusov <ivprusov@salutedevices.com> 8 + */ 9 + 10 + #include <linux/kernel.h> 11 + #include <linux/clk.h> 12 + #include <linux/bits.h> 13 + #include <linux/reset.h> 14 + #include <linux/init.h> 15 + #include <linux/i2c.h> 16 + #include <linux/regmap.h> 17 + 18 + #include <sound/initval.h> 19 + #include <sound/core.h> 20 + #include <sound/pcm.h> 21 + #include <sound/pcm_params.h> 22 + #include <sound/soc.h> 23 + #include <sound/soc-component.h> 24 + #include <sound/tlv.h> 25 + 26 + #include "ntpfw.h" 27 + 28 + #define NTP8835_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 29 + SNDRV_PCM_FMTBIT_S20_3LE | \ 30 + SNDRV_PCM_FMTBIT_S24_LE | \ 31 + SNDRV_PCM_FMTBIT_S32_LE) 32 + 33 + #define NTP8835_INPUT_FMT 0x0 34 + #define NTP8835_INPUT_FMT_MASTER_MODE BIT(0) 35 + #define NTP8835_INPUT_FMT_GSA_MODE BIT(1) 36 + #define NTP8835_GSA_FMT 0x1 37 + #define NTP8835_GSA_BS_MASK GENMASK(3, 2) 38 + #define NTP8835_GSA_BS(x) ((x) << 2) 39 + #define NTP8835_GSA_RIGHT_J BIT(0) 40 + #define NTP8835_GSA_LSB BIT(1) 41 + #define NTP8835_MCLK_FREQ_CTRL 0x2 42 + #define NTP8835_MCLK_FREQ_MCF GENMASK(1, 0) 43 + #define NTP8835_SOFT_MUTE 0x26 44 + #define NTP8835_SOFT_MUTE_SM1 BIT(0) 45 + #define NTP8835_SOFT_MUTE_SM2 BIT(1) 46 + #define NTP8835_SOFT_MUTE_SM3 BIT(2) 47 + #define NTP8835_PWM_SWITCH 0x27 48 + #define NTP8835_PWM_SWITCH_POF1 BIT(0) 49 + #define NTP8835_PWM_SWITCH_POF2 BIT(1) 50 + #define NTP8835_PWM_SWITCH_POF3 BIT(2) 51 + #define NTP8835_PWM_MASK_CTRL0 0x28 52 + #define NTP8835_PWM_MASK_CTRL0_OUT_LOW BIT(1) 53 + #define NTP8835_PWM_MASK_CTRL0_FPMLD BIT(2) 54 + #define NTP8835_MASTER_VOL 0x2e 55 + #define NTP8835_CHNL_A_VOL 0x2f 56 + #define NTP8835_CHNL_B_VOL 0x30 57 + #define NTP8835_CHNL_C_VOL 0x31 58 + #define REG_MAX NTP8835_CHNL_C_VOL 59 + 60 + #define NTP8835_FW_NAME "eq_8835.bin" 61 + #define NTP8835_FW_MAGIC 0x38383335 /* "8835" */ 62 + 63 + struct ntp8835_priv { 64 + struct i2c_client *i2c; 65 + struct reset_control *reset; 66 + unsigned int format; 67 + struct clk *mclk; 68 + unsigned int mclk_rate; 69 + }; 70 + 71 + static const DECLARE_TLV_DB_RANGE(ntp8835_vol_scale, 72 + 0, 1, TLV_DB_SCALE_ITEM(-15000, 0, 0), 73 + 2, 6, TLV_DB_SCALE_ITEM(-15000, 1000, 0), 74 + 7, 0xff, TLV_DB_SCALE_ITEM(-10000, 50, 0), 75 + ); 76 + 77 + static int ntp8835_mute_info(struct snd_kcontrol *kcontrol, 78 + struct snd_ctl_elem_info *uinfo) 79 + { 80 + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 81 + uinfo->access = 82 + (SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE); 83 + uinfo->count = 1; 84 + 85 + uinfo->value.integer.min = 0; 86 + uinfo->value.integer.max = 1; 87 + uinfo->value.integer.step = 1; 88 + 89 + return 0; 90 + } 91 + 92 + static int ntp8835_mute_get(struct snd_kcontrol *kcontrol, 93 + struct snd_ctl_elem_value *ucontrol) 94 + { 95 + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 96 + unsigned int val; 97 + 98 + val = snd_soc_component_read(component, NTP8835_SOFT_MUTE); 99 + 100 + ucontrol->value.integer.value[0] = val ? 0 : 1; 101 + return 0; 102 + } 103 + 104 + static int ntp8835_mute_put(struct snd_kcontrol *kcontrol, 105 + struct snd_ctl_elem_value *ucontrol) 106 + { 107 + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 108 + unsigned int val; 109 + 110 + val = ucontrol->value.integer.value[0] ? 0 : 7; 111 + 112 + snd_soc_component_write(component, NTP8835_SOFT_MUTE, val); 113 + 114 + return 0; 115 + } 116 + 117 + static const struct snd_kcontrol_new ntp8835_vol_control[] = { 118 + SOC_SINGLE_TLV("Playback Volume", NTP8835_MASTER_VOL, 0, 119 + 0xff, 0, ntp8835_vol_scale), 120 + { 121 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 122 + .name = "Playback Switch", 123 + .info = ntp8835_mute_info, 124 + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, 125 + .get = ntp8835_mute_get, 126 + .put = ntp8835_mute_put, 127 + }, 128 + }; 129 + 130 + static void ntp8835_reset_gpio(struct ntp8835_priv *ntp8835) 131 + { 132 + /* 133 + * Proper initialization sequence for NTP835 amplifier requires driving 134 + * /RESET signal low during power up for at least 0.1us. The sequence is, 135 + * according to NTP8835 datasheet, 6.2 Timing Sequence (recommended): 136 + * Deassert for T2 >= 1ms... 137 + */ 138 + reset_control_deassert(ntp8835->reset); 139 + fsleep(1000); 140 + 141 + /* ...Assert for T3 >= 0.1us... */ 142 + reset_control_assert(ntp8835->reset); 143 + fsleep(1); 144 + 145 + /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */ 146 + reset_control_deassert(ntp8835->reset); 147 + fsleep(500); 148 + } 149 + 150 + static const struct reg_sequence ntp8835_sound_on[] = { 151 + { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_FPMLD }, 152 + { NTP8835_PWM_SWITCH, 0x00 }, 153 + { NTP8835_SOFT_MUTE, 0x00 }, 154 + }; 155 + 156 + static const struct reg_sequence ntp8835_sound_off[] = { 157 + { NTP8835_SOFT_MUTE, NTP8835_SOFT_MUTE_SM1 | 158 + NTP8835_SOFT_MUTE_SM2 | 159 + NTP8835_SOFT_MUTE_SM3 }, 160 + 161 + { NTP8835_PWM_SWITCH, NTP8835_PWM_SWITCH_POF1 | 162 + NTP8835_PWM_SWITCH_POF2 | 163 + NTP8835_PWM_SWITCH_POF3 }, 164 + 165 + { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_OUT_LOW | 166 + NTP8835_PWM_MASK_CTRL0_FPMLD }, 167 + }; 168 + 169 + static int ntp8835_load_firmware(struct ntp8835_priv *ntp8835) 170 + { 171 + int ret; 172 + 173 + ret = ntpfw_load(ntp8835->i2c, NTP8835_FW_NAME, NTP8835_FW_MAGIC); 174 + if (ret == -ENOENT) { 175 + dev_warn_once(&ntp8835->i2c->dev, 176 + "Could not find firmware %s\n", NTP8835_FW_NAME); 177 + return 0; 178 + } 179 + 180 + return ret; 181 + } 182 + 183 + static int ntp8835_snd_suspend(struct snd_soc_component *component) 184 + { 185 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 186 + 187 + regcache_cache_only(component->regmap, true); 188 + 189 + regmap_multi_reg_write_bypassed(component->regmap, 190 + ntp8835_sound_off, 191 + ARRAY_SIZE(ntp8835_sound_off)); 192 + 193 + /* 194 + * According to NTP8835 datasheet, 6.2 Timing Sequence (recommended): 195 + * wait after sound off for T6 >= 0.5ms 196 + */ 197 + fsleep(500); 198 + reset_control_assert(ntp8835->reset); 199 + 200 + regcache_mark_dirty(component->regmap); 201 + clk_disable_unprepare(ntp8835->mclk); 202 + 203 + return 0; 204 + } 205 + 206 + static int ntp8835_snd_resume(struct snd_soc_component *component) 207 + { 208 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 209 + int ret; 210 + 211 + ntp8835_reset_gpio(ntp8835); 212 + ret = clk_prepare_enable(ntp8835->mclk); 213 + if (ret) 214 + return ret; 215 + 216 + regmap_multi_reg_write_bypassed(component->regmap, 217 + ntp8835_sound_on, 218 + ARRAY_SIZE(ntp8835_sound_on)); 219 + 220 + ret = ntp8835_load_firmware(ntp8835); 221 + if (ret) { 222 + dev_err(&ntp8835->i2c->dev, "Failed to load firmware\n"); 223 + return ret; 224 + } 225 + 226 + regcache_cache_only(component->regmap, false); 227 + snd_soc_component_cache_sync(component); 228 + 229 + return 0; 230 + } 231 + 232 + static int ntp8835_probe(struct snd_soc_component *component) 233 + { 234 + int ret; 235 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 236 + struct device *dev = component->dev; 237 + 238 + ret = snd_soc_add_component_controls(component, ntp8835_vol_control, 239 + ARRAY_SIZE(ntp8835_vol_control)); 240 + if (ret) 241 + return dev_err_probe(dev, ret, "Failed to add controls\n"); 242 + 243 + ret = ntp8835_load_firmware(ntp8835); 244 + if (ret) 245 + return dev_err_probe(dev, ret, "Failed to load firmware\n"); 246 + 247 + return 0; 248 + } 249 + 250 + static const struct snd_soc_dapm_widget ntp8835_dapm_widgets[] = { 251 + SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0), 252 + 253 + SND_SOC_DAPM_OUTPUT("OUT1"), 254 + SND_SOC_DAPM_OUTPUT("OUT2"), 255 + SND_SOC_DAPM_OUTPUT("OUT3"), 256 + }; 257 + 258 + static const struct snd_soc_dapm_route ntp8835_dapm_routes[] = { 259 + { "OUT1", NULL, "AIFIN" }, 260 + { "OUT2", NULL, "AIFIN" }, 261 + { "OUT3", NULL, "AIFIN" }, 262 + }; 263 + 264 + static int ntp8835_set_component_sysclk(struct snd_soc_component *component, 265 + int clk_id, int source, 266 + unsigned int freq, int dir) 267 + { 268 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 269 + 270 + switch (freq) { 271 + case 12288000: 272 + case 24576000: 273 + case 18432000: 274 + ntp8835->mclk_rate = freq; 275 + break; 276 + default: 277 + ntp8835->mclk_rate = 0; 278 + dev_err(component->dev, "Unsupported MCLK value: %u", freq); 279 + return -EINVAL; 280 + }; 281 + 282 + return 0; 283 + } 284 + 285 + static const struct snd_soc_component_driver soc_component_ntp8835 = { 286 + .probe = ntp8835_probe, 287 + .suspend = ntp8835_snd_suspend, 288 + .resume = ntp8835_snd_resume, 289 + .dapm_widgets = ntp8835_dapm_widgets, 290 + .num_dapm_widgets = ARRAY_SIZE(ntp8835_dapm_widgets), 291 + .dapm_routes = ntp8835_dapm_routes, 292 + .num_dapm_routes = ARRAY_SIZE(ntp8835_dapm_routes), 293 + .set_sysclk = ntp8835_set_component_sysclk, 294 + }; 295 + 296 + static int ntp8835_hw_params(struct snd_pcm_substream *substream, 297 + struct snd_pcm_hw_params *params, 298 + struct snd_soc_dai *dai) 299 + { 300 + struct snd_soc_component *component = dai->component; 301 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 302 + unsigned int input_fmt = 0; 303 + unsigned int gsa_fmt = 0; 304 + unsigned int gsa_fmt_mask; 305 + unsigned int mcf; 306 + int ret; 307 + 308 + switch (ntp8835->mclk_rate) { 309 + case 12288000: 310 + mcf = 0; 311 + break; 312 + case 24576000: 313 + mcf = 1; 314 + break; 315 + case 18432000: 316 + mcf = 2; 317 + break; 318 + default: 319 + return -EINVAL; 320 + } 321 + 322 + ret = snd_soc_component_update_bits(component, NTP8835_MCLK_FREQ_CTRL, 323 + NTP8835_MCLK_FREQ_MCF, mcf); 324 + if (ret) 325 + return ret; 326 + 327 + switch (ntp8835->format) { 328 + case SND_SOC_DAIFMT_I2S: 329 + break; 330 + case SND_SOC_DAIFMT_RIGHT_J: 331 + input_fmt |= NTP8835_INPUT_FMT_GSA_MODE; 332 + gsa_fmt |= NTP8835_GSA_RIGHT_J; 333 + break; 334 + case SND_SOC_DAIFMT_LEFT_J: 335 + input_fmt |= NTP8835_INPUT_FMT_GSA_MODE; 336 + break; 337 + } 338 + 339 + ret = snd_soc_component_update_bits(component, NTP8835_INPUT_FMT, 340 + NTP8835_INPUT_FMT_MASTER_MODE | 341 + NTP8835_INPUT_FMT_GSA_MODE, 342 + input_fmt); 343 + 344 + if (!(input_fmt & NTP8835_INPUT_FMT_GSA_MODE) || ret < 0) 345 + return ret; 346 + 347 + switch (params_width(params)) { 348 + case 24: 349 + gsa_fmt |= NTP8835_GSA_BS(0); 350 + break; 351 + case 20: 352 + gsa_fmt |= NTP8835_GSA_BS(1); 353 + break; 354 + case 18: 355 + gsa_fmt |= NTP8835_GSA_BS(2); 356 + break; 357 + case 16: 358 + gsa_fmt |= NTP8835_GSA_BS(3); 359 + break; 360 + default: 361 + return -EINVAL; 362 + } 363 + 364 + gsa_fmt_mask = NTP8835_GSA_BS_MASK | 365 + NTP8835_GSA_RIGHT_J | 366 + NTP8835_GSA_LSB; 367 + return snd_soc_component_update_bits(component, NTP8835_GSA_FMT, 368 + gsa_fmt_mask, gsa_fmt); 369 + } 370 + 371 + static int ntp8835_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 372 + { 373 + struct snd_soc_component *component = dai->component; 374 + struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component); 375 + 376 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 377 + case SND_SOC_DAIFMT_I2S: 378 + case SND_SOC_DAIFMT_RIGHT_J: 379 + case SND_SOC_DAIFMT_LEFT_J: 380 + ntp8835->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 381 + break; 382 + default: 383 + return -EINVAL; 384 + } 385 + return 0; 386 + }; 387 + 388 + static const struct snd_soc_dai_ops ntp8835_dai_ops = { 389 + .hw_params = ntp8835_hw_params, 390 + .set_fmt = ntp8835_set_fmt, 391 + }; 392 + 393 + static struct snd_soc_dai_driver ntp8835_dai = { 394 + .name = "ntp8835-amplifier", 395 + .playback = { 396 + .stream_name = "Playback", 397 + .channels_min = 1, 398 + .channels_max = 3, 399 + .rates = SNDRV_PCM_RATE_8000_192000, 400 + .formats = NTP8835_FORMATS, 401 + }, 402 + .ops = &ntp8835_dai_ops, 403 + }; 404 + 405 + static const struct regmap_config ntp8835_regmap = { 406 + .reg_bits = 8, 407 + .val_bits = 8, 408 + .max_register = REG_MAX, 409 + .cache_type = REGCACHE_MAPLE, 410 + }; 411 + 412 + static int ntp8835_i2c_probe(struct i2c_client *i2c) 413 + { 414 + struct ntp8835_priv *ntp8835; 415 + struct regmap *regmap; 416 + int ret; 417 + 418 + ntp8835 = devm_kzalloc(&i2c->dev, sizeof(*ntp8835), GFP_KERNEL); 419 + if (!ntp8835) 420 + return -ENOMEM; 421 + 422 + ntp8835->i2c = i2c; 423 + 424 + ntp8835->reset = devm_reset_control_get_shared(&i2c->dev, NULL); 425 + if (IS_ERR(ntp8835->reset)) 426 + return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->reset), 427 + "Failed to get reset\n"); 428 + 429 + ret = reset_control_deassert(ntp8835->reset); 430 + if (ret) 431 + return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->reset), 432 + "Failed to deassert reset\n"); 433 + 434 + dev_set_drvdata(&i2c->dev, ntp8835); 435 + 436 + ntp8835_reset_gpio(ntp8835); 437 + 438 + regmap = devm_regmap_init_i2c(i2c, &ntp8835_regmap); 439 + if (IS_ERR(regmap)) 440 + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), 441 + "Failed to allocate regmap\n"); 442 + 443 + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8835, 444 + &ntp8835_dai, 1); 445 + if (ret) 446 + return dev_err_probe(&i2c->dev, ret, 447 + "Failed to register component\n"); 448 + 449 + ntp8835->mclk = devm_clk_get_enabled(&i2c->dev, "mclk"); 450 + if (IS_ERR(ntp8835->mclk)) 451 + return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->mclk), "failed to get mclk\n"); 452 + 453 + return 0; 454 + } 455 + 456 + static const struct i2c_device_id ntp8835_i2c_id[] = { 457 + { "ntp8835", 0 }, 458 + {} 459 + }; 460 + MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id); 461 + 462 + static const struct of_device_id ntp8835_of_match[] = { 463 + {.compatible = "neofidelity,ntp8835",}, 464 + {} 465 + }; 466 + MODULE_DEVICE_TABLE(of, ntp8835_of_match); 467 + 468 + static struct i2c_driver ntp8835_i2c_driver = { 469 + .probe = ntp8835_i2c_probe, 470 + .id_table = ntp8835_i2c_id, 471 + .driver = { 472 + .name = "ntp8835", 473 + .of_match_table = ntp8835_of_match, 474 + }, 475 + }; 476 + module_i2c_driver(ntp8835_i2c_driver); 477 + 478 + MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>"); 479 + MODULE_DESCRIPTION("NTP8835 Audio Amplifier Driver"); 480 + MODULE_LICENSE("GPL");
+397
sound/soc/codecs/ntp8918.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Driver for the NTP8918 Audio Amplifier 4 + * 5 + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. 6 + * 7 + * Author: Igor Prusov <ivprusov@salutedevices.com> 8 + */ 9 + 10 + #include <linux/kernel.h> 11 + #include <linux/clk.h> 12 + #include <linux/reset.h> 13 + #include <linux/i2c.h> 14 + #include <linux/regmap.h> 15 + #include <linux/clk.h> 16 + #include <linux/clk-provider.h> 17 + 18 + #include <sound/initval.h> 19 + #include <sound/core.h> 20 + #include <sound/pcm.h> 21 + #include <sound/pcm_params.h> 22 + #include <sound/soc.h> 23 + #include <sound/soc-component.h> 24 + #include <sound/tlv.h> 25 + 26 + #include "ntpfw.h" 27 + 28 + #define NTP8918_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 29 + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 30 + 31 + #define NTP8918_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 32 + SNDRV_PCM_FMTBIT_S20_3LE | \ 33 + SNDRV_PCM_FMTBIT_S24_LE | \ 34 + SNDRV_PCM_FMTBIT_S32_LE) 35 + 36 + #define NTP8918_INPUT_FMT 0x0 37 + #define NTP8918_INPUT_FMT_MASTER_MODE BIT(0) 38 + #define NTP8918_INPUT_FMT_GSA_MODE BIT(1) 39 + #define NTP8918_GSA_FMT 0x1 40 + #define NTP8918_GSA_BS_MASK GENMASK(3, 2) 41 + #define NTP8918_GSA_BS(x) ((x) << 2) 42 + #define NTP8918_GSA_RIGHT_J BIT(0) 43 + #define NTP8918_GSA_LSB BIT(1) 44 + #define NTP8918_MCLK_FREQ_CTRL 0x2 45 + #define NTP8918_MCLK_FREQ_MCF GENMASK(1, 0) 46 + #define NTP8918_MASTER_VOL 0x0C 47 + #define NTP8918_CHNL_A_VOL 0x17 48 + #define NTP8918_CHNL_B_VOL 0x18 49 + #define NTP8918_SOFT_MUTE 0x33 50 + #define NTP8918_SOFT_MUTE_SM1 BIT(0) 51 + #define NTP8918_SOFT_MUTE_SM2 BIT(1) 52 + #define NTP8918_PWM_SWITCH 0x34 53 + #define NTP8918_PWM_MASK_CTRL0 0x35 54 + #define REG_MAX NTP8918_PWM_MASK_CTRL0 55 + 56 + #define NTP8918_FW_NAME "eq_8918.bin" 57 + #define NTP8918_FW_MAGIC 0x38393138 /* "8918" */ 58 + 59 + struct ntp8918_priv { 60 + struct i2c_client *i2c; 61 + struct clk *bck; 62 + struct reset_control *reset; 63 + unsigned int format; 64 + }; 65 + 66 + static const DECLARE_TLV_DB_SCALE(ntp8918_master_vol_scale, -12550, 50, 0); 67 + 68 + static const struct snd_kcontrol_new ntp8918_vol_control[] = { 69 + SOC_SINGLE_RANGE_TLV("Playback Volume", NTP8918_MASTER_VOL, 0, 70 + 0x04, 0xff, 0, ntp8918_master_vol_scale), 71 + SOC_SINGLE("Playback Switch", NTP8918_PWM_MASK_CTRL0, 1, 1, 1), 72 + }; 73 + 74 + static void ntp8918_reset_gpio(struct ntp8918_priv *ntp8918) 75 + { 76 + /* 77 + * Proper initialization sequence for NTP8918 amplifier requires driving 78 + * /RESET signal low during power up for at least 0.1us. The sequence is, 79 + * according to NTP8918 datasheet, 6.2 Timing Sequence 1: 80 + * Deassert for T2 >= 1ms... 81 + */ 82 + reset_control_deassert(ntp8918->reset); 83 + fsleep(1000); 84 + 85 + /* ...Assert for T3 >= 0.1us... */ 86 + reset_control_assert(ntp8918->reset); 87 + fsleep(1); 88 + 89 + /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */ 90 + reset_control_deassert(ntp8918->reset); 91 + fsleep(500); 92 + } 93 + 94 + static const struct reg_sequence ntp8918_sound_off[] = { 95 + { NTP8918_MASTER_VOL, 0 }, 96 + }; 97 + 98 + static const struct reg_sequence ntp8918_sound_on[] = { 99 + { NTP8918_MASTER_VOL, 0b11 }, 100 + }; 101 + 102 + static int ntp8918_load_firmware(struct ntp8918_priv *ntp8918) 103 + { 104 + int ret; 105 + 106 + ret = ntpfw_load(ntp8918->i2c, NTP8918_FW_NAME, NTP8918_FW_MAGIC); 107 + if (ret == -ENOENT) { 108 + dev_warn_once(&ntp8918->i2c->dev, "Could not find firmware %s\n", 109 + NTP8918_FW_NAME); 110 + return 0; 111 + } 112 + 113 + return ret; 114 + } 115 + 116 + static int ntp8918_snd_suspend(struct snd_soc_component *component) 117 + { 118 + struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component); 119 + 120 + regcache_cache_only(component->regmap, true); 121 + 122 + regmap_multi_reg_write_bypassed(component->regmap, 123 + ntp8918_sound_off, 124 + ARRAY_SIZE(ntp8918_sound_off)); 125 + 126 + /* 127 + * According to NTP8918 datasheet, 6.2 Timing Sequence 1: 128 + * wait after sound off for T6 >= 0.5ms 129 + */ 130 + fsleep(500); 131 + reset_control_assert(ntp8918->reset); 132 + 133 + regcache_mark_dirty(component->regmap); 134 + clk_disable_unprepare(ntp8918->bck); 135 + 136 + return 0; 137 + } 138 + 139 + static int ntp8918_snd_resume(struct snd_soc_component *component) 140 + { 141 + struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component); 142 + int ret; 143 + 144 + ret = clk_prepare_enable(ntp8918->bck); 145 + if (ret) 146 + return ret; 147 + 148 + ntp8918_reset_gpio(ntp8918); 149 + 150 + regmap_multi_reg_write_bypassed(component->regmap, 151 + ntp8918_sound_on, 152 + ARRAY_SIZE(ntp8918_sound_on)); 153 + 154 + ret = ntp8918_load_firmware(ntp8918); 155 + if (ret) { 156 + dev_err(&ntp8918->i2c->dev, "Failed to load firmware\n"); 157 + return ret; 158 + } 159 + 160 + regcache_cache_only(component->regmap, false); 161 + snd_soc_component_cache_sync(component); 162 + 163 + return 0; 164 + } 165 + 166 + static int ntp8918_probe(struct snd_soc_component *component) 167 + { 168 + int ret; 169 + struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component); 170 + struct device *dev = component->dev; 171 + 172 + ret = snd_soc_add_component_controls(component, ntp8918_vol_control, 173 + ARRAY_SIZE(ntp8918_vol_control)); 174 + if (ret) 175 + return dev_err_probe(dev, ret, "Failed to add controls\n"); 176 + 177 + ret = ntp8918_load_firmware(ntp8918); 178 + if (ret) 179 + return dev_err_probe(dev, ret, "Failed to load firmware\n"); 180 + 181 + return 0; 182 + } 183 + 184 + static const struct snd_soc_dapm_widget ntp8918_dapm_widgets[] = { 185 + SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0), 186 + 187 + SND_SOC_DAPM_OUTPUT("OUT1"), 188 + SND_SOC_DAPM_OUTPUT("OUT2"), 189 + }; 190 + 191 + static const struct snd_soc_dapm_route ntp8918_dapm_routes[] = { 192 + { "OUT1", NULL, "AIFIN" }, 193 + { "OUT2", NULL, "AIFIN" }, 194 + }; 195 + 196 + static const struct snd_soc_component_driver soc_component_ntp8918 = { 197 + .probe = ntp8918_probe, 198 + .suspend = ntp8918_snd_suspend, 199 + .resume = ntp8918_snd_resume, 200 + .dapm_widgets = ntp8918_dapm_widgets, 201 + .num_dapm_widgets = ARRAY_SIZE(ntp8918_dapm_widgets), 202 + .dapm_routes = ntp8918_dapm_routes, 203 + .num_dapm_routes = ARRAY_SIZE(ntp8918_dapm_routes), 204 + }; 205 + 206 + static int ntp8918_hw_params(struct snd_pcm_substream *substream, 207 + struct snd_pcm_hw_params *params, 208 + struct snd_soc_dai *dai) 209 + { 210 + struct snd_soc_component *component = dai->component; 211 + struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component); 212 + unsigned int input_fmt = 0; 213 + unsigned int gsa_fmt = 0; 214 + unsigned int gsa_fmt_mask; 215 + unsigned int mcf; 216 + int bclk; 217 + int ret; 218 + 219 + bclk = snd_soc_params_to_bclk(params); 220 + switch (bclk) { 221 + case 3072000: 222 + case 2822400: 223 + mcf = 0; 224 + break; 225 + case 6144000: 226 + mcf = 1; 227 + break; 228 + case 2048000: 229 + mcf = 2; 230 + break; 231 + default: 232 + return -EINVAL; 233 + } 234 + 235 + ret = snd_soc_component_update_bits(component, NTP8918_MCLK_FREQ_CTRL, 236 + NTP8918_MCLK_FREQ_MCF, mcf); 237 + if (ret) 238 + return ret; 239 + 240 + switch (ntp8918->format) { 241 + case SND_SOC_DAIFMT_I2S: 242 + break; 243 + case SND_SOC_DAIFMT_RIGHT_J: 244 + input_fmt |= NTP8918_INPUT_FMT_GSA_MODE; 245 + gsa_fmt |= NTP8918_GSA_RIGHT_J; 246 + break; 247 + case SND_SOC_DAIFMT_LEFT_J: 248 + input_fmt |= NTP8918_INPUT_FMT_GSA_MODE; 249 + break; 250 + } 251 + 252 + ret = snd_soc_component_update_bits(component, NTP8918_INPUT_FMT, 253 + NTP8918_INPUT_FMT_MASTER_MODE | 254 + NTP8918_INPUT_FMT_GSA_MODE, 255 + input_fmt); 256 + 257 + if (!(input_fmt & NTP8918_INPUT_FMT_GSA_MODE) || ret < 0) 258 + return ret; 259 + 260 + switch (params_width(params)) { 261 + case 24: 262 + gsa_fmt |= NTP8918_GSA_BS(0); 263 + break; 264 + case 20: 265 + gsa_fmt |= NTP8918_GSA_BS(1); 266 + break; 267 + case 18: 268 + gsa_fmt |= NTP8918_GSA_BS(2); 269 + break; 270 + case 16: 271 + gsa_fmt |= NTP8918_GSA_BS(3); 272 + break; 273 + default: 274 + return -EINVAL; 275 + } 276 + 277 + gsa_fmt_mask = NTP8918_GSA_BS_MASK | 278 + NTP8918_GSA_RIGHT_J | 279 + NTP8918_GSA_LSB; 280 + return snd_soc_component_update_bits(component, NTP8918_GSA_FMT, 281 + gsa_fmt_mask, gsa_fmt); 282 + } 283 + 284 + static int ntp8918_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 285 + { 286 + struct snd_soc_component *component = dai->component; 287 + struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component); 288 + 289 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 290 + case SND_SOC_DAIFMT_I2S: 291 + case SND_SOC_DAIFMT_RIGHT_J: 292 + case SND_SOC_DAIFMT_LEFT_J: 293 + ntp8918->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 294 + break; 295 + default: 296 + return -EINVAL; 297 + } 298 + return 0; 299 + } 300 + 301 + static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream) 302 + { 303 + unsigned int mute_mask = NTP8918_SOFT_MUTE_SM1 | 304 + NTP8918_SOFT_MUTE_SM2; 305 + 306 + return snd_soc_component_update_bits(dai->component, NTP8918_SOFT_MUTE, 307 + mute_mask, mute ? mute_mask : 0); 308 + } 309 + 310 + static const struct snd_soc_dai_ops ntp8918_dai_ops = { 311 + .hw_params = ntp8918_hw_params, 312 + .set_fmt = ntp8918_set_fmt, 313 + .mute_stream = ntp8918_digital_mute, 314 + }; 315 + 316 + static struct snd_soc_dai_driver ntp8918_dai = { 317 + .name = "ntp8918-amplifier", 318 + .playback = { 319 + .stream_name = "Playback", 320 + .channels_min = 1, 321 + .channels_max = 2, 322 + .rates = NTP8918_RATES, 323 + .formats = NTP8918_FORMATS, 324 + }, 325 + .ops = &ntp8918_dai_ops, 326 + }; 327 + 328 + static const struct regmap_config ntp8918_regmap = { 329 + .reg_bits = 8, 330 + .val_bits = 8, 331 + .max_register = REG_MAX, 332 + .cache_type = REGCACHE_MAPLE, 333 + }; 334 + 335 + static int ntp8918_i2c_probe(struct i2c_client *i2c) 336 + { 337 + struct ntp8918_priv *ntp8918; 338 + int ret; 339 + struct regmap *regmap; 340 + 341 + ntp8918 = devm_kzalloc(&i2c->dev, sizeof(*ntp8918), GFP_KERNEL); 342 + if (!ntp8918) 343 + return -ENOMEM; 344 + 345 + ntp8918->i2c = i2c; 346 + 347 + ntp8918->reset = devm_reset_control_get_shared(&i2c->dev, NULL); 348 + if (IS_ERR(ntp8918->reset)) 349 + return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->reset), "Failed to get reset\n"); 350 + 351 + dev_set_drvdata(&i2c->dev, ntp8918); 352 + 353 + ntp8918_reset_gpio(ntp8918); 354 + 355 + regmap = devm_regmap_init_i2c(i2c, &ntp8918_regmap); 356 + if (IS_ERR(regmap)) 357 + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), 358 + "Failed to allocate regmap\n"); 359 + 360 + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8918, 361 + &ntp8918_dai, 1); 362 + if (ret) 363 + return dev_err_probe(&i2c->dev, ret, 364 + "Failed to register component\n"); 365 + 366 + ntp8918->bck = devm_clk_get_enabled(&i2c->dev, "bck"); 367 + if (IS_ERR(ntp8918->bck)) 368 + return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->bck), "failed to get bck clock\n"); 369 + 370 + return 0; 371 + } 372 + 373 + static const struct i2c_device_id ntp8918_i2c_id[] = { 374 + { "ntp8918", 0 }, 375 + {} 376 + }; 377 + MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id); 378 + 379 + static const struct of_device_id ntp8918_of_match[] = { 380 + {.compatible = "neofidelity,ntp8918"}, 381 + {} 382 + }; 383 + MODULE_DEVICE_TABLE(of, ntp8918_of_match); 384 + 385 + static struct i2c_driver ntp8918_i2c_driver = { 386 + .probe = ntp8918_i2c_probe, 387 + .id_table = ntp8918_i2c_id, 388 + .driver = { 389 + .name = "ntp8918", 390 + .of_match_table = ntp8918_of_match, 391 + }, 392 + }; 393 + module_i2c_driver(ntp8918_i2c_driver); 394 + 395 + MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>"); 396 + MODULE_DESCRIPTION("NTP8918 Audio Amplifier Driver"); 397 + MODULE_LICENSE("GPL");
+137
sound/soc/codecs/ntpfw.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * ntpfw.c - Firmware helper functions for Neofidelity codecs 4 + * 5 + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. 6 + */ 7 + 8 + #include <linux/i2c.h> 9 + #include <linux/firmware.h> 10 + #include <linux/module.h> 11 + 12 + #include "ntpfw.h" 13 + 14 + struct ntpfw_chunk { 15 + __be16 length; 16 + u8 step; 17 + u8 data[]; 18 + } __packed; 19 + 20 + struct ntpfw_header { 21 + __be32 magic; 22 + } __packed; 23 + 24 + static bool ntpfw_verify(struct device *dev, const u8 *buf, size_t buf_size, u32 magic) 25 + { 26 + const struct ntpfw_header *header = (struct ntpfw_header *)buf; 27 + u32 buf_magic; 28 + 29 + if (buf_size <= sizeof(*header)) { 30 + dev_err(dev, "Failed to load firmware: image too small\n"); 31 + return false; 32 + } 33 + 34 + buf_magic = be32_to_cpu(header->magic); 35 + if (buf_magic != magic) { 36 + dev_err(dev, "Failed to load firmware: invalid magic 0x%x:\n", buf_magic); 37 + return false; 38 + } 39 + 40 + return true; 41 + } 42 + 43 + static bool ntpfw_verify_chunk(struct device *dev, const struct ntpfw_chunk *chunk, size_t buf_size) 44 + { 45 + size_t chunk_size; 46 + 47 + if (buf_size <= sizeof(*chunk)) { 48 + dev_err(dev, "Failed to load firmware: chunk size too big\n"); 49 + return false; 50 + } 51 + 52 + if (chunk->step != 2 && chunk->step != 5) { 53 + dev_err(dev, "Failed to load firmware: invalid chunk step: %d\n", chunk->step); 54 + return false; 55 + } 56 + 57 + chunk_size = be16_to_cpu(chunk->length); 58 + if (chunk_size > buf_size) { 59 + dev_err(dev, "Failed to load firmware: invalid chunk length\n"); 60 + return false; 61 + } 62 + 63 + if (chunk_size % chunk->step) { 64 + dev_err(dev, "Failed to load firmware: chunk length and step mismatch\n"); 65 + return false; 66 + } 67 + 68 + return true; 69 + } 70 + 71 + static int ntpfw_send_chunk(struct i2c_client *i2c, const struct ntpfw_chunk *chunk) 72 + { 73 + int ret; 74 + size_t i; 75 + size_t length = be16_to_cpu(chunk->length); 76 + 77 + for (i = 0; i < length; i += chunk->step) { 78 + ret = i2c_master_send(i2c, &chunk->data[i], chunk->step); 79 + if (ret != chunk->step) { 80 + dev_err(&i2c->dev, "I2C send failed: %d\n", ret); 81 + return ret < 0 ? ret : -EIO; 82 + } 83 + } 84 + 85 + return 0; 86 + } 87 + 88 + int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) 89 + { 90 + struct device *dev = &i2c->dev; 91 + const struct ntpfw_chunk *chunk; 92 + const struct firmware *fw; 93 + const u8 *data; 94 + size_t leftover; 95 + int ret; 96 + 97 + ret = request_firmware(&fw, name, dev); 98 + if (ret) { 99 + dev_warn(dev, "request_firmware '%s' failed with %d\n", 100 + name, ret); 101 + return ret; 102 + } 103 + 104 + if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { 105 + ret = -EINVAL; 106 + goto done; 107 + } 108 + 109 + data = fw->data + sizeof(struct ntpfw_header); 110 + leftover = fw->size - sizeof(struct ntpfw_header); 111 + 112 + while (leftover) { 113 + chunk = (struct ntpfw_chunk *)data; 114 + 115 + if (!ntpfw_verify_chunk(dev, chunk, leftover)) { 116 + ret = -EINVAL; 117 + goto done; 118 + } 119 + 120 + ret = ntpfw_send_chunk(i2c, chunk); 121 + if (ret) 122 + goto done; 123 + 124 + data += be16_to_cpu(chunk->length) + sizeof(*chunk); 125 + leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); 126 + } 127 + 128 + done: 129 + release_firmware(fw); 130 + 131 + return ret; 132 + } 133 + EXPORT_SYMBOL_GPL(ntpfw_load); 134 + 135 + MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>"); 136 + MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware"); 137 + MODULE_LICENSE("GPL");
+23
sound/soc/codecs/ntpfw.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /** 3 + * ntpfw.h - Firmware helper functions for Neofidelity codecs 4 + * 5 + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. 6 + */ 7 + 8 + #ifndef __NTPFW_H__ 9 + #define __NTPFW_H__ 10 + #include <linux/i2c.h> 11 + #include <linux/firmware.h> 12 + 13 + /** 14 + * ntpfw_load - load firmware to amplifier over i2c interface. 15 + * 16 + * @i2c Pointer to amplifier's I2C client. 17 + * @name Firmware file name. 18 + * @magic Magic number to validate firmware. 19 + * @return 0 or error code upon error. 20 + */ 21 + int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); 22 + 23 + #endif /* __NTPFW_H__ */