Blame view

sound/soc/soc-cache.c 7.81 KB
17a52fd60   Mark Brown   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   Mark Brown   ASoC: Add I/O con...
13
  #include <linux/i2c.h>
27ded041f   Mark Brown   ASoC: Factor out ...
14
  #include <linux/spi/spi.h>
17a52fd60   Mark Brown   ASoC: Begin to fa...
15
  #include <sound/soc.h>
cc28fb8e7   Dimitris Papastamos   ASoC: soc-cache: ...
16
  #include <linux/bitmap.h>
a7f387d5a   Dimitris Papastamos   ASoC: soc-cache: ...
17
  #include <linux/rbtree.h>
d81a6d717   Paul Gortmaker   sound: Add export...
18
  #include <linux/export.h>
17a52fd60   Mark Brown   ASoC: Begin to fa...
19

c358e640a   Dimitris Papastamos   ASoC: soc-cache: ...
20
  #include <trace/events/asoc.h>
1321e8838   Dimitris Papastamos   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   Mark Brown   ASoC: Check for N...
48
49
  	if (!base)
  		return -1;
1321e8838   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
65
66
67
  static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
  {
  	int i;
7a33d4ce8   Dimitris Papastamos   ASoC: soc-cache: ...
68
  	int ret;
001ae4c03   Mark Brown   ASoC: Constify st...
69
  	const struct snd_soc_codec_driver *codec_drv;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
74
75
76
  		ret = snd_soc_cache_read(codec, i, &val);
  		if (ret)
  			return ret;
d779fce5d   Dimitris Papastamos   ASoC: soc-cache: ...
77
78
  		if (codec->reg_def_copy)
  			if (snd_soc_get_cache_val(codec->reg_def_copy,
1321e8838   Dimitris Papastamos   ASoC: soc-cache: ...
79
80
  						  i, codec_drv->reg_word_size) == val)
  				continue;
6c5b756aa   Lars-Peter Clausen   ASoC: Fix registe...
81
82
  
  		WARN_ON(!snd_soc_codec_writable_register(codec, i));
7a33d4ce8   Dimitris Papastamos   ASoC: soc-cache: ...
83
84
85
  		ret = snd_soc_write(codec, i, val);
  		if (ret)
  			return ret;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
96
97
  	snd_soc_set_cache_val(codec->reg_cache, reg, value,
  			      codec->driver->reg_word_size);
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
104
105
  	*value = snd_soc_get_cache_val(codec->reg_cache, reg,
  				       codec->driver->reg_word_size);
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
120
121
  	if (codec->reg_def_copy)
  		codec->reg_cache = kmemdup(codec->reg_def_copy,
aea170a09   Dimitris Papastamos   ASoC: soc-cache: ...
122
  					   codec->reg_size, GFP_KERNEL);
7a30a3db3   Dimitris Papastamos   ASoC: soc-cache: ...
123
  	else
aea170a09   Dimitris Papastamos   ASoC: soc-cache: ...
124
  		codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
7a30a3db3   Dimitris Papastamos   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   Mark Brown   ASoC: If we can't...
133
  	/* Flat *must* be the first entry for fallback */
7a30a3db3   Dimitris Papastamos   ASoC: soc-cache: ...
134
  	{
df0701bb8   Dimitris Papastamos   ASoC: soc-cache: ...
135
  		.id = SND_SOC_FLAT_COMPRESSION,
0d735eaa2   Dimitris Papastamos   ASoC: soc-cache: ...
136
  		.name = "flat",
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
142
  	},
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: Add compres...
150
  		if (cache_types[i].id == codec->compress_type)
7a30a3db3   Dimitris Papastamos   ASoC: soc-cache: ...
151
  			break;
be4fcddd1   Mark Brown   ASoC: If we can't...
152
153
  
  	/* Fall back to flat compression */
7a30a3db3   Dimitris Papastamos   ASoC: soc-cache: ...
154
  	if (i == ARRAY_SIZE(cache_types)) {
be4fcddd1   Mark Brown   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   Dimitris Papastamos   ASoC: soc-cache: ...
159
160
161
162
  	}
  
  	mutex_init(&codec->cache_rw_mutex);
  	codec->cache_ops = &cache_types[i];
0d735eaa2   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
168
  		return codec->cache_ops->init(codec);
0d735eaa2   Dimitris Papastamos   ASoC: soc-cache: ...
169
  	}
acd61451e   Dimitris Papastamos   ASoC: soc-cache: ...
170
  	return -ENOSYS;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
184
  		return codec->cache_ops->exit(codec);
0d735eaa2   Dimitris Papastamos   ASoC: soc-cache: ...
185
  	}
acd61451e   Dimitris Papastamos   ASoC: soc-cache: ...
186
  	return -ENOSYS;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
210
  	return -ENOSYS;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
235
  	return -ENOSYS;
7a30a3db3   Dimitris Papastamos   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   Dimitris Papastamos   ASoC: soc-cache: ...
251
  	const char *name;
7a30a3db3   Dimitris Papastamos   ASoC: soc-cache: ...
252
253
254
255
  
  	if (!codec->cache_sync) {
  		return 0;
  	}
46fdaa3be   Dan Carpenter   ASoC: soc-cache: ...
256
  	if (!codec->cache_ops || !codec->cache_ops->sync)
acd61451e   Dimitris Papastamos   ASoC: soc-cache: ...
257
  		return -ENOSYS;
46fdaa3be   Dan Carpenter   ASoC: soc-cache: ...
258

c358e640a   Dimitris Papastamos   ASoC: soc-cache: ...
259
260
261
262
  	if (codec->cache_ops->name)
  		name = codec->cache_ops->name;
  	else
  		name = "unknown";
46fdaa3be   Dan Carpenter   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   Dimitris Papastamos   ASoC: soc-cache: ...
273
274
  }
  EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
066d16c3e   Dimitris Papastamos   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   Dimitris Papastamos   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);