aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/sigmadsp.c
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2011-11-28 03:44:17 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-11-29 07:01:10 -0500
commit40216ce7aa88c2e70869723a0f5929fdbd4a91c5 (patch)
tree58591841674e312f96317621843b365b8fd9fed2 /sound/soc/codecs/sigmadsp.c
parent1db7c89c62b3c0a0ebf8c07975ae907fa404116a (diff)
ASoC: Move SigmaDSP firmware loader to ASoC
It has been pointed out previously, that the firmware subsystem is not the right place for the SigmaDSP firmware loader. Furthermore the SigmaDSP is currently only used in audio products and we are aiming for better integration into the ASoC framework in the future, with support for ALSA controls for firmware parameters and support dynamic power management as well. So the natural choice for the SigmaDSP firmware loader is the ASoC subsystem. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/sigmadsp.c')
-rw-r--r--sound/soc/codecs/sigmadsp.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
new file mode 100644
index 000000000000..acb97a9834aa
--- /dev/null
+++ b/sound/soc/codecs/sigmadsp.c
@@ -0,0 +1,154 @@
1/*
2 * Load Analog Devices SigmaStudio firmware files
3 *
4 * Copyright 2009-2011 Analog Devices Inc.
5 *
6 * Licensed under the GPL-2 or later.
7 */
8
9#include <linux/crc32.h>
10#include <linux/delay.h>
11#include <linux/firmware.h>
12#include <linux/kernel.h>
13#include <linux/i2c.h>
14#include <linux/module.h>
15
16#include "sigmadsp.h"
17
18static size_t sigma_action_size(struct sigma_action *sa)
19{
20 size_t payload = 0;
21
22 switch (sa->instr) {
23 case SIGMA_ACTION_WRITEXBYTES:
24 case SIGMA_ACTION_WRITESINGLE:
25 case SIGMA_ACTION_WRITESAFELOAD:
26 payload = sigma_action_len(sa);
27 break;
28 default:
29 break;
30 }
31
32 payload = ALIGN(payload, 2);
33
34 return payload + sizeof(struct sigma_action);
35}
36
37/*
38 * Returns a negative error value in case of an error, 0 if processing of
39 * the firmware should be stopped after this action, 1 otherwise.
40 */
41static int
42process_sigma_action(struct i2c_client *client, struct sigma_action *sa)
43{
44 size_t len = sigma_action_len(sa);
45 int ret;
46
47 pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
48 sa->instr, sa->addr, len);
49
50 switch (sa->instr) {
51 case SIGMA_ACTION_WRITEXBYTES:
52 case SIGMA_ACTION_WRITESINGLE:
53 case SIGMA_ACTION_WRITESAFELOAD:
54 ret = i2c_master_send(client, (void *)&sa->addr, len);
55 if (ret < 0)
56 return -EINVAL;
57 break;
58 case SIGMA_ACTION_DELAY:
59 udelay(len);
60 len = 0;
61 break;
62 case SIGMA_ACTION_END:
63 return 0;
64 default:
65 return -EINVAL;
66 }
67
68 return 1;
69}
70
71static int
72process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
73{
74 struct sigma_action *sa;
75 size_t size;
76 int ret;
77
78 while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
79 sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
80
81 size = sigma_action_size(sa);
82 ssfw->pos += size;
83 if (ssfw->pos > ssfw->fw->size || size == 0)
84 break;
85
86 ret = process_sigma_action(client, sa);
87
88 pr_debug("%s: action returned %i\n", __func__, ret);
89
90 if (ret <= 0)
91 return ret;
92 }
93
94 if (ssfw->pos != ssfw->fw->size)
95 return -EINVAL;
96
97 return 0;
98}
99
100int process_sigma_firmware(struct i2c_client *client, const char *name)
101{
102 int ret;
103 struct sigma_firmware_header *ssfw_head;
104 struct sigma_firmware ssfw;
105 const struct firmware *fw;
106 u32 crc;
107
108 pr_debug("%s: loading firmware %s\n", __func__, name);
109
110 /* first load the blob */
111 ret = request_firmware(&fw, name, &client->dev);
112 if (ret) {
113 pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
114 return ret;
115 }
116 ssfw.fw = fw;
117
118 /* then verify the header */
119 ret = -EINVAL;
120
121 /*
122 * Reject too small or unreasonable large files. The upper limit has been
123 * chosen a bit arbitrarily, but it should be enough for all practical
124 * purposes and having the limit makes it easier to avoid integer
125 * overflows later in the loading process.
126 */
127 if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000)
128 goto done;
129
130 ssfw_head = (void *)fw->data;
131 if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic)))
132 goto done;
133
134 crc = crc32(0, fw->data + sizeof(*ssfw_head),
135 fw->size - sizeof(*ssfw_head));
136 pr_debug("%s: crc=%x\n", __func__, crc);
137 if (crc != le32_to_cpu(ssfw_head->crc))
138 goto done;
139
140 ssfw.pos = sizeof(*ssfw_head);
141
142 /* finally process all of the actions */
143 ret = process_sigma_actions(client, &ssfw);
144
145 done:
146 release_firmware(fw);
147
148 pr_debug("%s: loaded %s\n", __func__, name);
149
150 return ret;
151}
152EXPORT_SYMBOL(process_sigma_firmware);
153
154MODULE_LICENSE("GPL");