Sophie

Sophie

distrib > Fedora > 18 > x86_64 > media > updates > by-pkgid > 1a595394b241504ff370a8d12ebfcea7 > files > 5339

kernel-doc-3.11.10-100.fc18.noarch.rpm

<?xml version="1.0" encoding="ANSI_X3.4-1968" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=ANSI_X3.4-1968" /><title>Chapter&#160;13.&#160;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&#160;12.&#160;Proc Interface" /><link rel="next" href="module-parameters.html" title="Chapter&#160;14.&#160;Module Parameters" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter&#160;13.&#160;Power Management</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="proc-interface.html">Prev</a>&#160;</td><th width="60%" align="center">&#160;</th><td width="20%" align="right">&#160;<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 id="power-management"></a>Chapter&#160;13.&#160;Power Management</h1></div></div></div><p>
      If the chip is supposed to work with suspend/resume
      functions, you need to add power-management code to the
      driver. The additional code for power-management should be
      <code class="function">ifdef</code>'ed with
      <code class="constant">CONFIG_PM</code>. 
    </p><p>
	If the driver <span class="emphasis"><em>fully</em></span> supports suspend/resume
	that is, the device can be
	properly resumed to its state when suspend was called,
	you can set the <code class="constant">SNDRV_PCM_INFO_RESUME</code> flag
	in the pcm info field.  Usually, this is possible when the
	registers of the chip can be safely saved and restored to
	RAM. If this is set, the trigger callback is called with
	<code class="constant">SNDRV_PCM_TRIGGER_RESUME</code> after the resume
	callback completes. 
	</p><p>
	Even if the driver doesn't support PM fully but 
	partial suspend/resume is still possible, it's still worthy to
	implement suspend/resume callbacks. In such a case, applications
	would reset the status by calling
	<code class="function">snd_pcm_prepare()</code> and restart the stream
	appropriately.  Hence, you can define suspend/resume callbacks
	below but don't set <code class="constant">SNDRV_PCM_INFO_RESUME</code>
	info flag to the PCM.
	</p><p>
	Note that the trigger with SUSPEND can always be called when
	<code class="function">snd_pcm_suspend_all</code> is called,
	regardless of the <code class="constant">SNDRV_PCM_INFO_RESUME</code> flag.
	The <code class="constant">RESUME</code> flag affects only the behavior
	of <code class="function">snd_pcm_resume()</code>.
	(Thus, in theory,
	<code class="constant">SNDRV_PCM_TRIGGER_RESUME</code> isn't needed
	to be handled in the trigger callback when no
	<code class="constant">SNDRV_PCM_INFO_RESUME</code> flag is set.  But,
	it's better to keep it for compatibility reasons.)
	</p><p>
      In the earlier version of ALSA drivers, a common
      power-management layer was provided, but it has been removed.
      The driver needs to define the suspend/resume hooks according to
      the bus the device is connected to.  In the case of PCI drivers, the
      callbacks look like below:

      </p><div class="informalexample"><pre class="programlisting">

  #ifdef CONFIG_PM
  static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
  {
          .... /* do things for suspend */
          return 0;
  }
  static int snd_my_resume(struct pci_dev *pci)
  {
          .... /* do things for suspend */
          return 0;
  }
  #endif

        </pre></div><p>
    </p><p>
      The scheme of the real suspend job is as follows.

      </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
	  <code class="constant">SNDRV_CTL_POWER_D3hot</code> to change the
	  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
	<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
	  <code class="function">pci_disable_device()</code>.  Then, call
          <code class="function">pci_save_state()</code> at last.</p></li></ol></div><p>
    </p><p>
      A typical code would be like:

      </p><div class="informalexample"><pre class="programlisting">

  static int mychip_suspend(struct pci_dev *pci, pm_message_t state)
  {
          /* (1) */
          struct snd_card *card = pci_get_drvdata(pci);
          struct mychip *chip = card-&gt;private_data;
          /* (2) */
          snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
          /* (3) */
          snd_pcm_suspend_all(chip-&gt;pcm);
          /* (4) */
          snd_ac97_suspend(chip-&gt;ac97);
          /* (5) */
          snd_mychip_save_registers(chip);
          /* (6) */
          snd_mychip_stop_hardware(chip);
          /* (7) */
          pci_disable_device(pci);
          pci_save_state(pci);
          return 0;
  }

        </pre></div><p>
    </p><p>
    The scheme of the real resume job is as follows.

    </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>.
    	Then enable the pci device again by calling <code class="function">pci_enable_device()</code>.
	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
    <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
	<code class="constant">SNDRV_CTL_POWER_D0</code> to notify the processes.</p></li></ol></div><p>
    </p><p>
    A typical code would be like:

      </p><div class="informalexample"><pre class="programlisting">

  static int mychip_resume(struct pci_dev *pci)
  {
          /* (1) */
          struct snd_card *card = pci_get_drvdata(pci);
          struct mychip *chip = card-&gt;private_data;
          /* (2) */
          pci_restore_state(pci);
          pci_enable_device(pci);
          pci_set_master(pci);
          /* (3) */
          snd_mychip_reinit_chip(chip);
          /* (4) */
          snd_mychip_restore_registers(chip);
          /* (5) */
          snd_ac97_resume(chip-&gt;ac97);
          /* (6) */
          snd_mychip_restart_chip(chip);
          /* (7) */
          snd_power_change_state(card, SNDRV_CTL_POWER_D0);
          return 0;
  }

        </pre></div><p>
    </p><p>
	As shown in the above, it's better to save registers after
	suspending the PCM operations via
	<code class="function">snd_pcm_suspend_all()</code> or
	<code class="function">snd_pcm_suspend()</code>.  It means that the PCM
	streams are already stoppped when the register snapshot is
	taken.  But, remember that you don't have to restart the PCM
	stream in the resume callback. It'll be restarted via 
	trigger call with <code class="constant">SNDRV_PCM_TRIGGER_RESUME</code>
	when necessary.
    </p><p>
      OK, we have all callbacks now. Let's set them up. In the
      initialization of the card, make sure that you can get the chip
      data from the card instance, typically via
      <em class="structfield"><code>private_data</code></em> field, in case you
      created the chip data individually.

      </p><div class="informalexample"><pre class="programlisting">

  static int snd_mychip_probe(struct pci_dev *pci,
                              const struct pci_device_id *pci_id)
  {
          ....
          struct snd_card *card;
          struct mychip *chip;
          int err;
          ....
          err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &amp;card);
          ....
          chip = kzalloc(sizeof(*chip), GFP_KERNEL);
          ....
          card-&gt;private_data = chip;
          ....
  }

        </pre></div><p>

	When you created the chip data with
	<code class="function">snd_card_create()</code>, it's anyway accessible
	via <em class="structfield"><code>private_data</code></em> field.

      </p><div class="informalexample"><pre class="programlisting">

  static int snd_mychip_probe(struct pci_dev *pci,
                              const struct pci_device_id *pci_id)
  {
          ....
          struct snd_card *card;
          struct mychip *chip;
          int err;
          ....
          err = snd_card_create(index[dev], id[dev], THIS_MODULE,
                                sizeof(struct mychip), &amp;card);
          ....
          chip = card-&gt;private_data;
          ....
  }

        </pre></div><p>

    </p><p>
      If you need a space to save the registers, allocate the
	buffer for it here, too, since it would be fatal
    if you cannot allocate a memory in the suspend phase.
    The allocated buffer should be released in the corresponding
    destructor.
    </p><p>
      And next, set suspend/resume callbacks to the pci_driver.

      </p><div class="informalexample"><pre class="programlisting">

  static struct pci_driver driver = {
          .name = KBUILD_MODNAME,
          .id_table = snd_my_ids,
          .probe = snd_my_probe,
          .remove = snd_my_remove,
  #ifdef CONFIG_PM
          .suspend = snd_my_suspend,
          .resume = snd_my_resume,
  #endif
  };

        </pre></div><p>
    </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>&#160;</td><td width="20%" align="center">&#160;</td><td width="40%" align="right">&#160;<a accesskey="n" href="module-parameters.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter&#160;12.&#160;Proc Interface&#160;</td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top">&#160;Chapter&#160;14.&#160;Module Parameters</td></tr></table></div></body></html>