ALSA: hdac: add link pm and ref counting
authorVinod Koul <vinod.koul@intel.com>
Thu, 12 May 2016 03:28:53 +0000 (08:58 +0530)
committerMark Brown <broonie@kernel.org>
Fri, 13 May 2016 10:43:00 +0000 (11:43 +0100)
The HDA links can be switched off when not is use, similarly
command DMA can be stopped as well. This calls for a reference
counting mechanism on the link by it's users to manage the link
power. The DMA can be turned off when all links are off

For this we add two APIs
snd_hdac_ext_bus_link_get
snd_hdac_ext_bus_link_put

They help users to turn up/down link and manage the DMA as well

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/hdaudio_ext.h
sound/hda/ext/hdac_ext_bus.c
sound/hda/ext/hdac_ext_controller.c

index 07fa59237feb032e3e6dcb6660cca6d17b5679e4..b9593b201599f94f49b35a36c82a6f927d97677b 100644 (file)
@@ -14,6 +14,8 @@
  * @gtscap: gts capabilities pointer
  * @drsmcap: dma resume capabilities pointer
  * @hlink_list: link list of HDA links
+ * @lock: lock for link mgmt
+ * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
  */
 struct hdac_ext_bus {
        struct hdac_bus bus;
@@ -27,6 +29,9 @@ struct hdac_ext_bus {
        void __iomem *drsmcap;
 
        struct list_head hlink_list;
+
+       struct mutex lock;
+       bool cmd_dma_state;
 };
 
 int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev,
@@ -142,6 +147,9 @@ struct hdac_ext_link {
        void __iomem *ml_addr; /* link output stream reg pointer */
        u32 lcaps;   /* link capablities */
        u16 lsdiid;  /* link sdi identifier */
+
+       int ref_count;
+
        struct list_head list;
 };
 
@@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
 void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
                                 int stream);
 
+int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link);
+int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link);
+
 /* update register macro */
 #define snd_hdac_updatel(addr, reg, mask, val)         \
        writel(((readl(addr + reg) & ~(mask)) | (val)), \
index 2433f7c81472848be51b9af420ec198b523871b1..3b7ae24900fda110fe1465bfe762b4d7974def15 100644 (file)
@@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
        INIT_LIST_HEAD(&ebus->hlink_list);
        ebus->idx = idx++;
 
+       mutex_init(&ebus->lock);
+       ebus->cmd_dma_state = true;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
index 548cc1e4114bb81af0136cf77d517b4e751c6736..860f8cad6602d59332f1990b2e3ee216da0b72f6 100644 (file)
@@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
                hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
                hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
 
+               /* since link in On, update the ref */
+               hlink->ref_count = 1;
+
                list_add_tail(&hlink->list, &ebus->hlink_list);
        }
 
@@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
+
+int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link)
+{
+       int ret = 0;
+
+       mutex_lock(&ebus->lock);
+
+       /*
+        * if we move from 0 to 1, count will be 1 so power up this link
+        * as well, also check the dma status and trigger that
+        */
+       if (++link->ref_count == 1) {
+               if (!ebus->cmd_dma_state) {
+                       snd_hdac_bus_init_cmd_io(&ebus->bus);
+                       ebus->cmd_dma_state = true;
+               }
+
+               ret = snd_hdac_ext_bus_link_power_up(link);
+       }
+
+       mutex_unlock(&ebus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
+
+int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link)
+{
+       int ret = 0;
+       struct hdac_ext_link *hlink;
+       bool link_up = false;
+
+       mutex_lock(&ebus->lock);
+
+       /*
+        * if we move from 1 to 0, count will be 0
+        * so power down this link as well
+        */
+       if (--link->ref_count == 0) {
+               ret = snd_hdac_ext_bus_link_power_down(link);
+
+               /*
+                * now check if all links are off, if so turn off
+                * cmd dma as well
+                */
+               list_for_each_entry(hlink, &ebus->hlink_list, list) {
+                       if (hlink->ref_count) {
+                               link_up = true;
+                               break;
+                       }
+               }
+
+               if (!link_up) {
+                       snd_hdac_bus_stop_cmd_io(&ebus->bus);
+                       ebus->cmd_dma_state = false;
+               }
+       }
+
+       mutex_unlock(&ebus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);