diff options
-rw-r--r-- | sound/soc/soc-cache.c | 232 |
1 files changed, 177 insertions, 55 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 687beec56476..78c9869615df 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c | |||
@@ -595,31 +595,85 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, | |||
595 | } | 595 | } |
596 | 596 | ||
597 | struct snd_soc_rbtree_node { | 597 | struct snd_soc_rbtree_node { |
598 | struct rb_node node; | 598 | struct rb_node node; /* the actual rbtree node holding this block */ |
599 | unsigned int reg; | 599 | unsigned int base_reg; /* base register handled by this block */ |
600 | unsigned int value; | 600 | unsigned int word_size; /* number of bytes needed to represent the register index */ |
601 | unsigned int defval; | 601 | void *block; /* block of adjacent registers */ |
602 | unsigned int blklen; /* number of registers available in the block */ | ||
602 | } __attribute__ ((packed)); | 603 | } __attribute__ ((packed)); |
603 | 604 | ||
604 | struct snd_soc_rbtree_ctx { | 605 | struct snd_soc_rbtree_ctx { |
605 | struct rb_root root; | 606 | struct rb_root root; |
606 | }; | 607 | }; |
607 | 608 | ||
609 | static inline void snd_soc_rbtree_get_base_top_reg( | ||
610 | struct snd_soc_rbtree_node *rbnode, | ||
611 | unsigned int *base, unsigned int *top) | ||
612 | { | ||
613 | *base = rbnode->base_reg; | ||
614 | *top = rbnode->base_reg + rbnode->blklen - 1; | ||
615 | } | ||
616 | |||
617 | static unsigned int snd_soc_rbtree_get_register( | ||
618 | struct snd_soc_rbtree_node *rbnode, unsigned int idx) | ||
619 | { | ||
620 | unsigned int val; | ||
621 | |||
622 | switch (rbnode->word_size) { | ||
623 | case 1: { | ||
624 | u8 *p = rbnode->block; | ||
625 | val = p[idx]; | ||
626 | return val; | ||
627 | } | ||
628 | case 2: { | ||
629 | u16 *p = rbnode->block; | ||
630 | val = p[idx]; | ||
631 | return val; | ||
632 | } | ||
633 | default: | ||
634 | BUG(); | ||
635 | break; | ||
636 | } | ||
637 | return -1; | ||
638 | } | ||
639 | |||
640 | static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode, | ||
641 | unsigned int idx, unsigned int val) | ||
642 | { | ||
643 | switch (rbnode->word_size) { | ||
644 | case 1: { | ||
645 | u8 *p = rbnode->block; | ||
646 | p[idx] = val; | ||
647 | break; | ||
648 | } | ||
649 | case 2: { | ||
650 | u16 *p = rbnode->block; | ||
651 | p[idx] = val; | ||
652 | break; | ||
653 | } | ||
654 | default: | ||
655 | BUG(); | ||
656 | break; | ||
657 | } | ||
658 | } | ||
659 | |||
608 | static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup( | 660 | static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup( |
609 | struct rb_root *root, unsigned int reg) | 661 | struct rb_root *root, unsigned int reg) |
610 | { | 662 | { |
611 | struct rb_node *node; | 663 | struct rb_node *node; |
612 | struct snd_soc_rbtree_node *rbnode; | 664 | struct snd_soc_rbtree_node *rbnode; |
665 | unsigned int base_reg, top_reg; | ||
613 | 666 | ||
614 | node = root->rb_node; | 667 | node = root->rb_node; |
615 | while (node) { | 668 | while (node) { |
616 | rbnode = container_of(node, struct snd_soc_rbtree_node, node); | 669 | rbnode = container_of(node, struct snd_soc_rbtree_node, node); |
617 | if (rbnode->reg < reg) | 670 | snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); |
618 | node = node->rb_left; | 671 | if (reg >= base_reg && reg <= top_reg) |
619 | else if (rbnode->reg > reg) | ||
620 | node = node->rb_right; | ||
621 | else | ||
622 | return rbnode; | 672 | return rbnode; |
673 | else if (reg > top_reg) | ||
674 | node = node->rb_right; | ||
675 | else if (reg < base_reg) | ||
676 | node = node->rb_left; | ||
623 | } | 677 | } |
624 | 678 | ||
625 | return NULL; | 679 | return NULL; |
@@ -630,19 +684,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root, | |||
630 | { | 684 | { |
631 | struct rb_node **new, *parent; | 685 | struct rb_node **new, *parent; |
632 | struct snd_soc_rbtree_node *rbnode_tmp; | 686 | struct snd_soc_rbtree_node *rbnode_tmp; |
687 | unsigned int base_reg_tmp, top_reg_tmp; | ||
688 | unsigned int base_reg; | ||
633 | 689 | ||
634 | parent = NULL; | 690 | parent = NULL; |
635 | new = &root->rb_node; | 691 | new = &root->rb_node; |
636 | while (*new) { | 692 | while (*new) { |
637 | rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node, | 693 | rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node, |
638 | node); | 694 | node); |
695 | /* base and top registers of the current rbnode */ | ||
696 | snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, | ||
697 | &top_reg_tmp); | ||
698 | /* base register of the rbnode to be added */ | ||
699 | base_reg = rbnode->base_reg; | ||
639 | parent = *new; | 700 | parent = *new; |
640 | if (rbnode_tmp->reg < rbnode->reg) | 701 | /* if this register has already been inserted, just return */ |
641 | new = &((*new)->rb_left); | 702 | if (base_reg >= base_reg_tmp && |
642 | else if (rbnode_tmp->reg > rbnode->reg) | 703 | base_reg <= top_reg_tmp) |
643 | new = &((*new)->rb_right); | ||
644 | else | ||
645 | return 0; | 704 | return 0; |
705 | else if (base_reg > top_reg_tmp) | ||
706 | new = &((*new)->rb_right); | ||
707 | else if (base_reg < base_reg_tmp) | ||
708 | new = &((*new)->rb_left); | ||
646 | } | 709 | } |
647 | 710 | ||
648 | /* insert the node into the rbtree */ | 711 | /* insert the node into the rbtree */ |
@@ -657,57 +720,120 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) | |||
657 | struct snd_soc_rbtree_ctx *rbtree_ctx; | 720 | struct snd_soc_rbtree_ctx *rbtree_ctx; |
658 | struct rb_node *node; | 721 | struct rb_node *node; |
659 | struct snd_soc_rbtree_node *rbnode; | 722 | struct snd_soc_rbtree_node *rbnode; |
723 | unsigned int regtmp; | ||
660 | unsigned int val; | 724 | unsigned int val; |
661 | int ret; | 725 | int ret; |
726 | int i; | ||
662 | 727 | ||
663 | rbtree_ctx = codec->reg_cache; | 728 | rbtree_ctx = codec->reg_cache; |
664 | for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { | 729 | for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { |
665 | rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); | 730 | rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); |
666 | if (rbnode->value == rbnode->defval) | 731 | for (i = 0; i < rbnode->blklen; ++i) { |
667 | continue; | 732 | regtmp = rbnode->base_reg + i; |
668 | WARN_ON(codec->writable_register && | 733 | WARN_ON(codec->writable_register && |
669 | codec->writable_register(codec, rbnode->reg)); | 734 | codec->writable_register(codec, regtmp)); |
670 | ret = snd_soc_cache_read(codec, rbnode->reg, &val); | 735 | val = snd_soc_rbtree_get_register(rbnode, i); |
671 | if (ret) | 736 | codec->cache_bypass = 1; |
672 | return ret; | 737 | ret = snd_soc_write(codec, regtmp, val); |
673 | codec->cache_bypass = 1; | 738 | codec->cache_bypass = 0; |
674 | ret = snd_soc_write(codec, rbnode->reg, val); | 739 | if (ret) |
675 | codec->cache_bypass = 0; | 740 | return ret; |
676 | if (ret) | 741 | dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", |
677 | return ret; | 742 | regtmp, val); |
678 | dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", | 743 | } |
679 | rbnode->reg, val); | ||
680 | } | 744 | } |
681 | 745 | ||
682 | return 0; | 746 | return 0; |
683 | } | 747 | } |
684 | 748 | ||
749 | static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode, | ||
750 | unsigned int pos, unsigned int reg, | ||
751 | unsigned int value) | ||
752 | { | ||
753 | u8 *blk; | ||
754 | |||
755 | blk = krealloc(rbnode->block, | ||
756 | (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL); | ||
757 | if (!blk) | ||
758 | return -ENOMEM; | ||
759 | |||
760 | /* insert the register value in the correct place in the rbnode block */ | ||
761 | memmove(blk + (pos + 1) * rbnode->word_size, | ||
762 | blk + pos * rbnode->word_size, | ||
763 | (rbnode->blklen - pos) * rbnode->word_size); | ||
764 | |||
765 | /* update the rbnode block, its size and the base register */ | ||
766 | rbnode->block = blk; | ||
767 | rbnode->blklen++; | ||
768 | if (!pos) | ||
769 | rbnode->base_reg = reg; | ||
770 | |||
771 | snd_soc_rbtree_set_register(rbnode, pos, value); | ||
772 | return 0; | ||
773 | } | ||
774 | |||
685 | static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, | 775 | static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, |
686 | unsigned int reg, unsigned int value) | 776 | unsigned int reg, unsigned int value) |
687 | { | 777 | { |
688 | struct snd_soc_rbtree_ctx *rbtree_ctx; | 778 | struct snd_soc_rbtree_ctx *rbtree_ctx; |
689 | struct snd_soc_rbtree_node *rbnode; | 779 | struct snd_soc_rbtree_node *rbnode, *rbnode_tmp; |
780 | struct rb_node *node; | ||
781 | unsigned int val; | ||
782 | unsigned int reg_tmp; | ||
783 | unsigned int pos; | ||
784 | int i; | ||
785 | int ret; | ||
690 | 786 | ||
691 | rbtree_ctx = codec->reg_cache; | 787 | rbtree_ctx = codec->reg_cache; |
692 | rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); | 788 | rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); |
693 | if (rbnode) { | 789 | if (rbnode) { |
694 | if (rbnode->value == value) | 790 | reg_tmp = reg - rbnode->base_reg; |
791 | val = snd_soc_rbtree_get_register(rbnode, reg_tmp); | ||
792 | if (val == value) | ||
695 | return 0; | 793 | return 0; |
696 | rbnode->value = value; | 794 | snd_soc_rbtree_set_register(rbnode, reg_tmp, value); |
697 | } else { | 795 | } else { |
698 | /* bail out early, no need to create the rbnode yet */ | 796 | /* bail out early, no need to create the rbnode yet */ |
699 | if (!value) | 797 | if (!value) |
700 | return 0; | 798 | return 0; |
701 | /* | 799 | /* look for an adjacent register to the one we are about to add */ |
702 | * for uninitialized registers whose value is changed | 800 | for (node = rb_first(&rbtree_ctx->root); node; |
703 | * from the default zero, create an rbnode and insert | 801 | node = rb_next(node)) { |
704 | * it into the tree. | 802 | rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node); |
803 | for (i = 0; i < rbnode_tmp->blklen; ++i) { | ||
804 | reg_tmp = rbnode_tmp->base_reg + i; | ||
805 | if (abs(reg_tmp - reg) != 1) | ||
806 | continue; | ||
807 | /* decide where in the block to place our register */ | ||
808 | if (reg_tmp + 1 == reg) | ||
809 | pos = i + 1; | ||
810 | else | ||
811 | pos = i; | ||
812 | ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos, | ||
813 | reg, value); | ||
814 | if (ret) | ||
815 | return ret; | ||
816 | return 0; | ||
817 | } | ||
818 | } | ||
819 | /* we did not manage to find a place to insert it in an existing | ||
820 | * block so create a new rbnode with a single register in its block. | ||
821 | * This block will get populated further if any other adjacent | ||
822 | * registers get modified in the future. | ||
705 | */ | 823 | */ |
706 | rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); | 824 | rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); |
707 | if (!rbnode) | 825 | if (!rbnode) |
708 | return -ENOMEM; | 826 | return -ENOMEM; |
709 | rbnode->reg = reg; | 827 | rbnode->blklen = 1; |
710 | rbnode->value = value; | 828 | rbnode->base_reg = reg; |
829 | rbnode->word_size = codec->driver->reg_word_size; | ||
830 | rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size, | ||
831 | GFP_KERNEL); | ||
832 | if (!rbnode->block) { | ||
833 | kfree(rbnode); | ||
834 | return -ENOMEM; | ||
835 | } | ||
836 | snd_soc_rbtree_set_register(rbnode, 0, value); | ||
711 | snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); | 837 | snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); |
712 | } | 838 | } |
713 | 839 | ||
@@ -719,11 +845,13 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec, | |||
719 | { | 845 | { |
720 | struct snd_soc_rbtree_ctx *rbtree_ctx; | 846 | struct snd_soc_rbtree_ctx *rbtree_ctx; |
721 | struct snd_soc_rbtree_node *rbnode; | 847 | struct snd_soc_rbtree_node *rbnode; |
848 | unsigned int reg_tmp; | ||
722 | 849 | ||
723 | rbtree_ctx = codec->reg_cache; | 850 | rbtree_ctx = codec->reg_cache; |
724 | rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); | 851 | rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); |
725 | if (rbnode) { | 852 | if (rbnode) { |
726 | *value = rbnode->value; | 853 | reg_tmp = reg - rbnode->base_reg; |
854 | *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); | ||
727 | } else { | 855 | } else { |
728 | /* uninitialized registers default to 0 */ | 856 | /* uninitialized registers default to 0 */ |
729 | *value = 0; | 857 | *value = 0; |
@@ -749,6 +877,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) | |||
749 | rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node); | 877 | rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node); |
750 | next = rb_next(&rbtree_node->node); | 878 | next = rb_next(&rbtree_node->node); |
751 | rb_erase(&rbtree_node->node, &rbtree_ctx->root); | 879 | rb_erase(&rbtree_node->node, &rbtree_ctx->root); |
880 | kfree(rbtree_node->block); | ||
752 | kfree(rbtree_node); | 881 | kfree(rbtree_node); |
753 | } | 882 | } |
754 | 883 | ||
@@ -761,10 +890,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) | |||
761 | 890 | ||
762 | static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) | 891 | static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) |
763 | { | 892 | { |
764 | struct snd_soc_rbtree_node *rbtree_node; | ||
765 | struct snd_soc_rbtree_ctx *rbtree_ctx; | 893 | struct snd_soc_rbtree_ctx *rbtree_ctx; |
766 | unsigned int val; | ||
767 | unsigned int word_size; | 894 | unsigned int word_size; |
895 | unsigned int val; | ||
768 | int i; | 896 | int i; |
769 | int ret; | 897 | int ret; |
770 | 898 | ||
@@ -778,28 +906,22 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) | |||
778 | if (!codec->reg_def_copy) | 906 | if (!codec->reg_def_copy) |
779 | return 0; | 907 | return 0; |
780 | 908 | ||
781 | /* | ||
782 | * populate the rbtree with the initialized registers. All other | ||
783 | * registers will be inserted when they are first modified. | ||
784 | */ | ||
785 | word_size = codec->driver->reg_word_size; | 909 | word_size = codec->driver->reg_word_size; |
786 | for (i = 0; i < codec->driver->reg_cache_size; ++i) { | 910 | for (i = 0; i < codec->driver->reg_cache_size; ++i) { |
787 | val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); | 911 | val = snd_soc_get_cache_val(codec->reg_def_copy, i, |
912 | word_size); | ||
788 | if (!val) | 913 | if (!val) |
789 | continue; | 914 | continue; |
790 | rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); | 915 | ret = snd_soc_rbtree_cache_write(codec, i, val); |
791 | if (!rbtree_node) { | 916 | if (ret) |
792 | ret = -ENOMEM; | 917 | goto err; |
793 | snd_soc_cache_exit(codec); | ||
794 | break; | ||
795 | } | ||
796 | rbtree_node->reg = i; | ||
797 | rbtree_node->value = val; | ||
798 | rbtree_node->defval = val; | ||
799 | snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); | ||
800 | } | 918 | } |
801 | 919 | ||
802 | return 0; | 920 | return 0; |
921 | |||
922 | err: | ||
923 | snd_soc_cache_exit(codec); | ||
924 | return ret; | ||
803 | } | 925 | } |
804 | 926 | ||
805 | #ifdef CONFIG_SND_SOC_CACHE_LZO | 927 | #ifdef CONFIG_SND_SOC_CACHE_LZO |