Commit 82441fffc8d9c28e168df3bcd9f9c95bfae1ebbc
Exists in
master
and in
20 other branches
Merge remote-tracking branch 'asoc/fix/sigmadsp' into asoc-next
Showing 1 changed file Inline Diff
sound/soc/codecs/sigmadsp.c
1 | /* | 1 | /* |
2 | * Load Analog Devices SigmaStudio firmware files | 2 | * Load Analog Devices SigmaStudio firmware files |
3 | * | 3 | * |
4 | * Copyright 2009-2011 Analog Devices Inc. | 4 | * Copyright 2009-2011 Analog Devices Inc. |
5 | * | 5 | * |
6 | * Licensed under the GPL-2 or later. | 6 | * Licensed under the GPL-2 or later. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/crc32.h> | 9 | #include <linux/crc32.h> |
10 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
11 | #include <linux/firmware.h> | 11 | #include <linux/firmware.h> |
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/i2c.h> | 13 | #include <linux/i2c.h> |
14 | #include <linux/regmap.h> | 14 | #include <linux/regmap.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | 16 | ||
17 | #include "sigmadsp.h" | 17 | #include "sigmadsp.h" |
18 | 18 | ||
19 | #define SIGMA_MAGIC "ADISIGM" | 19 | #define SIGMA_MAGIC "ADISIGM" |
20 | 20 | ||
21 | struct sigma_firmware_header { | 21 | struct sigma_firmware_header { |
22 | unsigned char magic[7]; | 22 | unsigned char magic[7]; |
23 | u8 version; | 23 | u8 version; |
24 | __le32 crc; | 24 | __le32 crc; |
25 | } __packed; | 25 | } __packed; |
26 | 26 | ||
27 | enum { | 27 | enum { |
28 | SIGMA_ACTION_WRITEXBYTES = 0, | 28 | SIGMA_ACTION_WRITEXBYTES = 0, |
29 | SIGMA_ACTION_WRITESINGLE, | 29 | SIGMA_ACTION_WRITESINGLE, |
30 | SIGMA_ACTION_WRITESAFELOAD, | 30 | SIGMA_ACTION_WRITESAFELOAD, |
31 | SIGMA_ACTION_DELAY, | 31 | SIGMA_ACTION_DELAY, |
32 | SIGMA_ACTION_PLLWAIT, | 32 | SIGMA_ACTION_PLLWAIT, |
33 | SIGMA_ACTION_NOOP, | 33 | SIGMA_ACTION_NOOP, |
34 | SIGMA_ACTION_END, | 34 | SIGMA_ACTION_END, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | struct sigma_action { | 37 | struct sigma_action { |
38 | u8 instr; | 38 | u8 instr; |
39 | u8 len_hi; | 39 | u8 len_hi; |
40 | __le16 len; | 40 | __le16 len; |
41 | __be16 addr; | 41 | __be16 addr; |
42 | unsigned char payload[]; | 42 | unsigned char payload[]; |
43 | } __packed; | 43 | } __packed; |
44 | 44 | ||
45 | struct sigma_firmware { | 45 | struct sigma_firmware { |
46 | const struct firmware *fw; | 46 | const struct firmware *fw; |
47 | size_t pos; | 47 | size_t pos; |
48 | 48 | ||
49 | void *control_data; | 49 | void *control_data; |
50 | int (*write)(void *control_data, const struct sigma_action *sa, | 50 | int (*write)(void *control_data, const struct sigma_action *sa, |
51 | size_t len); | 51 | size_t len); |
52 | }; | 52 | }; |
53 | 53 | ||
54 | static inline u32 sigma_action_len(struct sigma_action *sa) | 54 | static inline u32 sigma_action_len(struct sigma_action *sa) |
55 | { | 55 | { |
56 | return (sa->len_hi << 16) | le16_to_cpu(sa->len); | 56 | return (sa->len_hi << 16) | le16_to_cpu(sa->len); |
57 | } | 57 | } |
58 | 58 | ||
59 | static size_t sigma_action_size(struct sigma_action *sa) | 59 | static size_t sigma_action_size(struct sigma_action *sa) |
60 | { | 60 | { |
61 | size_t payload = 0; | 61 | size_t payload = 0; |
62 | 62 | ||
63 | switch (sa->instr) { | 63 | switch (sa->instr) { |
64 | case SIGMA_ACTION_WRITEXBYTES: | 64 | case SIGMA_ACTION_WRITEXBYTES: |
65 | case SIGMA_ACTION_WRITESINGLE: | 65 | case SIGMA_ACTION_WRITESINGLE: |
66 | case SIGMA_ACTION_WRITESAFELOAD: | 66 | case SIGMA_ACTION_WRITESAFELOAD: |
67 | payload = sigma_action_len(sa); | 67 | payload = sigma_action_len(sa); |
68 | break; | 68 | break; |
69 | default: | 69 | default: |
70 | break; | 70 | break; |
71 | } | 71 | } |
72 | 72 | ||
73 | payload = ALIGN(payload, 2); | 73 | payload = ALIGN(payload, 2); |
74 | 74 | ||
75 | return payload + sizeof(struct sigma_action); | 75 | return payload + sizeof(struct sigma_action); |
76 | } | 76 | } |
77 | 77 | ||
78 | /* | 78 | /* |
79 | * Returns a negative error value in case of an error, 0 if processing of | 79 | * Returns a negative error value in case of an error, 0 if processing of |
80 | * the firmware should be stopped after this action, 1 otherwise. | 80 | * the firmware should be stopped after this action, 1 otherwise. |
81 | */ | 81 | */ |
82 | static int | 82 | static int |
83 | process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) | 83 | process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa) |
84 | { | 84 | { |
85 | size_t len = sigma_action_len(sa); | 85 | size_t len = sigma_action_len(sa); |
86 | int ret; | 86 | int ret; |
87 | 87 | ||
88 | pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, | 88 | pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, |
89 | sa->instr, sa->addr, len); | 89 | sa->instr, sa->addr, len); |
90 | 90 | ||
91 | switch (sa->instr) { | 91 | switch (sa->instr) { |
92 | case SIGMA_ACTION_WRITEXBYTES: | 92 | case SIGMA_ACTION_WRITEXBYTES: |
93 | case SIGMA_ACTION_WRITESINGLE: | 93 | case SIGMA_ACTION_WRITESINGLE: |
94 | case SIGMA_ACTION_WRITESAFELOAD: | 94 | case SIGMA_ACTION_WRITESAFELOAD: |
95 | ret = ssfw->write(ssfw->control_data, sa, len); | 95 | ret = ssfw->write(ssfw->control_data, sa, len); |
96 | if (ret < 0) | 96 | if (ret < 0) |
97 | return -EINVAL; | 97 | return -EINVAL; |
98 | break; | 98 | break; |
99 | case SIGMA_ACTION_DELAY: | 99 | case SIGMA_ACTION_DELAY: |
100 | udelay(len); | 100 | udelay(len); |
101 | len = 0; | 101 | len = 0; |
102 | break; | 102 | break; |
103 | case SIGMA_ACTION_END: | 103 | case SIGMA_ACTION_END: |
104 | return 0; | 104 | return 0; |
105 | default: | 105 | default: |
106 | return -EINVAL; | 106 | return -EINVAL; |
107 | } | 107 | } |
108 | 108 | ||
109 | return 1; | 109 | return 1; |
110 | } | 110 | } |
111 | 111 | ||
112 | static int | 112 | static int |
113 | process_sigma_actions(struct sigma_firmware *ssfw) | 113 | process_sigma_actions(struct sigma_firmware *ssfw) |
114 | { | 114 | { |
115 | struct sigma_action *sa; | 115 | struct sigma_action *sa; |
116 | size_t size; | 116 | size_t size; |
117 | int ret; | 117 | int ret; |
118 | 118 | ||
119 | while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) { | 119 | while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) { |
120 | sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos); | 120 | sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos); |
121 | 121 | ||
122 | size = sigma_action_size(sa); | 122 | size = sigma_action_size(sa); |
123 | ssfw->pos += size; | 123 | ssfw->pos += size; |
124 | if (ssfw->pos > ssfw->fw->size || size == 0) | 124 | if (ssfw->pos > ssfw->fw->size || size == 0) |
125 | break; | 125 | break; |
126 | 126 | ||
127 | ret = process_sigma_action(ssfw, sa); | 127 | ret = process_sigma_action(ssfw, sa); |
128 | 128 | ||
129 | pr_debug("%s: action returned %i\n", __func__, ret); | 129 | pr_debug("%s: action returned %i\n", __func__, ret); |
130 | 130 | ||
131 | if (ret <= 0) | 131 | if (ret <= 0) |
132 | return ret; | 132 | return ret; |
133 | } | 133 | } |
134 | 134 | ||
135 | if (ssfw->pos != ssfw->fw->size) | 135 | if (ssfw->pos != ssfw->fw->size) |
136 | return -EINVAL; | 136 | return -EINVAL; |
137 | 137 | ||
138 | return 0; | 138 | return 0; |
139 | } | 139 | } |
140 | 140 | ||
141 | static int _process_sigma_firmware(struct device *dev, | 141 | static int _process_sigma_firmware(struct device *dev, |
142 | struct sigma_firmware *ssfw, const char *name) | 142 | struct sigma_firmware *ssfw, const char *name) |
143 | { | 143 | { |
144 | int ret; | 144 | int ret; |
145 | struct sigma_firmware_header *ssfw_head; | 145 | struct sigma_firmware_header *ssfw_head; |
146 | const struct firmware *fw; | 146 | const struct firmware *fw; |
147 | u32 crc; | 147 | u32 crc; |
148 | 148 | ||
149 | pr_debug("%s: loading firmware %s\n", __func__, name); | 149 | pr_debug("%s: loading firmware %s\n", __func__, name); |
150 | 150 | ||
151 | /* first load the blob */ | 151 | /* first load the blob */ |
152 | ret = request_firmware(&fw, name, dev); | 152 | ret = request_firmware(&fw, name, dev); |
153 | if (ret) { | 153 | if (ret) { |
154 | pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); | 154 | pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); |
155 | return ret; | 155 | return ret; |
156 | } | 156 | } |
157 | ssfw->fw = fw; | 157 | ssfw->fw = fw; |
158 | 158 | ||
159 | /* then verify the header */ | 159 | /* then verify the header */ |
160 | ret = -EINVAL; | 160 | ret = -EINVAL; |
161 | 161 | ||
162 | /* | 162 | /* |
163 | * Reject too small or unreasonable large files. The upper limit has been | 163 | * Reject too small or unreasonable large files. The upper limit has been |
164 | * chosen a bit arbitrarily, but it should be enough for all practical | 164 | * chosen a bit arbitrarily, but it should be enough for all practical |
165 | * purposes and having the limit makes it easier to avoid integer | 165 | * purposes and having the limit makes it easier to avoid integer |
166 | * overflows later in the loading process. | 166 | * overflows later in the loading process. |
167 | */ | 167 | */ |
168 | if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { | 168 | if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { |
169 | dev_err(dev, "Failed to load firmware: Invalid size\n"); | 169 | dev_err(dev, "Failed to load firmware: Invalid size\n"); |
170 | goto done; | 170 | goto done; |
171 | } | 171 | } |
172 | 172 | ||
173 | ssfw_head = (void *)fw->data; | 173 | ssfw_head = (void *)fw->data; |
174 | if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { | 174 | if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { |
175 | dev_err(dev, "Failed to load firmware: Invalid magic\n"); | 175 | dev_err(dev, "Failed to load firmware: Invalid magic\n"); |
176 | goto done; | 176 | goto done; |
177 | } | 177 | } |
178 | 178 | ||
179 | crc = crc32(0, fw->data + sizeof(*ssfw_head), | 179 | crc = crc32(0, fw->data + sizeof(*ssfw_head), |
180 | fw->size - sizeof(*ssfw_head)); | 180 | fw->size - sizeof(*ssfw_head)); |
181 | pr_debug("%s: crc=%x\n", __func__, crc); | 181 | pr_debug("%s: crc=%x\n", __func__, crc); |
182 | if (crc != le32_to_cpu(ssfw_head->crc)) { | 182 | if (crc != le32_to_cpu(ssfw_head->crc)) { |
183 | dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", | 183 | dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", |
184 | le32_to_cpu(ssfw_head->crc), crc); | 184 | le32_to_cpu(ssfw_head->crc), crc); |
185 | goto done; | 185 | goto done; |
186 | } | 186 | } |
187 | 187 | ||
188 | ssfw->pos = sizeof(*ssfw_head); | 188 | ssfw->pos = sizeof(*ssfw_head); |
189 | 189 | ||
190 | /* finally process all of the actions */ | 190 | /* finally process all of the actions */ |
191 | ret = process_sigma_actions(ssfw); | 191 | ret = process_sigma_actions(ssfw); |
192 | 192 | ||
193 | done: | 193 | done: |
194 | release_firmware(fw); | 194 | release_firmware(fw); |
195 | 195 | ||
196 | pr_debug("%s: loaded %s\n", __func__, name); | 196 | pr_debug("%s: loaded %s\n", __func__, name); |
197 | 197 | ||
198 | return ret; | 198 | return ret; |
199 | } | 199 | } |
200 | 200 | ||
201 | #if IS_ENABLED(CONFIG_I2C) | 201 | #if IS_ENABLED(CONFIG_I2C) |
202 | 202 | ||
203 | static int sigma_action_write_i2c(void *control_data, | 203 | static int sigma_action_write_i2c(void *control_data, |
204 | const struct sigma_action *sa, size_t len) | 204 | const struct sigma_action *sa, size_t len) |
205 | { | 205 | { |
206 | return i2c_master_send(control_data, (const unsigned char *)&sa->addr, | 206 | return i2c_master_send(control_data, (const unsigned char *)&sa->addr, |
207 | len); | 207 | len); |
208 | } | 208 | } |
209 | 209 | ||
210 | int process_sigma_firmware(struct i2c_client *client, const char *name) | 210 | int process_sigma_firmware(struct i2c_client *client, const char *name) |
211 | { | 211 | { |
212 | struct sigma_firmware ssfw; | 212 | struct sigma_firmware ssfw; |
213 | 213 | ||
214 | ssfw.control_data = client; | 214 | ssfw.control_data = client; |
215 | ssfw.write = sigma_action_write_i2c; | 215 | ssfw.write = sigma_action_write_i2c; |
216 | 216 | ||
217 | return _process_sigma_firmware(&client->dev, &ssfw, name); | 217 | return _process_sigma_firmware(&client->dev, &ssfw, name); |
218 | } | 218 | } |
219 | EXPORT_SYMBOL(process_sigma_firmware); | 219 | EXPORT_SYMBOL(process_sigma_firmware); |
220 | 220 | ||
221 | #endif | 221 | #endif |
222 | 222 | ||
223 | #if IS_ENABLED(CONFIG_REGMAP) | 223 | #if IS_ENABLED(CONFIG_REGMAP) |
224 | 224 | ||
225 | static int sigma_action_write_regmap(void *control_data, | 225 | static int sigma_action_write_regmap(void *control_data, |
226 | const struct sigma_action *sa, size_t len) | 226 | const struct sigma_action *sa, size_t len) |
227 | { | 227 | { |
228 | return regmap_raw_write(control_data, le16_to_cpu(sa->addr), | 228 | return regmap_raw_write(control_data, be16_to_cpu(sa->addr), |
229 | sa->payload, len - 2); | 229 | sa->payload, len - 2); |
230 | } | 230 | } |
231 | 231 | ||
232 | int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, | 232 | int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap, |
233 | const char *name) | 233 | const char *name) |
234 | { | 234 | { |
235 | struct sigma_firmware ssfw; | 235 | struct sigma_firmware ssfw; |
236 | 236 | ||
237 | ssfw.control_data = regmap; | 237 | ssfw.control_data = regmap; |
238 | ssfw.write = sigma_action_write_regmap; | 238 | ssfw.write = sigma_action_write_regmap; |
239 | 239 | ||
240 | return _process_sigma_firmware(dev, &ssfw, name); | 240 | return _process_sigma_firmware(dev, &ssfw, name); |
241 | } | 241 | } |
242 | EXPORT_SYMBOL(process_sigma_firmware_regmap); | 242 | EXPORT_SYMBOL(process_sigma_firmware_regmap); |
243 | 243 | ||
244 | #endif | 244 | #endif |
245 | 245 | ||
246 | MODULE_LICENSE("GPL"); | 246 | MODULE_LICENSE("GPL"); |
247 | 247 |