Update of /cvsroot/alsa/alsa-kernel/Documentation/DocBook
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19034/Documentation/DocBook
Modified Files:
writing-an-alsa-driver.tmpl
Log Message:
Clean up of power-management codes.
- moved commonly used codes to the core layer.
- using the unified suspend/resume callbacks for PCI and ISA
- added snd_card_set_pm_callbacks() and snd_card_set_isa_pm_callbacks()
as the registration functions.
Index: writing-an-alsa-driver.tmpl
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/Documentation/DocBook/writing-an-alsa-driver.tmpl,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -r1.25 -r1.26
--- writing-an-alsa-driver.tmpl 7 Apr 2004 17:48:10 -0000 1.25
+++ writing-an-alsa-driver.tmpl 8 Apr 2004 16:34:59 -0000 1.26
@@ -511,7 +511,7 @@
}
// (7)
- pci_set_drvdata(pci, chip);
+ pci_set_drvdata(pci, card);
dev++;
return 0;
}
@@ -519,10 +519,7 @@
// destructor -- see "Destructor" sub-section
static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(pci), return);
- if (chip)
- snd_card_free(chip->card);
+ snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
]]>
@@ -691,21 +688,16 @@
<informalexample>
<programlisting>
<![CDATA[
- pci_set_drvdata(pci, chip);
+ pci_set_drvdata(pci, card);
dev++;
return 0;
]]>
</programlisting>
</informalexample>
- In the above, the chip record is stored. This pointer is
+ In the above, the card record is stored. This pointer is
referred in the remove callback and power-management
callbacks, too.
- If the card doesn't support the suspend/resume, you can store
- the card pointer instead of the chip pointer, so that
- <function>snd_card_free</function> can be called directly
- without cast in the remove callback. But anyway, be sure
- which pointer is used.
</para>
</section>
</section>
@@ -726,21 +718,15 @@
<![CDATA[
static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(pci), return);
- if (chip)
- snd_card_free(chip->card);
+ snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
]]>
</programlisting>
</informalexample>
- The above code assumes that the chip is allocated
- with snd_magic stuff and
- has the field to hold the card pointer (see <link
- linkend="card-management"><citetitle>the next
- section</citetitle></link>).
+ The above code assumes that the card pointer is set to the PCI
+ driver data.
</para>
</section>
@@ -5254,47 +5240,23 @@
</para>
<para>
- Basic jobs of suspend/resume are done in
- <structfield>suspend</structfield> and
- <structfield>resume</structfield> callbacks of
- <structname>pci_driver</structname> struct. Unfortunately, the
- API of these callbacks was changed at the middle time of Linux
- 2.4.x, if you want to keep the support for older kernels, you
- have to write two different callbacks. The example below is the
- skeleton callbacks which just call the real suspend and resume
- functions.
+ ALSA provides the common power-management layer. Each card driver
+ needs to have only low-level suspend and resume callbacks.
<informalexample>
<programlisting>
<![CDATA[
- #ifndef PCI_OLD_SUSPEND
- static int snd_my_suspend(struct pci_dev *dev, u32 state)
+ #ifdef CONFIG_PM
+ static int snd_my_suspend(snd_card_t *card, unsigned int state)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return -ENXIO);
- mychip_suspend(chip);
+ .... // do things for suspsend
return 0;
}
- static int snd_my_resume(struct pci_dev *dev)
+ static int snd_my_resume(snd_card_t *card, unsigned int state)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return -ENXIO);
- mychip_resume(chip);
+ .... // do things for suspsend
return 0;
}
- #else
- static void snd_my_suspend(struct pci_dev *dev)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return);
- mychip_suspend(chip);
- }
- static void snd_mychip_resume(struct pci_dev *dev)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return);
- mychip_resume(chip);
- }
#endif
]]>
</programlisting>
@@ -5302,17 +5264,10 @@
</para>
<para>
- For keeping the readability of 2.6 source code, it's recommended to
- separate the above ifdef condition as the patch file in alsa-driver
- directory.
- See <filename>alsa-driver/pci/ali5451.c</filename> for example.
- </para>
-
- <para>
The scheme of the real suspend job is as following.
<orderedlist>
- <listitem><para>Check whether the power-state is already D3hot. If yes, skip
the job.</para></listitem>
+ <listitem><para>Retrieve the chip data from pm_private_data
field.</para></listitem>
<listitem><para>Call <function>snd_pcm_suspend_all()</function> to suspend
the running PCM streams.</para></listitem>
<listitem><para>Save the register values if necessary.</para></listitem>
<listitem><para>Stop the hardware if necessary.</para></listitem>
@@ -5326,12 +5281,11 @@
<informalexample>
<programlisting>
<![CDATA[
- static void mychip_suspend(mychip_t *chip)
+ static int mychip_suspend(snd_card_t *card, unsigned int state)
{
- snd_card_t *card = chip->card;
// (1)
- if (card->power_state == SNDRV_CTL_POWER_D3hot)
- return;
+ mychip_t *chip = snd_magic_cast(mychip_t, card->pm_private_data,
+ return -ENXIO);
// (2)
snd_pcm_suspend_all(chip->pcm);
// (3)
@@ -5340,6 +5294,7 @@
snd_mychip_stop_hardware(chip);
// (5)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ return 0;
}
]]>
</programlisting>
@@ -5350,8 +5305,7 @@
The scheme of the real resume job is as following.
<orderedlist>
- <listitem><para>Check whether the power-state is already D0.
- If yes, skip the job.</para></listitem>
+ <listitem><para>Retrieve the chip data from pm_private_data
field.</para></listitem>
<listitem><para>Enable the pci device again by calling
<function>pci_enable_device()</function>.</para></listitem>
<listitem><para>Re-initialize the chip.</para></listitem>
@@ -5372,10 +5326,9 @@
<![CDATA[
static void mychip_resume(mychip_t *chip)
{
- snd_card_t *card = chip->card;
// (1)
- if (card->power_state == SNDRV_CTL_POWER_D0)
- return;
+ mychip_t *chip = snd_magic_cast(mychip_t, card->pm_private_data,
+ return -ENXIO);
// (2)
pci_enable_device(chip->pci);
// (3)
@@ -5388,38 +5341,6 @@
snd_mychip_restart_chip(chip);
// (7)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- In addition to the callbacks above, you should define a callback
- for the changes via the ALSA control interface. It's defined
- like below:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_set_power_state(snd_card_t *card,
- unsigned int power_state)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- card->power_state_private_data, return -ENXIO);
- switch (power_state) {
- case SNDRV_CTL_POWER_D0:
- case SNDRV_CTL_POWER_D1:
- case SNDRV_CTL_POWER_D2:
- mychip_resume(chip);
- break;
- case SNDRV_CTL_POWER_D3hot:
- case SNDRV_CTL_POWER_D3cold:
- mychip_suspend(chip);
- break;
- default:
- return -EINVAL;
- }
return 0;
}
]]>
@@ -5439,41 +5360,42 @@
{
....
snd_card_t *card;
+ mychip_t *chip;
....
- #ifdef CONFIG_PM
- card->set_power_state = snd_mychip_set_power_state;
- card->power_state_private_data = chip;
- #endif
+ snd_card_set_pm_callback(card, snd_my_suspend, snd_my_resume, chip);
....
}
]]>
</programlisting>
</informalexample>
+
+ Here you don't have to put ifdef CONFIG_PM around, since it's already
+ checked in the header and expanded to empty if not needed.
</para>
<para>
If you need a space for saving the registers, you'll need to
- allocate the buffer for it here, too, since you cannot call
- <function>kmalloc()</function> with
- <constant>GFP_KERNEL</constant> flag or
- <function>vmalloc()</function> in the suspend callback.
+ 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.
</para>
<para>
And next, set suspend/resume callbacks to the pci_driver,
+ This can be done by passing a macro SND_PCI_PM_CALLBACKS
+ in the pci_driver struct. This macro is expanded to the correct
+ (global) callbacks if CONFIG_PM is set.
<informalexample>
<programlisting>
<![CDATA[
static struct pci_driver driver = {
.name = "My Chip",
- ....
- #ifdef CONFIG_PM
- .suspend = snd_mychip_suspend,
- .resume = snd_mychip_resume,
- #endif
+ .id_table = snd_my_ids,
+ .probe = snd_my_probe,
+ .remove = __devexit_p(snd_my_remove),
+ SND_PCI_PM_CALLBACKS
};
]]>
</programlisting>
-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
_______________________________________________
Alsa-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-cvslog