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

ASoC: codecs: Add NeoFidelity Firmware helpers

Add support for loading firmware for NeoFidelity amplifiers.

Signed-off-by: Igor Prusov <ivprusov@salutedevices.com>
Link: https://patch.msgid.link/20240925-ntp-amps-8918-8835-v3-2-e2459a8191a6@salutedevices.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Igor Prusov and committed by
Mark Brown
ba1850dc 5d9e6d6f

+165
+3
sound/soc/codecs/Kconfig
··· 2565 2565 tristate 2566 2566 depends on I2C 2567 2567 2568 + config SND_SOC_NTPFW 2569 + tristate 2570 + 2568 2571 config SND_SOC_TPA6130A2 2569 2572 tristate "Texas Instruments TPA6130A2 headphone amplifier" 2570 2573 depends on I2C
+2
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-ntpfw-y := ntpfw.o 192 193 snd-soc-hdmi-codec-y := hdmi-codec.o 193 194 snd-soc-pcm1681-y := pcm1681.o 194 195 snd-soc-pcm1789-codec-y := pcm1789.o ··· 592 591 obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o 593 592 obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o 594 593 obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o 594 + obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.o 595 595 obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o 596 596 obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o 597 597 obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
+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__ */