ASoC: SigmaDSP: Move private structs and functions to C file
[deliverable/linux.git] / sound / soc / codecs / sigmadsp.c
CommitLineData
e359dc24
MF
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>
27c46a25 14#include <linux/module.h>
40216ce7
LPC
15
16#include "sigmadsp.h"
e359dc24 17
a4c1d7e6
LPC
18#define SIGMA_MAGIC "ADISIGM"
19
20struct sigma_firmware_header {
21 unsigned char magic[7];
22 u8 version;
23 __le32 crc;
24} __packed;
25
26enum {
27 SIGMA_ACTION_WRITEXBYTES = 0,
28 SIGMA_ACTION_WRITESINGLE,
29 SIGMA_ACTION_WRITESAFELOAD,
30 SIGMA_ACTION_DELAY,
31 SIGMA_ACTION_PLLWAIT,
32 SIGMA_ACTION_NOOP,
33 SIGMA_ACTION_END,
34};
35
36struct sigma_action {
37 u8 instr;
38 u8 len_hi;
39 __le16 len;
40 __be16 addr;
41 unsigned char payload[];
42} __packed;
43
44struct sigma_firmware {
45 const struct firmware *fw;
46 size_t pos;
47};
48
49static inline u32 sigma_action_len(struct sigma_action *sa)
50{
51 return (sa->len_hi << 16) | le16_to_cpu(sa->len);
52}
53
4f718a29
LPC
54static size_t sigma_action_size(struct sigma_action *sa)
55{
56 size_t payload = 0;
57
58 switch (sa->instr) {
59 case SIGMA_ACTION_WRITEXBYTES:
60 case SIGMA_ACTION_WRITESINGLE:
61 case SIGMA_ACTION_WRITESAFELOAD:
62 payload = sigma_action_len(sa);
63 break;
64 default:
65 break;
66 }
67
68 payload = ALIGN(payload, 2);
69
70 return payload + sizeof(struct sigma_action);
71}
72
73/*
74 * Returns a negative error value in case of an error, 0 if processing of
75 * the firmware should be stopped after this action, 1 otherwise.
76 */
e359dc24 77static int
4f718a29 78process_sigma_action(struct i2c_client *client, struct sigma_action *sa)
e359dc24 79{
e359dc24 80 size_t len = sigma_action_len(sa);
4f718a29 81 int ret;
e359dc24
MF
82
83 pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
84 sa->instr, sa->addr, len);
85
86 switch (sa->instr) {
87 case SIGMA_ACTION_WRITEXBYTES:
88 case SIGMA_ACTION_WRITESINGLE:
89 case SIGMA_ACTION_WRITESAFELOAD:
e359dc24
MF
90 ret = i2c_master_send(client, (void *)&sa->addr, len);
91 if (ret < 0)
92 return -EINVAL;
93 break;
e359dc24 94 case SIGMA_ACTION_DELAY:
e359dc24
MF
95 udelay(len);
96 len = 0;
97 break;
e359dc24 98 case SIGMA_ACTION_END:
4f718a29 99 return 0;
e359dc24
MF
100 default:
101 return -EINVAL;
102 }
103
4f718a29 104 return 1;
e359dc24
MF
105}
106
107static int
108process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
109{
4f718a29
LPC
110 struct sigma_action *sa;
111 size_t size;
112 int ret;
113
114 while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
115 sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
116
117 size = sigma_action_size(sa);
118 ssfw->pos += size;
119 if (ssfw->pos > ssfw->fw->size || size == 0)
120 break;
121
122 ret = process_sigma_action(client, sa);
e359dc24 123
e359dc24 124 pr_debug("%s: action returned %i\n", __func__, ret);
4f718a29
LPC
125
126 if (ret <= 0)
e359dc24
MF
127 return ret;
128 }
4f718a29
LPC
129
130 if (ssfw->pos != ssfw->fw->size)
131 return -EINVAL;
132
133 return 0;
e359dc24
MF
134}
135
136int process_sigma_firmware(struct i2c_client *client, const char *name)
137{
138 int ret;
139 struct sigma_firmware_header *ssfw_head;
140 struct sigma_firmware ssfw;
141 const struct firmware *fw;
142 u32 crc;
143
144 pr_debug("%s: loading firmware %s\n", __func__, name);
145
146 /* first load the blob */
147 ret = request_firmware(&fw, name, &client->dev);
148 if (ret) {
149 pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
150 return ret;
151 }
152 ssfw.fw = fw;
153
154 /* then verify the header */
155 ret = -EINVAL;
4f718a29
LPC
156
157 /*
158 * Reject too small or unreasonable large files. The upper limit has been
159 * chosen a bit arbitrarily, but it should be enough for all practical
160 * purposes and having the limit makes it easier to avoid integer
161 * overflows later in the loading process.
162 */
48afc527
LPC
163 if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) {
164 dev_err(&client->dev, "Failed to load firmware: Invalid size\n");
e359dc24 165 goto done;
48afc527 166 }
e359dc24
MF
167
168 ssfw_head = (void *)fw->data;
48afc527
LPC
169 if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) {
170 dev_err(&client->dev, "Failed to load firmware: Invalid magic\n");
e359dc24 171 goto done;
48afc527 172 }
e359dc24 173
c56935bd
LPC
174 crc = crc32(0, fw->data + sizeof(*ssfw_head),
175 fw->size - sizeof(*ssfw_head));
e359dc24 176 pr_debug("%s: crc=%x\n", __func__, crc);
48afc527
LPC
177 if (crc != le32_to_cpu(ssfw_head->crc)) {
178 dev_err(&client->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
179 le32_to_cpu(ssfw_head->crc), crc);
e359dc24 180 goto done;
48afc527 181 }
e359dc24
MF
182
183 ssfw.pos = sizeof(*ssfw_head);
184
185 /* finally process all of the actions */
186 ret = process_sigma_actions(client, &ssfw);
187
188 done:
189 release_firmware(fw);
190
191 pr_debug("%s: loaded %s\n", __func__, name);
192
193 return ret;
194}
195EXPORT_SYMBOL(process_sigma_firmware);
27c46a25
RD
196
197MODULE_LICENSE("GPL");
This page took 0.075767 seconds and 5 git commands to generate.