diff options
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r-- | sound/soc/soc-cache.c | 413 |
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 | ||
18 | static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, | 20 | static 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 | } |
759 | EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); | 761 | EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); |
760 | 762 | ||
763 | struct 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 | ||
775 | static int snd_soc_lzo_block_count(void) | ||
776 | { | ||
777 | return LZO_BLOCK_NUM; | ||
778 | } | ||
779 | |||
780 | static 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 | |||
788 | static 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 | |||
801 | static 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 | |||
814 | static 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 | |||
832 | static 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 | |||
850 | static 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 | |||
862 | static 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 | |||
874 | static 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 | |||
884 | static 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 | |||
901 | static 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; | ||
976 | out: | ||
977 | lzo_block->dst = tmp_dst; | ||
978 | lzo_block->dst_len = tmp_dst_len; | ||
979 | return ret; | ||
980 | } | ||
981 | |||
982 | static 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 | |||
1037 | static 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 | |||
1067 | static 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; | ||
1158 | err: | ||
1159 | snd_soc_cache_exit(codec); | ||
1160 | err_tofree: | ||
1161 | if (tofree) | ||
1162 | kfree(codec_drv->reg_cache_default); | ||
1163 | return ret; | ||
1164 | } | ||
1165 | |||
761 | static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) | 1166 | static 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 | ||