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