#ifndef __SOUND_HDA_CODEC_H
 #define __SOUND_HDA_CODEC_H
 
-#include <linux/kref.h>
+#include <linux/refcount.h>
 #include <linux/mod_devicetable.h>
 #include <sound/info.h>
 #include <sound/control.h>
        bool own_chmap;         /* codec driver provides own channel maps */
        /* private: */
        struct hda_codec *codec;
-       struct kref kref;
        struct list_head list;
+       unsigned int disconnected:1;
 };
 
 /* codec information */
 
        /* PCM to create, set by patch_ops.build_pcms callback */
        struct list_head pcm_list_head;
+       refcount_t pcm_ref;
+       wait_queue_head_t remove_sleep;
 
        /* codec specific info */
        void *spec;
 
 static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
 {
-       kref_get(&pcm->kref);
+       refcount_inc(&pcm->codec->pcm_ref);
 }
 void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
 
 
 /*
  * PCM device
  */
-static void release_pcm(struct kref *kref)
-{
-       struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
-
-       if (pcm->pcm)
-               snd_device_free(pcm->codec->card, pcm->pcm);
-       clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
-       kfree(pcm->name);
-       kfree(pcm);
-}
-
 void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
 {
-       kref_put(&pcm->kref, release_pcm);
+       if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+               wake_up(&pcm->codec->remove_sleep);
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
 
                return NULL;
 
        pcm->codec = codec;
-       kref_init(&pcm->kref);
        va_start(args, fmt);
        pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
        va_end(args);
        }
 
        list_add_tail(&pcm->list, &codec->pcm_list_head);
+       refcount_inc(&codec->pcm_ref);
        return pcm;
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
 /*
  * codec destructor
  */
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm;
+
+       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+               if (pcm->disconnected)
+                       continue;
+               if (pcm->pcm)
+                       snd_device_disconnect(codec->card, pcm->pcm);
+               snd_hda_codec_pcm_put(pcm);
+               pcm->disconnected = 1;
+       }
+}
+
 static void codec_release_pcms(struct hda_codec *codec)
 {
        struct hda_pcm *pcm, *n;
 
        list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
-               list_del_init(&pcm->list);
+               list_del(&pcm->list);
                if (pcm->pcm)
-                       snd_device_disconnect(codec->card, pcm->pcm);
-               snd_hda_codec_pcm_put(pcm);
+                       snd_device_free(pcm->codec->card, pcm->pcm);
+               clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+               kfree(pcm->name);
+               kfree(pcm);
        }
 }
 
                codec->registered = 0;
        }
 
+       snd_hda_codec_disconnect_pcms(codec);
        cancel_delayed_work_sync(&codec->jackpoll_work);
        if (!codec->in_freeing)
                snd_hda_ctls_clear(codec);
        remove_conn_list(codec);
        snd_hdac_regmap_exit(&codec->core);
        codec->configured = 0;
+       refcount_set(&codec->pcm_ref, 1); /* reset refcount */
 }
 EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
 
        snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
        INIT_LIST_HEAD(&codec->conn_list);
        INIT_LIST_HEAD(&codec->pcm_list_head);
+       refcount_set(&codec->pcm_ref, 1);
+       init_waitqueue_head(&codec->remove_sleep);
 
        INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
        codec->depop_delay = -1;