Blame view
sound/soc/soc-cache.c
7.81 KB
17a52fd60 ASoC: Begin to fa... |
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * soc-cache.c -- ASoC register cache helpers * * Copyright 2009 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ |
7084a42b9 ASoC: Add I/O con... |
13 |
#include <linux/i2c.h> |
27ded041f ASoC: Factor out ... |
14 |
#include <linux/spi/spi.h> |
17a52fd60 ASoC: Begin to fa... |
15 |
#include <sound/soc.h> |
cc28fb8e7 ASoC: soc-cache: ... |
16 |
#include <linux/bitmap.h> |
a7f387d5a ASoC: soc-cache: ... |
17 |
#include <linux/rbtree.h> |
d81a6d717 sound: Add export... |
18 |
#include <linux/export.h> |
17a52fd60 ASoC: Begin to fa... |
19 |
|
c358e640a ASoC: soc-cache: ... |
20 |
#include <trace/events/asoc.h> |
1321e8838 ASoC: soc-cache: ... |
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
static bool snd_soc_set_cache_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size) { switch (word_size) { case 1: { u8 *cache = base; if (cache[idx] == val) return true; cache[idx] = val; break; } case 2: { u16 *cache = base; if (cache[idx] == val) return true; cache[idx] = val; break; } default: BUG(); } return false; } static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, unsigned int word_size) { |
fd137e2bb ASoC: Check for N... |
48 49 |
if (!base) return -1; |
1321e8838 ASoC: soc-cache: ... |
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
switch (word_size) { case 1: { const u8 *cache = base; return cache[idx]; } case 2: { const u16 *cache = base; return cache[idx]; } default: BUG(); } /* unreachable */ return -1; } |
7a30a3db3 ASoC: soc-cache: ... |
65 66 67 |
static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) { int i; |
7a33d4ce8 ASoC: soc-cache: ... |
68 |
int ret; |
001ae4c03 ASoC: Constify st... |
69 |
const struct snd_soc_codec_driver *codec_drv; |
7a30a3db3 ASoC: soc-cache: ... |
70 71 72 73 |
unsigned int val; codec_drv = codec->driver; for (i = 0; i < codec_drv->reg_cache_size; ++i) { |
7a33d4ce8 ASoC: soc-cache: ... |
74 75 76 |
ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; |
d779fce5d ASoC: soc-cache: ... |
77 78 |
if (codec->reg_def_copy) if (snd_soc_get_cache_val(codec->reg_def_copy, |
1321e8838 ASoC: soc-cache: ... |
79 80 |
i, codec_drv->reg_word_size) == val) continue; |
6c5b756aa ASoC: Fix registe... |
81 82 |
WARN_ON(!snd_soc_codec_writable_register(codec, i)); |
7a33d4ce8 ASoC: soc-cache: ... |
83 84 85 |
ret = snd_soc_write(codec, i, val); if (ret) return ret; |
7a30a3db3 ASoC: soc-cache: ... |
86 87 88 89 90 91 92 93 94 95 |
dev_dbg(codec->dev, "Synced register %#x, value = %#x ", i, val); } return 0; } static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { |
1321e8838 ASoC: soc-cache: ... |
96 97 |
snd_soc_set_cache_val(codec->reg_cache, reg, value, codec->driver->reg_word_size); |
7a30a3db3 ASoC: soc-cache: ... |
98 99 100 101 102 103 |
return 0; } static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { |
1321e8838 ASoC: soc-cache: ... |
104 105 |
*value = snd_soc_get_cache_val(codec->reg_cache, reg, codec->driver->reg_word_size); |
7a30a3db3 ASoC: soc-cache: ... |
106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
return 0; } static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) { if (!codec->reg_cache) return 0; kfree(codec->reg_cache); codec->reg_cache = NULL; return 0; } static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { |
d779fce5d ASoC: soc-cache: ... |
120 121 |
if (codec->reg_def_copy) codec->reg_cache = kmemdup(codec->reg_def_copy, |
aea170a09 ASoC: soc-cache: ... |
122 |
codec->reg_size, GFP_KERNEL); |
7a30a3db3 ASoC: soc-cache: ... |
123 |
else |
aea170a09 ASoC: soc-cache: ... |
124 |
codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); |
7a30a3db3 ASoC: soc-cache: ... |
125 126 127 128 129 130 131 132 |
if (!codec->reg_cache) return -ENOMEM; return 0; } /* an array of all supported compression types */ static const struct snd_soc_cache_ops cache_types[] = { |
be4fcddd1 ASoC: If we can't... |
133 |
/* Flat *must* be the first entry for fallback */ |
7a30a3db3 ASoC: soc-cache: ... |
134 |
{ |
df0701bb8 ASoC: soc-cache: ... |
135 |
.id = SND_SOC_FLAT_COMPRESSION, |
0d735eaa2 ASoC: soc-cache: ... |
136 |
.name = "flat", |
7a30a3db3 ASoC: soc-cache: ... |
137 138 139 140 141 |
.init = snd_soc_flat_cache_init, .exit = snd_soc_flat_cache_exit, .read = snd_soc_flat_cache_read, .write = snd_soc_flat_cache_write, .sync = snd_soc_flat_cache_sync |
cc28fb8e7 ASoC: soc-cache: ... |
142 |
}, |
7a30a3db3 ASoC: soc-cache: ... |
143 144 145 146 147 148 149 |
}; int snd_soc_cache_init(struct snd_soc_codec *codec) { int i; for (i = 0; i < ARRAY_SIZE(cache_types); ++i) |
23bbce34f ASoC: Add compres... |
150 |
if (cache_types[i].id == codec->compress_type) |
7a30a3db3 ASoC: soc-cache: ... |
151 |
break; |
be4fcddd1 ASoC: If we can't... |
152 153 |
/* Fall back to flat compression */ |
7a30a3db3 ASoC: soc-cache: ... |
154 |
if (i == ARRAY_SIZE(cache_types)) { |
be4fcddd1 ASoC: If we can't... |
155 156 157 158 |
dev_warn(codec->dev, "Could not match compress type: %d ", codec->compress_type); i = 0; |
7a30a3db3 ASoC: soc-cache: ... |
159 160 161 162 |
} mutex_init(&codec->cache_rw_mutex); codec->cache_ops = &cache_types[i]; |
0d735eaa2 ASoC: soc-cache: ... |
163 164 165 166 167 |
if (codec->cache_ops->init) { if (codec->cache_ops->name) dev_dbg(codec->dev, "Initializing %s cache for %s codec ", codec->cache_ops->name, codec->name); |
7a30a3db3 ASoC: soc-cache: ... |
168 |
return codec->cache_ops->init(codec); |
0d735eaa2 ASoC: soc-cache: ... |
169 |
} |
acd61451e ASoC: soc-cache: ... |
170 |
return -ENOSYS; |
7a30a3db3 ASoC: soc-cache: ... |
171 172 173 174 175 176 177 178 |
} /* * NOTE: keep in mind that this function might be called * multiple times. */ int snd_soc_cache_exit(struct snd_soc_codec *codec) { |
0d735eaa2 ASoC: soc-cache: ... |
179 180 181 182 183 |
if (codec->cache_ops && codec->cache_ops->exit) { if (codec->cache_ops->name) dev_dbg(codec->dev, "Destroying %s cache for %s codec ", codec->cache_ops->name, codec->name); |
7a30a3db3 ASoC: soc-cache: ... |
184 |
return codec->cache_ops->exit(codec); |
0d735eaa2 ASoC: soc-cache: ... |
185 |
} |
acd61451e ASoC: soc-cache: ... |
186 |
return -ENOSYS; |
7a30a3db3 ASoC: soc-cache: ... |
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
} /** * snd_soc_cache_read: Fetch the value of a given register from the cache. * * @codec: CODEC to configure. * @reg: The register index. * @value: The value to be returned. */ int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { int ret; mutex_lock(&codec->cache_rw_mutex); if (value && codec->cache_ops && codec->cache_ops->read) { ret = codec->cache_ops->read(codec, reg, value); mutex_unlock(&codec->cache_rw_mutex); return ret; } mutex_unlock(&codec->cache_rw_mutex); |
acd61451e ASoC: soc-cache: ... |
210 |
return -ENOSYS; |
7a30a3db3 ASoC: soc-cache: ... |
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
} EXPORT_SYMBOL_GPL(snd_soc_cache_read); /** * snd_soc_cache_write: Set the value of a given register in the cache. * * @codec: CODEC to configure. * @reg: The register index. * @value: The new register value. */ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret; mutex_lock(&codec->cache_rw_mutex); if (codec->cache_ops && codec->cache_ops->write) { ret = codec->cache_ops->write(codec, reg, value); mutex_unlock(&codec->cache_rw_mutex); return ret; } mutex_unlock(&codec->cache_rw_mutex); |
acd61451e ASoC: soc-cache: ... |
235 |
return -ENOSYS; |
7a30a3db3 ASoC: soc-cache: ... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
} EXPORT_SYMBOL_GPL(snd_soc_cache_write); /** * snd_soc_cache_sync: Sync the register cache with the hardware. * * @codec: CODEC to configure. * * Any registers that should not be synced should be marked as * volatile. In general drivers can choose not to use the provided * syncing functionality if they so require. */ int snd_soc_cache_sync(struct snd_soc_codec *codec) { int ret; |
c358e640a ASoC: soc-cache: ... |
251 |
const char *name; |
7a30a3db3 ASoC: soc-cache: ... |
252 253 254 255 |
if (!codec->cache_sync) { return 0; } |
46fdaa3be ASoC: soc-cache: ... |
256 |
if (!codec->cache_ops || !codec->cache_ops->sync) |
acd61451e ASoC: soc-cache: ... |
257 |
return -ENOSYS; |
46fdaa3be ASoC: soc-cache: ... |
258 |
|
c358e640a ASoC: soc-cache: ... |
259 260 261 262 |
if (codec->cache_ops->name) name = codec->cache_ops->name; else name = "unknown"; |
46fdaa3be ASoC: soc-cache: ... |
263 264 265 266 267 268 269 270 271 272 |
if (codec->cache_ops->name) dev_dbg(codec->dev, "Syncing %s cache for %s codec ", codec->cache_ops->name, codec->name); trace_snd_soc_cache_sync(codec, name, "start"); ret = codec->cache_ops->sync(codec); if (!ret) codec->cache_sync = 0; trace_snd_soc_cache_sync(codec, name, "end"); return ret; |
7a30a3db3 ASoC: soc-cache: ... |
273 274 |
} EXPORT_SYMBOL_GPL(snd_soc_cache_sync); |
066d16c3e ASoC: soc-cache: ... |
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; unsigned int min, max, index; codec_drv = codec->driver; min = 0; max = codec_drv->reg_access_size - 1; do { index = (min + max) / 2; if (codec_drv->reg_access_default[index].reg == reg) return index; if (codec_drv->reg_access_default[index].reg < reg) min = index + 1; else max = index; } while (min <= max); return -1; } int snd_soc_default_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { int index; if (reg >= codec->driver->reg_cache_size) return 1; index = snd_soc_get_reg_access_index(codec, reg); if (index < 0) return 0; return codec->driver->reg_access_default[index].vol; } EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); int snd_soc_default_readable_register(struct snd_soc_codec *codec, unsigned int reg) { int index; if (reg >= codec->driver->reg_cache_size) return 1; index = snd_soc_get_reg_access_index(codec, reg); if (index < 0) return 0; return codec->driver->reg_access_default[index].read; } EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); |
8020454c9 ASoC: Add default... |
324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
int snd_soc_default_writable_register(struct snd_soc_codec *codec, unsigned int reg) { int index; if (reg >= codec->driver->reg_cache_size) return 1; index = snd_soc_get_reg_access_index(codec, reg); if (index < 0) return 0; return codec->driver->reg_access_default[index].write; } EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); |