aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-cache.c
diff options
context:
space:
mode:
authorDimitris Papastamos <dp@opensource.wolfsonmicro.com>2010-11-11 05:04:58 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-11 10:59:01 -0500
commitcc28fb8e7d55d4d7c1661dc0b236f4faddecdd9e (patch)
treea7a0123df99c39db8c5084303a07c2dfae385add /sound/soc/soc-cache.c
parent7a30a3db34cc7b2180a1a6c4a51d19d93c8a8b80 (diff)
ASoC: soc-cache: Add support for LZO register caching
This patch adds support for LZO compression when storing the register cache. The initial register defaults cache is marked as __devinitconst and the only change required for a driver to use LZO compression is to set the compress_type member in codec->driver to SND_SOC_LZO_COMPRESSION. For a typical device whose register map would normally occupy 25kB or 50kB by using the LZO compression technique, one can get down to ~5-7kB. There might be a performance penalty associated with each individual read/write due to decompressing/compressing the underlying cache, however that should not be noticeable. These memory benefits depend on whether the target architecture can get rid of the memory occupied by the original register defaults cache which is marked as __devinitconst. Nevertheless there will be some memory gain even if the target architecture can't get rid of the original register map, this should be around ~30-32kB instead of 50kB. 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.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index cbf9694097b2..4581bf100222 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -14,6 +14,8 @@
14#include <linux/i2c.h> 14#include <linux/i2c.h>
15#include <linux/spi/spi.h> 15#include <linux/spi/spi.h>
16#include <sound/soc.h> 16#include <sound/soc.h>
17#include <linux/lzo.h>
18#include <linux/bitmap.h>
17 19
18static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, 20static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
19 unsigned int reg) 21 unsigned int reg)
@@ -758,6 +760,409 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
758} 760}
759EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); 761EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
760 762
763struct snd_soc_lzo_ctx {
764 void *wmem;
765 void *dst;
766 const void *src;
767 size_t src_len;
768 size_t dst_len;
769 size_t decompressed_size;
770 unsigned long *sync_bmp;
771 int sync_bmp_nbits;
772};
773
774#define LZO_BLOCK_NUM 8
775static int snd_soc_lzo_block_count(void)
776{
777 return LZO_BLOCK_NUM;
778}
779
780static int snd_soc_lzo_prepare(struct snd_soc_lzo_ctx *lzo_ctx)
781{
782 lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
783 if (!lzo_ctx->wmem)
784 return -ENOMEM;
785 return 0;
786}
787
788static int snd_soc_lzo_compress(struct snd_soc_lzo_ctx *lzo_ctx)
789{
790 size_t compress_size;
791 int ret;
792
793 ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
794 lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
795 if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
796 return -EINVAL;
797 lzo_ctx->dst_len = compress_size;
798 return 0;
799}
800
801static int snd_soc_lzo_decompress(struct snd_soc_lzo_ctx *lzo_ctx)
802{
803 size_t dst_len;
804 int ret;
805
806 dst_len = lzo_ctx->dst_len;
807 ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
808 lzo_ctx->dst, &dst_len);
809 if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
810 return -EINVAL;
811 return 0;
812}
813
814static int snd_soc_lzo_compress_cache_block(struct snd_soc_codec *codec,
815 struct snd_soc_lzo_ctx *lzo_ctx)
816{
817 int ret;
818
819 lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
820 lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
821 if (!lzo_ctx->dst) {
822 lzo_ctx->dst_len = 0;
823 return -ENOMEM;
824 }
825
826 ret = snd_soc_lzo_compress(lzo_ctx);
827 if (ret < 0)
828 return ret;
829 return 0;
830}
831
832static int snd_soc_lzo_decompress_cache_block(struct snd_soc_codec *codec,
833 struct snd_soc_lzo_ctx *lzo_ctx)
834{
835 int ret;
836
837 lzo_ctx->dst_len = lzo_ctx->decompressed_size;
838 lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
839 if (!lzo_ctx->dst) {
840 lzo_ctx->dst_len = 0;
841 return -ENOMEM;
842 }
843
844 ret = snd_soc_lzo_decompress(lzo_ctx);
845 if (ret < 0)
846 return ret;
847 return 0;
848}
849
850static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec,
851 unsigned int reg)
852{
853 struct snd_soc_codec_driver *codec_drv;
854 size_t reg_size;
855
856 codec_drv = codec->driver;
857 reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
858 return (reg * codec_drv->reg_word_size) /
859 DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
860}
861
862static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
863 unsigned int reg)
864{
865 struct snd_soc_codec_driver *codec_drv;
866 size_t reg_size;
867
868 codec_drv = codec->driver;
869 reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
870 return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) /
871 codec_drv->reg_word_size);
872}
873
874static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
875{
876 struct snd_soc_codec_driver *codec_drv;
877 size_t reg_size;
878
879 codec_drv = codec->driver;
880 reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
881 return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
882}
883
884static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
885{
886 struct snd_soc_lzo_ctx **lzo_blocks;
887 unsigned int val;
888 int i;
889
890 lzo_blocks = codec->reg_cache;
891 for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
892 snd_soc_cache_read(codec, i, &val);
893 snd_soc_write(codec, i, val);
894 dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
895 i, val);
896 }
897
898 return 0;
899}
900
901static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec,
902 unsigned int reg, unsigned int value)
903{
904 struct snd_soc_lzo_ctx *lzo_block, **lzo_blocks;
905 int ret, blkindex, blkpos;
906 size_t blksize, tmp_dst_len;
907 void *tmp_dst;
908
909 /* index of the compressed lzo block */
910 blkindex = snd_soc_lzo_get_blkindex(codec, reg);
911 /* register index within the decompressed block */
912 blkpos = snd_soc_lzo_get_blkpos(codec, reg);
913 /* size of the compressed block */
914 blksize = snd_soc_lzo_get_blksize(codec);
915 lzo_blocks = codec->reg_cache;
916 lzo_block = lzo_blocks[blkindex];
917
918 /* save the pointer and length of the compressed block */
919 tmp_dst = lzo_block->dst;
920 tmp_dst_len = lzo_block->dst_len;
921
922 /* prepare the source to be the compressed block */
923 lzo_block->src = lzo_block->dst;
924 lzo_block->src_len = lzo_block->dst_len;
925
926 /* decompress the block */
927 ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
928 if (ret < 0) {
929 kfree(lzo_block->dst);
930 goto out;
931 }
932
933 /* write the new value to the cache */
934 switch (codec->driver->reg_word_size) {
935 case 1: {
936 u8 *cache;
937 cache = lzo_block->dst;
938 if (cache[blkpos] == value) {
939 kfree(lzo_block->dst);
940 goto out;
941 }
942 cache[blkpos] = value;
943 }
944 break;
945 case 2: {
946 u16 *cache;
947 cache = lzo_block->dst;
948 if (cache[blkpos] == value) {
949 kfree(lzo_block->dst);
950 goto out;
951 }
952 cache[blkpos] = value;
953 }
954 break;
955 default:
956 BUG();
957 }
958
959 /* prepare the source to be the decompressed block */
960 lzo_block->src = lzo_block->dst;
961 lzo_block->src_len = lzo_block->dst_len;
962
963 /* compress the block */
964 ret = snd_soc_lzo_compress_cache_block(codec, lzo_block);
965 if (ret < 0) {
966 kfree(lzo_block->dst);
967 kfree(lzo_block->src);
968 goto out;
969 }
970
971 /* set the bit so we know we have to sync this register */
972 set_bit(reg, lzo_block->sync_bmp);
973 kfree(tmp_dst);
974 kfree(lzo_block->src);
975 return 0;
976out:
977 lzo_block->dst = tmp_dst;
978 lzo_block->dst_len = tmp_dst_len;
979 return ret;
980}
981
982static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec,
983 unsigned int reg, unsigned int *value)
984{
985 struct snd_soc_lzo_ctx *lzo_block, **lzo_blocks;
986 int ret, blkindex, blkpos;
987 size_t blksize, tmp_dst_len;
988 void *tmp_dst;
989
990 *value = 0;
991 /* index of the compressed lzo block */
992 blkindex = snd_soc_lzo_get_blkindex(codec, reg);
993 /* register index within the decompressed block */
994 blkpos = snd_soc_lzo_get_blkpos(codec, reg);
995 /* size of the compressed block */
996 blksize = snd_soc_lzo_get_blksize(codec);
997 lzo_blocks = codec->reg_cache;
998 lzo_block = lzo_blocks[blkindex];
999
1000 /* save the pointer and length of the compressed block */
1001 tmp_dst = lzo_block->dst;
1002 tmp_dst_len = lzo_block->dst_len;
1003
1004 /* prepare the source to be the compressed block */
1005 lzo_block->src = lzo_block->dst;
1006 lzo_block->src_len = lzo_block->dst_len;
1007
1008 /* decompress the block */
1009 ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
1010 if (ret >= 0) {
1011 /* fetch the value from the cache */
1012 switch (codec->driver->reg_word_size) {
1013 case 1: {
1014 u8 *cache;
1015 cache = lzo_block->dst;
1016 *value = cache[blkpos];
1017 }
1018 break;
1019 case 2: {
1020 u16 *cache;
1021 cache = lzo_block->dst;
1022 *value = cache[blkpos];
1023 }
1024 break;
1025 default:
1026 BUG();
1027 }
1028 }
1029
1030 kfree(lzo_block->dst);
1031 /* restore the pointer and length of the compressed block */
1032 lzo_block->dst = tmp_dst;
1033 lzo_block->dst_len = tmp_dst_len;
1034 return 0;
1035}
1036
1037static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec)
1038{
1039 struct snd_soc_lzo_ctx **lzo_blocks;
1040 int i, blkcount;
1041
1042 lzo_blocks = codec->reg_cache;
1043 if (!lzo_blocks)
1044 return 0;
1045
1046 blkcount = snd_soc_lzo_block_count();
1047 /*
1048 * the pointer to the bitmap used for syncing the cache
1049 * is shared amongst all lzo_blocks. Ensure it is freed
1050 * only once.
1051 */
1052 if (lzo_blocks[0])
1053 kfree(lzo_blocks[0]->sync_bmp);
1054 for (i = 0; i < blkcount; ++i) {
1055 if (lzo_blocks[i]) {
1056 kfree(lzo_blocks[i]->wmem);
1057 kfree(lzo_blocks[i]->dst);
1058 }
1059 /* each lzo_block is a pointer returned by kmalloc or NULL */
1060 kfree(lzo_blocks[i]);
1061 }
1062 kfree(lzo_blocks);
1063 codec->reg_cache = NULL;
1064 return 0;
1065}
1066
1067static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
1068{
1069 struct snd_soc_lzo_ctx **lzo_blocks;
1070 size_t reg_size, bmp_size;
1071 struct snd_soc_codec_driver *codec_drv;
1072 int ret, tofree, i, blksize, blkcount;
1073 const char *p, *end;
1074 unsigned long *sync_bmp;
1075
1076 ret = 0;
1077 codec_drv = codec->driver;
1078 reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
1079
1080 /*
1081 * If we have not been given a default register cache
1082 * then allocate a dummy zero-ed out region, compress it
1083 * and remember to free it afterwards.
1084 */
1085 tofree = 0;
1086 if (!codec_drv->reg_cache_default)
1087 tofree = 1;
1088
1089 if (!codec_drv->reg_cache_default) {
1090 codec_drv->reg_cache_default = kzalloc(reg_size,
1091 GFP_KERNEL);
1092 if (!codec_drv->reg_cache_default)
1093 return -ENOMEM;
1094 }
1095
1096 blkcount = snd_soc_lzo_block_count();
1097 codec->reg_cache = kzalloc(blkcount * sizeof *lzo_blocks,
1098 GFP_KERNEL);
1099 if (!codec->reg_cache) {
1100 ret = -ENOMEM;
1101 goto err_tofree;
1102 }
1103 lzo_blocks = codec->reg_cache;
1104
1105 /*
1106 * allocate a bitmap to be used when syncing the cache with
1107 * the hardware. Each time a register is modified, the corresponding
1108 * bit is set in the bitmap, so we know that we have to sync
1109 * that register.
1110 */
1111 bmp_size = codec_drv->reg_cache_size;
1112 sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof (long),
1113 GFP_KERNEL);
1114 if (!sync_bmp) {
1115 ret = -ENOMEM;
1116 goto err;
1117 }
1118 bitmap_zero(sync_bmp, reg_size);
1119
1120 /* allocate the lzo blocks and initialize them */
1121 for (i = 0; i < blkcount; ++i) {
1122 lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
1123 GFP_KERNEL);
1124 if (!lzo_blocks[i]) {
1125 kfree(sync_bmp);
1126 ret = -ENOMEM;
1127 goto err;
1128 }
1129 lzo_blocks[i]->sync_bmp = sync_bmp;
1130 lzo_blocks[i]->sync_bmp_nbits = reg_size;
1131 /* alloc the working space for the compressed block */
1132 ret = snd_soc_lzo_prepare(lzo_blocks[i]);
1133 if (ret < 0)
1134 goto err;
1135 }
1136
1137 blksize = snd_soc_lzo_get_blksize(codec);
1138 p = codec_drv->reg_cache_default;
1139 end = codec_drv->reg_cache_default + reg_size;
1140 /* compress the register map and fill the lzo blocks */
1141 for (i = 0; i < blkcount; ++i, p += blksize) {
1142 lzo_blocks[i]->src = p;
1143 if (p + blksize > end)
1144 lzo_blocks[i]->src_len = end - p;
1145 else
1146 lzo_blocks[i]->src_len = blksize;
1147 ret = snd_soc_lzo_compress_cache_block(codec,
1148 lzo_blocks[i]);
1149 if (ret < 0)
1150 goto err;
1151 lzo_blocks[i]->decompressed_size =
1152 lzo_blocks[i]->src_len;
1153 }
1154
1155 if (tofree)
1156 kfree(codec_drv->reg_cache_default);
1157 return 0;
1158err:
1159 snd_soc_cache_exit(codec);
1160err_tofree:
1161 if (tofree)
1162 kfree(codec_drv->reg_cache_default);
1163 return ret;
1164}
1165
761static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) 1166static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
762{ 1167{
763 int i; 1168 int i;
@@ -883,6 +1288,14 @@ static const struct snd_soc_cache_ops cache_types[] = {
883 .read = snd_soc_flat_cache_read, 1288 .read = snd_soc_flat_cache_read,
884 .write = snd_soc_flat_cache_write, 1289 .write = snd_soc_flat_cache_write,
885 .sync = snd_soc_flat_cache_sync 1290 .sync = snd_soc_flat_cache_sync
1291 },
1292 {
1293 .id = SND_SOC_LZO_COMPRESSION,
1294 .init = snd_soc_lzo_cache_init,
1295 .exit = snd_soc_lzo_cache_exit,
1296 .read = snd_soc_lzo_cache_read,
1297 .write = snd_soc_lzo_cache_write,
1298 .sync = snd_soc_lzo_cache_sync
886 } 1299 }
887}; 1300};
888 1301