diff options
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r-- | sound/soc/soc-cache.c | 263 |
1 files changed, 61 insertions, 202 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index e72f55428f0b..1b6663f45b34 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c | |||
@@ -11,12 +11,9 @@ | |||
11 | * option) any later version. | 11 | * option) any later version. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <sound/soc.h> | 14 | #include <sound/soc.h> |
17 | #include <linux/bitmap.h> | ||
18 | #include <linux/rbtree.h> | ||
19 | #include <linux/export.h> | 15 | #include <linux/export.h> |
16 | #include <linux/slab.h> | ||
20 | 17 | ||
21 | #include <trace/events/asoc.h> | 18 | #include <trace/events/asoc.h> |
22 | 19 | ||
@@ -66,126 +63,42 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, | |||
66 | return -1; | 63 | return -1; |
67 | } | 64 | } |
68 | 65 | ||
69 | static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) | 66 | int snd_soc_cache_init(struct snd_soc_codec *codec) |
70 | { | 67 | { |
71 | int i; | 68 | const struct snd_soc_codec_driver *codec_drv = codec->driver; |
72 | int ret; | 69 | size_t reg_size; |
73 | const struct snd_soc_codec_driver *codec_drv; | ||
74 | unsigned int val; | ||
75 | 70 | ||
76 | codec_drv = codec->driver; | 71 | reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; |
77 | for (i = 0; i < codec_drv->reg_cache_size; ++i) { | ||
78 | ret = snd_soc_cache_read(codec, i, &val); | ||
79 | if (ret) | ||
80 | return ret; | ||
81 | if (codec->reg_def_copy) | ||
82 | if (snd_soc_get_cache_val(codec->reg_def_copy, | ||
83 | i, codec_drv->reg_word_size) == val) | ||
84 | continue; | ||
85 | 72 | ||
86 | WARN_ON(!snd_soc_codec_writable_register(codec, i)); | 73 | mutex_init(&codec->cache_rw_mutex); |
87 | |||
88 | ret = snd_soc_write(codec, i, val); | ||
89 | if (ret) | ||
90 | return ret; | ||
91 | dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", | ||
92 | i, val); | ||
93 | } | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, | ||
98 | unsigned int reg, unsigned int value) | ||
99 | { | ||
100 | snd_soc_set_cache_val(codec->reg_cache, reg, value, | ||
101 | codec->driver->reg_word_size); | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, | ||
106 | unsigned int reg, unsigned int *value) | ||
107 | { | ||
108 | *value = snd_soc_get_cache_val(codec->reg_cache, reg, | ||
109 | codec->driver->reg_word_size); | ||
110 | return 0; | ||
111 | } | ||
112 | 74 | ||
113 | static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) | 75 | dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", |
114 | { | 76 | codec->name); |
115 | if (!codec->reg_cache) | ||
116 | return 0; | ||
117 | kfree(codec->reg_cache); | ||
118 | codec->reg_cache = NULL; | ||
119 | return 0; | ||
120 | } | ||
121 | 77 | ||
122 | static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) | 78 | if (codec_drv->reg_cache_default) |
123 | { | 79 | codec->reg_cache = kmemdup(codec_drv->reg_cache_default, |
124 | if (codec->reg_def_copy) | 80 | reg_size, GFP_KERNEL); |
125 | codec->reg_cache = kmemdup(codec->reg_def_copy, | ||
126 | codec->reg_size, GFP_KERNEL); | ||
127 | else | 81 | else |
128 | codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); | 82 | codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); |
129 | if (!codec->reg_cache) | 83 | if (!codec->reg_cache) |
130 | return -ENOMEM; | 84 | return -ENOMEM; |
131 | 85 | ||
132 | return 0; | 86 | return 0; |
133 | } | 87 | } |
134 | 88 | ||
135 | /* an array of all supported compression types */ | ||
136 | static const struct snd_soc_cache_ops cache_types[] = { | ||
137 | /* Flat *must* be the first entry for fallback */ | ||
138 | { | ||
139 | .id = SND_SOC_FLAT_COMPRESSION, | ||
140 | .name = "flat", | ||
141 | .init = snd_soc_flat_cache_init, | ||
142 | .exit = snd_soc_flat_cache_exit, | ||
143 | .read = snd_soc_flat_cache_read, | ||
144 | .write = snd_soc_flat_cache_write, | ||
145 | .sync = snd_soc_flat_cache_sync | ||
146 | }, | ||
147 | }; | ||
148 | |||
149 | int snd_soc_cache_init(struct snd_soc_codec *codec) | ||
150 | { | ||
151 | int i; | ||
152 | |||
153 | for (i = 0; i < ARRAY_SIZE(cache_types); ++i) | ||
154 | if (cache_types[i].id == codec->compress_type) | ||
155 | break; | ||
156 | |||
157 | /* Fall back to flat compression */ | ||
158 | if (i == ARRAY_SIZE(cache_types)) { | ||
159 | dev_warn(codec->dev, "ASoC: Could not match compress type: %d\n", | ||
160 | codec->compress_type); | ||
161 | i = 0; | ||
162 | } | ||
163 | |||
164 | mutex_init(&codec->cache_rw_mutex); | ||
165 | codec->cache_ops = &cache_types[i]; | ||
166 | |||
167 | if (codec->cache_ops->init) { | ||
168 | if (codec->cache_ops->name) | ||
169 | dev_dbg(codec->dev, "ASoC: Initializing %s cache for %s codec\n", | ||
170 | codec->cache_ops->name, codec->name); | ||
171 | return codec->cache_ops->init(codec); | ||
172 | } | ||
173 | return -ENOSYS; | ||
174 | } | ||
175 | |||
176 | /* | 89 | /* |
177 | * NOTE: keep in mind that this function might be called | 90 | * NOTE: keep in mind that this function might be called |
178 | * multiple times. | 91 | * multiple times. |
179 | */ | 92 | */ |
180 | int snd_soc_cache_exit(struct snd_soc_codec *codec) | 93 | int snd_soc_cache_exit(struct snd_soc_codec *codec) |
181 | { | 94 | { |
182 | if (codec->cache_ops && codec->cache_ops->exit) { | 95 | dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", |
183 | if (codec->cache_ops->name) | 96 | codec->name); |
184 | dev_dbg(codec->dev, "ASoC: Destroying %s cache for %s codec\n", | 97 | if (!codec->reg_cache) |
185 | codec->cache_ops->name, codec->name); | 98 | return 0; |
186 | return codec->cache_ops->exit(codec); | 99 | kfree(codec->reg_cache); |
187 | } | 100 | codec->reg_cache = NULL; |
188 | return -ENOSYS; | 101 | return 0; |
189 | } | 102 | } |
190 | 103 | ||
191 | /** | 104 | /** |
@@ -198,18 +111,15 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) | |||
198 | int snd_soc_cache_read(struct snd_soc_codec *codec, | 111 | int snd_soc_cache_read(struct snd_soc_codec *codec, |
199 | unsigned int reg, unsigned int *value) | 112 | unsigned int reg, unsigned int *value) |
200 | { | 113 | { |
201 | int ret; | 114 | if (!value) |
115 | return -EINVAL; | ||
202 | 116 | ||
203 | mutex_lock(&codec->cache_rw_mutex); | 117 | mutex_lock(&codec->cache_rw_mutex); |
204 | 118 | *value = snd_soc_get_cache_val(codec->reg_cache, reg, | |
205 | if (value && codec->cache_ops && codec->cache_ops->read) { | 119 | codec->driver->reg_word_size); |
206 | ret = codec->cache_ops->read(codec, reg, value); | ||
207 | mutex_unlock(&codec->cache_rw_mutex); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | mutex_unlock(&codec->cache_rw_mutex); | 120 | mutex_unlock(&codec->cache_rw_mutex); |
212 | return -ENOSYS; | 121 | |
122 | return 0; | ||
213 | } | 123 | } |
214 | EXPORT_SYMBOL_GPL(snd_soc_cache_read); | 124 | EXPORT_SYMBOL_GPL(snd_soc_cache_read); |
215 | 125 | ||
@@ -223,20 +133,42 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_read); | |||
223 | int snd_soc_cache_write(struct snd_soc_codec *codec, | 133 | int snd_soc_cache_write(struct snd_soc_codec *codec, |
224 | unsigned int reg, unsigned int value) | 134 | unsigned int reg, unsigned int value) |
225 | { | 135 | { |
136 | mutex_lock(&codec->cache_rw_mutex); | ||
137 | snd_soc_set_cache_val(codec->reg_cache, reg, value, | ||
138 | codec->driver->reg_word_size); | ||
139 | mutex_unlock(&codec->cache_rw_mutex); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(snd_soc_cache_write); | ||
144 | |||
145 | static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) | ||
146 | { | ||
147 | int i; | ||
226 | int ret; | 148 | int ret; |
149 | const struct snd_soc_codec_driver *codec_drv; | ||
150 | unsigned int val; | ||
227 | 151 | ||
228 | mutex_lock(&codec->cache_rw_mutex); | 152 | codec_drv = codec->driver; |
153 | for (i = 0; i < codec_drv->reg_cache_size; ++i) { | ||
154 | ret = snd_soc_cache_read(codec, i, &val); | ||
155 | if (ret) | ||
156 | return ret; | ||
157 | if (codec_drv->reg_cache_default) | ||
158 | if (snd_soc_get_cache_val(codec_drv->reg_cache_default, | ||
159 | i, codec_drv->reg_word_size) == val) | ||
160 | continue; | ||
229 | 161 | ||
230 | if (codec->cache_ops && codec->cache_ops->write) { | 162 | WARN_ON(!snd_soc_codec_writable_register(codec, i)); |
231 | ret = codec->cache_ops->write(codec, reg, value); | ||
232 | mutex_unlock(&codec->cache_rw_mutex); | ||
233 | return ret; | ||
234 | } | ||
235 | 163 | ||
236 | mutex_unlock(&codec->cache_rw_mutex); | 164 | ret = snd_soc_write(codec, i, val); |
237 | return -ENOSYS; | 165 | if (ret) |
166 | return ret; | ||
167 | dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", | ||
168 | i, val); | ||
169 | } | ||
170 | return 0; | ||
238 | } | 171 | } |
239 | EXPORT_SYMBOL_GPL(snd_soc_cache_write); | ||
240 | 172 | ||
241 | /** | 173 | /** |
242 | * snd_soc_cache_sync: Sync the register cache with the hardware. | 174 | * snd_soc_cache_sync: Sync the register cache with the hardware. |
@@ -249,92 +181,19 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); | |||
249 | */ | 181 | */ |
250 | int snd_soc_cache_sync(struct snd_soc_codec *codec) | 182 | int snd_soc_cache_sync(struct snd_soc_codec *codec) |
251 | { | 183 | { |
184 | const char *name = "flat"; | ||
252 | int ret; | 185 | int ret; |
253 | const char *name; | ||
254 | 186 | ||
255 | if (!codec->cache_sync) { | 187 | if (!codec->cache_sync) |
256 | return 0; | 188 | return 0; |
257 | } | ||
258 | |||
259 | if (!codec->cache_ops || !codec->cache_ops->sync) | ||
260 | return -ENOSYS; | ||
261 | 189 | ||
262 | if (codec->cache_ops->name) | 190 | dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", |
263 | name = codec->cache_ops->name; | 191 | codec->name); |
264 | else | ||
265 | name = "unknown"; | ||
266 | |||
267 | if (codec->cache_ops->name) | ||
268 | dev_dbg(codec->dev, "ASoC: Syncing %s cache for %s codec\n", | ||
269 | codec->cache_ops->name, codec->name); | ||
270 | trace_snd_soc_cache_sync(codec, name, "start"); | 192 | trace_snd_soc_cache_sync(codec, name, "start"); |
271 | ret = codec->cache_ops->sync(codec); | 193 | ret = snd_soc_flat_cache_sync(codec); |
272 | if (!ret) | 194 | if (!ret) |
273 | codec->cache_sync = 0; | 195 | codec->cache_sync = 0; |
274 | trace_snd_soc_cache_sync(codec, name, "end"); | 196 | trace_snd_soc_cache_sync(codec, name, "end"); |
275 | return ret; | 197 | return ret; |
276 | } | 198 | } |
277 | EXPORT_SYMBOL_GPL(snd_soc_cache_sync); | 199 | EXPORT_SYMBOL_GPL(snd_soc_cache_sync); |
278 | |||
279 | static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, | ||
280 | unsigned int reg) | ||
281 | { | ||
282 | const struct snd_soc_codec_driver *codec_drv; | ||
283 | unsigned int min, max, index; | ||
284 | |||
285 | codec_drv = codec->driver; | ||
286 | min = 0; | ||
287 | max = codec_drv->reg_access_size - 1; | ||
288 | do { | ||
289 | index = (min + max) / 2; | ||
290 | if (codec_drv->reg_access_default[index].reg == reg) | ||
291 | return index; | ||
292 | if (codec_drv->reg_access_default[index].reg < reg) | ||
293 | min = index + 1; | ||
294 | else | ||
295 | max = index; | ||
296 | } while (min <= max); | ||
297 | return -1; | ||
298 | } | ||
299 | |||
300 | int snd_soc_default_volatile_register(struct snd_soc_codec *codec, | ||
301 | unsigned int reg) | ||
302 | { | ||
303 | int index; | ||
304 | |||
305 | if (reg >= codec->driver->reg_cache_size) | ||
306 | return 1; | ||
307 | index = snd_soc_get_reg_access_index(codec, reg); | ||
308 | if (index < 0) | ||
309 | return 0; | ||
310 | return codec->driver->reg_access_default[index].vol; | ||
311 | } | ||
312 | EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); | ||
313 | |||
314 | int snd_soc_default_readable_register(struct snd_soc_codec *codec, | ||
315 | unsigned int reg) | ||
316 | { | ||
317 | int index; | ||
318 | |||
319 | if (reg >= codec->driver->reg_cache_size) | ||
320 | return 1; | ||
321 | index = snd_soc_get_reg_access_index(codec, reg); | ||
322 | if (index < 0) | ||
323 | return 0; | ||
324 | return codec->driver->reg_access_default[index].read; | ||
325 | } | ||
326 | EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); | ||
327 | |||
328 | int snd_soc_default_writable_register(struct snd_soc_codec *codec, | ||
329 | unsigned int reg) | ||
330 | { | ||
331 | int index; | ||
332 | |||
333 | if (reg >= codec->driver->reg_cache_size) | ||
334 | return 1; | ||
335 | index = snd_soc_get_reg_access_index(codec, reg); | ||
336 | if (index < 0) | ||
337 | return 0; | ||
338 | return codec->driver->reg_access_default[index].write; | ||
339 | } | ||
340 | EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); | ||