linux/sound/soc/soc-cache.c
<<
>>
Prefs
   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
 100        kfree(codec->reg_cache);
 101        codec->reg_cache = NULL;
 102        return 0;
 103}
 104
 105/**
 106 * snd_soc_cache_read: Fetch the value of a given register from the cache.
 107 *
 108 * @codec: CODEC to configure.
 109 * @reg: The register index.
 110 * @value: The value to be returned.
 111 */
 112int snd_soc_cache_read(struct snd_soc_codec *codec,
 113                       unsigned int reg, unsigned int *value)
 114{
 115        if (!value)
 116                return -EINVAL;
 117
 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);
 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        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);
 143
 144        return 0;
 145}
 146EXPORT_SYMBOL_GPL(snd_soc_cache_write);
 147
 148static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
 149{
 150        int i;
 151        int ret;
 152        const struct snd_soc_codec_driver *codec_drv;
 153        unsigned int val;
 154
 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);
 158                if (ret)
 159                        return ret;
 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)
 163                                continue;
 164
 165                WARN_ON(!snd_soc_codec_writable_register(codec, i));
 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->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);
 203