Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
soc-cache.c
Go to the documentation of this file.
1 /*
2  * soc-cache.c -- ASoC register cache helpers
3  *
4  * Copyright 2009 Wolfson Microelectronics PLC.
5  *
6  * Author: Mark Brown <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  */
13 
14 #include <linux/i2c.h>
15 #include <linux/spi/spi.h>
16 #include <sound/soc.h>
17 #include <linux/bitmap.h>
18 #include <linux/rbtree.h>
19 #include <linux/export.h>
20 
21 #include <trace/events/asoc.h>
22 
23 static bool snd_soc_set_cache_val(void *base, unsigned int idx,
24  unsigned int val, unsigned int word_size)
25 {
26  switch (word_size) {
27  case 1: {
28  u8 *cache = base;
29  if (cache[idx] == val)
30  return true;
31  cache[idx] = val;
32  break;
33  }
34  case 2: {
35  u16 *cache = base;
36  if (cache[idx] == val)
37  return true;
38  cache[idx] = val;
39  break;
40  }
41  default:
42  BUG();
43  }
44  return false;
45 }
46 
47 static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
48  unsigned int word_size)
49 {
50  if (!base)
51  return -1;
52 
53  switch (word_size) {
54  case 1: {
55  const u8 *cache = base;
56  return cache[idx];
57  }
58  case 2: {
59  const u16 *cache = base;
60  return cache[idx];
61  }
62  default:
63  BUG();
64  }
65  /* unreachable */
66  return -1;
67 }
68 
69 static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
70 {
71  int i;
72  int ret;
73  const struct snd_soc_codec_driver *codec_drv;
74  unsigned int val;
75 
76  codec_drv = codec->driver;
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 
87 
88  ret = snd_soc_write(codec, i, val);
89  if (ret)
90  return ret;
91  dev_dbg(codec->dev, "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 
113 static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec)
114 {
115  if (!codec->reg_cache)
116  return 0;
117  kfree(codec->reg_cache);
118  codec->reg_cache = NULL;
119  return 0;
120 }
121 
122 static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
123 {
124  if (codec->reg_def_copy)
125  codec->reg_cache = kmemdup(codec->reg_def_copy,
126  codec->reg_size, GFP_KERNEL);
127  else
128  codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
129  if (!codec->reg_cache)
130  return -ENOMEM;
131 
132  return 0;
133 }
134 
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  {
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 
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, "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, "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 /*
177  * NOTE: keep in mind that this function might be called
178  * multiple times.
179  */
181 {
182  if (codec->cache_ops && codec->cache_ops->exit) {
183  if (codec->cache_ops->name)
184  dev_dbg(codec->dev, "Destroying %s cache for %s codec\n",
185  codec->cache_ops->name, codec->name);
186  return codec->cache_ops->exit(codec);
187  }
188  return -ENOSYS;
189 }
190 
199  unsigned int reg, unsigned int *value)
200 {
201  int ret;
202 
203  mutex_lock(&codec->cache_rw_mutex);
204 
205  if (value && codec->cache_ops && codec->cache_ops->read) {
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);
212  return -ENOSYS;
213 }
215 
224  unsigned int reg, unsigned int value)
225 {
226  int ret;
227 
228  mutex_lock(&codec->cache_rw_mutex);
229 
230  if (codec->cache_ops && codec->cache_ops->write) {
231  ret = codec->cache_ops->write(codec, reg, value);
232  mutex_unlock(&codec->cache_rw_mutex);
233  return ret;
234  }
235 
236  mutex_unlock(&codec->cache_rw_mutex);
237  return -ENOSYS;
238 }
240 
251 {
252  int ret;
253  const char *name;
254 
255  if (!codec->cache_sync) {
256  return 0;
257  }
258 
259  if (!codec->cache_ops || !codec->cache_ops->sync)
260  return -ENOSYS;
261 
262  if (codec->cache_ops->name)
263  name = codec->cache_ops->name;
264  else
265  name = "unknown";
266 
267  if (codec->cache_ops->name)
268  dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
269  codec->cache_ops->name, codec->name);
270  trace_snd_soc_cache_sync(codec, name, "start");
271  ret = codec->cache_ops->sync(codec);
272  if (!ret)
273  codec->cache_sync = 0;
274  trace_snd_soc_cache_sync(codec, name, "end");
275  return ret;
276 }
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 
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 }
313 
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 }
327 
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 }