aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/soc.h3
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/soc-cache.c413
3 files changed, 417 insertions, 1 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 275e41133468..10f5932d3d07 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -256,7 +256,8 @@ enum snd_soc_control_type {
256}; 256};
257 257
258enum snd_soc_compress_type { 258enum snd_soc_compress_type {
259 SND_SOC_NO_COMPRESSION 259 SND_SOC_NO_COMPRESSION,
260 SND_SOC_LZO_COMPRESSION
260}; 261};
261 262
262int snd_soc_register_platform(struct device *dev, 263int snd_soc_register_platform(struct device *dev,
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3e598e756e54..4562c898a7ef 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -4,6 +4,8 @@
4 4
5menuconfig SND_SOC 5menuconfig SND_SOC
6 tristate "ALSA for SoC audio support" 6 tristate "ALSA for SoC audio support"
7 select LZO_COMPRESS
8 select LZO_DECOMPRESS
7 select SND_PCM 9 select SND_PCM
8 select AC97_BUS if SND_SOC_AC97_BUS 10 select AC97_BUS if SND_SOC_AC97_BUS
9 select SND_JACK if INPUT=y || INPUT=SND 11 select SND_JACK if INPUT=y || INPUT=SND
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