at v3.14 201 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 mutex_init(&codec->cache_rw_mutex); 76 77 dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", 78 codec->name); 79 80 if (codec_drv->reg_cache_default) 81 codec->reg_cache = kmemdup(codec_drv->reg_cache_default, 82 reg_size, GFP_KERNEL); 83 else 84 codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); 85 if (!codec->reg_cache) 86 return -ENOMEM; 87 88 return 0; 89} 90 91/* 92 * NOTE: keep in mind that this function might be called 93 * multiple times. 94 */ 95int snd_soc_cache_exit(struct snd_soc_codec *codec) 96{ 97 dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", 98 codec->name); 99 if (!codec->reg_cache) 100 return 0; 101 kfree(codec->reg_cache); 102 codec->reg_cache = NULL; 103 return 0; 104} 105 106/** 107 * snd_soc_cache_read: Fetch the value of a given register from the cache. 108 * 109 * @codec: CODEC to configure. 110 * @reg: The register index. 111 * @value: The value to be returned. 112 */ 113int snd_soc_cache_read(struct snd_soc_codec *codec, 114 unsigned int reg, unsigned int *value) 115{ 116 if (!value) 117 return -EINVAL; 118 119 mutex_lock(&codec->cache_rw_mutex); 120 *value = snd_soc_get_cache_val(codec->reg_cache, reg, 121 codec->driver->reg_word_size); 122 mutex_unlock(&codec->cache_rw_mutex); 123 124 return 0; 125} 126EXPORT_SYMBOL_GPL(snd_soc_cache_read); 127 128/** 129 * snd_soc_cache_write: Set the value of a given register in the cache. 130 * 131 * @codec: CODEC to configure. 132 * @reg: The register index. 133 * @value: The new register value. 134 */ 135int snd_soc_cache_write(struct snd_soc_codec *codec, 136 unsigned int reg, unsigned int value) 137{ 138 mutex_lock(&codec->cache_rw_mutex); 139 snd_soc_set_cache_val(codec->reg_cache, reg, value, 140 codec->driver->reg_word_size); 141 mutex_unlock(&codec->cache_rw_mutex); 142 143 return 0; 144} 145EXPORT_SYMBOL_GPL(snd_soc_cache_write); 146 147static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) 148{ 149 int i; 150 int ret; 151 const struct snd_soc_codec_driver *codec_drv; 152 unsigned int val; 153 154 codec_drv = codec->driver; 155 for (i = 0; i < codec_drv->reg_cache_size; ++i) { 156 ret = snd_soc_cache_read(codec, i, &val); 157 if (ret) 158 return ret; 159 if (codec_drv->reg_cache_default) 160 if (snd_soc_get_cache_val(codec_drv->reg_cache_default, 161 i, codec_drv->reg_word_size) == val) 162 continue; 163 164 WARN_ON(!snd_soc_codec_writable_register(codec, i)); 165 166 ret = snd_soc_write(codec, i, val); 167 if (ret) 168 return ret; 169 dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", 170 i, val); 171 } 172 return 0; 173} 174 175/** 176 * snd_soc_cache_sync: Sync the register cache with the hardware. 177 * 178 * @codec: CODEC to configure. 179 * 180 * Any registers that should not be synced should be marked as 181 * volatile. In general drivers can choose not to use the provided 182 * syncing functionality if they so require. 183 */ 184int snd_soc_cache_sync(struct snd_soc_codec *codec) 185{ 186 const char *name = "flat"; 187 int ret; 188 189 if (!codec->cache_sync) 190 return 0; 191 192 dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", 193 codec->name); 194 trace_snd_soc_cache_sync(codec, name, "start"); 195 ret = snd_soc_flat_cache_sync(codec); 196 if (!ret) 197 codec->cache_sync = 0; 198 trace_snd_soc_cache_sync(codec, name, "end"); 199 return ret; 200} 201EXPORT_SYMBOL_GPL(snd_soc_cache_sync);