aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinod Koul <vinod.koul@intel.com>2016-05-11 23:28:53 -0400
committerMark Brown <broonie@kernel.org>2016-05-13 06:43:00 -0400
commit4446085d21e75dd6c0c45577f12db0bd7c7bf35f (patch)
tree2a96c35b59641cb3cf898333398388b0155af77e
parentbfb7802a06ac1855096a3f248822e8f943e6574d (diff)
ALSA: hdac: add link pm and ref counting
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>
-rw-r--r--include/sound/hdaudio_ext.h13
-rw-r--r--sound/hda/ext/hdac_ext_bus.c3
-rw-r--r--sound/hda/ext/hdac_ext_controller.c66
3 files changed, 82 insertions, 0 deletions
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h
index 07fa59237feb..b9593b201599 100644
--- a/include/sound/hdaudio_ext.h
+++ b/include/sound/hdaudio_ext.h
@@ -14,6 +14,8 @@
14 * @gtscap: gts capabilities pointer 14 * @gtscap: gts capabilities pointer
15 * @drsmcap: dma resume capabilities pointer 15 * @drsmcap: dma resume capabilities pointer
16 * @hlink_list: link list of HDA links 16 * @hlink_list: link list of HDA links
17 * @lock: lock for link mgmt
18 * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
17 */ 19 */
18struct hdac_ext_bus { 20struct hdac_ext_bus {
19 struct hdac_bus bus; 21 struct hdac_bus bus;
@@ -27,6 +29,9 @@ struct hdac_ext_bus {
27 void __iomem *drsmcap; 29 void __iomem *drsmcap;
28 30
29 struct list_head hlink_list; 31 struct list_head hlink_list;
32
33 struct mutex lock;
34 bool cmd_dma_state;
30}; 35};
31 36
32int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, 37int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev,
@@ -142,6 +147,9 @@ struct hdac_ext_link {
142 void __iomem *ml_addr; /* link output stream reg pointer */ 147 void __iomem *ml_addr; /* link output stream reg pointer */
143 u32 lcaps; /* link capablities */ 148 u32 lcaps; /* link capablities */
144 u16 lsdiid; /* link sdi identifier */ 149 u16 lsdiid; /* link sdi identifier */
150
151 int ref_count;
152
145 struct list_head list; 153 struct list_head list;
146}; 154};
147 155
@@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
154void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, 162void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
155 int stream); 163 int stream);
156 164
165int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
166 struct hdac_ext_link *link);
167int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
168 struct hdac_ext_link *link);
169
157/* update register macro */ 170/* update register macro */
158#define snd_hdac_updatel(addr, reg, mask, val) \ 171#define snd_hdac_updatel(addr, reg, mask, val) \
159 writel(((readl(addr + reg) & ~(mask)) | (val)), \ 172 writel(((readl(addr + reg) & ~(mask)) | (val)), \
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 2433f7c81472..3b7ae24900fd 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
105 INIT_LIST_HEAD(&ebus->hlink_list); 105 INIT_LIST_HEAD(&ebus->hlink_list);
106 ebus->idx = idx++; 106 ebus->idx = idx++;
107 107
108 mutex_init(&ebus->lock);
109 ebus->cmd_dma_state = true;
110
108 return 0; 111 return 0;
109} 112}
110EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); 113EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 548cc1e4114b..860f8cad6602 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
186 hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); 186 hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
187 hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); 187 hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
188 188
189 /* since link in On, update the ref */
190 hlink->ref_count = 1;
191
189 list_add_tail(&hlink->list, &ebus->hlink_list); 192 list_add_tail(&hlink->list, &ebus->hlink_list);
190 } 193 }
191 194
@@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
327 return 0; 330 return 0;
328} 331}
329EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); 332EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
333
334int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
335 struct hdac_ext_link *link)
336{
337 int ret = 0;
338
339 mutex_lock(&ebus->lock);
340
341 /*
342 * if we move from 0 to 1, count will be 1 so power up this link
343 * as well, also check the dma status and trigger that
344 */
345 if (++link->ref_count == 1) {
346 if (!ebus->cmd_dma_state) {
347 snd_hdac_bus_init_cmd_io(&ebus->bus);
348 ebus->cmd_dma_state = true;
349 }
350
351 ret = snd_hdac_ext_bus_link_power_up(link);
352 }
353
354 mutex_unlock(&ebus->lock);
355 return ret;
356}
357EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
358
359int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
360 struct hdac_ext_link *link)
361{
362 int ret = 0;
363 struct hdac_ext_link *hlink;
364 bool link_up = false;
365
366 mutex_lock(&ebus->lock);
367
368 /*
369 * if we move from 1 to 0, count will be 0
370 * so power down this link as well
371 */
372 if (--link->ref_count == 0) {
373 ret = snd_hdac_ext_bus_link_power_down(link);
374
375 /*
376 * now check if all links are off, if so turn off
377 * cmd dma as well
378 */
379 list_for_each_entry(hlink, &ebus->hlink_list, list) {
380 if (hlink->ref_count) {
381 link_up = true;
382 break;
383 }
384 }
385
386 if (!link_up) {
387 snd_hdac_bus_stop_cmd_io(&ebus->bus);
388 ebus->cmd_dma_state = false;
389 }
390 }
391
392 mutex_unlock(&ebus->lock);
393 return ret;
394}
395EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);