1 // SPDX-License-Identifier: GPL-2.0-only
3 * ntpfw.c - Firmware helper functions for Neofidelity codecs
5 * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
9 #include <linux/firmware.h>
10 #include <linux/module.h>
24 static bool ntpfw_verify(struct device
*dev
, const u8
*buf
, size_t buf_size
, u32 magic
)
26 const struct ntpfw_header
*header
= (struct ntpfw_header
*)buf
;
29 if (buf_size
<= sizeof(*header
)) {
30 dev_err(dev
, "Failed to load firmware: image too small\n");
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
);
43 static bool ntpfw_verify_chunk(struct device
*dev
, const struct ntpfw_chunk
*chunk
, size_t buf_size
)
47 if (buf_size
<= sizeof(*chunk
)) {
48 dev_err(dev
, "Failed to load firmware: chunk size too big\n");
52 if (chunk
->step
!= 2 && chunk
->step
!= 5) {
53 dev_err(dev
, "Failed to load firmware: invalid chunk step: %d\n", chunk
->step
);
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");
63 if (chunk_size
% chunk
->step
) {
64 dev_err(dev
, "Failed to load firmware: chunk length and step mismatch\n");
71 static int ntpfw_send_chunk(struct i2c_client
*i2c
, const struct ntpfw_chunk
*chunk
)
75 size_t length
= be16_to_cpu(chunk
->length
);
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
;
88 int ntpfw_load(struct i2c_client
*i2c
, const char *name
, u32 magic
)
90 struct device
*dev
= &i2c
->dev
;
91 const struct ntpfw_chunk
*chunk
;
92 const struct firmware
*fw
;
97 ret
= request_firmware(&fw
, name
, dev
);
99 dev_warn(dev
, "request_firmware '%s' failed with %d\n",
104 if (!ntpfw_verify(dev
, fw
->data
, fw
->size
, magic
)) {
109 data
= fw
->data
+ sizeof(struct ntpfw_header
);
110 leftover
= fw
->size
- sizeof(struct ntpfw_header
);
113 chunk
= (struct ntpfw_chunk
*)data
;
115 if (!ntpfw_verify_chunk(dev
, chunk
, leftover
)) {
120 ret
= ntpfw_send_chunk(i2c
, chunk
);
124 data
+= be16_to_cpu(chunk
->length
) + sizeof(*chunk
);
125 leftover
-= be16_to_cpu(chunk
->length
) + sizeof(*chunk
);
129 release_firmware(fw
);
133 EXPORT_SYMBOL_GPL(ntpfw_load
);
135 MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
136 MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware");
137 MODULE_LICENSE("GPL");