at v3.17 202 lines 4.7 kB view raw
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 20static 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 45static 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 68int 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 */ 98int 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 */ 114int 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} 128EXPORT_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 */ 137int 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} 148EXPORT_SYMBOL_GPL(snd_soc_cache_write); 149 150static 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 */ 185int 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} 202EXPORT_SYMBOL_GPL(snd_soc_cache_sync);