diff mbox series

[2/2] ALSA: core: split control primitives out of snd_card

Message ID 20230824210339.1126993-3-cujomalainey@chromium.org (mailing list archive)
State New, archived
Headers show
Series Refactor snd primitives refcounters | expand

Commit Message

Curtis Malainey Aug. 24, 2023, 9:02 p.m. UTC
From: Curtis Malainey <cujomalainey@chromium.org>

Having two kobj in the same struct is broken at its core. This splits
card_dev from ctl_dev so they can properly refcount and release on their
own schedules without the workaround of having them being just a
pointer.

Signed-off-by: Curtis Malainey <cujomalainey@chromium.org>
---
 include/sound/control.h                  |   1 +
 include/sound/core.h                     |  33 +--
 sound/core/control.c                     | 330 +++++++++++++----------
 sound/core/control_compat.c              |   8 +-
 sound/core/control_led.c                 |  18 +-
 sound/core/init.c                        |  14 +-
 sound/pci/hda/hda_codec.c                |   3 +-
 sound/soc/intel/atom/sst-atom-controls.c |   8 +-
 sound/soc/soc-card.c                     |   2 +-
 sound/usb/media.c                        |   2 +-
 10 files changed, 232 insertions(+), 187 deletions(-)

Comments

Mark Brown Aug. 24, 2023, 9:15 p.m. UTC | #1
On Thu, Aug 24, 2023 at 02:02:53PM -0700, cujomalainey@chromium.org wrote:
> From: Curtis Malainey <cujomalainey@chromium.org>
> 
> Having two kobj in the same struct is broken at its core. This splits
> card_dev from ctl_dev so they can properly refcount and release on their
> own schedules without the workaround of having them being just a
> pointer.

Acked-by: Mark Brown <broonie@kernel.org>
Takashi Iwai Aug. 25, 2023, 10:57 a.m. UTC | #2
On Thu, 24 Aug 2023 23:02:53 +0200,
cujomalainey@chromium.org wrote:
> 
> +struct snd_control
> +{

The open brace should be at the line above.

> +	struct device dev;		/* control device */
> +	struct rw_semaphore controls_rwsem;	/* controls lock (list and values) */
> +	rwlock_t files_rwlock;	/* ctl_files list lock */
> +	int controls_count;		/* count of all controls */
> +	size_t user_ctl_alloc_size;	// current memory allocation by user controls.

Better to have the same comment style if we move the whole stuff.

> +int snd_control_new(struct snd_card *card)
....
> +{
> +	struct snd_control *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
> +	int err;
> +
> +	if (snd_BUG_ON(!card))
> +		return -EINVAL;

This may leak the memory allocated in the above.

> +	/* the control interface cannot be accessed from the user space until */
> +	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
> +	err = snd_ctl_create(card);
> +	if (err < 0) {
> +		dev_err(card->dev, "unable to register control minors\n");
> +		kfree(ctl);
> +		return err;

This needs a more care.  snd_ctl_create() calls put_device() when
snd_device_new() fails, and this already does kfree().  OTOH, the
error path before that point doesn't release the object.

> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL(snd_control_new);

This is never called from the driver but only from snd_card_new() &
co, so no need to export.


thanks,

Takashi
kernel test robot Sept. 3, 2023, 10:38 p.m. UTC | #3
Hi,

kernel test robot noticed the following build errors:

[auto build test ERROR on tiwai-sound/for-next]
[also build test ERROR on tiwai-sound/for-linus linus/master next-20230831]
[cannot apply to broonie-sound/for-next v6.5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/cujomalainey-chromium-org/ALSA-core-add-snd_device_init/20230825-050745
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
patch link:    https://lore.kernel.org/r/20230824210339.1126993-3-cujomalainey%40chromium.org
patch subject: [PATCH 2/2] ALSA: core: split control primitives out of snd_card
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20230904/202309040658.LwLeCNwY-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230904/202309040658.LwLeCNwY-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202309040658.LwLeCNwY-lkp@intel.com/

All errors (new ones prefixed by >>):

   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_get_volume1_vol':
>> sound/core/oss/mixer_oss.c:542:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     542 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:545:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     545 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:563:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     563 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_get_volume1_sw':
   sound/core/oss/mixer_oss.c:581:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     581 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:584:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     584 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:603:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     603 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_put_volume1_vol':
   sound/core/oss/mixer_oss.c:647:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     647 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:650:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     650 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:671:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     671 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_put_volume1_sw':
   sound/core/oss/mixer_oss.c:690:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     690 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:693:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     693 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:718:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     718 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_get_recsrc2':
   sound/core/oss/mixer_oss.c:835:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     835 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:863:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     863 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_put_recsrc2':
   sound/core/oss/mixer_oss.c:888:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     888 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:919:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     919 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_build_test':
   sound/core/oss/mixer_oss.c:939:24: error: 'struct snd_card' has no member named 'controls_rwsem'
     939 |         down_read(&card->controls_rwsem);
         |                        ^~
   sound/core/oss/mixer_oss.c:942:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     942 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:947:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     947 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:952:30: error: 'struct snd_card' has no member named 'controls_rwsem'
     952 |                 up_read(&card->controls_rwsem);
         |                              ^~
   sound/core/oss/mixer_oss.c:957:22: error: 'struct snd_card' has no member named 'controls_rwsem'
     957 |         up_read(&card->controls_rwsem);
         |                      ^~
   sound/core/oss/mixer_oss.c: In function 'snd_mixer_oss_build_input':
   sound/core/oss/mixer_oss.c:1071:31: error: 'struct snd_card' has no member named 'controls_rwsem'
    1071 |         down_read(&mixer->card->controls_rwsem);
         |                               ^~
   sound/core/oss/mixer_oss.c:1080:45: error: 'struct snd_card' has no member named 'controls_rwsem'
    1080 |                         up_read(&mixer->card->controls_rwsem);
         |                                             ^~
   sound/core/oss/mixer_oss.c:1085:45: error: 'struct snd_card' has no member named 'controls_rwsem'
    1085 |                         up_read(&mixer->card->controls_rwsem);
         |                                             ^~
   sound/core/oss/mixer_oss.c:1101:61: error: 'struct snd_card' has no member named 'controls_rwsem'
    1101 |                                         up_read(&mixer->card->controls_rwsem);
         |                                                             ^~
   sound/core/oss/mixer_oss.c:1113:29: error: 'struct snd_card' has no member named 'controls_rwsem'
    1113 |         up_read(&mixer->card->controls_rwsem);
         |                             ^~
--
   In file included from include/linux/kernel.h:21,
                    from arch/x86/include/asm/percpu.h:27,
                    from arch/x86/include/asm/current.h:10,
                    from include/linux/sched.h:12,
                    from include/linux/ratelimit.h:6,
                    from include/linux/dev_printk.h:16,
                    from include/linux/device.h:15,
                    from include/linux/pm_runtime.h:11,
                    from sound/soc/intel/catpt/pcm.c:8:
   sound/soc/intel/catpt/pcm.c: In function 'catpt_dai_apply_usettings':
>> sound/soc/intel/catpt/pcm.c:357:60: error: 'struct snd_card' has no member named 'controls'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |                                                            ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/intel/catpt/pcm.c:357:9: note: in expansion of macro 'list_for_each_entry'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   In file included from include/linux/bits.h:21,
                    from include/linux/ratelimit_types.h:5,
                    from include/linux/ratelimit.h:5:
>> sound/soc/intel/catpt/pcm.c:357:60: error: 'struct snd_card' has no member named 'controls'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |                                                            ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/intel/catpt/pcm.c:357:9: note: in expansion of macro 'list_for_each_entry'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
>> sound/soc/intel/catpt/pcm.c:357:60: error: 'struct snd_card' has no member named 'controls'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |                                                            ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:21:23: note: in expansion of macro '__same_type'
      21 |                       __same_type(*(ptr), void),                        \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/intel/catpt/pcm.c:357:9: note: in expansion of macro 'list_for_each_entry'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:27: error: expression in static assertion is not an integer
     338 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/intel/catpt/pcm.c:357:9: note: in expansion of macro 'list_for_each_entry'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   In file included from include/linux/rculist.h:10,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14:
>> sound/soc/intel/catpt/pcm.c:357:60: error: 'struct snd_card' has no member named 'controls'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |                                                            ^~
   include/linux/list.h:680:27: note: in definition of macro 'list_entry_is_head'
     680 |         (&pos->member == (head))
         |                           ^~~~
   sound/soc/intel/catpt/pcm.c:357:9: note: in expansion of macro 'list_for_each_entry'
     357 |         list_for_each_entry(pos, &component->card->snd_card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   sound/soc/intel/catpt/pcm.c:362:63: error: 'struct snd_card' has no member named 'controls'
     362 |         if (list_entry_is_head(pos, &component->card->snd_card->controls, list))
         |                                                               ^~
   include/linux/list.h:680:27: note: in definition of macro 'list_entry_is_head'
     680 |         (&pos->member == (head))
         |                           ^~~~
--
   In file included from include/linux/kernel.h:21,
                    from arch/x86/include/asm/percpu.h:27,
                    from arch/x86/include/asm/current.h:10,
                    from include/linux/sched.h:12,
                    from include/linux/ratelimit.h:6,
                    from include/linux/dev_printk.h:16,
                    from include/linux/device.h:15,
                    from include/linux/pm_runtime.h:11,
                    from sound/soc/sh/rcar/core.c:93:
   sound/soc/sh/rcar/core.c: In function 'rsnd_kctrl_new':
>> sound/soc/sh/rcar/core.c:1813:41: error: 'struct snd_card' has no member named 'controls'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |                                         ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/sh/rcar/core.c:1813:9: note: in expansion of macro 'list_for_each_entry'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   In file included from include/linux/bits.h:21,
                    from include/linux/ratelimit_types.h:5,
                    from include/linux/ratelimit.h:5:
>> sound/soc/sh/rcar/core.c:1813:41: error: 'struct snd_card' has no member named 'controls'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/sh/rcar/core.c:1813:9: note: in expansion of macro 'list_for_each_entry'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
>> sound/soc/sh/rcar/core.c:1813:41: error: 'struct snd_card' has no member named 'controls'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:21:23: note: in expansion of macro '__same_type'
      21 |                       __same_type(*(ptr), void),                        \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/sh/rcar/core.c:1813:9: note: in expansion of macro 'list_for_each_entry'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:27: error: expression in static assertion is not an integer
     338 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:520:9: note: in expansion of macro 'container_of'
     520 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:531:9: note: in expansion of macro 'list_entry'
     531 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:689:20: note: in expansion of macro 'list_first_entry'
     689 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   sound/soc/sh/rcar/core.c:1813:9: note: in expansion of macro 'list_for_each_entry'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~
   In file included from include/linux/rculist.h:10,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14:
>> sound/soc/sh/rcar/core.c:1813:41: error: 'struct snd_card' has no member named 'controls'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |                                         ^~
   include/linux/list.h:680:27: note: in definition of macro 'list_entry_is_head'
     680 |         (&pos->member == (head))
         |                           ^~~~
   sound/soc/sh/rcar/core.c:1813:9: note: in expansion of macro 'list_for_each_entry'
    1813 |         list_for_each_entry(kctrl, &card->controls, list) {
         |         ^~~~~~~~~~~~~~~~~~~


vim +542 sound/core/oss/mixer_oss.c

^1da177e4c3f41 Linus Torvalds 2005-04-16  529  
f956b4a3ae790e Takashi Iwai   2005-11-17  530  static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
f956b4a3ae790e Takashi Iwai   2005-11-17  531  					  struct snd_mixer_oss_slot *pslot,
^1da177e4c3f41 Linus Torvalds 2005-04-16  532  					  unsigned int numid,
^1da177e4c3f41 Linus Torvalds 2005-04-16  533  					  int *left, int *right)
^1da177e4c3f41 Linus Torvalds 2005-04-16  534  {
f956b4a3ae790e Takashi Iwai   2005-11-17  535  	struct snd_ctl_elem_info *uinfo;
f956b4a3ae790e Takashi Iwai   2005-11-17  536  	struct snd_ctl_elem_value *uctl;
f956b4a3ae790e Takashi Iwai   2005-11-17  537  	struct snd_kcontrol *kctl;
f956b4a3ae790e Takashi Iwai   2005-11-17  538  	struct snd_card *card = fmixer->card;
^1da177e4c3f41 Linus Torvalds 2005-04-16  539  
^1da177e4c3f41 Linus Torvalds 2005-04-16  540  	if (numid == ID_UNKNOWN)
^1da177e4c3f41 Linus Torvalds 2005-04-16  541  		return;
^1da177e4c3f41 Linus Torvalds 2005-04-16 @542  	down_read(&card->controls_rwsem);
b1e055f67611da Takashi Iwai   2023-07-18  543  	kctl = snd_ctl_find_numid_locked(card, numid);
51c816fdd17c63 Takashi Iwai   2021-06-08  544  	if (!kctl) {
^1da177e4c3f41 Linus Torvalds 2005-04-16  545  		up_read(&card->controls_rwsem);
^1da177e4c3f41 Linus Torvalds 2005-04-16  546  		return;
^1da177e4c3f41 Linus Torvalds 2005-04-16  547  	}
ca2c0966562cfb Takashi Iwai   2005-09-09  548  	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
ca2c0966562cfb Takashi Iwai   2005-09-09  549  	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
^1da177e4c3f41 Linus Torvalds 2005-04-16  550  	if (uinfo == NULL || uctl == NULL)
^1da177e4c3f41 Linus Torvalds 2005-04-16  551  		goto __unalloc;
7c22f1aaa23370 Takashi Iwai   2005-10-10  552  	if (kctl->info(kctl, uinfo))
7c22f1aaa23370 Takashi Iwai   2005-10-10  553  		goto __unalloc;
7c22f1aaa23370 Takashi Iwai   2005-10-10  554  	if (kctl->get(kctl, uctl))
7c22f1aaa23370 Takashi Iwai   2005-10-10  555  		goto __unalloc;
7c22f1aaa23370 Takashi Iwai   2005-10-10  556  	if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
7c22f1aaa23370 Takashi Iwai   2005-10-10  557  	    uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
7c22f1aaa23370 Takashi Iwai   2005-10-10  558  		goto __unalloc;
^1da177e4c3f41 Linus Torvalds 2005-04-16  559  	*left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
^1da177e4c3f41 Linus Torvalds 2005-04-16  560  	if (uinfo->count > 1)
^1da177e4c3f41 Linus Torvalds 2005-04-16  561  		*right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
^1da177e4c3f41 Linus Torvalds 2005-04-16  562        __unalloc:
^1da177e4c3f41 Linus Torvalds 2005-04-16  563  	up_read(&card->controls_rwsem);
^1da177e4c3f41 Linus Torvalds 2005-04-16  564        	kfree(uctl);
^1da177e4c3f41 Linus Torvalds 2005-04-16  565        	kfree(uinfo);
^1da177e4c3f41 Linus Torvalds 2005-04-16  566  }
^1da177e4c3f41 Linus Torvalds 2005-04-16  567
diff mbox series

Patch

diff --git a/include/sound/control.h b/include/sound/control.h
index 9a4f4f7138da8..32920f33eb47e 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -128,6 +128,7 @@  typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,
 				      struct snd_ctl_file * control,
 				      unsigned int cmd, unsigned long arg);
 
+int snd_control_new(struct snd_card * card);
 void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id);
 void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kcontrol * kctl, unsigned int ioff);
 
diff --git a/include/sound/core.h b/include/sound/core.h
index a4744e142c7e3..93048a7a800f4 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -95,21 +95,7 @@  struct snd_card {
 	void (*private_free) (struct snd_card *card); /* callback for freeing of
 								private data */
 	struct list_head devices;	/* devices */
-
-	struct device *ctl_dev;		/* control device */
-	unsigned int last_numid;	/* last used numeric ID */
-	struct rw_semaphore controls_rwsem;	/* controls lock (list and values) */
-	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
-	int controls_count;		/* count of all controls */
-	size_t user_ctl_alloc_size;	// current memory allocation by user controls.
-	struct list_head controls;	/* all controls for this card */
-	struct list_head ctl_files;	/* active control files */
-#ifdef CONFIG_SND_CTL_FAST_LOOKUP
-	struct xarray ctl_numids;	/* hash table for numids */
-	struct xarray ctl_hash;		/* hash table for ctl id matching */
-	bool ctl_hash_collision;	/* ctl_hash collision seen? */
-#endif
-
+	struct snd_control *ctl;	/* control devices */
 	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
 	struct proc_dir_entry *proc_root_link;	/* number link to real id */
 
@@ -147,6 +133,23 @@  struct snd_card {
 #endif
 };
 
+struct snd_control
+{
+	struct device dev;		/* control device */
+	struct rw_semaphore controls_rwsem;	/* controls lock (list and values) */
+	rwlock_t files_rwlock;	/* ctl_files list lock */
+	int controls_count;		/* count of all controls */
+	size_t user_ctl_alloc_size;	// current memory allocation by user controls.
+	struct list_head controls;	/* all controls for this card */
+	struct list_head files;	/* active control files */
+	unsigned int last_numid;	/* last used numeric ID */
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+	struct xarray numids;	/* hash table for numids */
+	struct xarray hash;		/* hash table for ctl id matching */
+	bool hash_collision;	/* ctl_hash collision seen? */
+#endif
+};
+
 #define dev_to_snd_card(p)	container_of(p, struct snd_card, card_dev)
 
 #ifdef CONFIG_PM
diff --git a/sound/core/control.c b/sound/core/control.c
index 59c8658966d4c..09066d05a8800 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -41,12 +41,49 @@  static struct snd_ctl_layer_ops *snd_ctl_layer;
 
 static int snd_ctl_remove_locked(struct snd_card *card,
 				 struct snd_kcontrol *kcontrol);
+/**
+ * snd_control_new - Allocate and initialize snd_control
+ * @card: the card to be initialized
+ *
+ * This function creates and initializes snd_control
+ */
+int snd_control_new(struct snd_card *card)
+{
+	struct snd_control *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+	int err;
+
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
+	if (!ctl)
+		return -ENOMEM;
+
+	init_rwsem(&ctl->controls_rwsem);
+	rwlock_init(&ctl->files_rwlock);
+	INIT_LIST_HEAD(&ctl->controls);
+	INIT_LIST_HEAD(&ctl->files);
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+	xa_init(&ctl->numids);
+	xa_init(&ctl->hash);
+#endif
+	card->ctl = ctl;
+	/* the control interface cannot be accessed from the user space until */
+	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
+	err = snd_ctl_create(card);
+	if (err < 0) {
+		dev_err(card->dev, "unable to register control minors\n");
+		kfree(ctl);
+		return err;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(snd_control_new);
 
 static int snd_ctl_open(struct inode *inode, struct file *file)
 {
 	unsigned long flags;
 	struct snd_card *card;
-	struct snd_ctl_file *ctl;
+	struct snd_control *ctl;
+	struct snd_ctl_file *ctlf;
 	int i, err;
 
 	err = stream_open(inode, file);
@@ -58,6 +95,7 @@  static int snd_ctl_open(struct inode *inode, struct file *file)
 		err = -ENODEV;
 		goto __error1;
 	}
+	ctl = card->ctl;
 	err = snd_card_file_add(card, file);
 	if (err < 0) {
 		err = -ENODEV;
@@ -67,22 +105,22 @@  static int snd_ctl_open(struct inode *inode, struct file *file)
 		err = -EFAULT;
 		goto __error2;
 	}
-	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
-	if (ctl == NULL) {
+	ctlf = kzalloc(sizeof(*ctlf), GFP_KERNEL);
+	if (ctlf == NULL) {
 		err = -ENOMEM;
 		goto __error;
 	}
-	INIT_LIST_HEAD(&ctl->events);
-	init_waitqueue_head(&ctl->change_sleep);
-	spin_lock_init(&ctl->read_lock);
-	ctl->card = card;
+	INIT_LIST_HEAD(&ctlf->events);
+	init_waitqueue_head(&ctlf->change_sleep);
+	spin_lock_init(&ctlf->read_lock);
+	ctlf->card = card;
 	for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
-		ctl->preferred_subdevice[i] = -1;
-	ctl->pid = get_pid(task_pid(current));
-	file->private_data = ctl;
-	write_lock_irqsave(&card->ctl_files_rwlock, flags);
-	list_add_tail(&ctl->list, &card->ctl_files);
-	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+		ctlf->preferred_subdevice[i] = -1;
+	ctlf->pid = get_pid(task_pid(current));
+	file->private_data = ctlf;
+	write_lock_irqsave(&ctl->files_rwlock, flags);
+	list_add_tail(&ctlf->list, &ctl->files);
+	write_unlock_irqrestore(&ctl->files_rwlock, flags);
 	snd_card_unref(card);
 	return 0;
 
@@ -113,27 +151,26 @@  static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
 static int snd_ctl_release(struct inode *inode, struct file *file)
 {
 	unsigned long flags;
-	struct snd_card *card;
-	struct snd_ctl_file *ctl;
+	struct snd_ctl_file *ctlf = file->private_data;
+	struct snd_card *card = ctlf->card;
+	struct snd_control *ctl = card->ctl;
 	struct snd_kcontrol *control;
 	unsigned int idx;
 
-	ctl = file->private_data;
 	file->private_data = NULL;
-	card = ctl->card;
-	write_lock_irqsave(&card->ctl_files_rwlock, flags);
-	list_del(&ctl->list);
-	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
-	down_write(&card->controls_rwsem);
-	list_for_each_entry(control, &card->controls, list)
+	write_lock_irqsave(&ctl->files_rwlock, flags);
+	list_del(&ctlf->list);
+	write_unlock_irqrestore(&ctl->files_rwlock, flags);
+	down_write(&ctl->controls_rwsem);
+	list_for_each_entry(control, &ctl->controls, list)
 		for (idx = 0; idx < control->count; idx++)
-			if (control->vd[idx].owner == ctl)
+			if (control->vd[idx].owner == ctlf)
 				control->vd[idx].owner = NULL;
-	up_write(&card->controls_rwsem);
-	snd_fasync_free(ctl->fasync);
-	snd_ctl_empty_read_queue(ctl);
-	put_pid(ctl->pid);
-	kfree(ctl);
+	up_write(&ctl->controls_rwsem);
+	snd_fasync_free(ctlf->fasync);
+	snd_ctl_empty_read_queue(ctlf);
+	put_pid(ctlf->pid);
+	kfree(ctlf);
 	module_put(card->module);
 	snd_card_file_remove(card, file);
 	return 0;
@@ -160,11 +197,11 @@  void snd_ctl_notify(struct snd_card *card, unsigned int mask,
 		return;
 	if (card->shutdown)
 		return;
-	read_lock_irqsave(&card->ctl_files_rwlock, flags);
+	read_lock_irqsave(&card->ctl->files_rwlock, flags);
 #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
 	card->mixer_oss_change_count++;
 #endif
-	list_for_each_entry(ctl, &card->ctl_files, list) {
+	list_for_each_entry(ctl, &card->ctl->files, list) {
 		if (!ctl->subscribed)
 			continue;
 		spin_lock(&ctl->read_lock);
@@ -187,7 +224,7 @@  void snd_ctl_notify(struct snd_card *card, unsigned int mask,
 		spin_unlock(&ctl->read_lock);
 		snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
 	}
-	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+	read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
 }
 EXPORT_SYMBOL(snd_ctl_notify);
 
@@ -341,13 +378,13 @@  static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
 	struct snd_kcontrol *kctl;
 
 	/* Make sure that the ids assigned to the control do not wrap around */
-	if (card->last_numid >= UINT_MAX - count)
-		card->last_numid = 0;
+	if (card->ctl->last_numid >= UINT_MAX - count)
+		card->ctl->last_numid = 0;
 
-	list_for_each_entry(kctl, &card->controls, list) {
-		if (kctl->id.numid < card->last_numid + 1 + count &&
-		    kctl->id.numid + kctl->count > card->last_numid + 1) {
-		    	card->last_numid = kctl->id.numid + kctl->count - 1;
+	list_for_each_entry(kctl, &card->ctl->controls, list) {
+		if (kctl->id.numid < card->ctl->last_numid + 1 + count &&
+		    kctl->id.numid + kctl->count > card->ctl->last_numid + 1) {
+			card->ctl->last_numid = kctl->id.numid + kctl->count - 1;
 			return true;
 		}
 	}
@@ -406,18 +443,19 @@  static void add_hash_entries(struct snd_card *card,
 			     struct snd_kcontrol *kcontrol)
 {
 	struct snd_ctl_elem_id id = kcontrol->id;
+	struct snd_control *ctl = card->ctl;
 	int i;
 
-	xa_store_range(&card->ctl_numids, kcontrol->id.numid,
+	xa_store_range(&ctl->numids, kcontrol->id.numid,
 		       kcontrol->id.numid + kcontrol->count - 1,
 		       kcontrol, GFP_KERNEL);
 
 	for (i = 0; i < kcontrol->count; i++) {
 		id.index = kcontrol->id.index + i;
-		if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
+		if (xa_insert(&ctl->hash, get_ctl_id_hash(&id),
 			      kcontrol, GFP_KERNEL)) {
 			/* skip hash for this entry, noting we had collision */
-			card->ctl_hash_collision = true;
+			ctl->hash_collision = true;
 			dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
 				id.iface, id.name, id.index);
 		}
@@ -429,17 +467,18 @@  static void remove_hash_entries(struct snd_card *card,
 				struct snd_kcontrol *kcontrol)
 {
 	struct snd_ctl_elem_id id = kcontrol->id;
+	struct snd_control *ctl = card->ctl;
 	struct snd_kcontrol *matched;
 	unsigned long h;
 	int i;
 
 	for (i = 0; i < kcontrol->count; i++) {
-		xa_erase(&card->ctl_numids, id.numid);
+		xa_erase(&ctl->numids, id.numid);
 		h = get_ctl_id_hash(&id);
-		matched = xa_load(&card->ctl_hash, h);
+		matched = xa_load(&ctl->hash, h);
 		if (matched && (matched == kcontrol ||
 				elem_id_matches(matched, &id)))
-			xa_erase(&card->ctl_hash, h);
+			xa_erase(&ctl->hash, h);
 		id.index++;
 		id.numid++;
 	}
@@ -469,7 +508,7 @@  static int __snd_ctl_add_replace(struct snd_card *card,
 	struct snd_kcontrol *old;
 	int err;
 
-	lockdep_assert_held_write(&card->controls_rwsem);
+	lockdep_assert_held_write(&card->ctl->controls_rwsem);
 
 	id = kcontrol->id;
 	if (id.index > UINT_MAX - kcontrol->count)
@@ -496,10 +535,10 @@  static int __snd_ctl_add_replace(struct snd_card *card,
 	if (snd_ctl_find_hole(card, kcontrol->count) < 0)
 		return -ENOMEM;
 
-	list_add_tail(&kcontrol->list, &card->controls);
-	card->controls_count += kcontrol->count;
-	kcontrol->id.numid = card->last_numid + 1;
-	card->last_numid += kcontrol->count;
+	list_add_tail(&kcontrol->list, &card->ctl->controls);
+	card->ctl->controls_count += kcontrol->count;
+	kcontrol->id.numid = card->ctl->last_numid + 1;
+	card->ctl->last_numid += kcontrol->count;
 
 	add_hash_entries(card, kcontrol);
 
@@ -520,9 +559,9 @@  static int snd_ctl_add_replace(struct snd_card *card,
 	if (snd_BUG_ON(!card || !kcontrol->info))
 		goto error;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	err = __snd_ctl_add_replace(card, kcontrol, mode);
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	if (err < 0)
 		goto error;
 	return 0;
@@ -580,7 +619,7 @@  static int __snd_ctl_remove(struct snd_card *card,
 {
 	unsigned int idx;
 
-	lockdep_assert_held_write(&card->controls_rwsem);
+	lockdep_assert_held_write(&card->ctl->controls_rwsem);
 
 	if (snd_BUG_ON(!card || !kcontrol))
 		return -EINVAL;
@@ -589,7 +628,7 @@  static int __snd_ctl_remove(struct snd_card *card,
 	if (remove_hash)
 		remove_hash_entries(card, kcontrol);
 
-	card->controls_count -= kcontrol->count;
+	card->ctl->controls_count -= kcontrol->count;
 	for (idx = 0; idx < kcontrol->count; idx++)
 		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
 	snd_ctl_free_one(kcontrol);
@@ -618,9 +657,9 @@  int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
 {
 	int ret;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	ret = snd_ctl_remove_locked(card, kcontrol);
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return ret;
 }
 EXPORT_SYMBOL(snd_ctl_remove);
@@ -640,14 +679,14 @@  int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
 	struct snd_kcontrol *kctl;
 	int ret;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, id);
 	if (kctl == NULL) {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 		return -ENOENT;
 	}
 	ret = snd_ctl_remove_locked(card, kctl);
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return ret;
 }
 EXPORT_SYMBOL(snd_ctl_remove_id);
@@ -669,7 +708,7 @@  static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
 	struct snd_kcontrol *kctl;
 	int idx, ret;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, id);
 	if (kctl == NULL) {
 		ret = -ENOENT;
@@ -686,7 +725,7 @@  static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
 		}
 	ret = snd_ctl_remove_locked(card, kctl);
 error:
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return ret;
 }
 
@@ -710,7 +749,7 @@  int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
 	unsigned int index_offset;
 	int ret;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, id);
 	if (kctl == NULL) {
 		ret = -ENOENT;
@@ -729,13 +768,13 @@  int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
 		vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 	}
 	snd_ctl_build_ioff(id, kctl, index_offset);
-	downgrade_write(&card->controls_rwsem);
+	downgrade_write(&card->ctl->controls_rwsem);
 	snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return 1;
 
  unlock:
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
@@ -764,10 +803,10 @@  int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
 	struct snd_kcontrol *kctl;
 	int saved_numid;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, src_id);
 	if (kctl == NULL) {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 		return -ENOENT;
 	}
 	saved_numid = kctl->id.numid;
@@ -775,7 +814,7 @@  int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
 	kctl->id = *dst_id;
 	kctl->id.numid = saved_numid;
 	add_hash_entries(card, kctl);
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return 0;
 }
 EXPORT_SYMBOL(snd_ctl_rename_id);
@@ -793,7 +832,7 @@  EXPORT_SYMBOL(snd_ctl_rename_id);
 void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
 		    const char *name)
 {
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	remove_hash_entries(card, kctl);
 
 	if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -801,7 +840,7 @@  void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
 			name, kctl->id.name);
 
 	add_hash_entries(card, kctl);
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 }
 EXPORT_SYMBOL(snd_ctl_rename);
 
@@ -836,9 +875,9 @@  snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid)
 {
 	if (snd_BUG_ON(!card || !numid))
 		return NULL;
-	lockdep_assert_held(&card->controls_rwsem);
+	lockdep_assert_held(&card->ctl->controls_rwsem);
 #ifdef CONFIG_SND_CTL_FAST_LOOKUP
-	return xa_load(&card->ctl_numids, numid);
+	return xa_load(&card->ctl->numids, numid);
 #else
 	return snd_ctl_find_numid_slow(card, numid);
 #endif
@@ -861,9 +900,9 @@  struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
 {
 	struct snd_kcontrol *kctl;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_numid_locked(card, numid);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return kctl;
 }
 EXPORT_SYMBOL(snd_ctl_find_numid);
@@ -887,18 +926,18 @@  struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
 
 	if (snd_BUG_ON(!card || !id))
 		return NULL;
-	lockdep_assert_held(&card->controls_rwsem);
+	lockdep_assert_held(&card->ctl->controls_rwsem);
 	if (id->numid != 0)
 		return snd_ctl_find_numid_locked(card, id->numid);
 #ifdef CONFIG_SND_CTL_FAST_LOOKUP
-	kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
+	kctl = xa_load(&card->ctl->hash, get_ctl_id_hash(id));
 	if (kctl && elem_id_matches(kctl, id))
 		return kctl;
-	if (!card->ctl_hash_collision)
+	if (!card->ctl->hash_collision)
 		return NULL; /* we can rely on only hash table */
 #endif
 	/* no matching in hash table - try all as the last resort */
-	list_for_each_entry(kctl, &card->controls, list)
+	list_for_each_entry(kctl, &card->ctl->controls, list)
 		if (elem_id_matches(kctl, id))
 			return kctl;
 
@@ -922,9 +961,9 @@  struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
 {
 	struct snd_kcontrol *kctl;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, id);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return kctl;
 }
 EXPORT_SYMBOL(snd_ctl_find_id);
@@ -965,11 +1004,11 @@  static int snd_ctl_elem_list(struct snd_card *card,
 	offset = list->offset;
 	space = list->space;
 
-	down_read(&card->controls_rwsem);
-	list->count = card->controls_count;
+	down_read(&card->ctl->controls_rwsem);
+	list->count = card->ctl->controls_count;
 	list->used = 0;
 	if (space > 0) {
-		list_for_each_entry(kctl, &card->controls, list) {
+		list_for_each_entry(kctl, &card->ctl->controls, list) {
 			if (offset >= kctl->count) {
 				offset -= kctl->count;
 				continue;
@@ -989,7 +1028,7 @@  static int snd_ctl_elem_list(struct snd_card *card,
 		}
 	}
  out:
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return err;
 }
 
@@ -1240,13 +1279,13 @@  static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
 	struct snd_kcontrol *kctl;
 	int result;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, &info->id);
 	if (kctl == NULL)
 		result = -ENOENT;
 	else
 		result = __snd_ctl_elem_info(card, kctl, info, ctl);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return result;
 }
 
@@ -1279,7 +1318,7 @@  static int snd_ctl_elem_read(struct snd_card *card,
 	const u32 pattern = 0xdeadbeef;
 	int ret;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, &control->id);
 	if (kctl == NULL) {
 		ret = -ENOENT;
@@ -1323,7 +1362,7 @@  static int snd_ctl_elem_read(struct snd_card *card,
 		goto unlock;
 	}
 unlock:
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return ret;
 }
 
@@ -1356,10 +1395,10 @@  static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
 	unsigned int index_offset;
 	int result;
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, &control->id);
 	if (kctl == NULL) {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 		return -ENOENT;
 	}
 
@@ -1367,7 +1406,7 @@  static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
 	vd = &kctl->vd[index_offset];
 	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
 	    (file && vd->owner && vd->owner != file)) {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 		return -EPERM;
 	}
 
@@ -1388,16 +1427,16 @@  static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
 		result = kctl->put(kctl, control);
 	snd_power_unref(card);
 	if (result < 0) {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 		return result;
 	}
 
 	if (result > 0) {
-		downgrade_write(&card->controls_rwsem);
+		downgrade_write(&card->ctl->controls_rwsem);
 		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
-		up_read(&card->controls_rwsem);
+		up_read(&card->ctl->controls_rwsem);
 	} else {
-		up_write(&card->controls_rwsem);
+		up_write(&card->ctl->controls_rwsem);
 	}
 
 	return 0;
@@ -1437,7 +1476,7 @@  static int snd_ctl_elem_lock(struct snd_ctl_file *file,
 
 	if (copy_from_user(&id, _id, sizeof(id)))
 		return -EFAULT;
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, &id);
 	if (kctl == NULL) {
 		result = -ENOENT;
@@ -1450,7 +1489,7 @@  static int snd_ctl_elem_lock(struct snd_ctl_file *file,
 			result = 0;
 		}
 	}
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return result;
 }
 
@@ -1465,7 +1504,7 @@  static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
 
 	if (copy_from_user(&id, _id, sizeof(id)))
 		return -EFAULT;
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, &id);
 	if (kctl == NULL) {
 		result = -ENOENT;
@@ -1480,7 +1519,7 @@  static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
 			result = 0;
 		}
 	}
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return result;
 }
 
@@ -1497,7 +1536,7 @@  struct user_element {
 // check whether the addition (in bytes) of user ctl element may overflow the limit.
 static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
 {
-	return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
+	return (ssize_t)card->ctl->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
 }
 
 static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
@@ -1575,7 +1614,7 @@  static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
 	int i;
 	int change;
 
-	lockdep_assert_held_write(&ue->card->controls_rwsem);
+	lockdep_assert_held_write(&ue->card->ctl->controls_rwsem);
 
 	if (size > 1024 * 128)	/* sane value */
 		return -EINVAL;
@@ -1602,7 +1641,7 @@  static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
 			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 		mask = SNDRV_CTL_EVENT_MASK_INFO;
 	} else {
-		ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+		ue->card->ctl->user_ctl_alloc_size -= ue->tlv_data_size;
 		ue->tlv_data_size = 0;
 		kvfree(ue->tlv_data);
 	}
@@ -1610,7 +1649,7 @@  static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
 	ue->tlv_data = container;
 	ue->tlv_data_size = size;
 	// decremented at private_free.
-	ue->card->user_ctl_alloc_size += size;
+	ue->card->ctl->user_ctl_alloc_size += size;
 
 	mask |= SNDRV_CTL_EVENT_MASK_TLV;
 	for (i = 0; i < kctl->count; ++i)
@@ -1653,7 +1692,7 @@  static int snd_ctl_elem_init_enum_names(struct user_element *ue)
 	unsigned int i;
 	const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
 
-	lockdep_assert_held_write(&ue->card->controls_rwsem);
+	lockdep_assert_held_write(&ue->card->ctl->controls_rwsem);
 
 	buf_len = ue->info.value.enumerated.names_length;
 	if (buf_len > 64 * 1024)
@@ -1680,7 +1719,7 @@  static int snd_ctl_elem_init_enum_names(struct user_element *ue)
 	ue->priv_data = names;
 	ue->info.value.enumerated.names_ptr = 0;
 	// increment the allocation size; decremented again at private_free.
-	ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
+	ue->card->ctl->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
 
 	return 0;
 }
@@ -1695,10 +1734,10 @@  static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
 	struct user_element *ue = kcontrol->private_data;
 
 	// decrement the allocation size.
-	ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
-	ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+	ue->card->ctl->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
+	ue->card->ctl->user_ctl_alloc_size -= ue->tlv_data_size;
 	if (ue->priv_data)
-		ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
+		ue->card->ctl->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
 
 	kvfree(ue->tlv_data);
 	kvfree(ue->priv_data);
@@ -1763,7 +1802,7 @@  static int snd_ctl_elem_add(struct snd_ctl_file *file,
 	private_size = value_sizes[info->type] * info->count;
 	alloc_size = compute_user_elem_size(private_size, count);
 
-	down_write(&card->controls_rwsem);
+	down_write(&card->ctl->controls_rwsem);
 	if (check_user_elem_overflow(card, alloc_size)) {
 		err = -ENOMEM;
 		goto unlock;
@@ -1789,7 +1828,7 @@  static int snd_ctl_elem_add(struct snd_ctl_file *file,
 	kctl->private_free = snd_ctl_elem_user_free;
 
 	// increment the allocated size; decremented again at private_free.
-	card->user_ctl_alloc_size += alloc_size;
+	card->ctl->user_ctl_alloc_size += alloc_size;
 
 	/* Set private data for this userspace control. */
 	ue->card = card;
@@ -1833,7 +1872,7 @@  static int snd_ctl_elem_add(struct snd_ctl_file *file,
 	 * which locks the element.
 	 */
  unlock:
-	up_write(&card->controls_rwsem);
+	up_write(&card->ctl->controls_rwsem);
 	return err;
 }
 
@@ -1959,7 +1998,7 @@  static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
 	struct snd_ctl_elem_id id;
 	struct snd_kcontrol_volatile *vd;
 
-	lockdep_assert_held(&file->card->controls_rwsem);
+	lockdep_assert_held(&file->card->ctl->controls_rwsem);
 
 	if (copy_from_user(&header, buf, sizeof(header)))
 		return -EFAULT;
@@ -2036,19 +2075,19 @@  static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
 	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 		return snd_ctl_subscribe_events(ctl, ip);
 	case SNDRV_CTL_IOCTL_TLV_READ:
-		down_read(&ctl->card->controls_rwsem);
+		down_read(&ctl->card->ctl->controls_rwsem);
 		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
-		up_read(&ctl->card->controls_rwsem);
+		up_read(&ctl->card->ctl->controls_rwsem);
 		return err;
 	case SNDRV_CTL_IOCTL_TLV_WRITE:
-		down_write(&ctl->card->controls_rwsem);
+		down_write(&ctl->card->ctl->controls_rwsem);
 		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
-		up_write(&ctl->card->controls_rwsem);
+		up_write(&ctl->card->ctl->controls_rwsem);
 		return err;
 	case SNDRV_CTL_IOCTL_TLV_COMMAND:
-		down_write(&ctl->card->controls_rwsem);
+		down_write(&ctl->card->ctl->controls_rwsem);
 		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
-		up_write(&ctl->card->controls_rwsem);
+		up_write(&ctl->card->ctl->controls_rwsem);
 		return err;
 	case SNDRV_CTL_IOCTL_POWER:
 		return -ENOPROTOOPT;
@@ -2258,15 +2297,15 @@  int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
 	int subdevice = -1;
 	unsigned long flags;
 
-	read_lock_irqsave(&card->ctl_files_rwlock, flags);
-	list_for_each_entry(kctl, &card->ctl_files, list) {
+	read_lock_irqsave(&card->ctl->files_rwlock, flags);
+	list_for_each_entry(kctl, &card->ctl->files, list) {
 		if (kctl->pid == task_pid(current)) {
 			subdevice = kctl->preferred_subdevice[type];
 			if (subdevice != -1)
 				break;
 		}
 	}
-	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+	read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
 	return subdevice;
 }
 EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
@@ -2326,9 +2365,9 @@  void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
 	for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
 		card = snd_card_ref(card_number);
 		if (card) {
-			down_read(&card->controls_rwsem);
+			down_read(&card->ctl->controls_rwsem);
 			lops->lregister(card);
-			up_read(&card->controls_rwsem);
+			up_read(&card->ctl->controls_rwsem);
 			snd_card_unref(card);
 		}
 	}
@@ -2389,15 +2428,15 @@  static int snd_ctl_dev_register(struct snd_device *device)
 	int err;
 
 	err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
-				  &snd_ctl_f_ops, card, card->ctl_dev);
+				  &snd_ctl_f_ops, card, &card->ctl->dev);
 	if (err < 0)
 		return err;
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	down_read(&snd_ctl_layer_rwsem);
 	for (lops = snd_ctl_layer; lops; lops = lops->next)
 		lops->lregister(card);
 	up_read(&snd_ctl_layer_rwsem);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return 0;
 }
 
@@ -2411,21 +2450,21 @@  static int snd_ctl_dev_disconnect(struct snd_device *device)
 	struct snd_ctl_layer_ops *lops;
 	unsigned long flags;
 
-	read_lock_irqsave(&card->ctl_files_rwlock, flags);
-	list_for_each_entry(ctl, &card->ctl_files, list) {
+	read_lock_irqsave(&card->ctl->files_rwlock, flags);
+	list_for_each_entry(ctl, &card->ctl->files, list) {
 		wake_up(&ctl->change_sleep);
 		snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
 	}
-	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+	read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	down_read(&snd_ctl_layer_rwsem);
 	for (lops = snd_ctl_layer; lops; lops = lops->next)
 		lops->ldisconnect(card);
 	up_read(&snd_ctl_layer_rwsem);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 
-	return snd_unregister_device(card->ctl_dev);
+	return snd_unregister_device(&card->ctl->dev);
 }
 
 /*
@@ -2436,21 +2475,28 @@  static int snd_ctl_dev_free(struct snd_device *device)
 	struct snd_card *card = device->device_data;
 	struct snd_kcontrol *control;
 
-	down_write(&card->controls_rwsem);
-	while (!list_empty(&card->controls)) {
-		control = snd_kcontrol(card->controls.next);
+	down_write(&card->ctl->controls_rwsem);
+	while (!list_empty(&card->ctl->controls)) {
+		control = snd_kcontrol(card->ctl->controls.next);
 		__snd_ctl_remove(card, control, false);
 	}
 
 #ifdef CONFIG_SND_CTL_FAST_LOOKUP
-	xa_destroy(&card->ctl_numids);
-	xa_destroy(&card->ctl_hash);
+	xa_destroy(&card->ctl->numids);
+	xa_destroy(&card->ctl->hash);
 #endif
-	up_write(&card->controls_rwsem);
-	put_device(card->ctl_dev);
+	up_write(&card->ctl->controls_rwsem);
+	put_device(&card->ctl->dev);
 	return 0;
 }
 
+static void release_control_device(struct device *dev)
+{
+	struct snd_control *ctl = container_of(dev, struct snd_control, dev);
+
+	kfree(ctl);
+}
+
 /*
  * create control core:
  * called from init.c
@@ -2462,6 +2508,7 @@  int snd_ctl_create(struct snd_card *card)
 		.dev_register =	snd_ctl_dev_register,
 		.dev_disconnect = snd_ctl_dev_disconnect,
 	};
+	struct snd_control *ctl = card->ctl;
 	int err;
 
 	if (snd_BUG_ON(!card))
@@ -2469,14 +2516,13 @@  int snd_ctl_create(struct snd_card *card)
 	if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
 		return -ENXIO;
 
-	err = snd_device_alloc(&card->ctl_dev, card);
-	if (err < 0)
-		return err;
-	dev_set_name(card->ctl_dev, "controlC%d", card->number);
+	snd_device_init(&ctl->dev, card);
+	dev_set_name(&ctl->dev, "controlC%d", card->number);
+	ctl->dev.release = release_control_device;
 
 	err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
 	if (err < 0)
-		put_device(card->ctl_dev);
+		put_device(&ctl->dev);
 	return err;
 }
 
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 0e8b1bfb040e0..0e0b555f71537 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -172,15 +172,15 @@  static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
 	struct snd_ctl_elem_info *info;
 	int err;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 	kctl = snd_ctl_find_id_locked(card, id);
 	if (! kctl) {
-		up_read(&card->controls_rwsem);
+		up_read(&card->ctl->controls_rwsem);
 		return -ENOENT;
 	}
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (info == NULL) {
-		up_read(&card->controls_rwsem);
+		up_read(&card->ctl->controls_rwsem);
 		return -ENOMEM;
 	}
 	info->id = *id;
@@ -188,7 +188,7 @@  static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
 	if (!err)
 		err = kctl->info(kctl, info);
 	snd_power_unref(card);
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	if (err >= 0) {
 		err = info->type;
 		*countp = info->count;
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index a78eb48927c7b..6b53de7660438 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -243,6 +243,7 @@  static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
 			      unsigned int group, bool set)
 {
 	struct snd_card *card;
+	struct snd_control *ctl;
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol_volatile *vd;
 	unsigned int ioff, access, new_access;
@@ -250,7 +251,8 @@  static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
 
 	card = snd_card_ref(card_number);
 	if (card) {
-		down_write(&card->controls_rwsem);
+		ctl = card->ctl;
+		down_write(&ctl->controls_rwsem);
 		kctl = snd_ctl_find_id_locked(card, id);
 		if (kctl) {
 			ioff = snd_ctl_get_ioff(kctl, id);
@@ -271,7 +273,7 @@  static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
 			err = -ENOENT;
 		}
 unlock:
-		up_write(&card->controls_rwsem);
+		up_write(&ctl->controls_rwsem);
 		snd_card_unref(card);
 	} else {
 		err = -ENXIO;
@@ -357,7 +359,7 @@  static void snd_ctl_led_register(struct snd_card *card)
 	snd_ctl_led_card_valid[card->number] = true;
 	mutex_unlock(&snd_ctl_led_mutex);
 	/* the register callback is already called with held card->controls_rwsem */
-	list_for_each_entry(kctl, &card->controls, list)
+	list_for_each_entry(kctl, &card->ctl->controls, list)
 		for (ioff = 0; ioff < kctl->count; ioff++)
 			snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff);
 	snd_ctl_led_refresh();
@@ -617,12 +619,14 @@  static ssize_t list_show(struct device *dev,
 	struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
 	struct snd_card *card;
 	struct snd_ctl_led_ctl *lctl;
+	struct snd_control *ctl;
 	size_t l = 0;
 
 	card = snd_card_ref(led_card->number);
 	if (!card)
 		return -ENXIO;
-	down_read(&card->controls_rwsem);
+	ctl = card->ctl;
+	down_read(&ctl->controls_rwsem);
 	mutex_lock(&snd_ctl_led_mutex);
 	if (snd_ctl_led_card_valid[led_card->number]) {
 		list_for_each_entry(lctl, &led_card->led->controls, list) {
@@ -635,7 +639,7 @@  static ssize_t list_show(struct device *dev,
 		}
 	}
 	mutex_unlock(&snd_ctl_led_mutex);
-	up_read(&card->controls_rwsem);
+	up_read(&ctl->controls_rwsem);
 	snd_card_unref(card);
 	return l;
 }
@@ -688,7 +692,7 @@  static void snd_ctl_led_sysfs_add(struct snd_card *card)
 			goto cerr;
 		led->cards[card->number] = led_card;
 		snprintf(link_name, sizeof(link_name), "led-%s", led->name);
-		WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name),
+		WARN(sysfs_create_link(&card->ctl->dev.kobj, &led_card->dev.kobj, link_name),
 			"can't create symlink to controlC%i device\n", card->number);
 		WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
 			"can't create symlink to card%i\n", card->number);
@@ -714,7 +718,7 @@  static void snd_ctl_led_sysfs_remove(struct snd_card *card)
 		if (!led_card)
 			continue;
 		snprintf(link_name, sizeof(link_name), "led-%s", led->name);
-		sysfs_remove_link(&card->ctl_dev->kobj, link_name);
+		sysfs_remove_link(&card->ctl->dev.kobj, link_name);
 		sysfs_remove_link(&led_card->dev.kobj, "card");
 		device_unregister(&led_card->dev);
 		led->cards[card->number] = NULL;
diff --git a/sound/core/init.c b/sound/core/init.c
index 37a8e4791f781..695b080603e93 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -331,14 +331,6 @@  static int snd_card_init(struct snd_card *card, struct device *parent,
 	card->module = module;
 #endif
 	INIT_LIST_HEAD(&card->devices);
-	init_rwsem(&card->controls_rwsem);
-	rwlock_init(&card->ctl_files_rwlock);
-	INIT_LIST_HEAD(&card->controls);
-	INIT_LIST_HEAD(&card->ctl_files);
-#ifdef CONFIG_SND_CTL_FAST_LOOKUP
-	xa_init(&card->ctl_numids);
-	xa_init(&card->ctl_hash);
-#endif
 	spin_lock_init(&card->files_lock);
 	INIT_LIST_HEAD(&card->files_list);
 	mutex_init(&card->memory_mutex);
@@ -363,11 +355,9 @@  static int snd_card_init(struct snd_card *card, struct device *parent,
 	snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
 		 dev_driver_string(card->dev), dev_name(&card->card_dev));
 
-	/* the control interface cannot be accessed from the user space until */
-	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
-	err = snd_ctl_create(card);
+	err = snd_control_new(card);
 	if (err < 0) {
-		dev_err(parent, "unable to register control minors\n");
+		dev_err(parent, "unable to create controls and register\n");
 		goto __error;
 	}
 	err = snd_info_card_create(card);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 33af707a65ab1..7afc9a29ea9ec 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1784,13 +1784,14 @@  void snd_hda_ctls_clear(struct hda_codec *codec)
 int snd_hda_lock_devices(struct hda_bus *bus)
 {
 	struct snd_card *card = bus->card;
+	struct snd_control *ctl = card->ctl;
 	struct hda_codec *codec;
 
 	spin_lock(&card->files_lock);
 	if (card->shutdown)
 		goto err_unlock;
 	card->shutdown = 1;
-	if (!list_empty(&card->ctl_files))
+	if (!list_empty(&ctl->files))
 		goto err_clear;
 
 	list_for_each_codec(codec, bus) {
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index 38116c7587174..8cd5681de9fbd 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -1434,9 +1434,9 @@  static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
 	struct snd_card *card = component->card->snd_card;
 	char *idx;
 
-	down_read(&card->controls_rwsem);
+	down_read(&card->ctl->controls_rwsem);
 
-	list_for_each_entry(kctl, &card->controls, list) {
+	list_for_each_entry(kctl, &card->ctl->controls, list) {
 		idx = strchr(kctl->id.name, ' ');
 		if (idx == NULL)
 			continue;
@@ -1469,12 +1469,12 @@  static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
 		}
 
 		if (ret < 0) {
-			up_read(&card->controls_rwsem);
+			up_read(&card->ctl->controls_rwsem);
 			return ret;
 		}
 	}
 
-	up_read(&card->controls_rwsem);
+	up_read(&card->ctl->controls_rwsem);
 	return 0;
 }
 
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
index 285ab4c9c7168..1129eb33f78c9 100644
--- a/sound/soc/soc-card.c
+++ b/sound/soc/soc-card.c
@@ -35,7 +35,7 @@  struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
 	if (unlikely(!name))
 		return NULL;
 
-	list_for_each_entry(kctl, &card->controls, list)
+	list_for_each_entry(kctl, &card->ctl->controls, list)
 		if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
 			return kctl;
 	return NULL;
diff --git a/sound/usb/media.c b/sound/usb/media.c
index d48db6f3ae659..5298c2f49a164 100644
--- a/sound/usb/media.c
+++ b/sound/usb/media.c
@@ -163,7 +163,7 @@  void snd_media_stop_pipeline(struct snd_usb_substream *subs)
 
 static int snd_media_mixer_init(struct snd_usb_audio *chip)
 {
-	struct device *ctl_dev = chip->card->ctl_dev;
+	struct device *ctl_dev = &chip->card->ctl->dev;
 	struct media_intf_devnode *ctl_intf;
 	struct usb_mixer_interface *mixer;
 	struct media_device *mdev = chip->media_dev;