• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * soc-cache.c  --  ASoC register cache helpers
3  *
4  * Copyright 2009 Wolfson Microelectronics PLC.
5  *
6  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
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 <sound/soc.h>
15 #include <linux/export.h>
16 #include <linux/slab.h>
17 
18 #include <trace/events/asoc.h>
19 
snd_soc_set_cache_val(void * base,unsigned int idx,unsigned int val,unsigned int word_size)20 static bool snd_soc_set_cache_val(void *base, unsigned int idx,
21 				  unsigned int val, unsigned int word_size)
22 {
23 	switch (word_size) {
24 	case 1: {
25 		u8 *cache = base;
26 		if (cache[idx] == val)
27 			return true;
28 		cache[idx] = val;
29 		break;
30 	}
31 	case 2: {
32 		u16 *cache = base;
33 		if (cache[idx] == val)
34 			return true;
35 		cache[idx] = val;
36 		break;
37 	}
38 	default:
39 		WARN(1, "Invalid word_size %d\n", word_size);
40 		break;
41 	}
42 	return false;
43 }
44 
snd_soc_get_cache_val(const void * base,unsigned int idx,unsigned int word_size)45 static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
46 		unsigned int word_size)
47 {
48 	if (!base)
49 		return -1;
50 
51 	switch (word_size) {
52 	case 1: {
53 		const u8 *cache = base;
54 		return cache[idx];
55 	}
56 	case 2: {
57 		const u16 *cache = base;
58 		return cache[idx];
59 	}
60 	default:
61 		WARN(1, "Invalid word_size %d\n", word_size);
62 		break;
63 	}
64 	/* unreachable */
65 	return -1;
66 }
67 
snd_soc_cache_init(struct snd_soc_codec * codec)68 int snd_soc_cache_init(struct snd_soc_codec *codec)
69 {
70 	const struct snd_soc_codec_driver *codec_drv = codec->driver;
71 	size_t reg_size;
72 
73 	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
74 
75 	if (!reg_size)
76 		return 0;
77 
78 	mutex_init(&codec->cache_rw_mutex);
79 
80 	dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
81 				codec->component.name);
82 
83 	if (codec_drv->reg_cache_default)
84 		codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
85 					   reg_size, GFP_KERNEL);
86 	else
87 		codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
88 	if (!codec->reg_cache)
89 		return -ENOMEM;
90 
91 	return 0;
92 }
93 
94 /*
95  * NOTE: keep in mind that this function might be called
96  * multiple times.
97  */
snd_soc_cache_exit(struct snd_soc_codec * codec)98 int snd_soc_cache_exit(struct snd_soc_codec *codec)
99 {
100 	dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n",
101 			codec->component.name);
102 	kfree(codec->reg_cache);
103 	codec->reg_cache = NULL;
104 	return 0;
105 }
106 
107 /**
108  * snd_soc_cache_read: Fetch the value of a given register from the cache.
109  *
110  * @codec: CODEC to configure.
111  * @reg: The register index.
112  * @value: The value to be returned.
113  */
snd_soc_cache_read(struct snd_soc_codec * codec,unsigned int reg,unsigned int * value)114 int snd_soc_cache_read(struct snd_soc_codec *codec,
115 		       unsigned int reg, unsigned int *value)
116 {
117 	if (!value)
118 		return -EINVAL;
119 
120 	mutex_lock(&codec->cache_rw_mutex);
121 	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
122 		*value = snd_soc_get_cache_val(codec->reg_cache, reg,
123 					       codec->driver->reg_word_size);
124 	mutex_unlock(&codec->cache_rw_mutex);
125 
126 	return 0;
127 }
128 EXPORT_SYMBOL_GPL(snd_soc_cache_read);
129 
130 /**
131  * snd_soc_cache_write: Set the value of a given register in the cache.
132  *
133  * @codec: CODEC to configure.
134  * @reg: The register index.
135  * @value: The new register value.
136  */
snd_soc_cache_write(struct snd_soc_codec * codec,unsigned int reg,unsigned int value)137 int snd_soc_cache_write(struct snd_soc_codec *codec,
138 			unsigned int reg, unsigned int value)
139 {
140 	mutex_lock(&codec->cache_rw_mutex);
141 	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
142 		snd_soc_set_cache_val(codec->reg_cache, reg, value,
143 				      codec->driver->reg_word_size);
144 	mutex_unlock(&codec->cache_rw_mutex);
145 
146 	return 0;
147 }
148 EXPORT_SYMBOL_GPL(snd_soc_cache_write);
149 
snd_soc_flat_cache_sync(struct snd_soc_codec * codec)150 static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
151 {
152 	int i;
153 	int ret;
154 	const struct snd_soc_codec_driver *codec_drv;
155 	unsigned int val;
156 
157 	codec_drv = codec->driver;
158 	for (i = 0; i < codec_drv->reg_cache_size; ++i) {
159 		ret = snd_soc_cache_read(codec, i, &val);
160 		if (ret)
161 			return ret;
162 		if (codec_drv->reg_cache_default)
163 			if (snd_soc_get_cache_val(codec_drv->reg_cache_default,
164 						  i, codec_drv->reg_word_size) == val)
165 				continue;
166 
167 		ret = snd_soc_write(codec, i, val);
168 		if (ret)
169 			return ret;
170 		dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n",
171 			i, val);
172 	}
173 	return 0;
174 }
175 
176 /**
177  * snd_soc_cache_sync: Sync the register cache with the hardware.
178  *
179  * @codec: CODEC to configure.
180  *
181  * Any registers that should not be synced should be marked as
182  * volatile.  In general drivers can choose not to use the provided
183  * syncing functionality if they so require.
184  */
snd_soc_cache_sync(struct snd_soc_codec * codec)185 int snd_soc_cache_sync(struct snd_soc_codec *codec)
186 {
187 	const char *name = "flat";
188 	int ret;
189 
190 	if (!codec->cache_sync)
191 		return 0;
192 
193 	dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
194 		codec->component.name);
195 	trace_snd_soc_cache_sync(codec, name, "start");
196 	ret = snd_soc_flat_cache_sync(codec);
197 	if (!ret)
198 		codec->cache_sync = 0;
199 	trace_snd_soc_cache_sync(codec, name, "end");
200 	return ret;
201 }
202 EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
203