2 * soc-cache.c -- ASoC register cache helpers
4 * Copyright 2009 Wolfson Microelectronics PLC.
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
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.
14 #include <sound/soc.h>
15 #include <linux/export.h>
16 #include <linux/slab.h>
18 #include <trace/events/asoc.h>
20 static bool snd_soc_set_cache_val(void *base
, unsigned int idx
,
21 unsigned int val
, unsigned int word_size
)
26 if (cache
[idx
] == val
)
33 if (cache
[idx
] == val
)
39 WARN(1, "Invalid word_size %d\n", word_size
);
45 static unsigned int snd_soc_get_cache_val(const void *base
, unsigned int idx
,
46 unsigned int word_size
)
53 const u8
*cache
= base
;
57 const u16
*cache
= base
;
61 WARN(1, "Invalid word_size %d\n", word_size
);
68 int snd_soc_cache_init(struct snd_soc_codec
*codec
)
70 const struct snd_soc_codec_driver
*codec_drv
= codec
->driver
;
73 reg_size
= codec_drv
->reg_cache_size
* codec_drv
->reg_word_size
;
75 mutex_init(&codec
->cache_rw_mutex
);
77 dev_dbg(codec
->dev
, "ASoC: Initializing cache for %s codec\n",
80 if (codec_drv
->reg_cache_default
)
81 codec
->reg_cache
= kmemdup(codec_drv
->reg_cache_default
,
82 reg_size
, GFP_KERNEL
);
84 codec
->reg_cache
= kzalloc(reg_size
, GFP_KERNEL
);
85 if (!codec
->reg_cache
)
92 * NOTE: keep in mind that this function might be called
95 int snd_soc_cache_exit(struct snd_soc_codec
*codec
)
97 dev_dbg(codec
->dev
, "ASoC: Destroying cache for %s codec\n",
100 kfree(codec
->reg_cache
);
101 codec
->reg_cache
= NULL
;
106 * snd_soc_cache_read: Fetch the value of a given register from the cache.
108 * @codec: CODEC to configure.
109 * @reg: The register index.
110 * @value: The value to be returned.
112 int snd_soc_cache_read(struct snd_soc_codec
*codec
,
113 unsigned int reg
, unsigned int *value
)
118 mutex_lock(&codec
->cache_rw_mutex
);
119 if (!ZERO_OR_NULL_PTR(codec
->reg_cache
))
120 *value
= snd_soc_get_cache_val(codec
->reg_cache
, reg
,
121 codec
->driver
->reg_word_size
);
122 mutex_unlock(&codec
->cache_rw_mutex
);
126 EXPORT_SYMBOL_GPL(snd_soc_cache_read
);
129 * snd_soc_cache_write: Set the value of a given register in the cache.
131 * @codec: CODEC to configure.
132 * @reg: The register index.
133 * @value: The new register value.
135 int snd_soc_cache_write(struct snd_soc_codec
*codec
,
136 unsigned int reg
, unsigned int value
)
138 mutex_lock(&codec
->cache_rw_mutex
);
139 if (!ZERO_OR_NULL_PTR(codec
->reg_cache
))
140 snd_soc_set_cache_val(codec
->reg_cache
, reg
, value
,
141 codec
->driver
->reg_word_size
);
142 mutex_unlock(&codec
->cache_rw_mutex
);
146 EXPORT_SYMBOL_GPL(snd_soc_cache_write
);
148 static int snd_soc_flat_cache_sync(struct snd_soc_codec
*codec
)
152 const struct snd_soc_codec_driver
*codec_drv
;
155 codec_drv
= codec
->driver
;
156 for (i
= 0; i
< codec_drv
->reg_cache_size
; ++i
) {
157 ret
= snd_soc_cache_read(codec
, i
, &val
);
160 if (codec_drv
->reg_cache_default
)
161 if (snd_soc_get_cache_val(codec_drv
->reg_cache_default
,
162 i
, codec_drv
->reg_word_size
) == val
)
165 WARN_ON(!snd_soc_codec_writable_register(codec
, i
));
167 ret
= snd_soc_write(codec
, i
, val
);
170 dev_dbg(codec
->dev
, "ASoC: Synced register %#x, value = %#x\n",
177 * snd_soc_cache_sync: Sync the register cache with the hardware.
179 * @codec: CODEC to configure.
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.
185 int snd_soc_cache_sync(struct snd_soc_codec
*codec
)
187 const char *name
= "flat";
190 if (!codec
->cache_sync
)
193 dev_dbg(codec
->dev
, "ASoC: Syncing cache for %s codec\n",
195 trace_snd_soc_cache_sync(codec
, name
, "start");
196 ret
= snd_soc_flat_cache_sync(codec
);
198 codec
->cache_sync
= 0;
199 trace_snd_soc_cache_sync(codec
, name
, "end");
202 EXPORT_SYMBOL_GPL(snd_soc_cache_sync
);