aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-cache.c
diff options
context:
space:
mode:
authorDimitris Papastamos <dp@opensource.wolfsonmicro.com>2010-11-11 05:04:59 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-11 10:59:22 -0500
commita7f387d5afd5e1102f909ab611370014f1f59ae2 (patch)
treea1a48921d3ec2279d99369b2f7cc0f28165f2fd4 /sound/soc/soc-cache.c
parentcc28fb8e7d55d4d7c1661dc0b236f4faddecdd9e (diff)
ASoC: soc-cache: Add support for rbtree based register caching
This patch adds support for rbtree compression when storing the register cache. It does this by not adding any uninitialized registers (those whose value is 0). If any of those registers is written with a nonzero value they get added into the rbtree. Consider a sample device with a large sparse register map. The register indices are between [0, 0x31ff]. An array of 12800 registers is thus created each of which is 2 bytes. This results in a 25kB region. This array normally lives outside soc-core, normally in the driver itself. The original soc-core code would kmemdup this region resulting in 50kB total memory. When using the rbtree compression technique and __devinitconst on the original array the figures are as follows. For this typical device, you might have 100 initialized registers, that is registers that are nonzero by default. We build an rbtree with 100 nodes, each of which is 24 bytes. This results in ~2kB of memory. Assuming that the target arch can freeup the memory used by the initial __devinitconst array, we end up using about ~2kB bytes of actual memory. The memory footprint will increase as uninitialized registers get written and thus new nodes created in the rbtree. In practice, most of those registers are never changed. If the target arch can't freeup the __devinitconst array, we end up using a total of ~27kB. The difference between the rbtree and the LZO caching techniques, is that if using the LZO technique the size of the cache will increase slower as more uninitialized registers get changed. Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r--sound/soc/soc-cache.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 4581bf100222..6c0589e3fefb 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -16,6 +16,7 @@
16#include <sound/soc.h> 16#include <sound/soc.h>
17#include <linux/lzo.h> 17#include <linux/lzo.h>
18#include <linux/bitmap.h> 18#include <linux/bitmap.h>
19#include <linux/rbtree.h>
19 20
20static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, 21static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
21 unsigned int reg) 22 unsigned int reg)
@@ -760,6 +761,229 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
760} 761}
761EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); 762EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
762 763
764struct snd_soc_rbtree_node {
765 struct rb_node node;
766 unsigned int reg;
767 unsigned int value;
768 unsigned int defval;
769} __attribute__ ((packed));
770
771struct snd_soc_rbtree_ctx {
772 struct rb_root root;
773};
774
775static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup(
776 struct rb_root *root, unsigned int reg)
777{
778 struct rb_node *node;
779 struct snd_soc_rbtree_node *rbnode;
780
781 node = root->rb_node;
782 while (node) {
783 rbnode = container_of(node, struct snd_soc_rbtree_node, node);
784 if (rbnode->reg < reg)
785 node = node->rb_left;
786 else if (rbnode->reg > reg)
787 node = node->rb_right;
788 else
789 return rbnode;
790 }
791
792 return NULL;
793}
794
795
796static int snd_soc_rbtree_insert(struct rb_root *root,
797 struct snd_soc_rbtree_node *rbnode)
798{
799 struct rb_node **new, *parent;
800 struct snd_soc_rbtree_node *rbnode_tmp;
801
802 parent = NULL;
803 new = &root->rb_node;
804 while (*new) {
805 rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node,
806 node);
807 parent = *new;
808 if (rbnode_tmp->reg < rbnode->reg)
809 new = &((*new)->rb_left);
810 else if (rbnode_tmp->reg > rbnode->reg)
811 new = &((*new)->rb_right);
812 else
813 return 0;
814 }
815
816 /* insert the node into the rbtree */
817 rb_link_node(&rbnode->node, parent, new);
818 rb_insert_color(&rbnode->node, root);
819
820 return 1;
821}
822
823static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
824{
825 struct snd_soc_rbtree_ctx *rbtree_ctx;
826 struct rb_node *node;
827 struct snd_soc_rbtree_node *rbnode;
828 unsigned int val;
829
830 rbtree_ctx = codec->reg_cache;
831 for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
832 rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
833 if (rbnode->value == rbnode->defval)
834 continue;
835 snd_soc_cache_read(codec, rbnode->reg, &val);
836 snd_soc_write(codec, rbnode->reg, val);
837 dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
838 rbnode->reg, val);
839 }
840
841 return 0;
842}
843
844static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec,
845 unsigned int reg, unsigned int value)
846{
847 struct snd_soc_rbtree_ctx *rbtree_ctx;
848 struct snd_soc_rbtree_node *rbnode;
849
850 rbtree_ctx = codec->reg_cache;
851 rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
852 if (rbnode) {
853 if (rbnode->value == value)
854 return 0;
855 rbnode->value = value;
856 } else {
857 /* bail out early, no need to create the rbnode yet */
858 if (!value)
859 return 0;
860 /*
861 * for uninitialized registers whose value is changed
862 * from the default zero, create an rbnode and insert
863 * it into the tree.
864 */
865 rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
866 if (!rbnode)
867 return -ENOMEM;
868 rbnode->reg = reg;
869 rbnode->value = value;
870 snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode);
871 }
872
873 return 0;
874}
875
876static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec,
877 unsigned int reg, unsigned int *value)
878{
879 struct snd_soc_rbtree_ctx *rbtree_ctx;
880 struct snd_soc_rbtree_node *rbnode;
881
882 rbtree_ctx = codec->reg_cache;
883 rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
884 if (rbnode) {
885 *value = rbnode->value;
886 } else {
887 /* uninitialized registers default to 0 */
888 *value = 0;
889 }
890
891 return 0;
892}
893
894static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
895{
896 struct rb_node *next;
897 struct snd_soc_rbtree_ctx *rbtree_ctx;
898 struct snd_soc_rbtree_node *rbtree_node;
899
900 /* if we've already been called then just return */
901 rbtree_ctx = codec->reg_cache;
902 if (!rbtree_ctx)
903 return 0;
904
905 /* free up the rbtree */
906 next = rb_first(&rbtree_ctx->root);
907 while (next) {
908 rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node);
909 next = rb_next(&rbtree_node->node);
910 rb_erase(&rbtree_node->node, &rbtree_ctx->root);
911 kfree(rbtree_node);
912 }
913
914 /* release the resources */
915 kfree(codec->reg_cache);
916 codec->reg_cache = NULL;
917
918 return 0;
919}
920
921static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
922{
923 struct snd_soc_rbtree_ctx *rbtree_ctx;
924
925 codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
926 if (!codec->reg_cache)
927 return -ENOMEM;
928
929 rbtree_ctx = codec->reg_cache;
930 rbtree_ctx->root = RB_ROOT;
931
932 if (!codec->driver->reg_cache_default)
933 return 0;
934
935/*
936 * populate the rbtree with the initialized registers. All other
937 * registers will be inserted into the tree when they are first written.
938 *
939 * The reasoning behind this, is that we need to step through and
940 * dereference the cache in u8/u16 increments without sacrificing
941 * portability. This could also be done using memcpy() but that would
942 * be slightly more cryptic.
943 */
944#define snd_soc_rbtree_populate(cache) \
945({ \
946 int ret, i; \
947 struct snd_soc_rbtree_node *rbtree_node; \
948 \
949 ret = 0; \
950 cache = codec->driver->reg_cache_default; \
951 for (i = 0; i < codec->driver->reg_cache_size; ++i) { \
952 if (!cache[i]) \
953 continue; \
954 rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \
955 if (!rbtree_node) { \
956 ret = -ENOMEM; \
957 snd_soc_cache_exit(codec); \
958 break; \
959 } \
960 rbtree_node->reg = i; \
961 rbtree_node->value = cache[i]; \
962 rbtree_node->defval = cache[i]; \
963 snd_soc_rbtree_insert(&rbtree_ctx->root, \
964 rbtree_node); \
965 } \
966 ret; \
967})
968
969 switch (codec->driver->reg_word_size) {
970 case 1: {
971 const u8 *cache;
972
973 return snd_soc_rbtree_populate(cache);
974 }
975 case 2: {
976 const u16 *cache;
977
978 return snd_soc_rbtree_populate(cache);
979 }
980 default:
981 BUG();
982 }
983
984 return 0;
985}
986
763struct snd_soc_lzo_ctx { 987struct snd_soc_lzo_ctx {
764 void *wmem; 988 void *wmem;
765 void *dst; 989 void *dst;
@@ -1296,6 +1520,14 @@ static const struct snd_soc_cache_ops cache_types[] = {
1296 .read = snd_soc_lzo_cache_read, 1520 .read = snd_soc_lzo_cache_read,
1297 .write = snd_soc_lzo_cache_write, 1521 .write = snd_soc_lzo_cache_write,
1298 .sync = snd_soc_lzo_cache_sync 1522 .sync = snd_soc_lzo_cache_sync
1523 },
1524 {
1525 .id = SND_SOC_RBTREE_COMPRESSION,
1526 .init = snd_soc_rbtree_cache_init,
1527 .exit = snd_soc_rbtree_cache_exit,
1528 .read = snd_soc_rbtree_cache_read,
1529 .write = snd_soc_rbtree_cache_write,
1530 .sync = snd_soc_rbtree_cache_sync
1299 } 1531 }
1300}; 1532};
1301 1533