1<html><head><meta http-equiv="Content-Type" content="text/html; charset=ANSI_X3.4-1968"><title>Chapter 13. Power Management</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="Writing an ALSA Driver"><link rel="up" href="index.html" title="Writing an ALSA Driver"><link rel="prev" href="proc-interface.html" title="Chapter 12. Proc Interface"><link rel="next" href="module-parameters.html" title="Chapter 14. Module Parameters"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 13. Power Management</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="proc-interface.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="module-parameters.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="power-management"></a>Chapter 13. Power Management</h1></div></div></div><p> 2 If the chip is supposed to work with suspend/resume 3 functions, you need to add power-management code to the 4 driver. The additional code for power-management should be 5 <code class="function">ifdef</code>'ed with 6 <code class="constant">CONFIG_PM</code>. 7 </p><p> 8 If the driver <span class="emphasis"><em>fully</em></span> supports suspend/resume 9 that is, the device can be 10 properly resumed to its state when suspend was called, 11 you can set the <code class="constant">SNDRV_PCM_INFO_RESUME</code> flag 12 in the pcm info field. Usually, this is possible when the 13 registers of the chip can be safely saved and restored to 14 RAM. If this is set, the trigger callback is called with 15 <code class="constant">SNDRV_PCM_TRIGGER_RESUME</code> after the resume 16 callback completes. 17 </p><p> 18 Even if the driver doesn't support PM fully but 19 partial suspend/resume is still possible, it's still worthy to 20 implement suspend/resume callbacks. In such a case, applications 21 would reset the status by calling 22 <code class="function">snd_pcm_prepare()</code> and restart the stream 23 appropriately. Hence, you can define suspend/resume callbacks 24 below but don't set <code class="constant">SNDRV_PCM_INFO_RESUME</code> 25 info flag to the PCM. 26 </p><p> 27 Note that the trigger with SUSPEND can always be called when 28 <code class="function">snd_pcm_suspend_all</code> is called, 29 regardless of the <code class="constant">SNDRV_PCM_INFO_RESUME</code> flag. 30 The <code class="constant">RESUME</code> flag affects only the behavior 31 of <code class="function">snd_pcm_resume()</code>. 32 (Thus, in theory, 33 <code class="constant">SNDRV_PCM_TRIGGER_RESUME</code> isn't needed 34 to be handled in the trigger callback when no 35 <code class="constant">SNDRV_PCM_INFO_RESUME</code> flag is set. But, 36 it's better to keep it for compatibility reasons.) 37 </p><p> 38 In the earlier version of ALSA drivers, a common 39 power-management layer was provided, but it has been removed. 40 The driver needs to define the suspend/resume hooks according to 41 the bus the device is connected to. In the case of PCI drivers, the 42 callbacks look like below: 43 44 </p><div class="informalexample"><pre class="programlisting"> 45 46 #ifdef CONFIG_PM 47 static int snd_my_suspend(struct pci_dev *pci, pm_message_t state) 48 { 49 .... /* do things for suspend */ 50 return 0; 51 } 52 static int snd_my_resume(struct pci_dev *pci) 53 { 54 .... /* do things for suspend */ 55 return 0; 56 } 57 #endif 58 59 </pre></div><p> 60 </p><p> 61 The scheme of the real suspend job is as follows. 62 63 </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Retrieve the card and the chip data.</p></li><li class="listitem"><p>Call <code class="function">snd_power_change_state()</code> with 64 <code class="constant">SNDRV_CTL_POWER_D3hot</code> to change the 65 power status.</p></li><li class="listitem"><p>Call <code class="function">snd_pcm_suspend_all()</code> to suspend the running PCM streams.</p></li><li class="listitem"><p>If AC97 codecs are used, call 66 <code class="function">snd_ac97_suspend()</code> for each codec.</p></li><li class="listitem"><p>Save the register values if necessary.</p></li><li class="listitem"><p>Stop the hardware if necessary.</p></li><li class="listitem"><p>Disable the PCI device by calling 67 <code class="function">pci_disable_device()</code>. Then, call 68 <code class="function">pci_save_state()</code> at last.</p></li></ol></div><p> 69 </p><p> 70 A typical code would be like: 71 72 </p><div class="informalexample"><pre class="programlisting"> 73 74 static int mychip_suspend(struct pci_dev *pci, pm_message_t state) 75 { 76 /* (1) */ 77 struct snd_card *card = pci_get_drvdata(pci); 78 struct mychip *chip = card->private_data; 79 /* (2) */ 80 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 81 /* (3) */ 82 snd_pcm_suspend_all(chip->pcm); 83 /* (4) */ 84 snd_ac97_suspend(chip->ac97); 85 /* (5) */ 86 snd_mychip_save_registers(chip); 87 /* (6) */ 88 snd_mychip_stop_hardware(chip); 89 /* (7) */ 90 pci_disable_device(pci); 91 pci_save_state(pci); 92 return 0; 93 } 94 95 </pre></div><p> 96 </p><p> 97 The scheme of the real resume job is as follows. 98 99 </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Retrieve the card and the chip data.</p></li><li class="listitem"><p>Set up PCI. First, call <code class="function">pci_restore_state()</code>. 100 Then enable the pci device again by calling <code class="function">pci_enable_device()</code>. 101 Call <code class="function">pci_set_master()</code> if necessary, too.</p></li><li class="listitem"><p>Re-initialize the chip.</p></li><li class="listitem"><p>Restore the saved registers if necessary.</p></li><li class="listitem"><p>Resume the mixer, e.g. calling 102 <code class="function">snd_ac97_resume()</code>.</p></li><li class="listitem"><p>Restart the hardware (if any).</p></li><li class="listitem"><p>Call <code class="function">snd_power_change_state()</code> with 103 <code class="constant">SNDRV_CTL_POWER_D0</code> to notify the processes.</p></li></ol></div><p> 104 </p><p> 105 A typical code would be like: 106 107 </p><div class="informalexample"><pre class="programlisting"> 108 109 static int mychip_resume(struct pci_dev *pci) 110 { 111 /* (1) */ 112 struct snd_card *card = pci_get_drvdata(pci); 113 struct mychip *chip = card->private_data; 114 /* (2) */ 115 pci_restore_state(pci); 116 pci_enable_device(pci); 117 pci_set_master(pci); 118 /* (3) */ 119 snd_mychip_reinit_chip(chip); 120 /* (4) */ 121 snd_mychip_restore_registers(chip); 122 /* (5) */ 123 snd_ac97_resume(chip->ac97); 124 /* (6) */ 125 snd_mychip_restart_chip(chip); 126 /* (7) */ 127 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 128 return 0; 129 } 130 131 </pre></div><p> 132 </p><p> 133 As shown in the above, it's better to save registers after 134 suspending the PCM operations via 135 <code class="function">snd_pcm_suspend_all()</code> or 136 <code class="function">snd_pcm_suspend()</code>. It means that the PCM 137 streams are already stopped when the register snapshot is 138 taken. But, remember that you don't have to restart the PCM 139 stream in the resume callback. It'll be restarted via 140 trigger call with <code class="constant">SNDRV_PCM_TRIGGER_RESUME</code> 141 when necessary. 142 </p><p> 143 OK, we have all callbacks now. Let's set them up. In the 144 initialization of the card, make sure that you can get the chip 145 data from the card instance, typically via 146 <em class="structfield"><code>private_data</code></em> field, in case you 147 created the chip data individually. 148 149 </p><div class="informalexample"><pre class="programlisting"> 150 151 static int snd_mychip_probe(struct pci_dev *pci, 152 const struct pci_device_id *pci_id) 153 { 154 .... 155 struct snd_card *card; 156 struct mychip *chip; 157 int err; 158 .... 159 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 160 0, &card); 161 .... 162 chip = kzalloc(sizeof(*chip), GFP_KERNEL); 163 .... 164 card->private_data = chip; 165 .... 166 } 167 168 </pre></div><p> 169 170 When you created the chip data with 171 <code class="function">snd_card_new()</code>, it's anyway accessible 172 via <em class="structfield"><code>private_data</code></em> field. 173 174 </p><div class="informalexample"><pre class="programlisting"> 175 176 static int snd_mychip_probe(struct pci_dev *pci, 177 const struct pci_device_id *pci_id) 178 { 179 .... 180 struct snd_card *card; 181 struct mychip *chip; 182 int err; 183 .... 184 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 185 sizeof(struct mychip), &card); 186 .... 187 chip = card->private_data; 188 .... 189 } 190 191 </pre></div><p> 192 193 </p><p> 194 If you need a space to save the registers, allocate the 195 buffer for it here, too, since it would be fatal 196 if you cannot allocate a memory in the suspend phase. 197 The allocated buffer should be released in the corresponding 198 destructor. 199 </p><p> 200 And next, set suspend/resume callbacks to the pci_driver. 201 202 </p><div class="informalexample"><pre class="programlisting"> 203 204 static struct pci_driver driver = { 205 .name = KBUILD_MODNAME, 206 .id_table = snd_my_ids, 207 .probe = snd_my_probe, 208 .remove = snd_my_remove, 209 #ifdef CONFIG_PM 210 .suspend = snd_my_suspend, 211 .resume = snd_my_resume, 212 #endif 213 }; 214 215 </pre></div><p> 216 </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="proc-interface.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="module-parameters.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 12. Proc Interface </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 14. Module Parameters</td></tr></table></div></body></html> 217