diff options
| author | Jaroslav Kysela <perex@suse.cz> | 2003-01-27 20:31:11 +0100 |
|---|---|---|
| committer | Jaroslav Kysela <perex@suse.cz> | 2003-01-27 20:31:11 +0100 |
| commit | b8038484bf9c60e6e8669d098bb705deda099445 (patch) | |
| tree | e9cb19e14bffab7ab8b84ad1341be1c91c841bec | |
| parent | 191c011abbb58966ab1ec30421e082f9802f2c8d (diff) | |
ALSA update
- added DocBook documentation
- added many source comments
- simplified proc style interface (per card)
- updated PCM scatter-gather routines
- moved PM locking outside callbacks
75 files changed, 7972 insertions, 1474 deletions
diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl new file mode 100644 index 000000000000..6421246ddac0 --- /dev/null +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -0,0 +1,99 @@ +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<book> +<?dbhtml filename="index.html"> + +<!-- ****************************************************** --> +<!-- Header --> +<!-- ****************************************************** --> + <bookinfo> + <title>The ALSA Driver API</title> + + <legalnotice> + <para> + This document is free; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + </para> + + <para> + This document is distributed in the hope that it will be useful, + but <emphasis>WITHOUT ANY WARRANTY</emphasis>; without even the + implied warranty of <emphasis>MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE</emphasis>. See the GNU General Public License + for more details. + </para> + + <para> + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + </para> + </legalnotice> + + </bookinfo> + + <chapter><title>Management of Cards and Devices</title> + <sect1><title>Card Managment</title> +!Esound/core/init.c + </sect1> + <sect1><title>Device Components</title> +!Esound/core/device.c + </sect1> + <sect1><title>KMOD and Device File Entries</title> +!Esound/core/sound.c + </sect1> + <sect1><title>Memory Management Helpers</title> +!Esound/core/memory.c +!Iinclude/sound/sndmagic.h + </sect1> + </chapter> + <chapter><title>PCM API</title> + <sect1><title>PCM Core</title> +!Esound/core/pcm.c +!Esound/core/pcm_lib.c +!Esound/core/pcm_native.c + </sect1> + <sect1><title>PCM Format Helpers</title> +!Esound/core/pcm_misc.c + </sect1> + <sect1><title>PCM Memory Managment</title> +!Esound/core/pcm_memory.c + </sect1> + <sect1><title>SG-Buffer Helpers</title> +!Esound/core/pcm_sgbuf.c + </sect1> + </chapter> + <chapter><title>Control/Mixer API</title> + <sect1><title>General Control Interface</title> +!Esound/core/control.c + </sect1> + <sect1><title>AC97 Codec API</title> +!Esound/pci/ac97/ac97_codec.c + </sect1> + </chapter> + <chapter><title>MIDI API</title> + <sect1><title>Raw MIDI API</title> +!Esound/core/rawmidi.c + </sect1> + <sect1><title>MPU401-UART API</title> +!Esound/drivers/mpu401/mpu401_uart.c + </sect1> + </chapter> + <chapter><title>Proc Info API</title> + <sect1><title>Proc Info Interface</title> +!Esound/core/info.c + </sect1> + </chapter> + <chapter><title>Miscellaneous Functions</title> + <sect1><title>Hardware-Dependent Devices API</title> +!Esound/core/hwdep.c + </sect1> + <sect1><title>ISA DMA Helpers</title> +!Esound/core/isadma.c + </sect1> + </chapter> + +</book> diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl new file mode 100644 index 000000000000..8cf02dfa80d2 --- /dev/null +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -0,0 +1,5262 @@ +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<book> +<?dbhtml filename="index.html"> + +<!-- ****************************************************** --> +<!-- Header --> +<!-- ****************************************************** --> + <bookinfo> + <title>Writing an ALSA Driver</title> + <author> + <firstname>Takashi</firstname> + <surname>Iwai</surname> + <affiliation> + <address> + <email>tiwai@suse.de</email> + </address> + </affiliation> + </author> + + <date>Dec. 27, 2002</date> + <edition>0.2 (reborn at Christmas)</edition> + + <abstract> + <para> + This document describes how to write an ALSA (Advanced Linux + Sound Architecture) driver. + </para> + </abstract> + + <legalnotice> + <para> + Copyright (c) 2002 Takashi Iwai <email>tiwai@suse.de</email> + </para> + + <para> + This document is free; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + </para> + + <para> + This document is distributed in the hope that it will be useful, + but <emphasis>WITHOUT ANY WARRANTY</emphasis>; without even the + implied warranty of <emphasis>MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE</emphasis>. See the GNU General Public License + for more details. + </para> + + <para> + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + </para> + </legalnotice> + + </bookinfo> + +<!-- ****************************************************** --> +<!-- Preface --> +<!-- ****************************************************** --> + <preface id="preface"> + <title>Preface</title> + <para> + This document describes how to write an + <ulink url="http://www.alsa-project.org/"><citetitle> + ALSA (Advanced Linux Sound Architecture)</citetitle></ulink> + driver. The document focuses mainly on the PCI soundcard. + In the case of other device types, the API might + be different, too. However, at least the ALSA kernel API is + consistent, and therefore it would be still a bit help for + writing them. + </para> + + <para> + The target of this document is ones who already have enough + skill of C language and have the basic knowledge of linux + kernel programming. This document doesn't explain the general + topics of linux kernel codes and doesn't cover the detail of + implementation of each low-level driver. It describes only how is + the standard way to write a PCI sound driver on ALSA. + </para> + + <para> + If you are already familiar with the older ALSA ver.0.5.x, you + can check the drivers such as <filename>es1938.c</filename> or + <filename>maestro3.c</filename> which have also almost the same + code-base in the ALSA 0.5.x tree, so you can compare the differences. + </para> + + <para> + This document is still a draft version. Any feedbacks and + corrections, please!! + </para> + </preface> + + +<!-- ****************************************************** --> +<!-- File Tree Structure --> +<!-- ****************************************************** --> + <chapter id="file-tree"> + <title>File Tree Structure</title> + + <section id="file-tree-general"> + <title>General</title> + <para> + The ALSA drivers are provided in the two ways. + </para> + + <para> + One is the the trees provided as a tarball or via cvs from the + ALSA's ftp site, and another is the 2.5 (or later) Linux kernel + tree. To synchronize both, the ALSA driver tree is split to + two different trees: alsa-kernel and alsa-driver. The former + contains purely the source codes for the Linux 2.5 (or later) + tree. This tree is designed only for compilation on 2.5 or + later environment. The latter, alsa-driver, contains many subtle + files for compiling the ALSA driver on the outside of Linux + kernel like configure script, the wrapper functions for older, + 2.2 and 2.4 kernels, to adapt the latest kernel API, + and additional drivers which are still in development or in + tests. The drivers in alsa-driver tree will be moved to + alsa-kernel (eventually 2.5 kernel tree) once when they are + finished and confirmed to work fine. + </para> + + <para> + The file tree structure of ALSA driver is depicted below. Both + alsa-kernel and alsa-driver have almost the same file + structure, except for <quote>core</quote> directory. It's + named as <quote>acore</quote> in alsa-driver tree. + + <example> + <title>ALSA File Tree Structure</title> + <literallayout> + sound + /core + /oss + /seq + /oss + /instr + /ioctl32 + /include + /drivers + /mpu401 + /opl3 + /i2c + /l3 + /synth + /emux + /pci + /(cards) + /isa + /(cards) + /arm + /ppc + /sparc + /usb + /pcmcia /(cards) + /oss + </literallayout> + </example> + </para> + </section> + + <section id="file-tree-core-directory"> + <title>core directory</title> + <para> + This directory contains the middle layer, that is, the heart + of ALSA drivers. In this directory, the native ALSA modules are + stored. The sub-directories contain different modules and are + dependent upon the kernel config. + </para> + + <section id="file-tree-core-directory-oss"> + <title>core/oss</title> + + <para> + The codes for PCM and mixer OSS emulation modules are stored + in this directory. The rawmidi OSS emulation is included in + the ALSA rawmidi code since it's quite small. The sequencer + code is stored in core/seq/oss directory (see + <link linkend="file-tree-core-directory-seq-oss"><citetitle> + below</citetitle></link>). + </para> + </section> + + <section id="file-tree-core-directory-ioctl32"> + <title>core/ioctl32</title> + + <para> + This directory contains the 32bit-ioctl wrappers for 64bit + architectures such like x86-64, ppc64 and sparc64. For 32bit + and alpha architectures, these are not compiled. + </para> + </section> + + <section id="file-tree-core-directory-seq"> + <title>core/seq</title> + <para> + This and its sub-directories are for the ALSA + sequencer. This directory contains the sequencer core and + primary sequencer modules such like snd-seq-midi, + snd-seq-virmidi, etc. They are compiled only when + <constant>CONFIG_SND_SEQUENCER</constant> is set in the kernel + config. + </para> + </section> + + <section id="file-tree-core-directory-seq-oss"> + <title>core/seq/oss</title> + <para> + This contains the OSS sequencer emulation codes. + </para> + </section> + + <section id="file-tree-core-directory-deq-instr"> + <title>core/seq/instr</title> + <para> + This directory contains the modules for the sequencer + instrument layer. + </para> + </section> + </section> + + <section id="file-tree-include-directory"> + <title>include directory</title> + <para> + This is the place for the public header files of ALSA drivers, + which are to be exported to the user-space, or included by + several files at different directories. Basically, the private + header files should not be placed in this directory, but you may + still find files there, due to historical reason :) + </para> + </section> + + <section id="file-tree-drivers-directory"> + <title>drivers directory</title> + <para> + This directory contains the non-architecture-specific + codes. For example, the dummy pcm driver and the serial MIDI + driver are found in this directory. In the sub-directories, + there are the codes for components which are independent from + bus and cpu architectures. + </para> + + <section id="file-tree-drivers-directory-mpu401"> + <title>drivers/mpu401</title> + <para> + The MPU401 and MPU401-UART modules are stored here. + </para> + </section> + + <section id="file-tree-drivers-directory-opl3"> + <title>drivers/opl3</title> + <para> + The OPL3 FM-synth stuff is found here. + </para> + </section> + </section> + + <section id="file-tree-i2c-directory"> + <title>i2c directory</title> + <para> + This contains the ALSA i2c components. + </para> + + <para> + Although there is a standard i2c layer on Linux, ALSA uses its + own i2c codes for some cards, because the soundcard needs only a + simple operation and the standard API is too complicated for + such a purpose. + </para> + + <section id="file-tree-i2c-directory-l3"> + <title>i2c/l3</title> + <para> + This is a sub-directory for ARM L3 i2c. + </para> + </section> + </section> + + <section id="file-tree-synth-directory"> + <title>synth directory</title> + <para> + This contains the synth middle-level modules. + </para> + + <para> + So far, there is only Emu8000/Emu10k1 synth driver under + synth/emux sub-directory. + </para> + </section> + + <section id="file-tree-pci-directory"> + <title>pci directory</title> + <para> + This and its sub-directories hold the top-level card modules + for PCI soundcards. + </para> + + <para> + The drivers compiled from a single file is stored directly on + pci directory, while the drivers with several source files are + stored on its own sub-directory (e.g. emu10k1, ice1712). + </para> + </section> + + <section id="file-tree-isa-directory"> + <title>isa directory</title> + <para> + This and its sub-directories hold the top-level card modules + for ISA soundcards. + </para> + </section> + + <section id="file-tree-arm-ppc-sparc-directories"> + <title>arm, ppc, and sparc directories</title> + <para> + These are for the top-level card modules which are + architecture specific. + </para> + </section> + + <section id="file-tree-usb-directory"> + <title>usb directory</title> + <para> + This contains the USB-audio driver. On the latest version, the + USB MIDI driver is integrated together with usb-audio driver. + </para> + </section> + + <section id="file-tree-pcmcia-directory"> + <title>pcmcia directory</title> + <para> + The PCMCIA, especially PCCard drivers will go here. CardBus + drivers will be on pci directory, because its API is identical + with the standard PCI cards. + </para> + + <para> + At this moment, only VX-pocket driver exists. + </para> + </section> + + <section id="file-tree-oss-directory"> + <title>oss directory</title> + <para> + The OSS/Lite source files are stored here on Linux 2.5 (or + later) tree. (In the ALSA driver tarball, it's empty, of course :) + </para> + </section> + </chapter> + + +<!-- ****************************************************** --> +<!-- Basic Flow for PCI Drivers --> +<!-- ****************************************************** --> + <chapter id="basic-flow"> + <title>Basic Flow for PCI Drivers</title> + + <section id="basic-flow-outline"> + <title>Outline</title> + <para> + The minimum flow of PCI soundcard is like the following: + + <itemizedlist> + <listitem><para>define the PCI ID table (see the section + <link linkend="pci-resource-entries"><citetitle>PCI Entries + </citetitle></link>).</para></listitem> + <listitem><para>create <function>probe()</function> callback.</para></listitem> + <listitem><para>create <function>remove()</function> callback.</para></listitem> + <listitem><para>create pci_driver table which contains the three pointers above.</para></listitem> + <listitem><para>create <function>init()</function> function just calling <function>pci_module_init()</function> to register the pci_driver table defined above.</para></listitem> + <listitem><para>create <function>exit()</function> function to call <function>pci_unregister_driver()</function> function.</para></listitem> + </itemizedlist> + </para> + </section> + + <section id="basic-flow-example"> + <title>Full Code Example</title> + <para> + The code example is shown below. Some parts are kept + unimplemented at this moment but will be filled in the + succeeding sections. The numbers in comment lines of + <function>snd_mychip_probe()</function> function are the + markers. + + <example> + <title>Basic Flow for PCI Drivers Example</title> + <programlisting> +<![CDATA[ + #include <sound/driver.h> + #include <linux/init.h> + #include <linux/pci.h> + #include <linux/slab.h> + #include <sound/core.h> + #define SNDRV_GET_ID + #include <sound/initval.h> + + // module parameters (see "Module Parameters") + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; + static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + + // definition of the chip-specific record + typedef struct snd_mychip mychip_t; + struct snd_mychip { + snd_card_t *card; + // rest of implementation will be in the section + // "PCI Resource Managements" + }; + + // this should be go into <sound/sndmagic.h> + // (see "Management of Cards and Components") + #define mychip_t_magic 0xa15a4501 + + // chip-specific destructor + // (see "PCI Resource Managements") + static int snd_mychip_free(mychip_t *chip) + { + // will be implemented later... + } + + // component-destructor + // (see "Management of Cards and Components") + static int snd_mychip_dev_free(snd_device_t *device) + { + mychip_t *chip = snd_magic_cast(mychip_t, + device->device_data, return -ENXIO); + return snd_mychip_free(chip); + } + + // chip-specific constructor + // (see "Management of Cards and Components") + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_device *pci, + mychip_t *rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability here + // (see "PCI Resource Managements") + + // allocate a chip-specific data with magic-alloc + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + + // rest of initialization here; will be implemented + // later, see "PCI Resource Managements" + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // constructor -- see "Constructor" sub-section + static int __devinit snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; + snd_card_t *card; + mychip_t *chip; + int err; + + // (1) + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + // (2) + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + // (3) + if ((err = snd_mychip_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + // (4) + // implemented later + + // (5) + strcpy(card->driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); + + // (6) + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + // (7) + pci_set_drvdata(pci, chip); + dev++; + return 0; + } + + // 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); + pci_set_drvdata(pci, NULL); + } +]]> + </programlisting> + </example> + </para> + </section> + + <section id="basic-flow-constructor"> + <title>Constructor</title> + <para> + The real constructor of PCI drivers is probe callback. The + probe callback and other component-constructors which are called + from probe callback should be defined with + <parameter>__devinit</parameter> prefix. You + cannot use <parameter>__init</parameter> prefix for them, + because any PCI device could be a hotplug device. + </para> + + <para> + In the probe callback, the following scheme is often used. + </para> + + <section id="basic-flow-constructor-device-index"> + <title>1) Check and increment the device index.</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int dev; + .... + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } +]]> + </programlisting> + </informalexample> + + where enable[dev] is the module option. + </para> + + <para> + At each time probe callback is called, check the + availability of the device. If not available, simply increment + the device index and returns. dev will be incremented also + later (<link + linkend="basic-flow-constructor-set-pci"><citetitle>step + 7</citetitle></link>). + </para> + </section> + + <section id="basic-flow-constructor-create-card"> + <title>2) Create a card instance</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + snd_card_t *card; + .... + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The detail will be explained in the section + <link linkend="card-management-card-instance"><citetitle> + Management of Cards and Components</citetitle></link>. + </para> + </section> + + <section id="basic-flow-constructor-create-main"> + <title>3) Create a main component</title> + <para> + In this part, the PCI resources are allocated. + + <informalexample> + <programlisting> +<![CDATA[ + mychip_t *chip; + .... + if ((err = snd_mychip_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } +]]> + </programlisting> + </informalexample> + + The detail will be explained in the section <link + linkend="pci-resource"><citetitle>PCI Resource + Managements</citetitle></link>. + </para> + </section> + + <section id="basic-flow-constructor-create-other"> + <title>4) Create other components, such as mixer, MIDI, etc.</title> + <para> + Here you define the basic components such as + <link linkend="pcm-interface"><citetitle>PCM</citetitle></link>, + mixer (e.g. <link linkend="api-ac97"><citetitle>AC97</citetitle></link>), + MIDI (e.g. <link linkend="midi-interface"><citetitle>MPU-401</citetitle></link>), + and other interfaces. + Also, if you want a <link linkend="proc-interface"><citetitle>proc + file</citetitle></link>, define it here, too. + </para> + </section> + + <section id="basic-flow-constructor-main-component"> + <title>5) Set the driver ID and name strings.</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + strcpy(card->driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); +]]> + </programlisting> + </informalexample> + + The driver field holds the minimal ID string of the + chip. This is referred by alsa-lib's configurator, so keep it + simple but unique. + Even the same driver can have different driver IDs to + distinguish the functionality of each chip type. + </para> + + <para> + The shortname field is a string shown as more verbose + name. The longname field contains the information which is + shown in <filename>/proc/asound/cards</filename>. + </para> + </section> + + <section id="basic-flow-constructor-register-card"> + <title>6) Register the card instance.</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + Will be explained in the section <link + linkend="card-management-registration"><citetitle>Management + of Cards and Components</citetitle></link>, too. + </para> + </section> + + <section id="basic-flow-constructor-set-pci"> + <title>7) Set the PCI driver data and return zero.</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + pci_set_drvdata(pci, chip); + dev++; + return 0; +]]> + </programlisting> + </informalexample> + + In the above, the chip 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> + + <section id="basic-flow-destructor"> + <title>Destructor</title> + <para> + The destructor, remove callback, simply releases the card + instance. Then the ALSA middle layer will release all the + attached components automatically. + </para> + + <para> + It would be typically like the following: + + <informalexample> + <programlisting> +<![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); + 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>). + </para> + </section> + + <section id="basic-flow-header-files"> + <title>Header Files</title> + <para> + For the above example, at least the following include files + are necessary. + + <informalexample> + <programlisting> +<![CDATA[ + #include <sound/driver.h> + #include <linux/init.h> + #include <linux/pci.h> + #include <linux/slab.h> + #include <sound/core.h> + #define SNDRV_GET_ID + #include <sound/initval.h> +]]> + </programlisting> + </informalexample> + + where the last twos are necessary only when module options are + defined in the source file. If the codes are split to several + files, the file without module options don't need them. + </para> + + <para> + In addition to them, you'll need + <filename><linux/interrupt.h></filename> for the interrupt + handling, and <filename><asm/io.h></filename> for the i/o + access. If you use <function>mdelay()</function> or + <function>udelay()</function> functions, you'll need to include + <filename><linux/delay.h></filename>, too. + </para> + + <para> + The ALSA interfaces like PCM or control API are define in other + header files as <filename><sound/xxx.h></filename>. + They have to be included after + <filename><sound/core.h></filename>. + </para> + + </section> + </chapter> + + +<!-- ****************************************************** --> +<!-- Management of Cards and Components --> +<!-- ****************************************************** --> + <chapter id="card-management"> + <title>Management of Cards and Components</title> + + <section id="card-management-card-instance"> + <title>Card Instance</title> + <para> + For each soundcard, a <quote>card</quote> record must be allocated. + </para> + + <para> + A card record is the headquarters of the soundcard. It manages + the list of whole devices (components) on the soundcard, such as + PCM, mixers, MIDI, synthesizer, and so on. Also, the card + record holds the ID and the name strings of the card, manages + the root of proc files, and controls the power-management states + and hotplug disconnections. The component list on the card + record is used to manage the proper releases of resources at + destruction. + </para> + + <para> + As mentioned above, to create a card instance, call + <function>snd_card_new()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_card_t *card; + card = snd_card_new(index, id, module, extra_size); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The function takes four arguments, the card-index number, the + id string, the module pointer (usually + <constant>THIS_MODULE</constant>), + and the size of extra-data space. The last argument is used to + allocate card->private_data for the + chip-specific data. Note that this data + <emphasis>is</emphasis> allocated by + <function>snd_card_new()</function>. + </para> + </section> + + <section id="card-management-component"> + <title>Components</title> + <para> + After the card is created, you can attach the components + (devices) to the card instance. On ALSA driver, a component is + represented as a <type>snd_device_t</type> object. + A component can be a PCM instance, a control interface, a raw + MIDI interface, etc. Each of such instances has one component + entry. + </para> + + <para> + A component can be created via + <function>snd_device_new()</function> function. + + <informalexample> + <programlisting> +<![CDATA[ + snd_device_new(card, SNDRV_DEV_XXX, chip, &ops); +]]> + </programlisting> + </informalexample> + </para> + + <para> + This takes the card pointer, the device-level + (<constant>SNDRV_DEV_XXX</constant>), the data pointer, and the + callback pointers (<parameter>&ops</parameter>). The + device-level defines the type of components and the order of + registration and de-registration. For most of components, the + device-level is already defined. For a user-defined component, + you can use <constant>SNDRV_DEV_LOWLEVEL</constant>. + </para> + + <para> + This function itself doesn't allocate the data space. The data + must be allocated manually beforehand, and its pointer is passed + as the argument. This pointer is used as the identifier + (<parameter>chip</parameter> in the above example) for the + instance. + </para> + + <para> + Each ALSA pre-defined component such as ac97 or pcm calls + <function>snd_device_new()</function> inside its + constructor. The destructor for each component is defined in the + callback pointers. Hence, you don't need to take care of + calling a destructor for such a component. + </para> + + <para> + If you would like to create your own component, you need to + set the destructor function to dev_free callback in + <parameter>ops</parameter>, so that it can be released + automatically via <function>snd_card_free()</function>. The + example will be shown later as an implementation of a + chip-specific data. + </para> + </section> + + <section id="card-management-chip-specific"> + <title>Chip-Specific Data</title> + <para> + The chip-specific information, e.g. the i/o port address, its + resource pointer, or the irq number, is stored in the + chip-specific record. + Usually, the chip-specific record is typedef'ed as + <type>xxx_t</type> like the following: + + <informalexample> + <programlisting> +<![CDATA[ + typedef struct snd_mychip mychip_t; + struct snd_mychip { + .... + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + You might have objections against such a typedef, but this + typedef is necessary if you use a <quote>magic-cast</quote> + (explained <link + linkend="card-management-chip-what-advantage"><citetitle>later</citetitle></link>). + </para> + + <para> + In general, there are two ways to allocate the chip record. + </para> + + <section id="card-management-chip-specific-snd-card-new"> + <title>1. Allocating via <function>snd_card_new()</function>.</title> + <para> + As mentioned above, you can pass the extra-data-length to the 4th argument of <function>snd_card_new()</function>, i.e. + + <informalexample> + <programlisting> +<![CDATA[ + card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(mychip_t)); +]]> + </programlisting> + </informalexample> + + whether <type>mychip_t</type> is the type of the chip record. + </para> + + <para> + In return, the allocated record can be accessed as + + <informalexample> + <programlisting> +<![CDATA[ + mychip_t *chip = (mychip_t *)card->private_data; +]]> + </programlisting> + </informalexample> + + With this method, you don't have to allocate twice. But you + cannot use <quote>magic-cast</quote> for this record pointer, + instead. + </para> + </section> + + <section id="card-management-chip-specific-allocate-extra"> + <title>2. Allocating an extra device.</title> + + <para> + After allocating a card instance via + <function>snd_card_new()</function> (with + <constant>NULL</constant> on the 4th arg), call + <function>snd_magic_kcalloc()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_card_t *card; + mychip_t *chip; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL); + ..... + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); +]]> + </programlisting> + </informalexample> + + Once when the record is allocated via snd_magic stuff, you + can use <quote>magic-cast</quote> for the void pointer. + </para> + + <para> + The chip record should have the field to hold the card + pointer at least, + + <informalexample> + <programlisting> +<![CDATA[ + struct snd_mychip { + snd_card_t *card; + .... + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + Then, set the card pointer in the returned chip instance. + + <informalexample> + <programlisting> +<![CDATA[ + chip->card = card; +]]> + </programlisting> + </informalexample> + </para> + + <para> + Also, you need to define a magic-value for <type>mychip_t</type>. + + <informalexample> + <programlisting> +<![CDATA[ + #define mychip_t_magic 0xa15a4501 +]]> + </programlisting> + </informalexample> + (the detail will be described in the + <link linkend="card-management-chip-what-advantage"><citetitle> + next</citetitle></link> subsection). + </para> + + <para> + Next, initialize the fields, and register this chip + record as a low-level device with a specified + <parameter>ops</parameter>, + + <informalexample> + <programlisting> +<![CDATA[ + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + .... + snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); +]]> + </programlisting> + </informalexample> + + <function>snd_mychip_dev_free()</function> is the + device-destructor function, which will call the real + destructor. + </para> + + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_mychip_dev_free(snd_device_t *device) + { + mychip_t *chip = snd_magic_cast(mychip_t, device->device_data, + return -ENXIO); + return snd_mychip_free(chip); + } +]]> + </programlisting> + </informalexample> + + where <function>snd_mychip_free()</function> is the real destructor. + </para> + </section> + + <section id="card-management-chip-what-advantage"> + <title>Not a magic but a logic</title> + + <para>Now, you might have a question: What is the advantage of the + second method? Obviously, it looks far more complicated.</para> + <para> + As I wrote many times, the second method allows a + <quote>magic-cast</quote> for <type>mychip_t</type>. If you + have a void pointer (such as + pcm->private_data), the pointer type + is unknown at the compile time, and you cannot know even if a + wrong pointer type is passed. The compiler would accept + it. The magic-cast checks the pointer type at the runtime (and + whether it's a null pointer, too). Hence, the cast will be + much safer and good for debugging. + </para> + + <para> + As you have already seen, allocation with a magic-header can + be done via <function>snd_magic_kmalloc()</function> or + <function>snd_magic_kcalloc()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + mychip_t *chip; + chip = snd_magic_kmalloc(mychip_t, 0, GFP_KERNEL); + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); +]]> + </programlisting> + </informalexample> + + The difference of these two functions is whether the area is + zero-cleared (<function>kcalloc</function>) or not + (<function>kmalloc</function>). + </para> + + <para> + The first argument of the allocator is the type of the + record. The magic-constant has to be defined for this type + beforehand. In this case, we'll need to define + <constant>mychip_t_magic</constant>, for example, as already + seen, + + <informalexample> + <programlisting> +<![CDATA[ + #define mychip_t_magic 0xa15a4501 +]]> + </programlisting> + </informalexample> + + The value is arbitrary but should be unique. + This is usually defined in + <filename><include/sndmagic.h></filename> or + <filename><include/amagic.h></filename> for alsa-driver tree, + but you may define it locally in the code at the early + development stage, since changing + <filename>sndmagic.h</filename> will lead to the recompilation + of the whole driver codes. + </para> + + <para> + The second argument is the extra-data length. It is usually + zero. The third argument is the flags to be passed to kernel + memory allocator, <constant>GFP_XXX</constant>. Normally, + <constant>GFP_KERNEL</constant> is passed. + </para> + + <para> + For casting a pointer, use + <function>snd_magic_cast()</function> macro: + + <informalexample> + <programlisting> +<![CDATA[ + mychip_t *chip = snd_magic_cast(mychip_t, source_pointer, action); +]]> + </programlisting> + </informalexample> + + where <parameter>source_pointer</parameter> is the pointer to + be casted (e.g. pcm->private_data), and + <parameter>action</parameter> is the action to do if the cast + fails (e.g. return <constant>-EINVAL</constant>). + </para> + + <para> + For releasing the magic-allocated data, you need to call + <function>snd_magic_kfree()</function> function instead of + <function>kfree()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_magic_kfree(chip); +]]> + </programlisting> + </informalexample> + </para> + + <para> + If you call <function>kfree()</function> for the + magic-allocated value, it will lead to memory leaks. + When the ALSA drivers are compiled with + <constant>CONFIG_SND_DEBUG_MEMORY</constant> kernel config (or + configured with <option>--with-debug=full</option>), the + non-matching free will be checked and you'll see warning + messages. + </para> + + <para> + If you are 100% sure that your code is bug-free, you can + compile the driver without + <constant>CONFIG_SND_DEBUG_MEMORY</constant> kernel config, + so that the magic-allocator and the magic-cast will be + replaced to the normal kmalloc and cast. + </para> + </section> + </section> + + <section id="card-management-registration"> + <title>Registration and Release</title> + <para> + After all components are assigned, register the card instance + by calling <function>snd_card_register()</function>. The access + to the device files are enabled at this point. That is, before + <function>snd_card_register()</function> is called, the + components are safely inaccessible from external side. If this + call fails, exit the probe function after releasing the card via + <function>snd_card_free()</function>. + </para> + + <para> + For releasing the card instance, you can call simply + <function>snd_card_free()</function>. As already mentioned, all + components are released automatically by this call. + </para> + + <para> + As further notes, the destructors (both + <function>snd_mychip_dev_free</function> and + <function>snd_mychip_free</function>) cannot be defined with + <parameter>__devexit</parameter> prefix, because they may be + called from the constructor, too, at the false path. + </para> + + <para> + For a device which allows hotplugging, you can use + <function>snd_card_free_in_thread</function>. This one will + postpone the destruction and wait in a kernel-thread until all + devices are closed. + </para> + + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- PCI Resource Managements --> +<!-- ****************************************************** --> + <chapter id="pci-resource"> + <title>PCI Resource Managements</title> + + <section id="pci-resource-example"> + <title>Full Code Example</title> + <para> + In this section, we'll finish the chip-specific constructor, + destructor and PCI entries. The example code is shown first, + below. + + <example> + <title>PCI Resource Managements Example</title> + <programlisting> +<![CDATA[ + struct snd_mychip { + snd_card_t *card; + struct pci_dev *pci; + + unsigned long port; + struct resource *res_port; + + int irq; + }; + + static int snd_mychip_free(mychip_t *chip) + { + // disable hardware here if any + // (not implemented in this document) + + // release the i/o port + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + // release the irq + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + // release the data + snd_magic_kfree(chip); + return 0; + } + + // chip-specific constructor + static int __devinit snd_mychip_create(snd_card_t *card, + struct pci_dev *pci, + mychip_t **rchip) + { + mychip_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + // check PCI availability (28bit DMA) + if ((err = pci_enable_device(pci)) < 0) + return err; + if (!pci_dma_supported(pci, 0x0fffffff)) { + printk(KERN_ERR "error to set 28bit mask DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + // initialize the stuff + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + // (1) PCI resource allocation + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot allocate the port\n"); + return -EBUSY; + } + if (request_irq(pci->irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + + // (2) initialization of the chip hardware + // (not implemented in this document) + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_mychip_free(chip); + return err; + } + *rchip = chip; + return 0; + } + + // PCI IDs + static struct pci_device_id snd_mychip_ids[] __devinitdata = { + { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + .... + { 0, } + }; + MODULE_DEVICE_TABLE(pci, snd_mychip_ids); + + // pci_driver definition + static struct pci_driver driver = { + .name = "My Own Chip", + .id_table = snd_mychip_ids, + .probe = snd_mychip_probe, + .remove = __devexit_p(snd_mychip_remove), + }; + + // initialization of the module + static int __init alsa_card_mychip_init(void) + { + int err; + + if ((err = pci_module_init(&driver)) < 0) { + #ifdef MODULE + printk(KERN_ERR "My chip soundcard not found " + "or device busy\n"); + #endif + return err; + } + return 0; + } + + // clean up the module + static void __exit alsa_card_mychip_exit(void) + { + pci_unregister_driver(&driver); + } + + module_init(alsa_card_mychip_init) + module_exit(alsa_card_mychip_exit) + + EXPORT_NO_SYMBOLS; /* for old kernels only */ +]]> + </programlisting> + </example> + </para> + </section> + + <section id="pci-resource-some-haftas"> + <title>Some Hafta's</title> + <para> + The allocation of PCI resources is done in the + <function>probe()</function> function, and usually an extra + <function>xxx_create()</function> function is written for this + purpose. + </para> + + <para> + In the case of PCI devices, you have to call at first + <function>pci_enable_device()</function> function before + allocating resources. Also, you need to set the proper PCI DMA + mask to limit the accessed i/o range. In some cases, you might + need to call <function>pci_set_master()</function> function, + too. + </para> + + <para> + Suppose the 28bit mask, and the code to be added would be like: + + <informalexample> + <programlisting> +<![CDATA[ + if ((err = pci_enable_device(pci)) < 0) + return err; + if (!pci_dma_supported(pci, 0x0fffffff)) { + printk(KERN_ERR "error to set 28bit mask DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); +]]> + </programlisting> + </informalexample> + </para> + </section> + + <section id="pci-resource-resource-allocation"> + <title>Resource Allocation</title> + <para> + The allocation of ports and irqs are done via standard kernel + functions. Unlike ALSA ver.0.5.x., there are no helpers for + that. And these resources must be released in the destructor + function (see below). Also, on ALSA 0.9.x, you don't need + allocate (pseudo-)DMA for PCI like 0.5.x. + </para> + + <para> + Now assume that this PCI device has an I/O port with 8 bytes + and an interrupt. Then <type>mychip_t</type> will have the + following fields: + + <informalexample> + <programlisting> +<![CDATA[ + struct snd_mychip { + snd_card_t *card; + + unsigned long port; + struct resource *res_port; + + int irq; + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + For an i/o port (and also a memory region), you need to have + the resource pointer for the standard resource management. For + an irq, you have to keep only the irq number (integer). But you + need to initialize this number as -1 before actual allocation, + since irq 0 is valid. The port address and its resource pointer + can be initialized as null by + <function>snd_magic_kcalloc()</function> automatically, so you + don't have to take care of it. + </para> + + <para> + The allocation of an i/o port is done like this: + + <informalexample> + <programlisting> +<![CDATA[ + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 8, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the port 0x%lx\n", + chip->port); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + It will reserve the i/o port region of 8 bytes of the given + PCI device. The returned value, chip->res_port, is allocated + via <function>kmalloc()</function> by + <function>request_region()</function>. The pointer must be + released via <function>kfree()</function>, but there is some + problem regarding this. This issue will be explained more below. + </para> + + <para> + The allocation of an interrupt source is done like this: + + <informalexample> + <programlisting> +<![CDATA[ + if (request_irq(pci->irq, snd_mychip_interrupt, + SA_INTERRUPT|SA_SHIRQ, "My Chip", + (void *)chip)) { + snd_mychip_free(chip); + printk(KERN_ERR "cannot grab irq %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +]]> + </programlisting> + </informalexample> + + where <function>snd_mychip_interrupt()</function> is the + interrupt handler defined <link + linkend="pcm-interface-interrupt-handler"><citetitle>later</citetitle></link>. + Note that chip->irq should be defined + only when <function>request_irq()</function> succeeded. + </para> + + <para> + On the PCI bus, the interrupts can be shared. Thus, + <constant>SA_SHIRQ</constant> is given as the interrupt flag of + <function>request_irq()</function>. + </para> + + <para> + The last argument of <function>request_irq()</function> is the + data pointer passed to the interrupt handler. Usually, the + chip-specific record is used for that, but you can use what you + like, too. + </para> + + <para> + I won't define the detail of the interrupt handler at this + point, but at least its appearance can be explained now. The + interrupt handler looks usually like the following: + + <informalexample> + <programlisting> +<![CDATA[ + static void snd_mychip_interrupt(int irq, void *dev_id, + struct pt_regs *regs) + { + mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return); + .... + } +]]> + </programlisting> + </informalexample> + + Again the magic-cast is used here to get the correct pointer + from the second argument. + </para> + + <para> + Now let's write the corresponding destructor for the resources + above. The role of destructor is simple: disable the hardware + (if already activated) and release the resources. So far, we + have no hardware part, so the disabling is not written here. + </para> + + <para> + For releasing the resources, <quote>check-and-release</quote> + method is a safer way. For the i/o port, do like this: + + <informalexample> + <programlisting> +<![CDATA[ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + As you can see, the i/o resource pointer is also to be freed + via <function>kfree_nocheck()</function> after + <function>release_resource()</function> is called. You + cannot use <function>kfree()</function> here, because on ALSA, + <function>kfree()</function> may be a wrapper to its own + allocator with the memory debugging. Since the resource pointer + is allocated externally outside the ALSA, it must be released + via the native + <function>kfree()</function>. + <function>kfree_nocheck()</function> is used for that; it calls + the native <function>kfree()</function> without wrapper. + </para> + + <para> + For releasing the interrupt, do like this: + + <informalexample> + <programlisting> +<![CDATA[ + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); +]]> + </programlisting> + </informalexample> + + And finally, release the chip-specific record. + + <informalexample> + <programlisting> +<![CDATA[ + snd_magic_kfree(chip); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The chip instance is freed via + <function>snd_magic_kfree()</function>. Please use this function + for the object allocated by + <function>snd_magic_kmalloc()</function>. If you free it with + <function>kfree()</function>, it won't work properly and will + result in the memory leak. Also, again, remember that you cannot + set <parameter>__devexit</parameter> prefix for this destructor. + </para> + + <para> + We didn't implement the hardware-disabling part in the above. + If you need to do this, please note that the destructor may be + called even before the initialization of the chip is completed. + It would be better to have a flag to skip the hardware-disabling + if the hardware was not initialized yet. + </para> + + <para> + When the chip-data is assigned to the card using + <function>snd_device_new()</function> with + <constant>SNDRV_DEV_LOWLELVEL</constant> , its destructor is + called at the last. that is, it is assured that all other + components like PCMs and controls have been already released. + You don't have to call stopping PCMs, etc. explicitly, but just + stop the hardware in the low-level. + </para> + + <para> + The management of a memory-mapped region is almost as same as + the management of an i/o port. You'll need three fields like + the following: + + <informalexample> + <programlisting> +<![CDATA[ + struct snd_mychip { + .... + unsigned long iobase_phys; + unsigned long iobase_virt; + struct resource *res_iobase; + }; +]]> + </programlisting> + </informalexample> + + and the allocation would be (assuming its size is 512 bytes): + + <informalexample> + <programlisting> +<![CDATA[ + chip->iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = (unsigned long) + ioremap_nocache(chip->iobase_phys, 512); + if ((chip->res_port = request_mem_region(chip->port, 512, + "My Chip")) == NULL) { + printk(KERN_ERR "cannot allocate the memory region\n"); + snd_mychip_free(chip); + return -EBUSY; + } +]]> + </programlisting> + </informalexample> + + and the corresponding destructor would be: + + <informalexample> + <programlisting> +<![CDATA[ + static int snd_mychip_free(mychip_t *chip) + { + .... + if (chip->iobase_virt) + iounmap((void *)chip->iobase_virt); + if (chip->res_iobase) { + release_resource(chip->res_iobase); + kfree_nocheck(chip->res_iobase); + } + .... + } +]]> + </programlisting> + </informalexample> + </para> + + </section> + + <section id="pci-resource-entries"> + <title>PCI Entries</title> + <para> + So far, so good. Let's finish the rest of missing PCI + stuffs. At first, we need a + <structname>pci_device_id</structname> table for this + chipset. It's a table of PCI vendor/device ID number, and some + masks. + </para> + + <para> + For example, + + <informalexample> + <programlisting> +<![CDATA[ + static struct pci_device_id snd_mychip_ids[] __devinitdata = { + { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + .... + { 0, } + }; + MODULE_DEVICE_TABLE(pci, snd_mychip_ids); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The first and second fields of + <structname>pci_device_id</structname> struct are the vendor and + device IDs. If you have nothing special to filter the matching + devices, you can use the rest of fields like above. The last + field of <structname>pci_device_id</structname> struct is a + private data for this entry. You can specify any value here, for + example, to tell the type of different operations per each + device IDs. Such an example is found in intel8x0 driver. + </para> + + <para> + The last entry of this list is the terminator. You must + specify this all-zero entry. + </para> + + <para> + Then, prepare the <structname>pci_driver</structname> record: + + <informalexample> + <programlisting> +<![CDATA[ + static struct pci_driver driver = { + .name = "My Own Chip", + .id_table = snd_mychip_ids, + .probe = snd_mychip_probe, + .remove = __devexit_p(snd_mychip_remove), + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + The <structfield>probe</structfield> and + <structfield>remove</structfield> functions are what we already + defined in + the previous sections. The <structfield>remove</structfield> should + be defined with + <function>__devexit_p()</function> macro, so that it's not + defined for built-in (and non-hot-pluggable) case. The + <structfield>name</structfield> + field is the name string of this device. Note that you must not + use a slash <quote>/</quote> in this string. + </para> + + <para> + And at last, the module entries: + + <informalexample> + <programlisting> +<![CDATA[ + static int __init alsa_card_mychip_init(void) + { + int err; + + if ((err = pci_module_init(&driver)) < 0) { + #ifdef MODULE + printk(KERN_ERR "My chip soundcard not found" + " or device busy\n"); + #endif + return err; + } + return 0; + } + + static void __exit alsa_card_mychip_exit(void) + { + pci_unregister_driver(&driver); + } + + module_init(alsa_card_mychip_init) + module_exit(alsa_card_mychip_exit) +]]> + </programlisting> + </informalexample> + </para> + + <para> + Note that these module entries are tagged with + <parameter>__init</parameter> and + <parameter>__exit</parameter> prefixes, not + <parameter>__devinit</parameter> nor + <parameter>__devexit</parameter>. + </para> + + <para> + Oh, one thing was forgotten. If you have no exported symbols, + you need to declare it on 2.2 or 2.4 kernels (on 2.5 kernels + it's not necessary, though). + + <informalexample> + <programlisting> +<![CDATA[ + EXPORT_NO_SYMBOLS; +]]> + </programlisting> + </informalexample> + + That's all! + </para> + </section> + </chapter> + + +<!-- ****************************************************** --> +<!-- PCM Interface --> +<!-- ****************************************************** --> + <chapter id="pcm-interface"> + <title>PCM Interface</title> + + <section id="pcm-interface-general"> + <title>General</title> + <para> + The PCM middle layer of ALSA is quite powerful and it is only + necessary for each driver to implement the low-level functions + to access its hardware. + </para> + + <para> + For accessing to the PCM layer, you need to include + <filename><sound/pcm.h></filename> above all. In addition, + <filename><sound/pcm_params.h></filename> might be needed + if you access to some functions related with hw_param. + </para> + + <para> + Each card device can have up to four pcm instances. A pcm + instance corresponds to a pcm device file. The limitation of + number of instances comes only from the available bit size of + the linux's device number. Once when 64bit device number is + used, we'll have more available pcm instances. + </para> + + <para> + A pcm instance consists of pcm playback and capture streams, + and each pcm stream consists of one or more pcm substreams. Some + soundcard supports the multiple-playback function. For example, + emu10k1 has a PCM playback of 32 stereo substreams. In this case, at + each open, a free substream is (usually) automatically chosen + and opened. Meanwhile, when only one substream exists and it was + already opened, the succeeding open will result in the blocking + or the error with <constant>EAGAIN</constant> according to the + file open mode. But you don't have to know the detail in your + driver. The PCM middle layer will take all such jobs. + </para> + </section> + + <section id="pcm-interface-example"> + <title>Full Code Example</title> + <para> + The example code below does not include any hardware access + routines but shows only the skeleton, how to build up the PCM + interfaces. + + <example> + <title>PCM Example Code</title> + <programlisting> +<![CDATA[ + #include <sound/pcm.h> + .... + + #define chip_t mychip_t + .... + + /* hardware definition */ + static snd_pcm_hardware_t snd_mychip_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; + + /* open callback */ + static int snd_mychip_pcm_open(snd_pcm_substream_t *subs) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_mychip_playback_hw; + // more hardware-initialization will be done here + return 0; + } + + /* close callback */ + static int snd_mychip_pcm_close(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + // the hardware-specific codes will be here + return 0; + + } + + /* hw_params callback */ + static int snd_mychip_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + { + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } + + /* hw_free callback */ + static int snd_mychip_pcm_hw_free(snd_pcm_substream_t *substream) + { + return snd_pcm_lib_free_pages(substream); + } + + /* prepare callback */ + static int snd_mychip_pcm_prepare(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + // set up the hardware with the current configuration + // for example... + mychip_set_sample_format(chip, runtime->format); + mychip_set_sample_rate(chip, runtime->rate); + mychip_set_channels(chip, runtime->channels); + mychip_set_dma_setup(chip, runtime->dma_area, + chip->buffer_size, + chip->period_size); + return 0; + } + + /* trigger callback */ + static int snd_mychip_pcm_trigger(snd_pcm_substream_t *substream, + int cmd) + { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + // do something to start the PCM engine + break; + case SNDRV_PCM_TRIGGER_STOP: + // do something to stop the PCM engine + break; + default: + return -EINVAL; + } + } + + /* pointer callback */ + static snd_pcm_uframes_t + snd_mychip_pcm_pointer(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + unsigned int current_ptr; + + // get the current hardware pointer + current_ptr = mychip_get_hw_pointer(chip); + return current_ptr; + } + + /* operators */ + static snd_pcm_ops_t snd_mychip_playback_ops = { + .open = snd_mychip_playback_open, + .close = snd_mychip_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; + + /* + * definitions of capture are omitted here... + */ + + /* create a pcm device */ + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_mychip_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_mychip_capture_ops); + /* pre-allocation of buffers */ + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, + 64*1024, 64*1024); + return 0; + } +]]> + </programlisting> + </example> + </para> + </section> + + <section id="pcm-interface-constructor"> + <title>Constructor</title> + <para> + A pcm instance is allocated <function>snd_pcm_new()</function> + function. It would be better to create a constructor for pcm, + namely, + + <informalexample> + <programlisting> +<![CDATA[ + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, + &pcm)) < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + .... + return 0; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + The <function>snd_pcm_new()</function> function takes the four + arguments. The first argument is the card pointer to which this + pcm is assigned, and the second is the ID string. + </para> + + <para> + The third argument (<parameter>index</parameter>, 0 in the + above) is the index of this new pcm. It begins from zero. When + you will create more than one pcm instances, specify the + different numbers in this argument. For example, + <parameter>index</parameter> = 1 for the second PCM device. + </para> + + <para> + The fourth and fifth arguments are the number of substreams + for playback and capture, respectively. Here both 1 are given in + the above example. When no playback or no capture is available, + pass 0 to the corresponding argument. + </para> + + <para> + If a chip supports multiple playbacks or captures, you can + specify more numbers, but they must be handled properly in + open/close, etc. callbacks. When you need to know which + substream you are referring to, then it can be obtained from + <type>snd_pcm_substream_t</type> data passed to each callback + as follows: + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_substream_t *substream; + int index = substream->number; +]]> + </programlisting> + </informalexample> + </para> + + <para> + After the pcm is created, you need to set operators for each + pcm stream. + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_mychip_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_mychip_capture_ops); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The operators are defined typically like this: + + <informalexample> + <programlisting> +<![CDATA[ + static snd_pcm_ops_t snd_mychip_playback_ops = { + .open = snd_mychip_pcm_open, + .close = snd_mychip_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; +]]> + </programlisting> + </informalexample> + + Each of callbacks is explained in the subsection + <link linkend="pcm-interface-operators"><citetitle> + Operators</citetitle></link>. + </para> + + <para> + After setting the operators, most likely you'd like to + pre-allocate the buffer. For the pre-allocation, simply call + the following: + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, + 64*1024, 64*1024); +]]> + </programlisting> + </informalexample> + + It will allocate up to 64kB buffer as default. The details of + buffer management will be described in the later section <link + linkend="buffer-and-memory"><citetitle>Buffer and Memory + Management</citetitle></link>. + </para> + + <para> + Additionally, you can set some extra information for this pcm + in pcm->info_flags. + The available values are defined as + <constant>SNDRV_PCM_INFO_XXX</constant> in + <filename><sound/asound.h></filename>, which is used for + the hardware definition (described later). When your soundchip + supports only half-duplex, specify like this: + + <informalexample> + <programlisting> +<![CDATA[ + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; +]]> + </programlisting> + </informalexample> + </para> + </section> + + <section id="pcm-interface-destructor"> + <title>... And the Destructor?</title> + <para> + The destructor for a pcm instance is not always + necessary. Since the pcm device will be released by the middle + layer code automatically, you don't have to call destructor + explicitly. + </para> + + <para> + The destructor would be necessary when you created some + special records internally and need to release them. In such a + case, set the destructor function to + pcm->private_free: + + <example> + <title>PCM Instance with a Destructor</title> + <programlisting> +<![CDATA[ + static void mychip_pcm_free(snd_pcm_t *pcm) + { + mychip_t *chip = snd_magic_cast(mychip_t, + pcm->private_data, return); + // free your own data + kfree(chip->my_private_pcm_data); + // do what you like else... + } + + static int __devinit snd_mychip_new_pcm(mychip_t *chip) + { + snd_pcm_t *pcm; + .... + // allocate your own data + chip->my_private_pcm_data = kmalloc(...); + // set the destructor + pcm->private_data = chip; + pcm->private_free = mychip_pcm_free; + .... + } +]]> + </programlisting> + </example> + </para> + </section> + + <section id="pcm-interface-operators"> + <title>Operators</title> + <para> + OK, now let me explain the detail of each pcm callback + (<parameter>ops</parameter>). In general, every callback must + return 0 if successful, or a negative number with the error + number such as <constant>-EINVAL</constant> at any + error. + </para> + + <para> + The callback function takes at least the argument with + <type>snd_pcm_substream_t</type> pointer. For retrieving the + chip record from the given substream instance, you can use the + following macro. + + <informalexample> + <programlisting> +<![CDATA[ + #define chip_t mychip_t + + int xxx() { + mychip_t *chip = snd_pcm_substream_chip(substream); + .... + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + It's expanded with a magic-cast, so the cast-error is + automatically checked. You should define <type>chip_t</type> at + the beginning of the code, since this will be referred in many + places of pcm and control interfaces. + </para> + + <section id="pcm-interface-operators-open-callback"> + <title>open callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_open(snd_pcm_substream_t *subs); +]]> + </programlisting> + </informalexample> + + This is called when a pcm substream is opened. + </para> + + <para> + At least, here you have to initialize the runtime hardware + record. Typically, this is done by like this: + + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_open(snd_pcm_substream_t *substream) + { + mychip_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_mychip_playback_hw; + return 0; + } +]]> + </programlisting> + </informalexample> + + where <parameter>snd_mychip_playback_hw</parameter> is the + pre-defined hardware record. + + <informalexample> + <programlisting> +<![CDATA[ + static snd_pcm_hardware_t snd_mychip_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + The similar struct exists on ALSA 0.5.x driver, so you can + guess the values if you already wrote a driver. + </para> + + <para> + The <structfield>info</structfield> field contains the type and + capabilities of this pcm. The bit flags are defined in + <filename><sound/asound.h></filename> as + <constant>SNDRV_PCM_INFO_XXX</constant>. Here, at least, you + have to specify whether the mmap is supported and which + interleaved format is supported. + When the mmap is supported, add + <constant>SNDRV_PCM_INFO_MMAP</constant> flag here. When the + hardware supports the interleaved or the non-interleaved + format, <constant>SNDRV_PCM_INFO_INTERLEAVED</constant> or + <constant>SNDRV_PCM_INFO_NONINTERLEAVED</constant> flag must + be set, respectively. If both are supported, you can set both, + too. + </para> + + <para> + In the above example, <constant>MMAP_VALID</constant> and + <constant>BLOCK_TRANSFER</constant> are specified for OSS mmap + mode. Usually both are set. Of course, + <constant>MMAP_VALID</constant> is set only if the mmap is + really supported. + </para> + + <para> + The other possible flags are + <constant>SNDRV_PCM_INFO_PAUSE</constant> and + <constant>SNDRV_PCM_INFO_RESUME</constant>. The + <constant>PAUSE</constant> bit means that the pcm supports the + <quote>pause</quote> operation, while the + <constant>RESUME</constant> bit means that the pcm supports + the <quote>suspend/resume</quote> operation. If these flags + are set, the <structfield>trigger</structfield> callback below + must handle the corresponding commands. + </para> + + <para> + <structfield>formats</structfield> field contains the bit-flags + of supported formats (<constant>SNDRV_PCM_FMTBIT_XXX</constant>). + If the hardware supports more than one format, give all or'ed + bits. In the example above, the signed 16bit little-endian + format is specified. + </para> + + <para> + <structfield>rates</structfield> field contains the bit-flags of + supported rates (<constant>SNDRV_PCM_RATE_XXX</constant>). + When the chip supports continuous rates, pass + <constant>CONTINUOUS</constant> bit additionally. + The pre-defined rate bits are only for typical rates. If your + chip supports unconventional rates, you need to add + <constant>KNOT</constant> bit and set up the + constraint manually (explained later). + </para> + + <para> + There have been many changes of terminology between + ALSA 0.5.x and 0.9.x. + On the ALSA 0.9.x world, <quote>period</quote> means what is + known as <quote>fragment</quote> in the OSS. It's the least + size of (a part of) the buffer to generate an interrupt. + </para> + + <para> + Now, taking the new terminology into account, the other fields + are self-explanatory (I hope). Please note that here, both + min/max buffer and period sizes are specified in bytes. + </para> + + <para> + Some drivers allocate the private instance for each pcm + substream. It can be stored in + <constant>substream->runtime->private_data</constant>. + Since it's a void pointer, you + should use magic-kmalloc and magic-cast for such an object. + + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_open(snd_pcm_substream_t *substream) + { + my_pcm_data_t *data; + .... + data = snd_magic_kmalloc(my_pcm_data_t, 0, GFP_KERNEL); + substream->runtime->private_data = data; + .... + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + The allocated object must be released in the close callback below. + </para> + </section> + + <section id="pcm-interface-operators-close-callback"> + <title>close callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_close(snd_pcm_substream_t *subs); +]]> + </programlisting> + </informalexample> + + Obviously, this is called when a pcm substream is closed. + </para> + + <para> + Any private instance for a pcm substream allocated in the + open callback will be released here. + + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_close(snd_pcm_substream_t *substream) + { + .... + snd_magic_kfree(substream->runtime->private_data); + .... + } +]]> + </programlisting> + </informalexample> + </para> + </section> + + <section id="pcm-interface-operators-ioctl-callback"> + <title>ioctl callback</title> + <para> + This is used for any special action to pcm ioctls. But + usually you can pass a generic ioctl callback, + <function>snd_pcm_lib_ioctl</function>. + </para> + </section> + + <section id="pcm-interface-operators-hw-params-callback"> + <title>hw_params callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params); +]]> + </programlisting> + </informalexample> + + This and <structfield>hw_free</structfield> callbacks exist + only on ALSA 0.9.x. + </para> + + <para> + This is called when the hardware parameter + (<structfield>hw_params</structfield>) is set + up by the application, + that is, once when the buffer size, the period size, the + format, etc. are defined for the pcm substream. + </para> + + <para> + Many hardware set-up should be done in this callback, + including the allocation of buffers. + </para> + + <para> + Parameters to be initialized are retrieved by + <function>params_xxx()</function> macros. For allocating a + buffer, you can call a helper function, + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +]]> + </programlisting> + </informalexample> + + <function>snd_pcm_lib_malloc_pages()</function> is available + only when the DMA buffers have been pre-allocated. + See the section <link + linkend="buffer-and-memory-buffer-types"><citetitle> + Buffer Types</citetitle></link> for more details. + </para> + + <para> + Note that this and <structfield>prepare</structfield> callbacks + may be called multiple times per initialization. + For example, the OSS emulation may + call these callbacks at each change via its ioctl. + </para> + + <para> + Thus, you need to take care not to allocate the same buffers + many times, which will lead to memory leak! Calling the + helper function above many times is OK. It will release the + previous buffer automatically when it was already allocated. + </para> + + <para> + Another note is that this callback is non-atomic + (schedulable). This is important, because the + <structfield>prepare</structfield> callback + is atomic (non-schedulable). That is, mutex or any + schedule-related functions are available only in + <structfield>hw_params</structfield> callback. + Please see the subsection + <link linkend="pcm-interface-atomicity"><citetitle> + Atomicity</citetitle></link> for details. + </para> + </section> + + <section id="pcm-interface-operators-hw-free-callback"> + <title>hw_free callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_hw_free(snd_pcm_substream_t * substream); +]]> + </programlisting> + </informalexample> + </para> + + <para> + This is called to release the resources allocated via + <structfield>hw_params</structfield>. For example, releasing the + buffer via + <function>snd_pcm_lib_malloc_pages()</function> is done by + calling the following: + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_lib_free_pages(substream); +]]> + </programlisting> + </informalexample> + </para> + + <para> + This callback may be called multiple times, too. + Keep track whether the resource was already released. + </para> + </section> + + <section id="pcm-interface-operators-prepare-callback"> + <title>prepare callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_prepare(snd_pcm_substream_t * substream); +]]> + </programlisting> + </informalexample> + </para> + + <para> + This callback is called when the pcm is + <quote>prepared</quote>. You can set the format type, sample + rate, etc. here. The difference from + <structfield>hw_params</structfield> is that the + <structfield>prepare</structfield> callback will be called at each + time + <function>snd_pcm_prepare()</function> is called, i.e. when + recovered after underruns, etc. + </para> + + <para> + As mentioned above, this callback is atomic. + </para> + + <para> + In this and the following callbacks, you can refer to the + values via the runtime record, + substream->runtime. + For example, to get the current + rate, format or channels, access to + runtime->rate, + runtime->format or + runtime->channels, respectively. + The physical address of the allocated buffer is set to + runtime->dma_area. The buffer and period sizes are + in runtime->buffer_size and runtime->period_size, + respectively. + </para> + + <para> + Note that the period and the buffer sizes are stored in + <quote>frames</quote>. In the ALSA world, 1 frame = channels + * samples-size. For conversion between frames and bytes, you + can use the helper functions, + <function>frames_to_bytes()</function> and + <function>bytes_to_frames()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + period_bytes = frames_to_bytes(runtime, runtime->period_size); +]]> + </programlisting> + </informalexample> + </para> + + <para> + Be careful that this callback will be called many times at + each set up, too. + </para> + </section> + + <section id="pcm-interface-operators-trigger-callback"> + <title>trigger callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static int snd_xxx_trigger(snd_pcm_substream_t * substream, int cmd); +]]> + </programlisting> + </informalexample> + + This is called when the pcm is started, stopped or paused. + </para> + + <para> + Which action is specified in the second argument, + <constant>SNDRV_PCM_TRIGGER_XXX</constant> in + <filename><sound/pcm.h></filename>. At least, + <constant>START</constant> and <constant>STOP</constant> + commands must be defined in this callback. + + <informalexample> + <programlisting> +<![CDATA[ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + // do something to start the PCM engine + break; + case SNDRV_PCM_TRIGGER_STOP: + // do something to stop the PCM engine + break; + default: + return -EINVAL; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + When the pcm supports the pause operation (given in info + field of the hardware table), <constant>PAUSE_PUSE</constant> + and <constant>PAUSE_RELEASE</constant> commands must be + handled here, too. The former is the command to pause the pcm, + and the latter to restart the pcm again. + </para> + + <para> + When the pcm supports the suspend/resume operation, + <constant>SUSPEND</constant> and <constant>RESUME</constant> + commands must be handled, too. Obviously it does suspend and + resume of the pcm substream. Usually, the + <constant>SUSPEND</constant> is identical with + <constant>STOP</constant> command and the + <constant>RESUME</constant> is identical with + <constant>START</constant> command. + </para> + + <para> + This callback is also atomic. + </para> + </section> + + <section id="pcm-interface-operators-pointer-callback"> + <title>pointer callback</title> + <para> + <informalexample> + <programlisting> +<![CDATA[ + static snd_pcm_uframes_t snd_xxx_pointer(snd_pcm_substream_t * substream) +]]> + </programlisting> + </informalexample> + + This callback is called when the PCM middle layer inquires + the current hardware position on the buffer. The position must + be returned in frames (which was in bytes on ALSA 0.5.x), + ranged from 0 to buffer_size - 1. + </para> + + <para> + This is called usually from the buffer-update routine in the + pcm middle layer, which is invoked when + <function>snd_pcm_period_elapsed()</function> is called in the + interrupt routine. Then the pcm middle layer updates the + position and calculates the available space, and wakes up the + sleeping poll threads, etc. + </para> + + <para> + This callback is also atomic. + </para> + </section> + + <section id="pcm-interface-operators-copy-silence"> + <title>copy and silence callbacks</title> + <para> + These callbacks are not mandatory, and can be omitted in + most cases. These callbacks are used when the hardware buffer + cannot be on the normal memory space. Some chips have their + own buffer on the hardware which is not mappable. In such a + case, you have to transfer the data manually from the memory + buffer to the hardware buffer. Or, if the buffer is + non-contiguous on both physical and virtual memory spaces, + these callbacks must be defined, too. + </para> + + <para> + If these two callbacks are defined, copy and set-silence + operations are done by them. The detailed will be described in + the later section <link + linkend="buffer-and-memory"><citetitle>Buffer and Memory + Management</citetitle></link>. + </para> + </section> + + <section id="pcm-interface-operators-page-callback"> + <title>page callback</title> + + <para> + This callback is also not mandatory. This callback is used + mainly for the non-contiguous buffer. The mmap calls this + callback to get the page address. Some examples will be + explained in the later section <link + linkend="buffer-and-memory"><citetitle>Buffer and Memory + Management</citetitle></link>, too. + </para> + </section> + </section> + + <section id="pcm-interface-interrupt-handler"> + <title>Interrupt Handler</title> + <para> + The rest of pcm stuff is the PCM interrupt handler. The + role of PCM interrupt handler in the sound driver is to update + the buffer position and to tell the PCM middle layer when the + buffer position goes across the prescribed period size. To + inform this, call <function>snd_pcm_period_elapsed()</function> + function. + </para> + + <para> + There are several types of sound chips to generate the interrupts. + </para> + + <section id="pcm-interface-interrupt-handler-boundary"> + <title>Interrupts at the period (fragment) boundary</title> + <para> + This is the most frequently found type: the hardware + generates an interrupt at each period boundary. + In this case, you can call + <function>snd_pcm_period_elapsed()</function> at each + interrupt. + </para> + + <para> + <function>snd_pcm_period_elapsed()</function> takes the + substream pointer as its argument. Thus, you need to keep the + substream pointer accessible from the chip instance. For + example, define substream field in the chip record to hold the + current running substream pointer, and set the pointer value + at open callback (and reset at close callback). + </para> + + <para> + If you aquire a spinlock in the interrupt handler, and the + lock is used in other pcm callbacks, too, then you have to + release the lock before calling + <function>snd_pcm_period_elapsed()</function>, because + <function>snd_pcm_period_elapsed()</function> calls other pcm + callbacks inside. + </para> + + <para> + A typical coding would be like: + + <example> + <title>Interrupt Handler Case #1</title> + <programlisting> +<![CDATA[ + static void snd_mychip_interrupt(int irq, void *dev_id, + struct pt_regs *regs) + { + mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return); + spin_lock(&chip->lock); + .... + if (pcm_irq_invoked(chip)) { + // call updater, unlock before it + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->substream); + spin_lock(&chip->lock); + } + .... + spin_unlock(&chip->lock); + } +]]> + </programlisting> + </example> + </para> + </section> + + <section id="pcm-interface-interrupt-handler-timer"> + <title>High-frequent timer interrupts</title> + <para> + This is the case when the hardware doesn't generate interrupts + at the period boundary but do timer-interrupts at the fixed + timer rate (e.g. es1968 or ymfpci drivers). + In this case, you need to check the current hardware + position and accumulates the processed sample length at each + interrupt. When the accumulated size overcomes the period + size, call + <function>snd_pcm_period_elapsed()</function> and reset the + accumulator. + </para> + + <para> + A typical coding would be like the following. + + <example> + <title>Interrupt Handler Case #2</title> + <programlisting> +<![CDATA[ + static void snd_mychip_interrupt(int irq, void *dev_id, + struct pt_regs *regs) + { + mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return); + spin_lock(&chip->lock); + .... + if (pcm_irq_invoked(chip)) { + unsigned int last_ptr, size; + // get the current hardware pointer (in frames) + last_ptr = get_hw_ptr(chip); + // calculate the processed frames since the + // last update + if (last_ptr < chip->last_ptr) + size = runtime->buffer_size + last_ptr + - chip->last_ptr; + else + size = last_ptr - chip->last_ptr; + // remember the last updated point + chip->last_ptr = last_ptr; + // accumulate the size + chip->size += size; + // over the period boundary? + if (chip->size >= runtime->period_size) { + // reset the accumulator + chip->size %= runtime->period_size; + // call updater + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + } + .... + spin_unlock(&chip->lock); + } +]]> + </programlisting> + </example> + </para> + </section> + + <section id="pcm-interface-interrupt-handler-both"> + <title>On calling <function>snd_pcm_period_elapsed()</function></title> + <para> + In both cases, even if more than one period are elapsed, you + don't have to call + <function>snd_pcm_period_elapsed()</function> many times. Call + only once. And the pcm layer will check the current hardware + pointer and update to the latest status. + </para> + </section> + </section> + + <section id="pcm-interface-atomicity"> + <title>Atomicity</title> + <para> + One of the most important (and thus difficult to debug) problem + on the kernel programming is the race condition. + On linux kernel, usually it's solved via spin-locks or + semaphores. In general, if the race condition may + happen in the interrupt handler, it's handled as atomic, and you + have to use spinlock for protecting the critical session. If it + never happens in the interrupt and it may take relatively long + time, you should use semaphore. + </para> + + <para> + As already seen, some pcm callbacks are atomic and some are + not. For example, <parameter>hw_params</parameter> callback is + non-atomic, while <parameter>prepare</parameter> callback is + atomic. This means, the latter is called already in a spinlock + held by the PCM middle layer. Please take this atomicity into + account when you use a spinlock or a semaphore in the callbacks. + </para> + + <para> + In the atomic callbacks, you cannot use functions which may call + <function>schedule</function> or go to + <function>sleep</function>. The semaphore and mutex do sleep, + and hence they cannot be used inside the atomic callbacks + (e.g. <parameter>prepare</parameter> callback). + For taking a certain delay in such a callback, please use + <function>udelay()</function> or <function>mdelay()</function>. + </para> + + <para> + This atomicity problem appears also in the initialization of the + hardware when the power-management is supported. The functions + for suspending and resuming the chip must be atomic, i.e. no + mutex nor sleep can be used in them. + </para> + + </section> + <section id="pcm-interface-constraints"> + <title>Constraints</title> + <para> + If your chip supports unconventional sample rates, or only the + limited samples, you need to set a constraint for the + condition. + </para> + + <para> + For example, in order to restrict the sample rates in the some + supported values, use + <function>snd_pcm_hw_constraint_list()</function>. + You need to call this function in the open callback. + + <example> + <title>Example of Hardware Constraints</title> + <programlisting> +<![CDATA[ + static unsigned int rates[] = + {4000, 10000, 22050, 44100}; + static snd_pcm_hw_constraint_list_t constraints_rates = { + .count = sizeof(rates) / sizeof(rates[0]), + .list = rates, + .mask = 0, + }; + + static int snd_mychip_pcm_open(snd_pcm_substream_t *substream) + { + int err; + .... + err = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + if (err < 0) + return err; + .... + } +]]> + </programlisting> + </example> + </para> + + <para> + There are many different constraints. You can even define your + own constraint rules. I won't explain the details here, rather I + would like to say, <quote>Luke, use the source.</quote> + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- Control Interface --> +<!-- ****************************************************** --> + <chapter id="control-interface"> + <title>Control Interface</title> + + <section id="control-interface-general"> + <title>General</title> + <para> + The control interface is used widely for many switches, + sliders, etc. which are accessed from the user-space. Its most + important use is the mixer interface. In other words, on ALSA + 0.9.x, all the mixer stuff is implemented on the control kernel + API (while there was an independent mixer kernel API on 0.5.x). + </para> + + <para> + ALSA has a well-defined AC97 control module. If your chip + supports only the AC97 and nothing else, you can skip this + section. + </para> + + <para> + The control API is defined in + <filename><sound/control.h></filename>. + Include this file if you add your own controls. + </para> + </section> + + <section id="control-interface-definition"> + <title>Definition of Controls</title> + <para> + For creating a new control, you need to define the three + callbacks: <structfield>info</structfield>, + <structfield>get</structfield> and + <structfield>put</structfield>. Then, define a + <type>snd_kcontrol_new_t</type> record, such as: + + <example> + <title>Definition of a Control</title> + <programlisting> +<![CDATA[ + static snd_kcontrol_new_t my_control __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_values = 0xffff, + .info = my_control_info, + .get = my_control_get, + .put = my_control_put + }; +]]> + </programlisting> + </example> + </para> + + <para> + Most likely the control is created via + <function>snd_ctl_new1()</function>, and in such a case, you can + add <parameter>__devinitdata</parameter> prefix to the + definition like above. + </para> + + <para> + The <structfield>iface</structfield> field specifies the type of + the control, + <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>. There are + <constant>MIXER</constant>, <constant>PCM</constant>, + <constant>CARD</constant>, etc. + </para> + + <para> + The <structfield>name</structfield> is the name identifier + string. On ALSA 0.9.x, the control name is very important, + because its role is classified from its name. There are + pre-defined standard control names. The details are described in + the subsection + <link linkend="control-interface-control-names"><citetitle> + Control Names</citetitle></link>. + </para> + + <para> + The <structfield>index</structfield> field holds the index number + of this control. If there are several different controls with + the same name, they can be distinguished by the index + number. This is the case when + several codecs exist on the card. If the index is zero, you can + omit the definition above. + </para> + + <para> + The <structfield>access</structfield> field contains the access + type of this control. Give the combination of bit masks, + <constant>SNDRV_CTL_ELEM_ACCESS_XXX</constant>, there. + The detailed will be explained in the subsection + <link linkend="control-interface-access-flags"><citetitle> + Access Flags</citetitle></link>. + </para> + + <para> + The <structfield>private_values</structfield> field contains + an arbitrary long integer value for this record. When using + generic <structfield>info</structfield>, + <structfield>get</structfield> and + <structfield>put</structfield> callbacks, you can pass a value + through this field. If several small numbers are necessary, you can + combine them in bitwise. Or, it's possible to give a pointer + (casted to unsigned long) of some record to this field, too. + </para> + + <para> + The other three are + <link linkend="control-interface-callbacks"><citetitle> + callback functions</citetitle></link>. + </para> + </section> + + <section id="control-interface-control-names"> + <title>Control Names</title> + <para> + There are some standards for defining the control names. A + control is usually defined from the three parts as + <quote>SOURCE DIRECTION FUNCTION</quote>. + </para> + + <para> + The first, <constant>SOURCE</constant>, specifies the source + of the control, and is a string such as <quote>Master</quote>, + <quote>PCM</quote>, <quote>CD</quote> or + <quote>Line</quote>. There are many pre-defined sources. + </para> + + <para> + The second, <constant>DIRECTION</constant>, is one of the + following strings according to the direction of the control: + <quote>Playback</quote>, <quote>Capture</quote>, <quote>Bypass + Playback</quote> and <quote>Bypass Capture</quote>. Or, it can + be omitted, meaning both playback and capture directions. + </para> + + <para> + The third, <constant>FUNCTION</constant>, is one of the + following strings according to the function of the control: + <quote>Switch</quote>, <quote>Volume</quote> and + <quote>Route</quote>. + </para> + + <para> + The example of control names are, thus, <quote>Master Capture + Switch</quote> or <quote>PCM Playback Volume</quote>. + </para> + + <para> + There are some exceptions: + </para> + + <section id="control-interface-control-names-global"> + <title>Global capture and playback</title> + <para> + <quote>Capture Source</quote>, <quote>Capture Switch</quote> + and <quote>Capture Volume</quote> are used for the global + capture (input) source, switch and volume. Similarly, + <quote>Playback Switch</quote> and <quote>Playback + Volume</quote> are used for the global output gain switch and + volume. + </para> + </section> + + <section id="control-interface-control-names-tone"> + <title>Tone-controls</title> + <para> + tone-control switch and volumes are specified like + <quote>Tone Control - XXX</quote>, e.g. <quote>Tone Control - + Switch</quote>, <quote>Tone Control - Bass</quote>, + <quote>Tone Control - Center</quote>. + </para> + </section> + + <section id="control-interface-control-names-3d"> + <title>3D controls</title> + <para> + 3D-control switches and volumes are specified like <quote>3D + Control - XXX</quote>, e.g. <quote>3D Control - + Switch</quote>, <quote>3D Control - Center</quote>, <quote>3D + Control - Space</quote>. + </para> + </section> + + <section id="control-interface-control-names-mic"> + <title>Mic boost</title> + <para> + Mic-boost switch is set as <quote>Mic Boost</quote> or + <quote>Mic Boost (6dB)</quote>. + </para> + + <para> + More precise information can be found in + <filename>alsa-kernel/Documentation/ControlNames.txt</filename>. + </para> + </section> + </section> + + <section id="control-interface-access-flags"> + <title>Access Flags</title> + + <para> + The access flag is the bit-flags which specifies the access type + of the given control. The default access type is + <constant>SNDRV_CTL_ELEM_ACCESS_READWRITE</constant>, + which means both read and write are allowed to this control. + When the access flag is omitted (i.e. = 0), it is + regarded as <constant>READWRITE</constant> access as default. + </para> + + <para> + When the control is read-only, pass + <constant>SNDRV_CTL_ELEM_ACCESS_READ</constant> instead. + In this case, you don't have to define + <structfield>put</structfield> callback. + Similarly, when the control is write-only (although it's a rare + case), you can use <constant>WRITE</constant> flag instead, and + you don't need <structfield>get</structfield> callback. + </para> + + <para> + If the control value changes frequently (e.g. the VU meter), + <constant>VOLATILE</constant> flag should be given. This means + that the control may be changed without + <link linkend="control-interface-change-notification"><citetitle> + notification</citetitle></link>. Applications should poll such + a control constantly. + </para> + + <para> + When the control is inactive, set + <constant>INACTIVE</constant> flag, too. + There are <constant>LOCK</constant> and + <constant>OWNER</constant> flags for changing the write + permissions. + </para> + + </section> + + <section id="control-interface-callbacks"> + <title>Callbacks</title> + + <section id="control-interface-callbacks-info"> + <title>info callback</title> + <para> + The <structfield>info</structfield> callback is used to get + the detailed information of this control. This must store the + values of the given <type>snd_ctl_elem_info_t</type> + object. For example, for a boolean control with a single + element will be: + + <example> + <title>Example of info callback</title> + <programlisting> +<![CDATA[ + static int snd_myctl_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; + } +]]> + </programlisting> + </example> + </para> + + <para> + The <structfield>type</structfield> field specifies the type + of the control. There are <constant>BOOLEAN</constant>, + <constant>INTEGER</constant>, <constant>ENUMERATED</constant>, + <constant>BYTES</constant>, <constant>IEC958</constant> and + <constant>INTEGER64</constant>. The + <structfield>count</structfield> field specifies the + number of elements in this control. For example, a stereo + volume would have count = 2. The + <structfield>value</structfield> field is a union, and + the values stored are depending on the type. The boolean and + integer are identical. + </para> + + <para> + The enumerated type is a bit different from others. You'll + need to set the string for the currently given item index. + + <informalexample> + <programlisting> +<![CDATA[ + static int snd_myctl_info(snd_kcontrol_t *kcontrol, + static char *texts[4] = { + "First", "Second", "Third", "Fourth" + }; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); +]]> + </programlisting> + </informalexample> + </para> + </section> + + <section id="control-interface-callbacks-get"> + <title>get callback</title> + + <para> + This callback is used to read the current value of the + control and to return to the user-space. + </para> + + <para> + For example, + + <example> + <title>Example of get callback</title> + <programlisting> +<![CDATA[ + static int snd_myctl_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) + { + mychip_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = get_some_value(chip); + return 0; + } +]]> + </programlisting> + </example> + </para> + + <para> + Here, the chip instance is retrieved via + <function>snd_kcontrol_chip()</function> macro. This macro + converts from kcontrol->private_data to the type defined by + <type>chip_t</type>. The + kcontrol->private_data field is + given as the argument of <function>snd_ctl_new()</function> + (see the later subsection + <link linkend="control-interface-constructor"><citetitle>Constructor</citetitle></link>). + </para> + + <para> + The <structfield>value</structfield> field is depending on + the type of control as well as on info callback. For example, + the sb driver uses this field to store the register offset, + the bit-shift and the bit-mask. The + <structfield>private_value</structfield> is set like + <informalexample> + <programlisting> +<![CDATA[ + .private_value = reg | (shift << 16) | (mask << 24) +]]> + </programlisting> + </informalexample> + and is retrieved in callbacks like + <informalexample> + <programlisting> +<![CDATA[ + static int snd_sbmixer_get_single(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) + { + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + .... + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + In <structfield>get</structfield> callback, you have to fill all the elements if the + control has more than one elements, + i.e. <structfield>count</structfield> > 1. + In the example above, we filled only one element + (<structfield>value.integer.value[0]</structfield>) since it's + assumed as <structfield>count</structfield> = 1. + </para> + </section> + + <section id="control-interface-callbacks-put"> + <title>put callback</title> + + <para> + This callback is used to write a value from the user-space. + </para> + + <para> + For example, + + <example> + <title>Example of put callback</title> + <programlisting> +<![CDATA[ + static int snd_myctl_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) + { + mychip_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + if (chip->current_value != + ucontrol->value.integer.value[0]) { + change_current_value(chip, + ucontrol->value.integer.value[0]); + changed = 1; + } + return changed; + } +]]> + </programlisting> + </example> + + As seen above, you have to return 1 if the value is + changed. If the value is not changed, return 0 instead. + If any fatal error happens, return a negative error code as + usual. + </para> + + <para> + Like <structfield>get</structfield> callback, + when the control has more than one elements, + all elemehts must be evaluated in this callback, too. + </para> + </section> + + <section id="control-interface-callbacks-all"> + <title>Callbacks are not atomic</title> + <para> + All these three callbacks are basically not atomic. + </para> + </section> + </section> + + <section id="control-interface-constructor"> + <title>Constructor</title> + <para> + When everything is ready, finally we can create a new + control. For creating a control, there are two functions to be + called, <function>snd_ctl_new1()</function> and + <function>snd_ctl_add()</function>. + </para> + + <para> + In the simplest way, you can do like this: + + <informalexample> + <programlisting> +<![CDATA[ + if ((err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip))) < 0) + return err; +]]> + </programlisting> + </informalexample> + + where <parameter>my_control</parameter> is the + <type>snd_kcontrol_new_t</type> object defined above, and chip + is the object pointer to be passed to + kcontrol->private_data + which can be referred in callbacks. + </para> + + <para> + <function>snd_ctl_new1()</function> allocates a new + <type>snd_kcontrol_t</type> instance (that's why the definition + of <parameter>my_control</parameter> can be with + <parameter>__devinitdata</parameter> + prefix), and <function>snd_ctl_add</function> assigns the given + control component to the card. + </para> + </section> + + <section id="control-interface-change-notification"> + <title>Change Notification</title> + <para> + If you need to change and update a control in the interrupt + routine, you can call <function>snd_ctl_notify()</function>. For + example, + + <informalexample> + <programlisting> +<![CDATA[ + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer); +]]> + </programlisting> + </informalexample> + + This function takes the card pointer, the event-mask, and the + control id pointer for the notification. The event-mask + specifies the types of notification, for example, in the above + example, the change of control values is notified. + The id pointer is the pointer of <type>snd_ctl_elem_id_t</type> + to be notified. + You can find some examples in <filename>es1938.c</filename> or + <filename>es1968.c</filename> for hardware volume interrupts. + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- API for AC97 Codec --> +<!-- ****************************************************** --> + <chapter id="api-ac97"> + <title>API for AC97 Codec</title> + + <section> + <title>General</title> + <para> + The ALSA AC97 codec layer is a well-defined one, and you don't + have to write many codes to control it. Only low-level control + routines are necessary. The AC97 codec API is defined in + <filename><sound/ac97_codec.h></filename>. + </para> + </section> + + <section id="api-ac97-example"> + <title>Full Code Example</title> + <para> + <example> + <title>Example of AC97 Interface</title> + <programlisting> +<![CDATA[ + struct snd_mychip { + .... + ac97_t *ac97; + .... + }; + + static unsigned short snd_mychip_ac97_read(ac97_t *ac97, + unsigned short reg) + { + mychip_t *chip = snd_magic_cast(mychip_t, + ac97->private_data, return 0); + .... + // read a register value here from the codec + return the_register_value; + } + + static void snd_mychip_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) + { + mychip_t *chip = snd_magic_cast(mychip_t, + ac97->private_data, return 0); + .... + // write the given register value to the codec + } + + static int snd_mychip_ac97(mychip_t *chip) + { + ac97_t ac97; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_mychip_ac97_write; + ac97.read = snd_mychip_ac97_read; + ac97.private_data = chip; + return snd_ac97_mixer(card, &ac97, &chip->ac97); + } + +]]> + </programlisting> + </example> + </para> + </section> + + <section id="api-ac97-constructor"> + <title>Constructor</title> + <para> + For creating an ac97 instance, call + <function>snd_ac97_mixer()</function> with an <type>ac97_t</type> + record, in which the callbacks and the private_data is set. + + <informalexample> + <programlisting> +<![CDATA[ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_mychip_ac97_write; + ac97.read = snd_mychip_ac97_read; + ac97.private_data = chip; + snd_ac97_mixer(card, &ac97, &chip->ac97); +]]> + </programlisting> + </informalexample> + + where chip->ac97 is the pointer of a newly created + <type>ac97_t</type> instance. + This instance is not necessarily stored in the chip + record. When you need to change the register values from the + driver, or need the suspend/resume of ac97 codecs, keep this + pointer to pass to the corresponding functions. + </para> + </section> + + <section id="api-ac97-callbacks"> + <title>Callbacks</title> + <para> + The standard callbacks are <structfield>read</structfield> and + <structfield>write</structfield>. Obviously they + correspond to the functions for read and write accesses to the + hardware low-level codes. + </para> + + <para> + The <structfield>read</structfield> callback returns the + register value specified in the argument. + + <informalexample> + <programlisting> +<![CDATA[ + static unsigned short snd_mychip_ac97_read(ac97_t *ac97, + unsigned short reg) + { + mychip_t *chip = snd_magic_cast(mychip_t, + ac97->private_data, return 0); + .... + return the_register_value; + } +]]> + </programlisting> + </informalexample> + + Here, the chip can be cast from ac97->private_data. + </para> + + <para> + Meanwhile, the <structfield>write</structfield> callback is + used to set the register value. + + <informalexample> + <programlisting> +<![CDATA[ + static void snd_mychip_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +]]> + </programlisting> + </informalexample> + </para> + + <para> + Note that these callbacks are atomic unlike the callbacks of + control API since the ac97 middle-layer routine takes a + spinlock before calling these callbacks. Therefore you cannot + use mutex or schedule in them. However, these are never called + from interrupts unless you do by yourself. + Thus it's not necessarily protected with + <function>spin_lock_irqsave()</function> usually. + </para> + + <para> + There are also other callbacks: + <structfield>reset</structfield>, + <structfield>wait</structfield> and + <structfield>init</structfield>. + </para> + + <para> + The <structfield>reset</structfield> callback is used to reset + the codec. If the chip requires a special way of reset, you can + define this callback. + This callback is atomic, since it can be called in the suspend + phase, too. + </para> + + <para> + The <structfield>wait</structfield> callback is used for a + certain wait at the standard initialization of the codec. If the + chip requires the extra wait-time, define this callback. + This callback is non-atomic. + </para> + + <para> + The <structfield>init</structfield> callback is used for + additional initialization of the codec. This callback is called + after the reset phase, and should be atomic, since it's called + from the resume handler, too. + </para> + </section> + + <section id="api-ac97-updating-registers"> + <title>Updating Registers in The Driver</title> + <para> + If you need to access to the codec from the driver, you can + call the following functions: + <function>snd_ac97_write()</function>, + <function>snd_ac97_read()</function>, + <function>snd_ac97_update()</function> and + <function>snd_ac97_update_bits()</function>. + </para> + + <para> + Both <function>snd_ac97_write()</function> and + <function>snd_ac97_update()</function> functions are used to + set a value to the given register + (<constant>AC97_XXX</constant>). The different between them is + that <function>snd_ac97_update()</function> doesn't write a + value if the given value has been already set, while + <function>snd_ac97_write()</function> always rewrites the + value. + + <informalexample> + <programlisting> +<![CDATA[ + snd_ac97_write(ac97, AC97_MASTER, 0x8080); + snd_ac97_update(ac97, AC97_MASTER, 0x8080); +]]> + </programlisting> + </informalexample> + </para> + + <para> + <function>snd_ac97_read()</function> is used to read the value + of the given register. For example, + + <informalexample> + <programlisting> +<![CDATA[ + value = snd_ac97_read(ac97, AC97_MASTER); +]]> + </programlisting> + </informalexample> + </para> + + <para> + <function>snd_ac97_update_bits()</function> is used to update + some bits of the given register. + + <informalexample> + <programlisting> +<![CDATA[ + snd_ac97_update_bits(ac97, reg, mask, value); +]]> + </programlisting> + </informalexample> + </para> + + <para> + Also, there is a function to change the sample rate (of a + certain register such as + <constant>AC97_PCM_FRONT_DAC_RATE</constant>) when VRA is + supported by the codec: + <function>snd_ac97_set_rate()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_ac97_set_rate(ac97, AC97_PCM_FRONT_DAC_RATE, 44100); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The following registers are available for setting the rate: + <constant>AC97_PCM_MIC_ADC_RATE</constant>, + <constant>AC97_PCM_FRONT_DAC_RATE</constant>, + <constant>AC97_PCM_LR_ADC_RATE</constant>, + <constant>AC97_SPDIF</constant>. When the + <constant>AC97_SPDIF</constant> is specified, the register is + not really changed but the corresponding IEC958 status bits will + be updated. + </para> + </section> + + <section id="api-ac97-clock-adjustment"> + <title>Clock Adjustment</title> + <para> + On some chip, the clock of the codec isn't 48000 but using a + PCI clock (to save a quartz!). In this case, change the field + ac97->clock to the corresponding + value. For example, intel8x0 + and es1968 drivers have the auto-measurement function of the + clock. + </para> + </section> + + <section id="api-ac97-proc-files"> + <title>Proc Files</title> + <para> + The ALSA AC97 interface will create a proc file such as + <filename>/proc/asound/card0/ac97#0</filename> and + <filename>ac97#0regs</filename>. You can refer to these files to + see the current status and registers of the codec. + </para> + </section> + + <section id="api-ac97-multiple-codecs"> + <title>Multiple Codecs</title> + <para> + When there are several codecs on the same card, you need to + call <function>snd_ac97_new()</function> multiple times with + ac97.num=1 or greater. The <structfield>num</structfield> field + specifies the codec + number. + </para> + + <para> + If you have set up multiple codecs, you need to either write + different callbacks for each codec or check + ac97->num in the + callback routines. + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- MIDI (MPU401-UART) Interface --> +<!-- ****************************************************** --> + <chapter id="midi-interface"> + <title>MIDI (MPU401-UART) Interface</title> + + <section id="midi-interface-general"> + <title>General</title> + <para> + Many soundcards have built-in MIDI (MPU401-UART) + interfaces. When the soundcard supports the standard MPU401-UART + interface, most likely you can use the ALSA MPU401-UART API. The + MPU401-UART API is defined in + <filename><sound/mpu401.h></filename>. + </para> + + <para> + Some soundchips have similar but a little bit different + implementation of mpu401 stuff. For example, emu10k1 has its own + mpu401 routines. + </para> + + <para> + In this document, I won't explain the rawmidi interface API, + which is the basis of MPU401-UART implementation. + </para> + + <para> + For details, please check the source, + <filename>core/rawmidi.c</filename>, and examples such as + <filename>drivers/mpu401/mpu401_uart.c</filename> or + <filename>usb/usbmidi.c</filename>. + </para> + </section> + + <section id="midi-interface-constructor"> + <title>Constructor</title> + <para> + For creating a rawmidi object, call + <function>snd_mpu401_uart_new()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_rawmidi_t *rmidi; + snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, integrated, + irq, irq_flags, &rmidi); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The first argument is the card pointer, and the second is the + index of this component. You can create up to 8 rawmidi + devices. + </para> + + <para> + The third argument is the type of the hardware, + <constant>MPU401_HW_XXX</constant>. If it's not a special one, + you can use <constant>MPU401_HW_MPU401</constant>. + </para> + + <para> + The 4th argument is the i/o port address. Many + backward-compatible MPU401 has an i/o port such as 0x330. Or, it + might be a part of its own PCI i/o region. It depends on the + chip design. + </para> + + <para> + When the i/o port address above is a part of the PCI i/o + region, the MPU401 i/o port might have been already allocated + (reserved) by the driver itself. In such a case, pass non-zero + to the 5th argument + (<parameter>integrated</parameter>). Otherwise, pass 0 to it, + and + the mpu401-uart layer will allocate the i/o ports by itself. + </para> + + <para> + Usually, the port address corresponds to the command port and + port + 1 corresponds to the data port. If not, you may change + the <structfield>cport</structfield> field of + <type>mpu401_t</type> manually + afterward. However, <type>mpu401_t</type> pointer is not + returned explicitly by + <function>snd_mpu401_uart_new()</function>. You need to cast + rmidi->private_data to + <type>mpu401_t</type> explicitly, + + <informalexample> + <programlisting> +<![CDATA[ + mpu401_t *mpu; + mpu = snd_magic_cast(mpu401_t, rmidi->private_data, ); +]]> + </programlisting> + </informalexample> + + and reset the cport as you like: + + <informalexample> + <programlisting> +<![CDATA[ + mpu->cport = my_own_control_port; +]]> + </programlisting> + </informalexample> + </para> + + <para> + The 6th argument specifies the irq number for UART. If the irq + is already allocated, pass 0 to the 7th argument + (<parameter>irq_flags</parameter>). Otherwise, pass the flags + for irq allocation + (<constant>SA_XXX</constant> bits) to it, and the irq will be + reserved by the mpu401-uart layer. If the card doesn't generates + UART interrupts, pass -1 as the irq number. Then a timer + interrupt will be invoked for polling. + </para> + </section> + + <section id="midi-interface-interrupt-handler"> + <title>Interrupt Handler</title> + <para> + When the interrupt is allocated in + <function>snd_mpu401_uart_new()</function>, the private + interrupt handler is used, hence you don't have to do nothing + else than creating the mpu401 stuff. Otherwise, you have to call + <function>snd_mpu401_uart_interrupt()</function> explicitly when + a UART interrupt is invoked and checked in your own interrupt + handler. + </para> + + <para> + In this case, you need to pass the private_data of the + returned rawmidi object from + <function>snd_mpu401_uart_new()</function> as the second + argument of <function>snd_mpu401_uart_interrupt()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_mpu401_uart_interrupt(irq, rmidi->private_data, regs); +]]> + </programlisting> + </informalexample> + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- Miscellaneous Devices --> +<!-- ****************************************************** --> + <chapter id="misc-devices"> + <title>Miscellaneous Devices</title> + + <section id="misc-devices-opl3"> + <title>FM OPL3</title> + <para> + The FM OPL3 is still used on many chips (mainly for backward + compatibility). ALSA has a nice OPL3 FM control layer, too. The + OPL3 API is defined in + <filename><sound/opl3.h></filename>. + </para> + + <para> + FM registers can be directly accessed through direct-FM API, + defined in <filename><sound/asound_fm.h></filename>. In + ALSA native mode, FM registers are accessed through + Hardware-Dependant Device direct-FM extension API, whereas in + OSS compatible mode, FM registers can be accessed with OSS + direct-FM compatible API on <filename>/dev/dmfmX</filename> device. + </para> + + <para> + For creating the OPL3 component, you have two functions to + call. The first one is a constructor for <type>opl3_t</type> + instance. + + <informalexample> + <programlisting> +<![CDATA[ + opl3_t *opl3; + snd_opl3_create(card, lport, rport, OPL3_HW_OPL3_XXX, + integrated, &opl3); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The first argument is the card pointer, the second one is the + left port address, and the third is the right port address. In + most cases, the right port is placed at the left port + 2. + </para> + + <para> + The fourth argument is the hardware type. + </para> + + <para> + When the left and right ports have been already allocated by + the card driver, pass non-zero to the fifth argument + (<parameter>integrated</parameter>). Otherwise, opl3 module will + allocate the specified ports by itself. + </para> + + <para> + If this function returns successfully with 0, then create a + hwdep device for this opl3. + + <informalexample> + <programlisting> +<![CDATA[ + snd_hwdep_t *opl3hwdep; + snd_opl3_hwdep_new(opl3, 0, 1, &opl3hwdep); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The first argument is the <type>opl3_t</type> instance you + created, and the second is the index number, usually 0. + </para> + + <para> + The third argument is the index-offset for the sequencer + client assigned to the OPL3 port. When there is an MPU401-UART, + give 1 for here (UART always takes 0). + </para> + </section> + + <section id="misc-devices-hardware-dependent"> + <title>Hardware-Dependent Devices</title> + <para> + Some chips need the access from the user-space for special + controls or for loading the micro code. In such a case, you can + create a hwdep (hardware-dependent) device. The hwdep API is + defined in <filename><sound/hwdep.h></filename>. You can + find examples in opl3 driver or + <filename>isa/sb/sb16_csp.c</filename>. + </para> + + <para> + Creation of the <type>hwdep</type> instance is done via + <function>snd_hwdep_new()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_hwdep_t *hw; + snd_hwdep_new(card, "My HWDEP", 0, &hw); +]]> + </programlisting> + </informalexample> + + where the third argument is the index number. + </para> + + <para> + You can then pass any pointer value to the + <parameter>private_data</parameter>. Again, it should be a + magic-allocated record, so that the cast can be checked more + safely. If you assign a private data, you should define the + destructor, too. The destructor function is set to + <structfield>private_free</structfield> field. + + <informalexample> + <programlisting> +<![CDATA[ + mydata_t *p = snd_magic_kmalloc(mydata_t, 0, GFP_KERNEL); + hw->private_data = p; + hw->private_free = mydata_free; +]]> + </programlisting> + </informalexample> + + and the implementation of destructor would be: + + <informalexample> + <programlisting> +<![CDATA[ + static void mydata_free(snd_hwdep_t *hw) + { + mydata_t *p = snd_magic_cast(mydata_csp_t, + hw->private_data, return); + snd_magic_kfree(p); + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + The arbitrary file operations can be defined for this + instance. The file operators are defined in + <parameter>ops</parameter> table. For example, assume that + this chip needs an ioctl. + + <informalexample> + <programlisting> +<![CDATA[ + hw->ops.open = mydata_open; + hw->ops.ioctl = mydata_ioctl; + hw->ops.release = mydata_release; +]]> + </programlisting> + </informalexample> + + And implement the callback functions as you like. + </para> + </section> + + <section id="misc-devices-IEC958"> + <title>IEC958 (S/PDIF)</title> + <para> + Usually the controls for IEC958 devices are implemented via + control interface. There is a macro to compose a name string for + IEC958 controls, <function>SNDRV_CTL_NAME_IEC958()</function> + defined in <filename><include/asound.h></filename>. + </para> + + <para> + There are some standard controls for IEC958 status bits. These + controls use the type <type>SNDRV_CTL_ELEM_TYPE_IEC958</type>, + and the size of element is fixed as 4 bytes array + (value.iec958.status[x]). For <structfield>info</structfield> + callback, you don't specify + the value field for this type (the count field must be set, + though). + </para> + + <para> + <quote>IEC958 Playback Con Mask</quote> is used to return the + bit-mask for the IEC958 status bits of consumer mode. Similarly, + <quote>IEC958 Playback Pro Mask</quote> returns the bitmask for + professional mode. They are read-only controls, and are defined + as MIXER controls (iface = + <constant>SNDRV_CTL_ELEM_IFACE_MIXER</constant>). + </para> + + <para> + Meanwhile, <quote>IEC958 Playback Default</quote> control is + defined for getting and setting the current default IEC958 + bits. Note that this one is usually defined as a PCM control + (iface = <constant>SNDRV_CTL_ELEM_IFACE_PCM</constant>), + although in some places it's defined as a MIXER control. + </para> + + <para> + In addition, you can define the control switches to + enable/disable or to set the raw bit mode. The implementation + will depend on the chip, but the control should be named as + <quote>IEC958 xxx</quote>, preferably using + <function>SNDRV_CTL_NAME_IEC958()</function> macro. + </para> + + <para> + You can find several cases, for example, + <filename>pci/emu10k1</filename>, + <filename>pci/ice1712</filename>, or + <filename>pci/cmipci.c</filename>. + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- Buffer and Memory Management --> +<!-- ****************************************************** --> + <chapter id="buffer-and-memory"> + <title>Buffer and Memory Management</title> + + <section id="buffer-and-memory-buffer-types"> + <title>Buffer Types</title> + <para> + ALSA provides several different buffer allocation functions + depending on the bus and the architecture. All these have a + consistent API. The allocation of physically-contiguous pages is + done via + <function>snd_malloc_xxx_pages()</function> function, where xxx + is the bus type. + </para> + + <para> + The allocation of pages with fallback is + <function>snd_malloc_xxx_pages_fallback()</function>. This + function tries to allocate the specified pages but if the pages + are not available, it tries to reduce the page sizes until the + enough space is found. + </para> + + <para> + For releasing the space, call + <function>snd_free_xxx_pages()</function> function. + </para> + + <para> + Usually, ALSA drivers try to allocate and reserve + a large contiguous physical space + at the time the module is loaded for the later use. + This is called <quote>pre-allocation</quote>. + As already written, you can call the following function at the + construction of pcm instance (in the case of PCI bus). + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_lib_preallocate_pci_pages_for_all(pci, pcm, size, max); +]]> + </programlisting> + </informalexample> + + where <parameter>size</parameter> is the byte size to be + pre-allocated and the <parameter>max</parameter> is the maximal + size to be changed via <filename>prealloc</filename> proc file. + The allocator will try to get as the large area as possible + within the given size. + There are different versions of pre-allocator for different + buses. + </para> + + <para> + Once when the buffer is pre-allocated, you can use the + allocator in the <structfield>hw_params</structfield> callback + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_lib_malloc_pages(substream, size); +]]> + </programlisting> + </informalexample> + + Note that you have to pre-allocate to use this function + (i.e. you cannot use this function for + <link linkend="buffer-and-memory-non-contiguous"><citetitle> + a scatter-gather buffer</citetitle></link>). + </para> + </section> + + <section id="buffer-and-memory-external-hardware"> + <title>External Hardware Buffers</title> + <para> + Some chips have their own hardware buffers and the DMA + transfer from the host memory is not available. In such a case, + you need to either 1) copy/set the audio data directly to the + external hardware buffer, or 2) make an intermediate buffer and + copy/set the data from it to the external hardware buffer in + interrupts (or in tasklets, preferably). + </para> + + <para> + The first case works fine if the external hardware buffer is enough + large. This method doesn't need any extra buffers and thus is + more effective. You need to define the + <structfield>copy</structfield> and + <structfield>silence</structfield> callbacks for + the data transfer. However, there is a drawback: it cannot + be mmapped. The examples are GUS's GF1 PCM or emu8000's + wavetable PCM. + </para> + + <para> + The second case allows the mmap of the buffer, although you have + to handle an interrupt or a tasklet for transferring the data + from the intermediate buffer to the hardware buffer. You can find an + example in vxpocket driver. + </para> + + <para> + Another case is that the chip uses a PCI memory-map + region for the buffer instead of the host memory. In this case, + mmap is available only on certain architectures like intel. In + non-mmap mode, the data cannot be transferred as the normal + way. Thus you need to define <structfield>copy</structfield> and + <structfield>silence</structfield> callbacks as well + as in the cases above. The examples are found in + <filename>rme32.c</filename> and <filename>rme96.c</filename>. + </para> + + <para> + The implementation of <structfield>copy</structfield> and + <structfield>silence</structfield> callbacks depends upon + whether the hardware supports interleaved or non-interleaved + samples. The <structfield>copy</structfield> callback is + defined like below, a bit + differently depending whether the direction is playback or + capture: + + <informalexample> + <programlisting> +<![CDATA[ + static int playback_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count); + static int capture_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count); +]]> + </programlisting> + </informalexample> + </para> + + <para> + In the case of interleaved samples, the second argument + (<parameter>channel</parameter>) is not used. The third argument + (<parameter>pos</parameter>) points the + current position offset in frames. + </para> + + <para> + The meaning of the fourth argument is different between + playback and capture. For playback, it holds the source data + pointer, and for capture, it's the destination data pointer. + </para> + + <para> + The last argument is the number of frames to be copied. + </para> + + <para> + What you have to do in this callback is again different + between playback and capture directions. In the case of + playback, you do: copy the given amount of data + (<parameter>count</parameter>) at the specified pointer + (<parameter>src</parameter>) to the specified offset + (<parameter>pos</parameter>) on the hardware buffer. When + coded like memcpy-like way, the copy would be like: + + <informalexample> + <programlisting> +<![CDATA[ + my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src, + frames_to_bytes(runtime, count)); +]]> + </programlisting> + </informalexample> + </para> + + <para> + For the capture direction, you do: copy the given amount of + data (<parameter>count</parameter>) at the specified offset + (<parameter>pos</parameter>) on the hardware buffer to the + specified pointer (<parameter>dst</parameter>). + + <informalexample> + <programlisting> +<![CDATA[ + my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos), + frames_to_bytes(runtime, count)); +]]> + </programlisting> + </informalexample> + + Note that both of the position and the data amount are given + in frames. + </para> + + <para> + In the case of non-interleaved samples, the implementation + will be a bit more complicated. + </para> + + <para> + You need to check the channel argument, and if it's -1, copy + the whole channels. Otherwise, you have to copy only the + specified channel. Please check + <filename>isa/gus/gus_pcm.c</filename> as an example. + </para> + + <para> + The <structfield>silence</structfield> callback is also + implemented in a similar way. + + <informalexample> + <programlisting> +<![CDATA[ + static int silence(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The meanings of arguments are identical with the + <structfield>copy</structfield> + callback, although there is no <parameter>src/dst</parameter> + argument. In the case of interleaved samples, the channel + argument has no meaning, as well as on + <structfield>copy</structfield> callback. + </para> + + <para> + The role of <structfield>silence</structfield> callback is to + set the given amount + (<parameter>count</parameter>) of silence data at the + specified offset (<parameter>pos</parameter>) on the hardware + buffer. Suppose that the data format is signed (that is, the + silent-data is 0), and the implementation using a memset-like + function would be like: + + <informalexample> + <programlisting> +<![CDATA[ + my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0, + frames_to_bytes(runtime, count)); +]]> + </programlisting> + </informalexample> + </para> + + <para> + In the case of non-interleaved samples, again, the + implementation becomes a bit more complicated. See, for example, + <filename>isa/gus/gus_pcm.c</filename>. + </para> + </section> + + <section id="buffer-and-memory-non-contiguous"> + <title>Non-Contiguous Buffers</title> + <para> + If your hardware supports the page table like emu10k1 or the + buffer descriptors like via82xx, you can use the scatter-gather + (SG) DMA. ALSA provides an interface for handling SG-buffers. + The API is provided in <filename><sound/pcm_sgbuf.h></filename>. + </para> + + <para> + For creating the SG-buffer handler, call + <function>snd_pcm_sgbuf_init()</function> in the + <structfield>open</structfield> callback + of a pcm substream (or in the constructor of the pcm). + You need to pass the + <structname>pci_dev</structname> struct pointer of the chip + and the default table size (which can be changed + dynamically). The <type>snd_sg_buf_t</type> instance is created as + substream->dma_private. You can cast + the pointer like: + + <informalexample> + <programlisting> +<![CDATA[ + snd_pcm_sgbuf_t *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, + substream->dma_private, return -EINVAL); +]]> + </programlisting> + </informalexample> + </para> + + <para> + Then call <function>snd_pcm_sgbuf_alloc()</function> instead + of normal <function>snd_pcm_lib_malloc_pages()</function> in + <structfield>hw_params</structfield> callback. The SG-handler + will allocate single pages + and build the table on sgbuf->table. The pointer and the + physical address of each page is stored in this table. You can + get the physical address at a certain offset via + <function>snd_pcm_sgbuf_get_addr()</function>. + </para> + + <para> + When a SG-handler is used, you need to set + <function>snd_pcm_sgbuf_ops_copy_playback</function> and + <function>snd_pcm_ops_silence</function> as the + <structfield>copy</structfield> and the + <structfield>silence</structfield> callbacks, respectively. + All the jobs described in the previous section will be done + by these helper functions. + Also, the <structfield>page</structfield> callback must be set as + <function>snd_pcm_sgbuf_ops_page</function>. + </para> + + <para> + For releasing the data, call + <function>snd_pcm_sgbuf_free()</function> in the + <structfield>hw_free</structfield> callback, and call + <function>snd_pcm_sgbuf_delete()</function> in the + <structfield>close</structfield> callback (or in the destructor, + if <function>snd_pcm_sgbuf_init()</function> was called in the + constructor). + </para> + + <para> + Note that you must not do pre-allocation if a SG-handler is used. + They conflict with each other. + </para> + </section> + + <section id="buffer-and-memory-vmalloced"> + <title>Vmalloc'ed Buffers</title> + <para> + It's possible to use a buffer allocated via + <function>vmalloc</function>, for example, for an intermediate + buffer. Since the allocated pages are not contiguous, you need + to set the <structfield>page</structfield> callback to obtain + the physical address at every offset. + </para> + + <para> + The implementation of <structfield>page</structfield> callback + would be like this: + + <informalexample> + <programlisting> +<![CDATA[ + #include <linux/vmalloc.h> + #include <linux/pagemap.h> + + /* get the physical page pointer on the given offset */ + static struct page *mychip_page(snd_pcm_substream_t *subs, + unsigned long offset) + { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long lpage; + void *pageptr = subs->runtime->dma_area + offset; + struct page *page = NOPAGE_SIGBUS; + + lpage = VMALLOC_VMADDR(pageptr); + spin_lock(&init_mm.page_table_lock); + pgd = pgd_offset(&init_mm, lpage); + pmd = pmd_offset(pgd, lpage); + pte = pte_offset(pmd, lpage); + page = pte_page(*pte); + spin_unlock(&init_mm.page_table_lock); + + return page; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + You don't need <structfield>copy</structfield> and + <structfield>silence</structfield> callbacks in this case, + because the copy/set operations are available on the virtual + address. + </para> + </section> + + </chapter> + + +<!-- ****************************************************** --> +<!-- Proc Interface --> +<!-- ****************************************************** --> + <chapter id="proc-interface"> + <title>Proc Interface</title> + <para> + ALSA provides an easy interface for procfs. The proc files are + very useful for debugging. I recommend you set up proc files if + you write a driver and want to get a running status or register + dumps. The API is found in + <filename><sound/info.h></filename>. + </para> + + <para> + For creating a proc file, call + <function>snd_card_proc_new()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_info_entry_t *entry; + int err = snd_card_proc_new(card, "my-file", &entry); +]]> + </programlisting> + </informalexample> + + where the second argument specifies the proc-file name to be + created. The above example will create a file + <filename>my-file</filename> under the card directory, + e.g. <filename>/proc/asound/card0/my-file</filename>. + </para> + + <para> + Like other components, the proc entry created via + <function>snd_card_proc_new()</function> will be registered and + released automatically in the card registration and release + functions. + </para> + + <para> + When the creation is successful, the function stores a new + instance at the pointer given in the third argument. + It is initialized as a text proc file for read only. For using + this proc file as a read-only text file as it is, set the read + callback with a private data via + <function>snd_info_set_text_ops()</function>. + + <informalexample> + <programlisting> +<![CDATA[ + snd_info_set_text_ops(entry, chip, my_proc_read); +]]> + </programlisting> + </informalexample> + + where the second argument (<parameter>chip</parameter>) is the + private data to be used in the callbacks and the third + (<parameter>my_proc_read</parameter>) is the callback function, which + is defined like + + <informalexample> + <programlisting> +<![CDATA[ + static void my_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer); +]]> + </programlisting> + </informalexample> + + </para> + + <para> + In the read callback, use <function>snd_iprintf()</function> for + output strings, which works just like normal + <function>printf()</function>. For example, + + <informalexample> + <programlisting> +<![CDATA[ + static void my_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) + { + chip_t *cm = snd_magic_cast(mychip_t, + entry->private_data, return); + + snd_iprintf(buffer, "This is my chip!\n"); + snd_iprintf(buffer, "Port = %ld\n", chip->port); + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + The file permission can be changed afterwards. As default, it's + set as read only for all users. If you want to add the write + permission to the user (root as default), set like below: + + <informalexample> + <programlisting> +<![CDATA[ + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; +]]> + </programlisting> + </informalexample> + + and set the write buffer size and the callback + + <informalexample> + <programlisting> +<![CDATA[ + entry->c.text.write_size = 256; + entry->c.text.write = my_proc_write; +]]> + </programlisting> + </informalexample> + </para> + + <para> + The buffer size for read is set to 1024 implicitly by + <function>snd_info_set_text_ops()</function>. It should suffice + in most cases (the size will be aligned to + <constant>PAGE_SIZE</constant> anyway), but if you need to handle + very large text files, you can set it explicitly, too. + + <informalexample> + <programlisting> +<![CDATA[ + entry->c.text.read_size = 65536; +]]> + </programlisting> + </informalexample> + </para> + + <para> + For the write callback, you can use + <function>snd_info_get_line()</function> to get a text line, and + <function>snd_info_get_str()</function> to retrieve a string from + the line. Some examples are found in + <filename>core/oss/mixer_oss.c</filename>, core/oss/and + <filename>pcm_oss.c</filename>. + </para> + + <para> + For a raw-data proc-file, set the attributes like the following: + + <informalexample> + <programlisting> +<![CDATA[ + static struct snd_info_entry_ops my_file_io_ops = { + .read = my_file_io_read, + }; + + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &my_file_io_ops; + entry->size = 4096; + entry->mode = S_IFREG | S_IRUGO; +]]> + </programlisting> + </informalexample> + </para> + + <para> + The callback is much more complicated than the text-file + version. You need to use a low-level i/o functions such as + <function>copy_from/to_user()</function> to transfer the + data. Also, you have to keep tracking the file position, too. + + <informalexample> + <programlisting> +<![CDATA[ + static long my_file_io_read(snd_info_entry_t *entry, + void *file_private_data, + struct file *file, + char *buf, long count) + { + long size = count; + if (file->f_pos + size > local_max_size) + size = local_max_size - file->f_pos; + if (copy_to_user(buf, local_data + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + return size; + } +]]> + </programlisting> + </informalexample> + </para> + + </chapter> + + +<!-- ****************************************************** --> +<!-- Power Management --> +<!-- ****************************************************** --> + <chapter id="power-management"> + <title>Power Management</title> + <para> + If the chip is supposed to work with with suspend/resume + functions, you need to add the power-management codes to the + driver. The additional codes for the power-management should be + <function>ifdef</function>'ed with + <constant>CONFIG_PM</constant>. + </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. + + <informalexample> + <programlisting> +<![CDATA[ + #ifndef PCI_OLD_SUSPEND + static int snd_my_suspend(struct pci_dev *dev, u32 state) + { + mychip_t *chip = snd_magic_cast(mychip_t, + pci_get_drvdata(dev), return -ENXIO); + mychip_suspend(chip); + return 0; + } + static int snd_my_resume(struct pci_dev *dev) + { + mychip_t *chip = snd_magic_cast(mychip_t, + pci_get_drvdata(dev), return -ENXIO); + mychip_resume(chip); + 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> + </informalexample> + </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>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> + <listitem><para>Set the power-state as D3hot by calling <function>snd_power_change_state()</function>.</para></listitem> + </orderedlist> + </para> + + <para> + A typical code would be like: + + <informalexample> + <programlisting> +<![CDATA[ + static void mychip_suspend(mychip_t *chip) + { + snd_card_t *card = chip->card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + // (2) + snd_pcm_suspend_all(chip->pcm); + // (3) + snd_mychip_save_registers(chip); + // (4) + snd_mychip_stop_hardware(chip); + // (5) + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + 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>Enable the pci device again by calling + <function>pci_enable_device()</function>.</para></listitem> + <listitem><para>Re-initialize the chip.</para></listitem> + <listitem><para>Restore the saved registers if necessary.</para></listitem> + <listitem><para>Resume the mixer, e.g. calling + <function>snd_ac97_resume()</function>.</para></listitem> + <listitem><para>Restart the hardware (if any).</para></listitem> + <listitem><para>Set the power-state as D0 by calling + <function>snd_power_change_state()</function>.</para></listitem> + </orderedlist> + </para> + + <para> + A typical code would be like: + + <informalexample> + <programlisting> +<![CDATA[ + static void mychip_resume(mychip_t *chip) + { + snd_card_t *card = chip->card; + // (1) + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + // (2) + pci_enable_device(chip->pci); + // (3) + snd_mychip_reinit_chip(chip); + // (4) + snd_mychip_restore_registers(chip); + // (5) + snd_ac97_resume(chip->ac97); + // (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; + } +]]> + </programlisting> + </informalexample> + </para> + + <para> + OK, we have all callbacks now. Let's set up them now. In the + initialization of the card, add the following: + + <informalexample> + <programlisting> +<![CDATA[ + static int __devinit snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + .... + snd_card_t *card; + .... + #ifdef CONFIG_PM + card->set_power_state = snd_mychip_set_power_state; + card->power_state_private_data = chip; + #endif + .... + } +]]> + </programlisting> + </informalexample> + </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. + The allocated buffer should be released in the corresponding + destructor. + </para> + + <para> + And next, set suspend/resume callbacks to the pci_driver, + + <informalexample> + <programlisting> +<![CDATA[ + static struct pci_driver driver = { + .name = "My Chip", + .... + #ifdef CONFIG_PM + .suspend = snd_mychip_suspend, + .resume = snd_mychip_resume, + #endif + }; +]]> + </programlisting> + </informalexample> + </para> + + <para> + Last but not least: Please keep in mind that you cannot call + <function>schedule()</function> during the suspend and the resume + callbacks. If any delay is necessary, you have to use + <function>mdelay()</function> or <function>udelay()</function> + instead of <function>schedule_timeout()</function>! + Of course, semaphores cannot be used, too, which will invoke sleep + inside. + </para> + </chapter> + + +<!-- ****************************************************** --> +<!-- Module Parameters --> +<!-- ****************************************************** --> + <chapter id="module-parameters"> + <title>Module Parameters</title> + <para> + There are standard module options for ALSA. At least, each + module should have <parameter>index</parameter>, + <parameter>id</parameter> and <parameter>enable</parameter> + options. + </para> + + <para> + If the module supports multiple cards (usually up to + 8 = <constant>SNDRV_CARDS</constant> cards), they should be + arrays. The default initial values are defined already as + constants for ease of programming: + + <informalexample> + <programlisting> +<![CDATA[ + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; + static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +]]> + </programlisting> + </informalexample> + </para> + + <para> + If the module supports only a single card, they could be single + variables, instead. <parameter>enable</parameter> option is not + always necessary in this case, but it wouldn't be so bad to have a + dummy option for compatibility. + </para> + + <para> + The module parameters must be declared with the standard + <function>MODULE_PARM()</function> and + <function>MODULE_PARM_DESC()</function> macros. The ALSA provides + an additional macro, <function>MODULE_PARM_SYNTAX()</function>, + for describing its syntax. The strings will be written to + <filename>/lib/modules/XXX/modules.generic_string</filename> + file. + </para> + + <para> + For convenience, the typical string arguments given to + <function>MODULE_PARM_SYNTAX()</function> are defined in + <filename><sound/initval.h></filename>, such as + <constant>SNDRV_ID_DESC</constant> or + <constant>SNDRV_ENABLED</constant>. + </para> + + <para> + The typical coding would be like below: + + <informalexample> + <programlisting> +<![CDATA[ + #define CARD_NAME "My Chip" + + MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); + MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); + MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); + MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); + MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); + MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); + MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); + MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); + MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +]]> + </programlisting> + </informalexample> + </para> + + <para> + Also, don't forget to define the module description, classes, + license and devices. Especially, the recent modprobe requires to + define the module license as GPL, etc., otherwise the system is + shown as <quote>tainted</quote>. + + <informalexample> + <programlisting> +<![CDATA[ + MODULE_DESCRIPTION("My Chip"); + MODULE_CLASSES("{sound}"); + MODULE_LICENSE("GPL"); + MODULE_DEVICES("{{Vendor,My Chip Name}}"); +]]> + </programlisting> + </informalexample> + </para> + + <para> + For building the driver into kernel, you should define the + <function>setup()</function> function in addition, too. + ALSA provides <function>get_id()</function> function to retrieve + a string argument from the kernel boot parameters. + + <informalexample> + <programlisting> +<![CDATA[ + #ifndef MODULE + + /* format is: snd-mychip=enable,index,id */ + + static int __init alsa_card_mychip_setup(char *str) + { + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; + } + + __setup("snd-mychip=", alsa_card_mychip_setup); + + #endif /* ifndef MODULE */ +]]> + </programlisting> + </informalexample> + </para> + </chapter> + + +<!-- ****************************************************** --> +<!-- Useful Functions --> +<!-- ****************************************************** --> + <chapter id="useful-functions"> + <title>Useful Functions</title> + + <section id="useful-functions-snd-printk"> + <title><function>snd_printk()</function> and friends</title> + <para> + ALSA provides a verbose version of + <function>printk()</function> function. If a kernel config + <constant>CONFIG_SND_VERBOSE_PRINTK</constant> is set, this + function prints the given message together with the file name + and the line of the caller. The <constant>KERN_XXX</constant> + prefix is processed as + well as the original <function>printk()</function> does, so it's + recommended to add this prefix, e.g. + + <informalexample> + <programlisting> +<![CDATA[ + snd_printk(KERN_ERR "Oh my, sorry, it's extremely bad!\n"); +]]> + </programlisting> + </informalexample> + </para> + + <para> + There are also <function>printk()</function>'s for + debugging. <function>snd_printd()</function> can be used for + general debugging purposes. If + <constant>CONFIG_SND_DEBUG</constant> is set, this function is + compiled, and works just like + <function>snd_printk()</function>. If the ALSA is compiled + without the debugging flag, it's ignored. + </para> + + <para> + <function>snd_printdd()</function> is compiled in only when + <constant>CONFIG_SND_DEBUG_DETECT</constant> is set. Please note + that <constant>DEBUG_DETECT</constant> is not set as default + even if you configure the alsa-driver with + <option>--with-debug=full</option> option. You need to give + explicitly <option>--with-debug=detect</option> option instead. + </para> + </section> + + <section id="useful-functions-snd-assert"> + <title><function>snd_assert()</function></title> + <para> + <function>snd_assert()</function> macro is similar with the + normal <function>assert()</function> macro. For example, + + <informalexample> + <programlisting> +<![CDATA[ + snd_assert(pointer != NULL, return -EINVAL); +]]> + </programlisting> + </informalexample> + </para> + + <para> + The first argument is the expression to evaluate, and the + second argument is the action if it fails. When + <constant>CONFIG_SND_DEBUG</constant>, is set, it will show an + error message such as <computeroutput>BUG? (xxx) (called from + yyy)</computeroutput>. When no debug flag is set, this is + ignored. + </para> + </section> + + <section id="useful-functions-snd-runtime-check"> + <title><function>snd_runtime_check()</function></title> + <para> + This macro is quite similar with + <function>snd_assert()</function>. Unlike + <function>snd_assert()</function>, the expression is always + evaluated regardless of + <constant>CONFIG_SND_DEBUG</constant>. When + <constant>CONFIG_SND_DEBUG</constant> is set, the macro will + show a message like <computeroutput>ERROR (xx) (called from + yyy)</computeroutput>. + </para> + </section> + + <section id="useful-functions-snd-bug"> + <title><function>snd_BUG()</function></title> + <para> + It calls <function>snd_assert(0,)</function> -- that is, just + prints the error message at the point. It's useful to show that + a fatal error happens there. + </para> + </section> + </chapter> + + +<!-- ****************************************************** --> +<!-- Acknowledgments --> +<!-- ****************************************************** --> + <chapter id="acknowledments"> + <title>Acknowledgments</title> + <para> + I would like to thank Phil Kerr for his help for improvement and + corrections of this document. + </para> + <para> + Kevin Conder reformatted the original plain-text to the + DocBook format. + </para> + </chapter> + + +</book> diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index fd6c403d0828..47e5e67e990e 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -196,6 +196,8 @@ #define AC97_CXR_SPDIF_AC3 0x2 /* specific - ALC */ +#define AC97_ALC650_SURR_DAC_VOL 0x64 +#define AC97_ALC650_LFE_DAC_VOL 0x66 #define AC97_ALC650_MULTICH 0x6a #define AC97_ALC650_CLOCK 0x7a @@ -235,8 +237,6 @@ struct _snd_ac97 { unsigned short (*read) (ac97_t *ac97, unsigned short reg); void (*wait) (ac97_t *ac97); void (*init) (ac97_t *ac97); - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_regs_entry; void *private_data; void (*private_free) (ac97_t *ac97); /* --- */ diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h index 9ae8eb32db5f..18f1fec77e2d 100644 --- a/include/sound/ad1848.h +++ b/include/sound/ad1848.h @@ -162,6 +162,7 @@ int snd_ad1848_create(snd_card_t * card, ad1848_t ** chip); int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm); +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction); int snd_ad1848_mixer(ad1848_t * chip); void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); diff --git a/include/sound/ak4531_codec.h b/include/sound/ak4531_codec.h index bf8294d6722c..ad7b52bc2627 100644 --- a/include/sound/ak4531_codec.h +++ b/include/sound/ak4531_codec.h @@ -68,7 +68,6 @@ typedef struct _snd_ak4531 ak4531_t; struct _snd_ak4531 { void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val); - snd_info_entry_t *proc_entry; void *private_data; void (*private_free) (ak4531_t *ak4531); /* --- */ diff --git a/include/sound/core.h b/include/sound/core.h index 9d705453eca1..68b3a692eff7 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -55,6 +55,7 @@ typedef enum { SNDRV_DEV_TIMER, SNDRV_DEV_SEQUENCER, SNDRV_DEV_HWDEP, + SNDRV_DEV_INFO, SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) } snd_device_type_t; @@ -281,6 +282,8 @@ void snd_free_pages(void *ptr, unsigned long size); void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr); void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr); +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr); +#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr) #endif #ifdef CONFIG_SBUS void *snd_malloc_sbus_pages(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr); diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h index dc849a634aec..f60ea070bacd 100644 --- a/include/sound/cs46xx.h +++ b/include/sound/cs46xx.h @@ -1673,7 +1673,6 @@ typedef struct { unsigned long remap_addr; unsigned long size; struct resource *resource; - void *proc_entry; } snd_cs46xx_region_t; struct _snd_cs46xx { @@ -1726,7 +1725,6 @@ struct _snd_cs46xx { spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; - snd_info_entry_t *proc_entry; int amplifier; void (*amplifier_ctrl)(cs46xx_t *, int); diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 7d6b34144c0d..afdb277148c0 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -25,12 +25,11 @@ #ifdef __KERNEL__ -#include "pcm.h" -#include "pcm_sgbuf.h" -#include "rawmidi.h" -#include "hwdep.h" -#include "ac97_codec.h" -#include "util_mem.h" +#include <sound/pcm.h> +#include <sound/rawmidi.h> +#include <sound/hwdep.h> +#include <sound/ac97_codec.h> +#include <sound/util_mem.h> #include <asm/io.h> #ifndef PCI_VENDOR_ID_CREATIVE @@ -993,13 +992,6 @@ struct _snd_emu10k1 { emu10k1_midi_t midi2; /* for audigy */ unsigned int efx_voices_mask[2]; - - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_entry_fx8010_gpr; - snd_info_entry_t *proc_entry_fx8010_tram_data; - snd_info_entry_t *proc_entry_fx8010_tram_addr; - snd_info_entry_t *proc_entry_fx8010_code; - snd_info_entry_t *proc_entry_fx8010_iblocks; }; int snd_emu10k1_create(snd_card_t * card, @@ -1045,7 +1037,7 @@ unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); /* memory allocation */ -snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf); +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream); int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); @@ -1063,7 +1055,6 @@ int snd_emu10k1_audigy_midi(emu10k1_t * emu); /* proc interface */ int snd_emu10k1_proc_init(emu10k1_t * emu); -int snd_emu10k1_proc_done(emu10k1_t * emu); #endif /* __KERNEL__ */ diff --git a/include/sound/gus.h b/include/sound/gus.h index 2e84f2687588..a896bf9ff626 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -210,7 +210,6 @@ typedef struct _snd_gf1_mem { snd_gf1_bank_info_t banks_16[4]; snd_gf1_mem_block_t *first; snd_gf1_mem_block_t *last; - snd_info_entry_t *info_entry; struct semaphore memory_mutex; } snd_gf1_mem_t; @@ -332,8 +331,6 @@ struct _snd_gf1 { unsigned int rom_banks; /* GUS's ROM banks */ snd_gf1_mem_t mem_alloc; - snd_info_entry_t *ram_entries[4]; - snd_info_entry_t *rom_entries[4]; /* registers */ unsigned short reg_page; @@ -452,9 +449,6 @@ struct _snd_gus_card { int timer_dev; /* timer device */ struct _snd_gf1 gf1; /* gf1 specific variables */ -#ifdef CONFIG_SND_DEBUG - snd_info_entry_t *irq_entry; -#endif snd_pcm_t *pcm; snd_pcm_substream_t *pcm_cap_substream; unsigned int c_dma_size; @@ -601,7 +595,6 @@ int snd_gf1_mem_done(snd_gus_card_t * gus); /* gus_mem_proc.c */ int snd_gf1_mem_proc_init(snd_gus_card_t * gus); -int snd_gf1_mem_proc_done(snd_gus_card_t * gus); /* gus_dma.c */ @@ -676,7 +669,6 @@ int snd_gus_initialize(snd_gus_card_t * gus); void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs); #ifdef CONFIG_SND_DEBUG void snd_gus_irq_profile_init(snd_gus_card_t *gus); -void snd_gus_irq_profile_done(snd_gus_card_t *gus); #endif /* gus_uart.c */ diff --git a/include/sound/info.h b/include/sound/info.h index 6984c6e736dc..c9bb51aedb04 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -141,6 +141,19 @@ struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de); +/* for card drivers */ +int snd_card_proc_new(snd_card_t *card, const char *name, snd_info_entry_t **entryp); + +inline static void snd_info_set_text_ops(snd_info_entry_t *entry, + void *private_data, + void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + entry->private_data = private_data; + entry->c.text.read_size = 1024; + entry->c.text.read = read; +} + + #else #define snd_seq_root NULL @@ -169,6 +182,9 @@ static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mod static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de) { ; } +#define snd_card_proc_new(card,name,entryp) 0 /* always success */ +#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/ + #endif /* diff --git a/include/sound/pcm.h b/include/sound/pcm.h index d97f7e751d9a..0bc5fef9456e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -50,7 +50,7 @@ typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; typedef struct sndrv_mask snd_mask_t; -#define _snd_pcm_substream_chip(substream) ((substream)->pcm->private_data) +#define _snd_pcm_substream_chip(substream) ((substream)->private_data) #define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) #define _snd_pcm_chip(pcm) ((pcm)->private_data) #define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) @@ -120,10 +120,11 @@ typedef struct _snd_pcm_ops { #define SNDRV_PCM_TRIGGER_SUSPEND 5 #define SNDRV_PCM_TRIGGER_RESUME 6 -#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 0 /* continuous no-DMA memory */ -#define SNDRV_PCM_DMA_TYPE_ISA 1 /* ISA continuous */ -#define SNDRV_PCM_DMA_TYPE_PCI 2 /* PCI continuous */ -#define SNDRV_PCM_DMA_TYPE_SBUS 3 /* SBUS continuous */ +#define SNDRV_PCM_DMA_TYPE_UNKNOWN 0 /* not defined */ +#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ +#define SNDRV_PCM_DMA_TYPE_ISA 2 /* ISA continuous */ +#define SNDRV_PCM_DMA_TYPE_PCI 3 /* PCI continuous */ +#define SNDRV_PCM_DMA_TYPE_SBUS 4 /* SBUS continuous */ /* If you change this don't forget to change rates[] table in pcm_native.c */ #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ @@ -363,6 +364,7 @@ struct _snd_pcm_runtime { struct _snd_pcm_substream { snd_pcm_t *pcm; snd_pcm_str_t *pstr; + void *private_data; /* copied from pcm->private_data */ int number; char name[32]; /* substream name */ int stream; /* stream (direction) */ diff --git a/include/sound/pcm_sgbuf.h b/include/sound/pcm_sgbuf.h index bc6474559c57..e5dda1d053af 100644 --- a/include/sound/pcm_sgbuf.h +++ b/include/sound/pcm_sgbuf.h @@ -63,5 +63,8 @@ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count); struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); +#define _snd_pcm_substream_sgbuf(substream) ((substream)->dma_private) +#define snd_pcm_substream_sgbuf(substream) snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return -ENXIO) + #endif /* __SOUND_PCM_SGBUF_H */ diff --git a/include/sound/sb.h b/include/sound/sb.h index 53f243b7bf64..4b9b902e9ea7 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -305,6 +305,7 @@ int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi); /* sb16_init.c */ int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction); int snd_sb16dsp_configure(sb_t *chip); /* sb16.c */ void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs); diff --git a/include/sound/sb16_csp.h b/include/sound/sb16_csp.h index dfdb71dc6a7d..eb8368b56b16 100644 --- a/include/sound/sb16_csp.h +++ b/include/sound/sb16_csp.h @@ -159,7 +159,6 @@ struct snd_sb_csp { snd_kcontrol_t *qsound_space; struct semaphore access_mutex; /* locking */ - snd_info_entry_t *proc; /* proc interface */ }; int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep); diff --git a/include/sound/sndmagic.h b/include/sound/sndmagic.h index 83495c4ce9e5..560f248b9cdf 100644 --- a/include/sound/sndmagic.h +++ b/include/sound/sndmagic.h @@ -27,10 +27,45 @@ void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); -void snd_magic_kfree(void *ptr); -#define snd_magic_kcalloc(type, extra, flags) (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) -#define snd_magic_kmalloc(type, extra, flags) (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) +/** + * snd_magic_kmalloc - allocate a record with a magic-prefix + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Allocates a record of the given type with the extra space and + * returns its pointer. The allocated record has a secret magic-key + * to be checked via snd_magic_cast() for safe casts. + * + * The allocated pointer must be released via snd_magic_kfree(). + * + * The "struct xxx" style cannot be used as the type argument + * because the magic-key constant is generated from the type-name + * string. + */ +#define snd_magic_kmalloc(type, extra, flags) \ + (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) +/** + * snd_magic_kcalloc - allocate a record with a magic-prefix and initialize + * @type: the type to allocate a record (like xxx_t) + * @extra: the extra size to allocate in bytes + * @flags: the allocation condition (GFP_XXX) + * + * Works like snd_magic_kmalloc() but this clears the area with zero + * automatically. + */ +#define snd_magic_kcalloc(type, extra, flags) \ + (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) + +/** + * snd_magic_kfree - release the allocated area + * @ptr: the pointer allocated via snd_magic_kmalloc() or snd_magic_kcalloc() + * + * Releases the memory area allocated via snd_magic_kmalloc() or + * snd_magic_kcalloc() function. + */ +void snd_magic_kfree(void *ptr); static inline unsigned long _snd_magic_value(void *obj) { @@ -44,7 +79,19 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) -#define snd_magic_cast(type, ptr, action...) (type *) ({\ +/** + * snd_magic_cast - check and cast the magic-allocated pointer + * @type: the type of record to cast + * @ptr: the magic-allocated pointer + * @action...: the action to do if failed + * + * This macro provides a safe cast for the given type, which was + * allocated via snd_magic_kmalloc() or snd_magic_kcallc(). + * If the pointer is invalid, i.e. the cast-type doesn't match, + * the action arguments are called with a debug message. + */ +#define snd_magic_cast(type, ptr, action...) \ + (type *) ({\ void *__ptr = ptr;\ unsigned long __magic = _snd_magic_value(__ptr);\ if (__magic != type##_magic) {\ @@ -64,6 +111,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define snd_pcm_sgbuf_t_magic 0xa15a0107 #define snd_info_private_data_t_magic 0xa15a0201 +#define snd_info_entry_t_magic 0xa15a0202 #define snd_ctl_file_t_magic 0xa15a0301 #define snd_kcontrol_t_magic 0xa15a0302 #define snd_rawmidi_t_magic 0xa15a0401 diff --git a/include/sound/trident.h b/include/sound/trident.h index bf4ed45f1a63..f4f4042f6e7f 100644 --- a/include/sound/trident.h +++ b/include/sound/trident.h @@ -431,7 +431,8 @@ struct _snd_trident { int ChanPCM; /* max number of PCM channels */ int ChanPCMcnt; /* actual number of PCM channels */ - int ac97_detect; /* 1 = AC97 in detection phase */ + unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ + unsigned int in_suspend: 1; /* 1 during suspend/resume */ struct _snd_4dwave synth; /* synth specific variables */ @@ -452,7 +453,6 @@ struct _snd_trident { snd_trident_pcm_mixer_t pcm_mixer[32]; spinlock_t reg_lock; - snd_info_entry_t *proc_entry; struct snd_trident_gameport *gameport; }; @@ -479,7 +479,7 @@ void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voic void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max); /* TLB memory allocation */ -snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size); +snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream); int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk); snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size); int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk); diff --git a/include/sound/version.h b/include/sound/version.h index a4c7d9a7cd3a..323f4e184ed5 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc6" -#define CONFIG_SND_DATE " (Thu Dec 26 11:57:42 2002 UTC)" +#define CONFIG_SND_DATE " (Wed Jan 08 17:04:59 2003 UTC)" diff --git a/sound/core/control.c b/sound/core/control.c index 31173c043e10..da7471dca9a4 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -169,6 +169,15 @@ void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) read_unlock(&card->ctl_files_rwlock); } +/** + * snd_ctl_new - create a control instance from the template + * @control: the control template + * + * Allocates a new snd_kcontrol_t instance and copies the given template + * to the new instance. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) { snd_kcontrol_t *kctl; @@ -181,6 +190,17 @@ snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) return kctl; } +/** + * snd_ctl_new1 - create a control instance from the template + * @ncontrol: the initialization record + * @private_data: the private data to set + * + * Allocates a new snd_kcontrol_t instance and initialize from the given + * template. When the access field of ncontrol is 0, it's assumed as + * READWRITE access. + * + * Returns the pointer of the newly generated instance, or NULL on failure. + */ snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) { snd_kcontrol_t kctl; @@ -203,6 +223,14 @@ snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) return snd_ctl_new(&kctl); } +/** + * snd_ctl_free_one - release the control instance + * @kcontrol: the control instance + * + * Releases the control instance created via snd_ctl_new() + * or snd_ctl_new1(). + * Don't call this after the control was added to the card. + */ void snd_ctl_free_one(snd_kcontrol_t * kcontrol) { if (kcontrol) { @@ -212,6 +240,16 @@ void snd_ctl_free_one(snd_kcontrol_t * kcontrol) } } +/** + * snd_ctl_add - add the control instance to the card + * @card: the card instance + * @kcontrol: the control instance to add + * + * Adds the control instance created via snd_ctl_new() or + * snd_ctl_new1() to the given card. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) { snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); @@ -227,6 +265,16 @@ int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) return 0; } +/** + * snd_ctl_remove - remove the control from the card and release it + * @card: the card instance + * @kcontrol: the control instance to remove + * + * Removes the control from the card and then releases the instance. + * You don't need to call snd_ctl_free_one(). + * + * Returns 0 if successful, or a negative error code on failure. + */ int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) { snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); @@ -239,6 +287,16 @@ int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) return 0; } +/** + * snd_ctl_remove_id - remove the control of the given id and release it + * @card: the card instance + * @id: the control id to remove + * + * Finds the control instance with the given id, removes it from the + * card list and releases it. + * + * Returns 0 if successful, or a negative error code on failure. + */ int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) { snd_kcontrol_t *kctl; @@ -251,6 +309,17 @@ int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); /* w/o lock */ +/** + * snd_ctl_rename_id - replace the id of a control on the card + * @card: the card instance + * @src_id: the old id + * @dst_id: the new id + * + * Finds the control with the old id from the card, and replaces the + * id with the new one. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) { snd_kcontrol_t *kctl; @@ -306,7 +375,15 @@ static snd_kcontrol_t *_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) return NULL; } -/* exported: with read lock */ +/** + * snd_ctl_find_id - find the control instance with the given id + * @card: the card instance + * @id: the id to search + * + * Finds the control instance with the given id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) { snd_kcontrol_t *kctl; @@ -316,7 +393,15 @@ snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) return kctl; } -/* exported: with read lock */ +/** + * snd_ctl_find_numid - find the control instance with the given number-id + * @card: the card instance + * @numid: the number-id to search + * + * Finds the control instance with the given number-id from the card. + * + * Returns the pointer of the instance if found, or NULL if not. + */ snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) { snd_kcontrol_t *kctl; @@ -628,13 +713,15 @@ static int snd_ctl_ioctl(struct inode *inode, struct file *file, return -EFAULT; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + err = -ENOPROTOOPT; #ifdef CONFIG_PM - if (card->set_power_state == NULL) - return -ENOPROTOOPT; - return card->set_power_state(card, err); -#else - return -ENOPROTOOPT; + if (card->set_power_state) { + snd_power_lock(card); + err = card->set_power_state(card, err); + snd_power_unlock(card); + } #endif + return err; case SNDRV_CTL_IOCTL_POWER_STATE: #ifdef CONFIG_PM return put_user(card->power_state, (int *)arg) ? -EFAULT : 0; @@ -727,6 +814,10 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) return mask; } +/* + * register the device-specific control-ioctls. + * called from each device manager like pcm.c, hwdep.c, etc. + */ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) { snd_kctl_ioctl_t *pn; @@ -742,6 +833,9 @@ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) return 0; } +/* + * de-register the device-specific control-ioctls. + */ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) { struct list_head *list; @@ -797,6 +891,10 @@ static snd_minor_t snd_ctl_reg = .f_ops = &snd_ctl_f_ops, }; +/* + * registration of the control device: + * called from init.c + */ int snd_ctl_register(snd_card_t *card) { int err, cardnum; @@ -812,6 +910,10 @@ int snd_ctl_register(snd_card_t *card) return 0; } +/* + * disconnection of the control device: + * called from init.c + */ int snd_ctl_disconnect(snd_card_t *card) { struct list_head *flist; @@ -827,6 +929,10 @@ int snd_ctl_disconnect(snd_card_t *card) return 0; } +/* + * de-registration of the control device: + * called from init.c + */ int snd_ctl_unregister(snd_card_t *card) { int err, cardnum; diff --git a/sound/core/device.c b/sound/core/device.c index cb23f8a3eb7d..2665fb1e4974 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -25,6 +25,22 @@ #include <linux/errno.h> #include <sound/core.h> +/** + * snd_device_new - create an ALSA device component + * @card: the card instance + * @type: the device type, SNDRV_DEV_TYPE_XXX + * @device_data: the data pointer of this device + * @ops: the operator table + * + * Creates a new device component for the given data pointer. + * The device will be assigned to the card and managed together + * by the card. + * + * The data pointer plays a role as the identifier, too, so the + * pointer address must be unique and unchanged. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_device_new(snd_card_t *card, snd_device_type_t type, void *device_data, snd_device_ops_t *ops) { @@ -43,6 +59,18 @@ int snd_device_new(snd_card_t *card, snd_device_type_t type, return 0; } +/** + * snd_device_free - release the device from the card + * @card: the card instance + * @device_data: the data pointer to release + * + * Removes the device from the list on the card and invokes the + * callback, dev_unregister or dev_free, corresponding to the state. + * Then release the device. + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_free(snd_card_t *card, void *device_data) { struct list_head *list; @@ -73,6 +101,19 @@ int snd_device_free(snd_card_t *card, void *device_data) return -ENXIO; } +/** + * snd_device_free - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_disconnect(snd_card_t *card, void *device_data) { struct list_head *list; @@ -95,6 +136,19 @@ int snd_device_disconnect(snd_card_t *card, void *device_data) return -ENXIO; } +/** + * snd_device_register - register the device + * @card: the card instance + * @device_data: the data pointer to register + * + * Registers the device which was already created via + * snd_device_new(). Usually this is called from snd_card_register(), + * but it can be called later if any new devices are created after + * invokation of snd_card_register(). + * + * Returns zero if successful, or a negative error code on failure or if the + * device not found. + */ int snd_device_register(snd_card_t *card, void *device_data) { struct list_head *list; @@ -118,6 +172,10 @@ int snd_device_register(snd_card_t *card, void *device_data) return -ENXIO; } +/* + * register all the devices on the card. + * called from init.c + */ int snd_device_register_all(snd_card_t *card) { struct list_head *list; @@ -136,6 +194,10 @@ int snd_device_register_all(snd_card_t *card) return 0; } +/* + * disconnect all the devices on the card. + * called from init.c + */ int snd_device_disconnect_all(snd_card_t *card) { snd_device_t *dev; @@ -151,6 +213,10 @@ int snd_device_disconnect_all(snd_card_t *card) return err; } +/* + * release all the devices on the card. + * called from init.c + */ int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) { snd_device_t *dev; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 36a217c48e2c..5826ea7d5419 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -256,6 +256,19 @@ static snd_minor_t snd_hwdep_reg = .f_ops = &snd_hwdep_f_ops, }; +/** + * snd_hwdep_new - create a new hwdep instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero-based) + * @rhwdep: the pointer to store the new hwdep instance + * + * Creates a new hwdep instance with the given index on the card. + * The callbacks (hwdep->ops) must be set on the returned instance + * after this call manually by the caller. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) { snd_hwdep_t *hwdep; diff --git a/sound/core/info.c b/sound/core/info.c index b4e85fb69c21..b37093a9df78 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -83,10 +83,16 @@ typedef struct _snd_info_private_data { static int snd_info_version_init(void); static int snd_info_version_done(void); -/* +/** + * snd_iprintf - printf on the procfs buffer + * @buffer: the procfs buffer + * @fmt: the printf format + * + * Outputs the string on the procfs buffer just like printf(). + * + * Returns the size of output string. */ - int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { va_list args; @@ -591,6 +597,17 @@ struct inode_operations snd_info_card_link_inode_operations = .follow_link = snd_info_card_followlink, }; +/** + * snd_create_proc_entry - create a procfs entry + * @name: the name of the proc file + * @mode: the file permission bits, S_Ixxx + * @parent: the parent proc-directory entry + * + * Creates a new proc file entry with the given name and permission + * on the given directory. + * + * Returns the pointer of new instance or NULL on failure. + */ struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { @@ -682,6 +699,10 @@ int __exit snd_info_done(void) */ +/* + * create a card proc file + * called from init.c + */ int snd_info_card_create(snd_card_t * card) { char str[8]; @@ -701,6 +722,10 @@ int snd_info_card_create(snd_card_t * card) return 0; } +/* + * register the card proc file + * called from init.c + */ int snd_info_card_register(snd_card_t * card) { char *s; @@ -728,6 +753,10 @@ int snd_info_card_register(snd_card_t * card) return 0; } +/* + * de-register the card proc file + * called from init.c + */ int snd_info_card_free(snd_card_t * card) { void *data; @@ -747,10 +776,17 @@ int snd_info_card_free(snd_card_t * card) return 0; } -/* +/** + * snd_info_get_line - read one line from the procfs buffer + * @buffer: the procfs buffer + * @line: the buffer to store + * @len: the max. buffer size - 1 + * + * Reads one line from the buffer and stores the string. + * + * Returns zero if successful, or 1 if error or EOF. */ - int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { int c = -1; @@ -781,6 +817,18 @@ int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) return 0; } +/** + * snd_info_get_line - parse a string token + * @dest: the buffer to store the string token + * @src: the original string + * @len: the max. length of token - 1 + * + * Parses the original string and copy a token to the given + * string buffer. + * + * Returns the updated pointer of the original string so that + * it can be used for the next call. + */ char *snd_info_get_str(char *dest, char *src, int len) { int c; @@ -805,15 +853,27 @@ char *snd_info_get_str(char *dest, char *src, int len) return src; } +/** + * snd_info_create_entry - create an info entry + * @name: the proc file name + * + * Creates an info entry with the given file name and initializes as + * the default state. + * + * Usually called from other functions such as + * snd_info_create_card_entry(). + * + * Returns the pointer of the new instance, or NULL on failure. + */ static snd_info_entry_t *snd_info_create_entry(const char *name) { snd_info_entry_t *entry; - entry = (snd_info_entry_t *) snd_kcalloc(sizeof(snd_info_entry_t), GFP_KERNEL); + entry = snd_magic_kcalloc(snd_info_entry_t, 0, GFP_KERNEL); if (entry == NULL) return NULL; entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); if (entry->name == NULL) { - kfree(entry); + snd_magic_kfree(entry); return NULL; } entry->mode = S_IFREG | S_IRUGO; @@ -822,6 +882,16 @@ static snd_info_entry_t *snd_info_create_entry(const char *name) return entry; } +/** + * snd_info_create_module_entry - create an info entry for the given module + * @module: the module pointer + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given module. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name, snd_info_entry_t *parent) @@ -834,6 +904,16 @@ snd_info_entry_t *snd_info_create_module_entry(struct module * module, return entry; } +/** + * snd_info_create_card_entry - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @parent: the parent directory + * + * Creates a new info entry and assigns it to the given card. + * + * Returns the pointer of the new instance, or NULL on failure. + */ snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name, snd_info_entry_t * parent) @@ -847,6 +927,75 @@ snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, return entry; } +static int snd_info_dev_free_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_free_entry(entry); + return 0; +} + +static int snd_info_dev_register_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_register(entry); +} + +static int snd_info_dev_unregister_entry(snd_device_t *device) +{ + snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + return snd_info_unregister(entry); +} + +/** + * snd_card_proc_new - create an info entry for the given card + * @card: the card instance + * @name: the file name + * @entryp: the pointer to store the new info entry + * + * Creates a new info entry and assigns it to the given card. + * Unlike snd_info_create_card_entry(), this function registers the + * info entry as an ALSA device component, so that it can be + * unregistered/released without explicit call. + * Also, you don't have to register this entry via snd_info_register(), + * since this will be registered by snd_card_register() automatically. + * + * The parent is assumed as card->proc_root. + * + * For releasing this entry, use snd_device_free() instead of + * snd_info_free_entry(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_card_proc_new(snd_card_t *card, const char *name, + snd_info_entry_t **entryp) +{ + static snd_device_ops_t ops = { + .dev_free = snd_info_dev_free_entry, + .dev_register = snd_info_dev_register_entry, + // .dev_disconnect = snd_info_dev_disconnect_entry, + .dev_unregister = snd_info_dev_unregister_entry + }; + snd_info_entry_t *entry; + int err; + + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (! entry) + return -ENOMEM; + if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { + snd_info_free_entry(entry); + return err; + } + if (entryp) + *entryp = entry; + return 0; +} + +/** + * snd_info_free_entry - release the info entry + * @entry: the info entry + * + * Releases the info entry. Don't call this after registered. + */ void snd_info_free_entry(snd_info_entry_t * entry) { if (entry == NULL) @@ -855,7 +1004,7 @@ void snd_info_free_entry(snd_info_entry_t * entry) kfree((char *)entry->name); if (entry->private_free) entry->private_free(entry); - kfree(entry); + snd_magic_kfree(entry); } #ifdef LINUX_2_2 @@ -892,11 +1041,11 @@ static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_ } #endif /* LINUX_2_2 */ +/* + * create a procfs device file + */ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode) { -#ifdef CONFIG_DEVFS_FS - char dname[32]; -#endif unsigned short _major = number >> 16; unsigned short minor = (unsigned short) number; snd_info_entry_t *entry; @@ -933,6 +1082,7 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, up(&info_mutex); #ifdef CONFIG_DEVFS_FS if (strncmp(name, "controlC", 8)) { /* created in sound.c */ + char dname[32]; sprintf(dname, "snd/%s", name); devfs_register(NULL, dname, DEVFS_FL_DEFAULT, major, minor, mode, @@ -942,6 +1092,9 @@ snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, return entry; } +/* + * release a procfs device file + */ void snd_info_free_device(snd_info_entry_t * entry) { snd_runtime_check(entry, return); @@ -953,6 +1106,14 @@ void snd_info_free_device(snd_info_entry_t * entry) snd_info_free_entry(entry); } +/** + * snd_info_register - register the info entry + * @entry: the info entry + * + * Registers the proc info entry. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_info_register(snd_info_entry_t * entry) { struct proc_dir_entry *root, *p = NULL; @@ -982,6 +1143,14 @@ int snd_info_register(snd_info_entry_t * entry) return 0; } +/** + * snd_info_unregister - de-register the info entry + * @entry: the info entry + * + * De-registers the info entry and releases the instance. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_info_unregister(snd_info_entry_t * entry) { struct proc_dir_entry *root; diff --git a/sound/core/init.c b/sound/core/init.c index 26aa3d323c12..30e217be051d 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -51,14 +51,16 @@ static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer } /** - * snd_card_new: create and initialize a soundcard structure + * snd_card_new - create and initialize a soundcard structure * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] * @xid: card identification (ASCII string) * @module: top level module for locking * @extra_size: allocate this extra size after the main soundcard structure * + * Creates and initializes a soundcard structure. + * * Returns kmallocated snd_card_t structure. Creates the ALSA control interface - * (which is blocked until #snd_card_register function is called). + * (which is blocked until snd_card_register function is called). */ snd_card_t *snd_card_new(int idx, const char *xid, struct module *module, int extra_size) @@ -136,10 +138,12 @@ static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) } /** - * snd_card_disconnect: disconnect all APIs from the file-operations (user space) + * snd_card_disconnect - disconnect all APIs from the file-operations (user space) * @card: soundcard structure * - * Returns - zero, otherwise a negative error code. + * Disconnects all APIs from the file-operations (user space). + * + * Returns zero, otherwise a negative error code. * * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). @@ -219,19 +223,18 @@ int snd_card_disconnect(snd_card_t * card) } /** - * snd_card_free: frees given soundcard structure + * snd_card_free - frees given soundcard structure * @card: soundcard structure * * This function releases the soundcard structure and the all assigned * devices automatically. That is, you don't have to release the devices * by yourself. * - * Returns - zero. Frees all associated devices and frees the control + * Returns zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ int snd_card_free(snd_card_t * card) { - wait_queue_t wait; struct snd_shutdown_f_ops *s_f_ops; if (card == NULL) @@ -242,13 +245,7 @@ int snd_card_free(snd_card_t * card) write_unlock(&snd_card_rwlock); /* wait, until all devices are ready for the free operation */ - init_waitqueue_entry(&wait, current); - add_wait_queue(&card->shutdown_sleep, &wait); - while (card->files) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(30 * HZ); - } - remove_wait_queue(&card->shutdown_sleep, &wait); + wait_event(card->shutdown_sleep, card->files == NULL); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) if (snd_mixer_oss_notify_callback) @@ -299,24 +296,22 @@ static void snd_card_free_thread(void * __card) module = NULL; } - wait_event(card->shutdown_sleep, card->files == NULL); - snd_card_free(card); module_put(module); } /** - * snd_card_free_in_thread: call snd_card_free() in thread + * snd_card_free_in_thread - call snd_card_free() in thread * @card: soundcard structure * - * This function schedules the call of #snd_card_free function in a + * This function schedules the call of snd_card_free() function in a * work queue. When all devices are released (non-busy), the work - * is woken up and calls #snd_card_free. + * is woken up and calls snd_card_free(). * * When a card can be disconnected at any time by hotplug service, * this function should be used in disconnect (or detach) callback - * instead of calling #snd_card_free directly. + * instead of calling snd_card_free() directly. * * Returns - zero otherwise a negative error code if the start of thread failed. */ @@ -392,7 +387,7 @@ static void choose_default_id(snd_card_t * card) } /** - * snd_card_register: register the soundcard + * snd_card_register - register the soundcard * @card: soundcard structure * * This function registers all the devices assigned to the soundcard. @@ -400,7 +395,7 @@ static void choose_default_id(snd_card_t * card) * external accesses. Thus, you should call this function at the end * of the initialization of the card. * - * Returns - zero otherwise a negative error code if the registrain failed. + * Returns zero otherwise a negative error code if the registrain failed. */ int snd_card_register(snd_card_t * card) { @@ -517,14 +512,14 @@ int __exit snd_card_info_done(void) } /** - * snd_component_add: add a component string + * snd_component_add - add a component string * @card: soundcard structure * @component: the component id string * * This function adds the component id string to the supported list. * The component can be referred from the alsa-lib. * - * Returns - zero otherwise a negative error code. + * Returns zero otherwise a negative error code. */ int snd_component_add(snd_card_t *card, const char *component) @@ -548,7 +543,7 @@ int snd_component_add(snd_card_t *card, const char *component) } /** - * snd_card_file_add: add the file to the file list of the card + * snd_card_file_add - add the file to the file list of the card * @card: soundcard structure * @file: file pointer * @@ -580,15 +575,15 @@ int snd_card_file_add(snd_card_t *card, struct file *file) } /** - * snd_card_file_remove: remove the file from the file list + * snd_card_file_remove - remove the file from the file list * @card: soundcard structure * @file: file pointer * * This function removes the file formerly added to the card via - * #snd_card_file_add function. + * snd_card_file_add() function. * If all files are removed and the release of the card is - * scheduled, it will wake up the the thread to call #snd_card_free - * (see #snd_card_free_in_thread function). + * scheduled, it will wake up the the thread to call snd_card_free() + * (see snd_card_free_in_thread() function). * * Returns zero or a negative error code. */ @@ -623,9 +618,11 @@ int snd_card_file_remove(snd_card_t *card, struct file *file) #ifdef CONFIG_PM /** - * snd_power_wait: wait until the power-state is changed. + * snd_power_wait - wait until the power-state is changed. * @card: soundcard structure * + * Waits until the power-state is changed. + * * Note: the power lock must be active before call. */ void snd_power_wait(snd_card_t *card) diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 561c1111962f..48f355397df2 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -30,10 +30,15 @@ #include <sound/core.h> #include <asm/dma.h> -/* +/** + * snd_dma_program - program an ISA DMA transfer + * @dma: the dma number + * @addr: the physical address of the buffer + * @size: the DMA transfer size + * @mode: the DMA transfer mode, DMA_MODE_XXX * + * Programs an ISA DMA transfer for the given buffer. */ - void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode) @@ -51,6 +56,12 @@ void snd_dma_program(unsigned long dma, release_dma_lock(flags); } +/** + * snd_dma_disable - stop the ISA DMA transfer + * @dma: the dma number + * + * Stops the ISA DMA transfer. + */ void snd_dma_disable(unsigned long dma) { unsigned long flags; @@ -61,6 +72,12 @@ void snd_dma_disable(unsigned long dma) release_dma_lock(flags); } +/** + * snd_dma_residue - return the residue count of the given DMA + * @dma: the dma number + * + * Returns the residue count of the given DMA transfer. + */ unsigned int snd_dma_residue(unsigned long dma) { unsigned long flags; diff --git a/sound/core/memory.c b/sound/core/memory.c index c12a28ab2ecd..5a45ec66893d 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -264,7 +264,15 @@ int __exit snd_memory_info_done(void) #endif /* CONFIG_SND_DEBUG_MEMORY */ - +/** + * snd_malloc_pages - allocate pages with the given size + * @size: the size to allocate in bytes + * @dma_flags: the allocation conditions, GFP_XXX + * + * Allocates the physically contiguous pages with the given size. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) { int pg; @@ -285,6 +293,19 @@ void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) return res; } +/** + * snd_malloc_pages_fallback - allocate pages with the given size with fallback + * @size: the requested size to allocate in bytes + * @dma_flags: the allocation conditions, GFP_XXX + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size) { void *res; @@ -301,6 +322,13 @@ void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsi return NULL; } +/** + * snd_free_pages - release the pages + * @ptr: the buffer pointer to release + * @size: the allocated buffer size + * + * Releases the buffer allocated via snd_malloc_pages(). + */ void snd_free_pages(void *ptr, unsigned long size) { int pg; @@ -321,6 +349,16 @@ void snd_free_pages(void *ptr, unsigned long size) #if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) +/** + * snd_malloc_isa_pages - allocate pages for ISA bus with the given size + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * ISA bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) { void *dma_area; @@ -329,6 +367,19 @@ void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) return dma_area; } +/** + * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size) @@ -343,6 +394,17 @@ void *snd_malloc_isa_pages_fallback(unsigned long size, #ifdef CONFIG_PCI +/** + * snd_malloc_pci_pages - allocate pages for PCI bus with the given size + * @pci: the pci device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * PCI bus. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr) @@ -366,6 +428,20 @@ void *snd_malloc_pci_pages(struct pci_dev *pci, return res; } +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus + * @pci: pci device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for PCI bus. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, @@ -384,6 +460,15 @@ void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, return NULL; } +/** + * snd_free_pci_pages - release the pages + * @pci: pci device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, @@ -409,6 +494,17 @@ void snd_free_pci_pages(struct pci_dev *pci, #ifdef CONFIG_SBUS +/** + * snd_malloc_sbus_pages - allocate pages for SBUS with the given size + * @sdev: sbus device pointer + * @size: the size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * + * Allocates the physically contiguous pages with the given size for + * SBUS. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_sbus_pages(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr) @@ -432,6 +528,20 @@ void *snd_malloc_sbus_pages(struct sbus_dev *sdev, return res; } +/** + * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS + * @sdev: sbus device pointer + * @size: the requested size to allocate in bytes + * @dma_addr: the pointer to store the physical address of the buffer + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size for SBUS. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr, @@ -450,6 +560,15 @@ void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, return NULL; } +/** + * snd_free_sbus_pages - release the pages + * @sdev: sbus device pointer + * @size: the allocated buffer size + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_pages(). + */ void snd_free_sbus_pages(struct sbus_dev *sdev, unsigned long size, void *ptr, @@ -473,6 +592,15 @@ void snd_free_sbus_pages(struct sbus_dev *sdev, #endif /* CONFIG_SBUS */ +/** + * snd_kcalloc - memory allocation and zero-clear + * @size: the size to allocate in bytes + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and initializes it to zero. + * + * Returns the pointer, or NULL if no enoguh memory. + */ void *snd_kcalloc(size_t size, int flags) { void *ptr; @@ -483,6 +611,15 @@ void *snd_kcalloc(size_t size, int flags) return ptr; } +/** + * snd_kmalloc_strdup - copy the string + * @string: the original string + * @flags: allocation conditions, GFP_XXX + * + * Allocates a memory chunk via kmalloc() and copies the string to it. + * + * Returns the pointer, or NULL if no enoguh memory. + */ char *snd_kmalloc_strdup(const char *string, int flags) { size_t len; @@ -497,6 +634,16 @@ char *snd_kmalloc_strdup(const char *string, int flags) return ptr; } +/** + * copy_to_user_fromio - copy data from mmio-space to user-space + * @dst: the destination pointer on user-space + * @src: the source pointer on mmio + * @count: the data size to copy in bytes + * + * Copies the data from mmio-space to user-space. + * + * Returns zero if successful, or non-zero on failure. + */ int copy_to_user_fromio(void *dst, unsigned long src, size_t count) { #if defined(__i386__) || defined(CONFIG_SPARC32) @@ -518,6 +665,16 @@ int copy_to_user_fromio(void *dst, unsigned long src, size_t count) #endif } +/** + * copy_from_user_toio - copy data from user-space to mmio-space + * @dst: the destination pointer on mmio-space + * @src: the source pointer on user-space + * @count: the data size to copy in bytes + * + * Copies the data from user-space to mmio-space. + * + * Returns zero if successful, or non-zero on failure. + */ int copy_from_user_toio(unsigned long dst, const void *src, size_t count) { #if defined(__i386__) || defined(CONFIG_SPARC32) @@ -538,3 +695,81 @@ int copy_from_user_toio(unsigned long dst, const void *src, size_t count) return 0; #endif } + + +#ifdef CONFIG_PCI + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(__i386__) +/* + * on ix86, we allocate a page with GFP_KERNEL to assure the + * allocation. the code is almost same with kernel/i386/pci-dma.c but + * it allocates only a single page and checks the validity of the + * page address with the given pci dma mask. + */ + +/** + * snd_malloc_pci_page - allocate a page in the valid pci dma mask + * @pci: pci device pointer + * @addrp: the pointer to store the physical address of the buffer + * + * Allocates a single page for the given PCI device and returns + * the virtual address and stores the physical address on addrp. + * + * This function cannot be called from interrupt handlers or + * within spinlocks. + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + void *ptr; + dma_addr_t addr; + unsigned long rmask; + + rmask = ~(unsigned long)(pci ? pci->dma_mask : 0x00ffffff); + ptr = (void *)__get_free_page(GFP_KERNEL); + if (ptr) { + addr = virt_to_phys(ptr); + if (((unsigned long)addr + PAGE_SIZE - 1) & rmask) { + /* try to reallocate with the GFP_DMA */ + free_page((unsigned long)ptr); + ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (ptr) /* ok, the address must be within lower 16MB... */ + addr = virt_to_phys(ptr); + else + addr = 0; + } + } else + addr = 0; + if (ptr) { + struct page *page = virt_to_page(ptr); + memset(ptr, 0, PAGE_SIZE); + SetPageReserved(page); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages++; +#endif + } + *addrp = addr; + return ptr; +} +#else +/* on other architectures, call snd_malloc_pci_pages() helper function + * which uses pci_alloc_consistent(). + */ +void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) +{ + return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp); +} +#endif /* 2.4 && i386 */ + +#if 0 /* for kernel-doc */ +/** + * snd_free_pci_page - release a page + * @pci: pci device pointer + * @ptr: the buffer pointer to release + * @dma_addr: the physical address of the buffer + * + * Releases the buffer allocated via snd_malloc_pci_page(). + */ +void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr); +#endif /* for kernel-doc */ + +#endif /* CONFIG_PCI */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 562f4f4ca6a2..d9383e69f19c 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -542,6 +542,19 @@ static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) return 0; } +/** + * snd_pcm_new_stream - create a new PCM stream + * @pcm: the pcm instance + * @stream: the stream direction, SNDRV_PCM_STREAM_XXX + * @substream_count: the number of substreams + * + * Creates a new stream for the pcm. + * The corresponding stream on the pcm must have been empty before + * calling this, i.e. zero must be given to the argument of + * snd_pcm_new(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) { int idx, err; @@ -582,7 +595,7 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) snd_magic_kfree(substream); return err; } - substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_type = SNDRV_PCM_DMA_TYPE_UNKNOWN; substream->dma_private = NULL; spin_lock_init(&substream->timer_lock); prev = substream; @@ -590,6 +603,22 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) return 0; } +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_new(snd_card_t * card, char *id, int device, int playback_count, int capture_count, snd_pcm_t ** rpcm) @@ -660,6 +689,7 @@ static int snd_pcm_free(snd_pcm_t *pcm) snd_assert(pcm != NULL, return -ENXIO); if (pcm->private_free) pcm->private_free(pcm); + snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); snd_magic_kfree(pcm); @@ -774,6 +804,7 @@ int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, runtime->status->state = SNDRV_PCM_STATE_OPEN; substream->runtime = runtime; + substream->private_data = pcm->private_data; pstr->substream_opened++; *rsubstream = substream; return 0; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e5c4d15e55c9..1c3a6ec1dc50 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -214,10 +214,14 @@ int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) return 0; } -/* - * Operations +/** + * snd_pcm_set_ops - set the PCM operators + * @pcm: the pcm instance + * @direction: stream direction, SNDRV_PCM_STREAM_XXX + * @ops: the operator table + * + * Sets the given PCM operators to the pcm instance. */ - void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) { snd_pcm_str_t *stream = &pcm->streams[direction]; @@ -227,10 +231,13 @@ void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) substream->ops = ops; } -/* - * Sync + +/** + * snd_pcm_sync - set the PCM sync id + * @substream: the pcm substream + * + * Sets the PCM sync identifier for the card. */ - void snd_pcm_set_sync(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -354,7 +361,17 @@ int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) return changed; } -/* r <- v */ +/** + * snd_interval_refine - refine the interval value of configurator + * @i: the interval value to refine + * @v: the interval value to refer to + * + * Refines the interval value with the reference value. + * The interval is changed to the range satisfying both intervals. + * The interval status (min, max, integer, etc.) are evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) { int changed = 0; @@ -445,6 +462,13 @@ void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_inte c->integer = (a->integer && b->integer); } +/** + * snd_interval_div - refine the interval value with division + * + * c = a / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) { unsigned int r; @@ -469,7 +493,13 @@ void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_inte c->integer = 0; } -/* a * b / k */ +/** + * snd_interval_muldivk - refine the interval value + * + * c = a * b / k + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, unsigned int k, snd_interval_t *c) { @@ -490,7 +520,13 @@ void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, c->integer = 0; } -/* a * k / b */ +/** + * snd_interval_mulkdiv - refine the interval value + * + * c = a * k / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, const snd_interval_t *b, snd_interval_t *c) { @@ -520,6 +556,11 @@ void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, /* ---- */ +/** + * snd_interval_ratnum - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_ratnum(snd_interval_t *i, unsigned int rats_count, ratnum_t *rats, unsigned int *nump, unsigned int *denp) @@ -612,6 +653,11 @@ int snd_interval_ratnum(snd_interval_t *i, return err; } +/** + * snd_interval_ratden - refine the interval value + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_ratden(snd_interval_t *i, unsigned int rats_count, ratden_t *rats, unsigned int *nump, unsigned int *denp) @@ -698,6 +744,19 @@ int snd_interval_ratden(snd_interval_t *i, return err; } +/** + * snd_interval_list - refine the interval value from the list + * @i: the interval value to refine + * @count: the number of elements in the list + * @list: the value list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Returns non-zero if the value is changed, zero if not changed. + */ int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) { unsigned int k; @@ -762,6 +821,17 @@ int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) /* Info constraints helpers */ +/** + * snd_pcm_hw_rule_add - add the hw-constraint rule + * @runtime: the pcm runtime instance + * @cond: condition bits + * @var: the variable to evaluate + * @func: the evaluation function + * @private: the private data pointer passed to function + * @dep: the dependent variables + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, int var, snd_pcm_hw_rule_func_t func, void *private, @@ -808,6 +878,9 @@ int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, return 0; } +/** + * snd_pcm_hw_constraint_mask + */ int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, u_int32_t mask) { @@ -820,6 +893,9 @@ int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t va return 0; } +/** + * snd_pcm_hw_constraint_mask64 + */ int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, u_int64_t mask) { @@ -833,12 +909,18 @@ int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t return 0; } +/** + * snd_pcm_hw_constraint_integer + */ int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) { snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var)); } +/** + * snd_pcm_hw_constraint_minmax + */ int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, unsigned int min, unsigned int max) { @@ -859,6 +941,9 @@ static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, } +/** + * snd_pcm_hw_constraint_list + */ int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -884,6 +969,9 @@ static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, return err; } +/** + * snd_pcm_hw_constraint_ratnums + */ int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -908,6 +996,9 @@ static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, return err; } +/** + * snd_pcm_hw_constraint_ratdens + */ int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -930,6 +1021,9 @@ static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, return 0; } +/** + * snd_pcm_hw_constraint_msbits + */ int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, unsigned int cond, unsigned int width, @@ -949,6 +1043,9 @@ static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, return snd_interval_step(hw_param_interval(params, rule->var), 0, step); } +/** + * snd_pcm_hw_constraint_step + */ int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var, @@ -971,6 +1068,9 @@ static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t * sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0); } +/** + * snd_pcm_hw_constraint_pow2 + */ int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, unsigned int cond, snd_pcm_hw_param_t var) @@ -1004,6 +1104,9 @@ void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) snd_BUG(); } +/** + * snd_pcm_hw_param_any + */ int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { @@ -1022,16 +1125,23 @@ void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) params->info = ~0U; } -/* Fill PARAMS with full configuration space boundaries */ +/** + * snd_pcm_hw_params_any + * + * Fill PARAMS with full configuration space boundaries + */ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { _snd_pcm_hw_params_any(params); return snd_pcm_hw_refine(pcm, params); } -/* Return the value for field PAR if it's fixed in configuration space - defined by PARAMS. Return -EINVAL otherwise -*/ +/** + * snd_pcm_hw_param_value + * + * Return the value for field PAR if it's fixed in configuration space + * defined by PARAMS. Return -EINVAL otherwise + */ int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1055,7 +1165,11 @@ int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, return -EINVAL; } -/* Return the minimum value for field PAR. */ +/** + * snd_pcm_hw_param_value_min + * + * Return the minimum value for field PAR. + */ unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1074,7 +1188,11 @@ unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, return -EINVAL; } -/* Return the maximum value for field PAR. */ +/** + * snd_pcm_hw_param_value_max + * + * Return the maximum value for field PAR. + */ unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) { @@ -1122,10 +1240,13 @@ int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - non integer values. Reduce configuration space accordingly. - Return -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_setinteger + * + * Inside configuration space defined by PARAMS remove from PAR all + * non integer values. Reduce configuration space accordingly. + * Return -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) @@ -1161,10 +1282,13 @@ int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, } -/* Inside configuration space defined by PARAMS remove from PAR all - values > minimum. Reduce configuration space accordingly. - Return the minimum. -*/ +/** + * snd_pcm_hw_param_first + * + * Inside configuration space defined by PARAMS remove from PAR all + * values > minimum. Reduce configuration space accordingly. + * Return the minimum. + */ int snd_pcm_hw_param_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) @@ -1199,10 +1323,13 @@ int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, } -/* Inside configuration space defined by PARAMS remove from PAR all - values < maximum. Reduce configuration space accordingly. - Return the maximum. -*/ +/** + * snd_pcm_hw_param_last + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < maximum. Reduce configuration space accordingly. + * Return the maximum. + */ int snd_pcm_hw_param_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, int *dir) @@ -1247,10 +1374,13 @@ int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values < VAL. Reduce configuration space accordingly. - Return new minimum or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_min + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < VAL. Reduce configuration space accordingly. + * Return new minimum or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int *dir) { @@ -1297,10 +1427,13 @@ int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values >= VAL + 1. Reduce configuration space accordingly. - Return new maximum or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_max + * + * Inside configuration space defined by PARAMS remove from PAR all + * values >= VAL + 1. Reduce configuration space accordingly. + * Return new maximum or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int *dir) { @@ -1364,10 +1497,13 @@ int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all - values != VAL. Reduce configuration space accordingly. - Return VAL or -EINVAL if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_set + * + * Inside configuration space defined by PARAMS remove from PAR all + * values != VAL. Reduce configuration space accordingly. + * Return VAL or -EINVAL if the configuration space is empty + */ int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { @@ -1395,13 +1531,16 @@ int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, return changed; } -/* Inside configuration space defined by PARAMS remove from PAR all values - not contained in MASK. Reduce configuration space accordingly. - This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - Return 0 on success or -EINVAL - if the configuration space is empty -*/ +/** + * snd_pcm_hw_param_mask + * + * Inside configuration space defined by PARAMS remove from PAR all values + * not contained in MASK. Reduce configuration space accordingly. + * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return 0 on success or -EINVAL + * if the configuration space is empty + */ int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, const snd_mask_t *val) { @@ -1464,12 +1603,15 @@ static int boundary_nearer(int min, int mindir, return boundary_lt(dmin, dmindir, dmax, dmaxdir); } -/* Inside configuration space defined by PARAMS set PAR to the available value - nearest to VAL. Reduce configuration space accordingly. - This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - Return the value found. - */ +/** + * snd_pcm_hw_param_near + * + * Inside configuration space defined by PARAMS set PAR to the available value + * nearest to VAL. Reduce configuration space accordingly. + * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return the value found. + */ int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int best, int *dir) { @@ -1537,17 +1679,14 @@ int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, return v; } -/* Choose one configuration from configuration space defined by PARAMS - The configuration choosen is that obtained fixing in this order: - first access - first format - first subformat - min channels - min rate - min period time - max buffer size - min tick time -*/ +/** + * snd_pcm_hw_param_choose + * + * Choose one configuration from configuration space defined by PARAMS + * The configuration choosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + */ int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { int err; @@ -1630,6 +1769,17 @@ static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, return 0; } +/** + * snd_pcm_lib_ioctl - a generic PCM ioctl callback + * @substream: the pcm substream instance + * @cmd: ioctl command + * @arg: ioctl argument + * + * Processes the generic ioctl commands for PCM. + * Can be passed as the ioctl callback for PCM ops. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg) { @@ -1648,30 +1798,70 @@ int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, * Conditions */ +/** + * snd_pcm_playback_ready - check whether the playback buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough free space is available on the playback buffer. + * + * Returns non-zero if available, or zero if not. + */ int snd_pcm_playback_ready(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; } +/** + * snd_pcm_capture_ready - check whether the capture buffer is available + * @substream: the pcm substream instance + * + * Checks whether enough capture data is available on the capture buffer. + * + * Returns non-zero if available, or zero if not. + */ int snd_pcm_capture_ready(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; } +/** + * snd_pcm_playback_data - check whether any data exists on the playback buffer + * @substream: the pcm substream instance + * + * Checks whether any data exists on the playback buffer. + * + * Returns non-zero if exists, or zero if not. + */ int snd_pcm_playback_data(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) < runtime->buffer_size; } +/** + * snd_pcm_playback_empty - check whether the playback buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the playback buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ int snd_pcm_playback_empty(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; } +/** + * snd_pcm_capture_empty - check whether the capture buffer is empty + * @substream: the pcm substream instance + * + * Checks whether the capture buffer is empty. + * + * Returns non-zero if empty, or zero if not. + */ int snd_pcm_capture_empty(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -1766,6 +1956,17 @@ void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) spin_unlock_irq(&runtime->lock); } +/** + * snd_pcm_period_elapsed - update the pcm status for the next period + * @substream: the pcm substream instance + * + * This function is called from the interrupt handler when the + * PCM has processed the period size. It will update the current + * pointer, set up the tick, wake up sleepers, etc. + * + * Even if more than one periods have elapsed since the last call, you + * have to call this only once. + */ void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime; diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 4cc1750197dd..b2da702f0d7e 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -37,36 +37,120 @@ MODULE_PARM(maximum_substreams, "i"); MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory."); MODULE_PARM_SYNTAX(maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); -static int snd_minimum_buffer = 16384; +const static int snd_minimum_buffer = 16384; -static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +/* + * allocate pages on the specified bus + */ +static int alloc_pcm_pages(snd_pcm_substream_t *substream, size_t size, + void **dma_area, dma_addr_t *dma_addr) +{ + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + *dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + *dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + *dma_area = snd_malloc_isa_pages(size, dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + *dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, dma_addr); + break; +#endif +#ifdef CONFIG_SBUS + case SNDRV_PCM_DMA_TYPE_SBUS: + *dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, dma_addr); + break; +#endif + default: + *dma_area = NULL; + *dma_addr = 0; + return -ENXIO; + } + return 0; +} + +/* + * try to allocate as the large pages as possible. + * stores the resultant memory size in *res_size. + * + * the minimum size is snd_minimum_buffer. it should be power of 2. + */ +static void *alloc_pcm_pages_fallback(snd_pcm_substream_t *substream, + size_t size, dma_addr_t *addrp, + size_t *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if (alloc_pcm_pages(substream, size, &res, addrp) < 0) + return NULL; + if (res) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= snd_minimum_buffer); + *res_size = 0; /* tell error */ + return NULL; +} + +/* + * release the pages on the specified bus + */ +static void free_pcm_pages(snd_pcm_substream_t *substream, size_t size, + void *dma_area, dma_addr_t dma_addr) { - if (substream->dma_area == NULL) - return; switch (substream->dma_type) { case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - snd_free_pages(substream->dma_area, substream->dma_bytes); + snd_free_pages(dma_area, size); break; #ifdef CONFIG_ISA case SNDRV_PCM_DMA_TYPE_ISA: - snd_free_isa_pages(substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_isa_pages(size, dma_area, dma_addr); break; #endif #ifdef CONFIG_PCI case SNDRV_PCM_DMA_TYPE_PCI: - snd_free_pci_pages((struct pci_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_pci_pages((struct pci_dev *)substream->dma_private, + size, dma_area, dma_addr); break; #endif #ifdef CONFIG_SBUS case SNDRV_PCM_DMA_TYPE_SBUS: - snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, + size, dma_area, dma_addr); break; #endif } +} + +/* + * release the preallocated buffer if not yet done. + */ +static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +{ + if (substream->dma_area == NULL) + return; + free_pcm_pages(substream, substream->dma_bytes, + substream->dma_area, substream->dma_addr); substream->dma_area = NULL; } +/** + * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream. + * @substream: the pcm substream instance + * + * Releases the pre-allocated buffer of the given substream. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) { snd_pcm_lib_preallocate_dma_free(substream); @@ -77,6 +161,14 @@ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) return 0; } +/** + * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm + * @pcm: the pcm instance + * + * Releases all the pre-allocated buffers on the given pcm. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) { snd_pcm_substream_t *substream; @@ -88,6 +180,11 @@ int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) return 0; } +/* + * read callback for prealloc proc file + * + * prints the current allocated size in kB. + */ static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { @@ -95,6 +192,11 @@ static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_bytes / 1024); } +/* + * write callback for prealloc proc file + * + * accepts the preallocation size in kB. + */ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { @@ -118,27 +220,7 @@ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, if (substream->dma_bytes == size) return; if (size > 0) { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages(size, &dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, &dma_addr); - break; -#endif - default: + if (alloc_pcm_pages(substream, size, &dma_area, &dma_addr) < 0) { dma_area = NULL; dma_addr = 0UL; } @@ -160,45 +242,19 @@ static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, } } +/* + * pre-allocate the buffer and create a proc file for the substream + */ static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, size_t size, size_t max) { - unsigned long rsize = 0; + size_t rsize = 0; void *dma_area = NULL; dma_addr_t dma_addr = 0UL; snd_info_entry_t *entry; - if (!size || !preallocate_dma || substream->number >= maximum_substreams) { - size = 0; - } else { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages_fallback(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff), &rsize); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages_fallback(size, &dma_addr, &rsize); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages_fallback((struct pci_dev *)substream->dma_private, size, &dma_addr, &rsize); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages_fallback((struct sbus_dev *)substream->dma_private, size, &dma_addr, &rsize); - break; -#endif - default: - size = 0; - } - if (rsize < snd_minimum_buffer) { - snd_pcm_lib_preallocate_dma_free(substream); - size = 0; - } - } + if (size > 0 && preallocate_dma && substream->number < maximum_substreams) + dma_area = alloc_pcm_pages_fallback(substream, size, &dma_addr, &rsize); substream->dma_area = dma_area; substream->dma_addr = dma_addr; substream->dma_bytes = rsize; @@ -220,6 +276,17 @@ static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, return 0; } +/** + * snd_pcm_lib_preallocate_pages - pre-allocation for the continuous memory type + * @substream: the pcm substream instance + * @size: the requested pre-allocation size in bytes + * @max: the max. allowed pre-allocation size + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation for the continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, size_t size, size_t max, unsigned int flags) @@ -229,6 +296,18 @@ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/** + * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * @flags: allocation condition, GFP_XXX + * + * Do pre-allocation to all substreams of the given pcm for the + * continuous memory type. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, size_t size, size_t max, unsigned int flags) @@ -244,6 +323,16 @@ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, } #ifdef CONFIG_ISA +/** + * snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, size_t size, size_t max) { @@ -252,6 +341,18 @@ int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_isa_pages_for_all - pre-allocation for the ISA bus (all substreams) + * @pcm: pcm to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * ISA bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, size_t size, size_t max) { @@ -266,12 +367,24 @@ int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, } #endif /* CONFIG_ISA */ +/** + * snd_pcm_lib_malloc_pages - allocate the DMA buffer + * @substream: the substream to allocate the DMA buffer to + * @size: the requested buffer size in bytes + * + * Allocates the DMA buffer on the BUS type given by + * snd_pcm_lib_preallocate_xxx_pages(). + * + * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * code on failure. + */ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) { snd_pcm_runtime_t *runtime; void *dma_area = NULL; dma_addr_t dma_addr = 0UL; + snd_assert(substream->dma_type != SNDRV_PCM_DMA_TYPE_UNKNOWN, return -EINVAL); snd_assert(substream != NULL, return -EINVAL); runtime = substream->runtime; snd_assert(runtime != NULL, return -EINVAL); @@ -287,29 +400,7 @@ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) dma_area = substream->dma_area; dma_addr = substream->dma_addr; } else { - switch (substream->dma_type) { - case SNDRV_PCM_DMA_TYPE_CONTINUOUS: - dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); - dma_addr = 0UL; /* not valid */ - break; -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - dma_area = snd_malloc_isa_pages(size, &dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - dma_area = snd_malloc_sbus_pages((struct sbus_dev *)substream->dma_private, size, &dma_addr); - break; -#endif - default: - return -ENXIO; - } + alloc_pcm_pages(substream, size, &dma_area, &dma_addr); } if (! dma_area) return -ENOMEM; @@ -319,6 +410,14 @@ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) return 1; /* area was changed */ } +/** + * snd_pcm_lib_free_pages - release the allocated DMA buffer. + * @substream: the substream to release the DMA buffer + * + * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime; @@ -328,25 +427,9 @@ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) snd_assert(runtime != NULL, return -EINVAL); if (runtime->dma_area == NULL) return 0; - if (runtime->dma_area != substream->dma_area) { - switch (substream->dma_type) { -#ifdef CONFIG_ISA - case SNDRV_PCM_DMA_TYPE_ISA: - snd_free_isa_pages(runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif -#ifdef CONFIG_PCI - case SNDRV_PCM_DMA_TYPE_PCI: - snd_free_pci_pages((struct pci_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_PCM_DMA_TYPE_SBUS: - snd_free_sbus_pages((struct sbus_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); - break; -#endif - } - } + if (runtime->dma_area != substream->dma_area) + free_pcm_pages(substream, runtime->dma_bytes, + runtime->dma_area, runtime->dma_addr); runtime->dma_area = NULL; runtime->dma_addr = 0UL; runtime->dma_bytes = 0; @@ -354,7 +437,18 @@ int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) } #ifdef CONFIG_PCI - +/** + * snd_pcm_lib_preallocate_pci_pages - pre-allocation for the PCI bus + * + * @pci: pci device + * @substream: substream to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, snd_pcm_substream_t *substream, size_t size, size_t max) @@ -364,6 +458,20 @@ int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_pci_pages_for_all - pre-allocation for the PCI bus (all substreams) + * @pci: pci device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * PCI bus. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, snd_pcm_t *pcm, size_t size, size_t max) @@ -381,7 +489,17 @@ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, #endif /* CONFIG_PCI */ #ifdef CONFIG_SBUS - +/** + * snd_pcm_lib_preallocate_sbus_pages - pre-allocation for the SBUS bus + * + * @sbus: SBUS device + * @substream: substream to assign the buffer + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation for the SBUS. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, snd_pcm_substream_t *substream, size_t size, size_t max) @@ -391,6 +509,20 @@ int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, return snd_pcm_lib_preallocate_pages1(substream, size, max); } +/* + * FIXME: the function name is too long for docbook! + * + * snd_pcm_lib_preallocate_sbus_pages_for_all - pre-allocation for the SBUS bus (all substreams) + * @sbus: SBUS device + * @pcm: pcm to assign the buffer + * @size: the requested pre-allocation size in bytes + * @max: max. buffer size acceptable for the changes via proc file + * + * Do pre-allocation to all substreams of the given pcm for the + * SUBS. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, snd_pcm_t *pcm, size_t size, size_t max) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 38a87b6d69eb..310f1f1c4006 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -30,6 +30,13 @@ #define snd_enum_to_int(v) (v) #define snd_int_to_enum(v) (v) +/** + * snd_pcm_format_signed - Check the PCM format is signed linear + * @format: the format to check + * + * Returns 1 if the given PCM format is signed linear, 0 if unsigned + * linear, and a negative error code for non-linear formats. + */ int snd_pcm_format_signed(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -66,6 +73,13 @@ int snd_pcm_format_signed(snd_pcm_format_t format) } } +/** + * snd_pcm_format_unsigned - Check the PCM format is unsigned linear + * @format: the format to check + * + * Returns 1 if the given PCM format is unsigned linear, 0 if signed + * linear, and a negative error code for non-linear formats. + */ int snd_pcm_format_unsigned(snd_pcm_format_t format) { int val; @@ -76,11 +90,24 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format) return !val; } +/** + * snd_pcm_format_linear - Check the PCM format is linear + * @format: the format to check + * + * Returns 1 if the given PCM format is linear, 0 if not. + */ int snd_pcm_format_linear(snd_pcm_format_t format) { return snd_pcm_format_signed(format) >= 0; } +/** + * snd_pcm_format_little_endian - Check the PCM format is little-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is little-endian, 0 if + * big-endian, or a negative error code if endian not specified. + */ int snd_pcm_format_little_endian(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -121,6 +148,13 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) } } +/** + * snd_pcm_format_big_endian - Check the PCM format is big-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is big-endian, 0 if + * little-endian, or a negative error code if endian not specified. + */ int snd_pcm_format_big_endian(snd_pcm_format_t format) { int val; @@ -131,6 +165,13 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format) return !val; } +/** + * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian + * @format: the format to check + * + * Returns 1 if the given PCM format is CPU-endian, 0 if + * opposite, or a negative error code if endian not specified. + */ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN @@ -140,6 +181,13 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) #endif } +/** + * snd_pcm_format_width - return the bit-width of the format + * @format: the format to check + * + * Returns the bit-width of the format, or a negative error code + * if unknown format. + */ int snd_pcm_format_width(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -193,6 +241,13 @@ int snd_pcm_format_width(snd_pcm_format_t format) } } +/** + * snd_pcm_format_physical_width - return the physical bit-width of the format + * @format: the format to check + * + * Returns the physical bit-width of the format, or a negative error code + * if unknown format. + */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -243,6 +298,13 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) } } +/** + * snd_pcm_format_size - return the byte size of samples on the given format + * @format: the format to check + * + * Returns the byte size of the given samples for the format, or a + * negative error code if unknown format. + */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) { switch (snd_enum_to_int(format)) { @@ -296,6 +358,12 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) } } +/** + * snd_pcm_format_silence_64 - return the silent data in 64bit integer + * @format: the format to check + * + * Returns the silent data in 64bit integer for the given format. + */ u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) { switch (snd_enum_to_int(format)) { @@ -451,6 +519,16 @@ u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) return (u_int8_t)snd_pcm_format_silence_64(format); } +/** + * snd_pcm_format_set_silence - set the silence data on the buffer + * @format: the PCM format + * @data: the buffer pointer + * @samples: the number of samples to set silence + * + * Sets the silence data on the buffer for the given samples. + * + * Returns zero if sucessful, or a negative error code on failure. + */ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) { if (samples == 0) @@ -544,6 +622,14 @@ static int linear_formats[4*2*2] = { SNDRV_PCM_FORMAT_U32_BE }; +/** + * snd_pcm_build_linear_format - return the suitable linear format for the given condition + * @width: the bit-width + * @unsignd: 1 if unsigned, 0 if signed. + * @big_endian: 1 if big-endian, 0 if little-endian + * + * Returns the suitable linear format for the given condition. + */ snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) { switch (width) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ce21615f6c92..c12bad5d92b4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -676,6 +676,9 @@ static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) snd_pcm_tick_prepare(substream); } +/** + * snd_pcm_sart + */ int snd_pcm_start(snd_pcm_substream_t *substream) { SND_PCM_ACTION(start, substream, 0); @@ -705,6 +708,9 @@ static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) wake_up(&runtime->sleep); } +/** + * snd_pcm_stop + */ int snd_pcm_stop(snd_pcm_substream_t *substream, int state) { SND_PCM_ACTION(stop, substream, state); @@ -781,11 +787,17 @@ static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int stat wake_up(&runtime->sleep); } +/** + * snd_pcm_suspend + */ int snd_pcm_suspend(snd_pcm_substream_t *substream) { SND_PCM_ACTION(suspend, substream, 0); } +/** + * snd_pcm_suspend_all + */ int snd_pcm_suspend_all(snd_pcm_t *pcm) { snd_pcm_substream_t *substream; @@ -978,6 +990,9 @@ static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int sta runtime->status->state = SNDRV_PCM_STATE_PREPARED; } +/** + * snd_pcm_prepare + */ int snd_pcm_prepare(snd_pcm_substream_t *substream) { int res; diff --git a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c index 2511b99b3ede..914373a2a7e9 100644 --- a/sound/core/pcm_sgbuf.c +++ b/sound/core/pcm_sgbuf.c @@ -41,16 +41,24 @@ static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages) return; while (sgbuf->pages > pages) { sgbuf->pages--; - snd_free_pci_pages(sgbuf->pci, PAGE_SIZE, - sgbuf->table[sgbuf->pages].buf, + snd_free_pci_page(sgbuf->pci, sgbuf->table[sgbuf->pages].buf, sgbuf->table[sgbuf->pages].addr); } } -/* - * initialize the sg buffer - * assigned to substream->dma_private. - * initialize the table with the given size. +/** + * snd_pcm_sgbuf_init - initialize the sg buffer + * @substream: the pcm substream instance + * @pci: pci device pointer + * @tblsize: the default table size + * + * Initializes the SG-buffer instance and assigns it to + * substream->dma_private. The SG-table is initialized with the + * given size. + * + * Call this function in the open callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize) { @@ -73,8 +81,15 @@ int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int return 0; } -/* - * release all pages and free the sgbuf instance +/** + * snd_pcm_sgbuf_delete - release all pages and free the sgbuf instance + * @substream: the pcm substream instance + * + * Releaes all pages and free the sgbuf instance. + * + * Call this function in the close callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) { @@ -92,60 +107,19 @@ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) return 0; } -/* - * snd_pci_alloc_page - allocate a page in the valid pci dma mask +/** + * snd_pcm_sgbuf_alloc - allocate the pages for the SG buffer + * @substream: the pcm substream instance + * @size: the requested buffer size in bytes * - * returns the virtual address and stores the physical address on - * addrp. this function cannot be called from interrupt handlers or - * within spinlocks. - */ -#ifdef __i386__ -/* - * on ix86, we allocate a page with GFP_KERNEL to assure the - * allocation. the code is almost same with kernel/i386/pci-dma.c but - * it allocates only a single page and checkes the validity of the - * page address with the given pci dma mask. - */ -inline static void *snd_pci_alloc_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - void *ptr; - dma_addr_t addr; - unsigned long rmask; - - if (pci) - rmask = ~(unsigned long)pci->dma_mask; - else - rmask = 0; - ptr = (void *)__get_free_page(GFP_KERNEL); - if (ptr) { - addr = virt_to_phys(ptr); - if (((unsigned long)addr + PAGE_SIZE - 1) & rmask) { - /* try to reallocate with the GFP_DMA */ - free_page((unsigned long)ptr); - ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); - if (ptr) /* ok, the address must be within lower 16MB... */ - addr = virt_to_phys(ptr); - else - addr = 0; - } - } else - addr = 0; - if (ptr) - memset(ptr, 0, PAGE_SIZE); - *addrp = addr; - return ptr; -} -#else -/* on other architectures, call snd_malloc_pci_pages() helper function - * which uses pci_alloc_consistent(). - */ -#define snd_pci_alloc_page(pci, addrp) snd_malloc_pci_pages(pci, PAGE_SIZE, addrp) -#endif - -/* - * allocate sg buffer table with the given byte size. - * if the buffer table already exists, try to resize it. - * call this from hw_params callback. + * Allocates the buffer pages for the given size and updates the + * sg buffer table. If the buffer table already exists, try to resize + * it. + * + * Call this function from hw_params callback. + * + * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * code on failure. */ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) { @@ -178,7 +152,7 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) while (sgbuf->pages < pages) { void *ptr; dma_addr_t addr; - ptr = snd_pci_alloc_page(sgbuf->pci, &addr); + ptr = snd_malloc_pci_page(sgbuf->pci, &addr); if (! ptr) return -ENOMEM; sgbuf->table[sgbuf->pages].buf = ptr; @@ -192,10 +166,15 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size) return changed; } -/* - * free the sg buffer - * the table is kept. - * call this from hw_free callback. +/** + * snd_pcm_sgbuf_free - free the sg buffer + * @substream: the pcm substream instance + * + * Releases the pages. The SG-table itself is still kept. + * + * Call this function from hw_free callback. + * + * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream) { @@ -221,9 +200,13 @@ static void *sgbuf_get_addr(snd_pcm_substream_t *substream, unsigned long offset return sgbuf->table[idx].buf; } -/* - * get the page struct at the given offset - * used as the page callback of pcm ops +/** + * snd_pcm_sgbuf_ops_page - get the page struct at the given offset + * @substream: the pcm substream instance + * @offset: the buffer offset + * + * Returns the page struct at the given buffer offset. + * Used as the page callback of PCM ops. */ struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) { @@ -324,7 +307,9 @@ static int set_silence_sg_buf(snd_pcm_substream_t *substream, return 0; } -/* +/** + * snd_pcm_sgbuf_ops_copy_playback - copy callback for playback pcm ops + * * copy callback for playback pcm ops */ int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, @@ -340,7 +325,9 @@ int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, } } -/* +/** + * snd_pcm_sgbuf_ops_copy_capture - copy callback for capture pcm ops + * * copy callback for capture pcm ops */ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, @@ -356,7 +343,9 @@ int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, } } -/* +/** + * snd_pcm_sgbuf_ops_silence - silence callback for pcm ops + * * silence callback for pcm ops */ int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 061cb55fa1e1..17376907b20f 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -815,6 +815,16 @@ int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, return -ENOIOCTLCMD; } +/** + * snd_rawmidi_receive - receive the input data from the device + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: the data size to read + * + * Reads the data from the internal buffer. + * + * Returns the size of read data, or a negative error code on failure. + */ int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count) { unsigned long flags; @@ -959,6 +969,12 @@ static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff return result; } +/** + * snd_rawmidi_transmit_empty - check whether the output buffer is empty + * @substream: the rawmidi substream + * + * Returns 1 if the internal output buffer is empty, 0 if not. + */ int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) { snd_rawmidi_runtime_t *runtime = substream->runtime; @@ -977,6 +993,20 @@ int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) return result; } +/** + * snd_rawmidi_transmit_peek - copy data from the internal buffer + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: data size to transfer + * + * Copies data from the internal output buffer to the given buffer. + * + * Call this in the interrupt handler when the midi output is ready, + * and call snd_rawmidi_transmit_ack() after the transmission is + * finished. + * + * Returns the size of copied data, or a negative error code on failure. + */ int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) { unsigned long flags; @@ -1014,6 +1044,17 @@ int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char return result; } +/** + * snd_rawmidi_transmit_ack - acknowledge the transmission + * @substream: the rawmidi substream + * @count: the tranferred count + * + * Advances the hardware pointer for the internal output buffer with + * the given size and updates the condition. + * Call after the transmission is finished. + * + * Returns the advanced size if successful, or a negative error code on failure. + */ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) { unsigned long flags; @@ -1041,6 +1082,16 @@ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) return count; } +/** + * snd_rawmidi_transmit - copy from the buffer to the device + * @substream: the rawmidi substream + * @buf: the buffer pointer + * @count: the data size to transfer + * + * Copies data from the buffer to the device and advances the pointer. + * + * Returns the copied size if successful, or a negative error code on failure. + */ int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) { count = snd_rawmidi_transmit_peek(substream, buffer, count); @@ -1304,6 +1355,20 @@ static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, return 0; } +/** + * snd_rawmidi_new - create a rawmidi instance + * @card: the card instance + * @id: the id string + * @device: the device index + * @output_count: the number of output streams + * @input_count: the number of input streams + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new rawmidi instance. + * Use snd_rawmidi_set_ops() to set the operators to the new instance. + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_rawmidi_new(snd_card_t * card, char *id, int device, int output_count, int input_count, snd_rawmidi_t ** rrawmidi) @@ -1513,6 +1578,14 @@ static int snd_rawmidi_dev_unregister(snd_device_t *device) return snd_rawmidi_free(rmidi); } +/** + * snd_rawmidi_set_ops - set the rawmidi operators + * @rmidi: the rawmidi instance + * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX + * @ops: the operator table + * + * Sets the rawmidi operators for the given stream direction. + */ void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) { struct list_head *list; diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 59b386b1f74a..1a8b6335f5cf 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -77,5 +77,6 @@ obj-$(CONFIG_SND_EMU10K1) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-virmidi obj-$(CONFIG_SND_TRIDENT) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_YMFPCI) += $(RAWMIDI_OBJS) $(OPL3_OBJS) obj-$(CONFIG_SND_USB_AUDIO) += $(RAWMIDI_OBJS) +obj-$(CONFIG_SND_HDSP) += $(RAWMIDI_OBJS) obj-m := $(sort $(obj-m)) diff --git a/sound/core/sound.c b/sound/core/sound.c index 0a18ccae9fd1..3f6a4b68cce4 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -73,6 +73,13 @@ static devfs_handle_t devfs_handle = NULL; #ifdef CONFIG_KMOD +/** + * snd_request_card - try to load the card module + * @card: the card number + * + * Tries to load the module "snd-card-X" for the given card number + * via KMOD. Returns immediately if already loaded. + */ void snd_request_card(int card) { char str[32]; @@ -188,6 +195,19 @@ static int snd_kernel_minor(int type, snd_card_t * card, int dev) return minor; } +/** + * snd_register_device - Register the ALSA device file for the card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * @reg: the snd_minor_t record + * @name: the device file name + * + * Registers an ALSA device file for the given card. + * The operators have to be set in reg parameter. + * + * Retrurns zero if successful, or a negative error code on failure. + */ int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) { int minor = snd_kernel_minor(type, card, dev); @@ -215,6 +235,17 @@ int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, return 0; } +/** + * snd_unregister_device - unregister the device on the given card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * + * Unregisters the device file already registered via + * snd_register_device(). + * + * Returns zero if sucecessful, or a negative error code on failure + */ int snd_unregister_device(int type, snd_card_t * card, int dev) { int minor = snd_kernel_minor(type, card, dev); @@ -411,6 +442,7 @@ EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); #ifdef CONFIG_PCI EXPORT_SYMBOL(snd_malloc_pci_pages); EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); +EXPORT_SYMBOL(snd_malloc_pci_page); EXPORT_SYMBOL(snd_free_pci_pages); #endif #ifdef CONFIG_SBUS @@ -463,6 +495,7 @@ EXPORT_SYMBOL(snd_info_create_device); EXPORT_SYMBOL(snd_info_free_device); EXPORT_SYMBOL(snd_info_register); EXPORT_SYMBOL(snd_info_unregister); +EXPORT_SYMBOL(snd_card_proc_new); #endif /* info_oss.c */ #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 88e3f72c89be..33aab157bf99 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -74,6 +74,14 @@ static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) snd_mpu401_uart_output_write(mpu); } +/** + * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler + * @irq: the irq number + * @dev_id: mpu401 instance + * @regs: the reigster + * + * Processes the interrupt for MPU401-UART i/o. + */ void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) { mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return); @@ -375,6 +383,25 @@ static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) snd_magic_kfree(mpu); } +/** + * snd_mpu401_uart_new - create an MPU401-UART instance + * @card: the card instance + * @device: the device index, zero-based + * @hardware: the hardware type, MPU401_HW_XXXX + * @port: the base address of MPU401 port + * @integrated: non-zero if the port was already reserved by the chip + * @irq: the irq number, -1 if no interrupt for mpu + * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. + * @rrawmidi: the pointer to store the new rawmidi instance + * + * Creates a new MPU-401 instance. + * + * Note that the rawmidi instance is returned on the rrawmidi argument, + * not the mpu401 instance itself. To access to the mpu401 instance, + * cast from rawmidi->private_data (with mpu401_t magic-cast). + * + * Returns zero if successful, or a negative error code. + */ int snd_mpu401_uart_new(snd_card_t * card, int device, unsigned short hardware, unsigned long port, int integrated, @@ -418,9 +445,9 @@ int snd_mpu401_uart_new(snd_card_t * card, int device, snd_device_free(card, rmidi); return -EBUSY; } - mpu->irq = irq; - mpu->irq_flags = irq_flags; } + mpu->irq = irq; + mpu->irq_flags = irq_flags; strcpy(rmidi->name, "MPU-401 (UART)"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index 51ae6acf0b85..caa103d9d33a 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -16,7 +16,7 @@ * 2002-04-12 Tomas Kasparek Proc interface update, code cleanup */ -/* $Id: uda1341.c,v 1.5 2002/11/09 13:12:19 perex Exp $ */ +/* $Id: uda1341.c,v 1.6 2003/01/07 10:36:28 tiwai Exp $ */ #include <sound/driver.h> #include <linux/module.h> @@ -128,9 +128,6 @@ struct uda1341{ snd_card_t *card; - snd_info_entry_t *proc_entry; - snd_info_entry_t *proc_regs_entry; - uda1341_cfg cfg; }; @@ -410,46 +407,10 @@ static void __devinit snd_uda1341_proc_init(snd_card_t *card, struct l3_client * DEBUG_NAME(KERN_DEBUG "proc_init\n"); - if ((entry = snd_info_create_card_entry(card, "uda1341", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = clnt; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; - entry->c.text.read = snd_uda1341_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - uda->proc_entry = entry; - if ((entry = snd_info_create_card_entry(card, "uda1341-regs", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = clnt; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; - entry->c.text.read = snd_uda1341_proc_regs_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - uda->proc_regs_entry = entry; -} - -static void snd_uda1341_proc_done(struct l3_client *clnt) -{ - struct uda1341 *uda = clnt->driver_data; - - DEBUG_NAME(KERN_DEBUG "proc_done\n"); - - if (uda->proc_regs_entry) { - snd_info_unregister(uda->proc_regs_entry); - uda->proc_regs_entry = NULL; - } - if (uda->proc_entry) { - snd_info_unregister(uda->proc_entry); - uda->proc_entry = NULL; - } + if (! snd_card_proc_new(card, "uda1341", &entry)) + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read); + if (! snd_card_proc_new(card, "uda1341-regs", &entry)) { + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read); } /* }}} */ @@ -731,7 +692,6 @@ void __init snd_chip_uda1341_mixer_del(snd_card_t *card) { DEBUG_NAME(KERN_DEBUG "uda1341 mixer_del\n"); - snd_uda1341_proc_done(uda1341); l3_detach_client(uda1341); snd_magic_kfree(uda1341); diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index d36d57a2efa9..a46d5a4613ad 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -936,6 +936,12 @@ int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) return 0; } +const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; +} + /* * MIXER part */ @@ -1160,6 +1166,7 @@ EXPORT_SYMBOL(snd_ad1848_mce_down); EXPORT_SYMBOL(snd_ad1848_interrupt); EXPORT_SYMBOL(snd_ad1848_create); EXPORT_SYMBOL(snd_ad1848_pcm); +EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); EXPORT_SYMBOL(snd_ad1848_mixer); EXPORT_SYMBOL(snd_ad1848_info_single); EXPORT_SYMBOL(snd_ad1848_get_single); diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 093420d84783..cf41048fbf65 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -147,6 +147,15 @@ struct snd_cmi8330 { struct isapnp_dev *cap; struct isapnp_dev *play; #endif + snd_card_t *card; + ad1848_t *wss; + sb_t *sb; + + snd_pcm_t *pcm; + snd_pcm_ops_t playback_ops; + int (*playback_open)(snd_pcm_substream_t *); + snd_pcm_ops_t capture_ops; + int (*capture_open)(snd_pcm_substream_t *); }; static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; @@ -294,6 +303,79 @@ static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) } #endif +/* + * PCM interface + * + * since we call the different chip interfaces for playback and capture + * directions, we need a trick. + * + * - copy the ops for each direction into a local record. + * - replace the open callback with the new one, which replaces the + * substream->private_data with the corresponding chip instance + * and calls again the original open callback of the chip. + * + */ + +static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->sb; + return chip->playback_open(substream); +} + +static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) +{ + struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + + /* replace the private_data and call the original open callback */ + substream->private_data = chip->wss; + return chip->capture_open(substream); +} + +static void snd_cmi8330_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_cmi8330_pcm(snd_card_t *card, struct snd_cmi8330 *chip) +{ + snd_pcm_t *pcm; + const snd_pcm_ops_t *ops; + int err; + + if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) + return err; + strcpy(pcm->name, "CMI8330"); + pcm->private_data = chip; + pcm->private_free = snd_cmi8330_pcm_free; + + /* playback - SB16 */ + ops = snd_sb16dsp_get_pcm_ops(SNDRV_PCM_STREAM_PLAYBACK); + chip->playback_ops = *ops; + chip->playback_open = ops->open; + chip->playback_ops.open = snd_cmi8330_playback_open; + + /* capture - AD1848 */ + ops = snd_ad1848_get_pcm_ops(SNDRV_PCM_STREAM_CAPTURE); + chip->capture_ops = *ops; + chip->capture_open = ops->open; + chip->capture_ops.open = snd_cmi8330_capture_open; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->capture_ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + chip->pcm = pcm; + + return 0; +} + + +/* + */ + static void snd_cmi8330_free(snd_card_t *card) { struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data; @@ -309,12 +391,8 @@ static int __init snd_cmi8330_probe(int dev) { snd_card_t *card; struct snd_cmi8330 *acard; - ad1848_t *chip_wss; - sb_t *chip_sb; unsigned long flags; int i, err; - snd_pcm_t *pcm, *wss_pcm, *sb_pcm; - snd_pcm_str_t *pstr; #ifdef __ISAPNP__ if (!isapnp[dev]) { @@ -337,6 +415,7 @@ static int __init snd_cmi8330_probe(int dev) return -ENOMEM; } acard = (struct snd_cmi8330 *)card->private_data; + acard->card = card; card->private_free = snd_cmi8330_free; #ifdef __ISAPNP__ @@ -352,83 +431,60 @@ static int __init snd_cmi8330_probe(int dev) wssirq[dev], wssdma[dev], AD1848_HW_DETECT, - &chip_wss)) < 0) { + &acard->wss)) < 0) { snd_printk("(AD1848) device busy??\n"); snd_card_free(card); return err; } - if (chip_wss->hardware != AD1848_HW_CMI8330) { + if (acard->wss->hardware != AD1848_HW_CMI8330) { snd_printk("(AD1848) not found during probe\n"); snd_card_free(card); return -ENODEV; } - if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) { - snd_printk("(AD1848) no enough memory??\n"); - snd_card_free(card); - return err; - } if ((err = snd_sbdsp_create(card, sbport[dev], sbirq[dev], snd_sb16dsp_interrupt, sbdma8[dev], sbdma16[dev], - SB_HW_AUTO, &chip_sb)) < 0) { + SB_HW_AUTO, &acard->sb)) < 0) { snd_printk("(SB16) device busy??\n"); snd_card_free(card); return err; } - if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) { - snd_printk("(SB16) no enough memory??\n"); - snd_card_free(card); - return err; - } - - if (chip_sb->hardware != SB_HW_16) { + if (acard->sb->hardware != SB_HW_16) { snd_printk("(SB16) not found during probe\n"); snd_card_free(card); return -ENODEV; } + memcpy(&acard->wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); - memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); + spin_lock_irqsave(&acard->wss->reg_lock, flags); + snd_ad1848_out(acard->wss, AD1848_MISC_INFO, /* switch on MODE2 */ + acard->wss->image[AD1848_MISC_INFO] |= 0x40); + spin_unlock_irqrestore(&acard->wss->reg_lock, flags); - spin_lock_irqsave(&chip_wss->reg_lock, flags); - snd_ad1848_out(chip_wss, AD1848_MISC_INFO, /* switch on MODE2 */ - chip_wss->image[AD1848_MISC_INFO] |= 0x40); - spin_unlock_irqrestore(&chip_wss->reg_lock, flags); - - if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) { + if ((err = snd_cmi8330_mixer(card, acard->wss)) < 0) { snd_printk("failed to create mixers\n"); snd_card_free(card); return err; } - spin_lock_irqsave(&chip_wss->reg_lock, flags); + spin_lock_irqsave(&acard->wss->reg_lock, flags); for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) - snd_ad1848_out(chip_wss, i, chip_wss->image[i]); - spin_unlock_irqrestore(&chip_wss->reg_lock, flags); - - /* - * KLUDGE ALERT - * disable AD1848 playback - * disable SB16 capture - */ - pcm = wss_pcm; - pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; - snd_magic_kfree(pstr->substream); - pstr->substream = 0; - pstr->substream_count = 0; - - pcm = sb_pcm; - pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - snd_magic_kfree(pstr->substream); - pstr->substream = 0; - pstr->substream_count = 0; + snd_ad1848_out(acard->wss, i, acard->wss->image[i]); + spin_unlock_irqrestore(&acard->wss->reg_lock, flags); + + if ((err = snd_cmi8330_pcm(card, acard)) < 0) { + snd_printk("failed to create pcms\n"); + snd_card_free(card); + return err; + } strcpy(card->driver, "CMI8330/C3D"); strcpy(card->shortname, "C-Media CMI8330/C3D"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", - wss_pcm->name, - chip_wss->port, + card->shortname, + acard->wss->port, wssirq[dev], wssdma[dev]); diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index 80ceb4581ba3..7aeaf531df97 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -158,7 +158,7 @@ static __CS4231_INLINE__ u8 cs4231_inb(cs4231_t *chip, u8 offset) } else { #endif #ifdef SBUS_SUPPORT - return sbus_writeb(chip->port + (offset << 2)); + return sbus_readb(chip->port + (offset << 2)); #endif #ifdef EBUS_SUPPORT } @@ -338,13 +338,13 @@ void snd_cs4231_mce_up(cs4231_t *chip) unsigned long flags; int timeout; - spin_lock_irqsave(&chip->reg_lock, flags); for (timeout = 250; timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); timeout--) udelay(100); #ifdef CONFIG_SND_DEBUG if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) snd_printk("mce_up - auto calibration time out (0)\n"); #endif + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit |= CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); if (timeout == 0x80) @@ -360,7 +360,6 @@ void snd_cs4231_mce_down(cs4231_t *chip) int timeout; signed long time; - spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_busy_wait(chip); #if 0 printk("(1) timeout = %i\n", timeout); @@ -369,14 +368,15 @@ void snd_cs4231_mce_down(cs4231_t *chip) if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); #endif + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit &= ~CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); if (timeout == 0x80) snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } snd_cs4231_busy_wait(chip); @@ -387,7 +387,6 @@ void snd_cs4231_mce_down(cs4231_t *chip) udelay(10); if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } #if 0 @@ -395,30 +394,25 @@ void snd_cs4231_mce_down(cs4231_t *chip) #endif time = HZ / 4; while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { - spin_unlock_irqrestore(&chip->reg_lock, flags); if (time <= 0) { snd_printk("mce_down - auto calibration time out (2)\n"); return; } set_current_state(TASK_INTERRUPTIBLE); time = schedule_timeout(time); - spin_lock_irqsave(&chip->reg_lock, flags); } #if 0 printk("(3) jiffies = %li\n", jiffies); #endif time = HZ / 10; while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { - spin_unlock_irqrestore(&chip->reg_lock, flags); if (time <= 0) { - snd_printk("mce_down - auto calibration time out (3)\n"); + snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); return; } set_current_state(TASK_INTERRUPTIBLE); time = schedule_timeout(time); - spin_lock_irqsave(&chip->reg_lock, flags); } - spin_unlock_irqrestore(&chip->reg_lock, flags); #if 0 printk("(4) jiffies = %li\n", jiffies); snd_printk("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL))); @@ -1375,20 +1369,19 @@ static void snd_cs4231_resume(cs4231_t *chip) This is the first half of copy of snd_cs4231_mce_down(), but doesn't include rescheduling. -- iwai */ - spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_busy_wait(chip); + spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit &= ~CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); if (timeout == 0x80) snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { - spin_unlock_irqrestore(&chip->reg_lock, flags); return; } snd_cs4231_busy_wait(chip); - spin_unlock_irqrestore(&chip->reg_lock, flags); #endif } diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index f62c466009fe..31442b641067 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -460,6 +460,11 @@ static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, return 0; } +static int snd_es18xx_pcm_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + static int snd_es18xx_playback1_prepare(es18xx_t *chip, snd_pcm_substream_t *substream) { @@ -1540,6 +1545,7 @@ static snd_pcm_ops_t snd_es18xx_playback_ops = { .close = snd_es18xx_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_playback_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_playback_prepare, .trigger = snd_es18xx_playback_trigger, .pointer = snd_es18xx_playback_pointer, @@ -1550,6 +1556,7 @@ static snd_pcm_ops_t snd_es18xx_capture_ops = { .close = snd_es18xx_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_capture_hw_params, + .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_capture_prepare, .trigger = snd_es18xx_capture_trigger, .pointer = snd_es18xx_capture_pointer, @@ -1606,9 +1613,8 @@ static void snd_es18xx_suspend(es18xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -1619,24 +1625,19 @@ static void snd_es18xx_suspend(es18xx_t *chip) snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void snd_es18xx_resume(es18xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* restore PM register, we won't wake till (not 0x07) i/o activity though */ snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } /* callback for control API */ diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c index 55fd6f5f6d6b..3ea4d76ca101 100644 --- a/sound/isa/gus/gus_irq.c +++ b/sound/isa/gus/gus_irq.c @@ -132,26 +132,8 @@ void snd_gus_irq_profile_init(snd_gus_card_t *gus) { snd_info_entry_t *entry; - gus->irq_entry = NULL; - entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->c.text.read_size = 512; - entry->c.text.read = snd_gus_irq_info_read; - entry->private_data = gus; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - gus->irq_entry = entry; + if (! snd_card_proc_new(gus->card, "gusirq", &entry)) + snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read); } -void snd_gus_irq_profile_done(snd_gus_card_t *gus) -{ - if (gus->irq_entry) { - snd_info_unregister(gus->irq_entry); - gus->irq_entry = NULL; - } -} #endif diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 9cedfa7e83da..b20cac1f6e6a 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -264,19 +264,10 @@ int snd_gf1_mem_init(snd_gus_card_t * gus) if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG - alloc->info_entry = NULL; - entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; + if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { + snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read); entry->c.text.read_size = 256 * 1024; - entry->c.text.read = snd_gf1_mem_info_read; - entry->private_data = gus; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - alloc->info_entry = entry; #endif return 0; } @@ -293,10 +284,6 @@ int snd_gf1_mem_done(snd_gus_card_t * gus) snd_gf1_mem_xfree(alloc, block); block = nblock; } -#ifdef CONFIG_SND_DEBUG - if (alloc->info_entry) - snd_info_unregister(alloc->info_entry); -#endif return 0; } diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c index 58284186c88c..32f02ad501da 100644 --- a/sound/isa/gus/gus_mem_proc.c +++ b/sound/isa/gus/gus_mem_proc.c @@ -96,73 +96,40 @@ int snd_gf1_mem_proc_init(snd_gus_card_t * gus) gus_proc_private_t *priv; snd_info_entry_t *entry; - memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries)); - memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries)); for (idx = 0; idx < 4; idx++) { if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); - if (priv == NULL) { - snd_gf1_mem_proc_done(gus); + if (priv == NULL) return -ENOMEM; - } priv->gus = gus; sprintf(name, "gus-ram-%i", idx); - entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); - if (entry) { + if (! snd_card_proc_new(gus->card, name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = priv; entry->private_free = snd_gf1_mem_proc_free; entry->c.ops = &snd_gf1_mem_proc_ops; priv->address = gus->gf1.mem_alloc.banks_8[idx].address; priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - gus->gf1.ram_entries[idx] = entry; } } for (idx = 0; idx < 4; idx++) { if (gus->gf1.rom_present & (1 << idx)) { priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); - if (priv == NULL) { - snd_gf1_mem_proc_done(gus); + if (priv == NULL) return -ENOMEM; - } priv->rom = 1; priv->gus = gus; sprintf(name, "gus-rom-%i", idx); - entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); - if (entry) { + if (! snd_card_proc_new(gus->card, name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = priv; entry->private_free = snd_gf1_mem_proc_free; entry->c.ops = &snd_gf1_mem_proc_ops; priv->address = idx * 4096 * 1024; priv->size = entry->size = gus->gf1.rom_memory; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - gus->gf1.rom_entries[idx] = entry; } } return 0; } - -int snd_gf1_mem_proc_done(snd_gus_card_t * gus) -{ - int idx; - - for (idx = 0; idx < 4; idx++) { - if (gus->gf1.ram_entries[idx]) - snd_info_unregister(gus->gf1.ram_entries[idx]); - } - for (idx = 0; idx < 4; idx++) { - if (gus->gf1.rom_entries[idx]) - snd_info_unregister(gus->gf1.rom_entries[idx]); - } - return 0; -} diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index a6f086781f0b..10182383a7a1 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -410,10 +410,6 @@ int snd_gf1_stop(snd_gus_card_t * gus) snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ snd_gf1_timers_done(gus); -#ifdef CONFIG_SND_DEBUG - snd_gus_irq_profile_done(gus); -#endif - snd_gf1_mem_proc_done(gus); snd_gf1_mem_done(gus); #if 0 snd_gf1_lfo_done(gus); diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 0b8edb576638..57c7116a1472 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -107,7 +107,6 @@ static void snd_sb_qsound_destroy(snd_sb_csp_t * p); static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); static int init_proc_entry(snd_sb_csp_t * p, int device); -static void delete_proc_entry(snd_sb_csp_t * p); static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); /* @@ -170,7 +169,6 @@ static void snd_sb_csp_free(snd_hwdep_t *hwdep) if (p) { if (p->running & SNDRV_SB_CSP_ST_RUNNING) snd_sb_csp_stop(p); - delete_proc_entry(p); snd_magic_kfree(p); } } @@ -1104,28 +1102,11 @@ static int init_proc_entry(snd_sb_csp_t * p, int device) char name[16]; snd_info_entry_t *entry; sprintf(name, "cspD%d", device); - entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->c.text.read_size = 256; - entry->c.text.read = info_read; - entry->private_data = p; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - p->proc = NULL; - } - } + if (! snd_card_proc_new(p->chip->card, name, &entry)) + snd_info_set_text_ops(entry, p, info_read); return 0; } -static void delete_proc_entry(snd_sb_csp_t * p) -{ - if (p->proc) { - snd_info_unregister(p->proc); - p->proc = NULL; - } -} - static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 30a1a67249ce..4b4ce71593e8 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -887,7 +887,14 @@ int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) return 0; } +const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction) +{ + return direction == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_sb16_playback_ops : &snd_sb16_capture_ops; +} + EXPORT_SYMBOL(snd_sb16dsp_pcm); +EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); EXPORT_SYMBOL(snd_sb16dsp_configure); EXPORT_SYMBOL(snd_sb16dsp_interrupt); diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index e63a3052def7..f555de1de90c 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -64,7 +64,8 @@ unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sbmixer_info_single, \ - .get = snd_sbmixer_get_single, put: snd_sbmixer_put_single, \ + .get = snd_sbmixer_get_single, \ + .put = snd_sbmixer_put_single, \ .private_value = reg | (shift << 16) | (mask << 24) } static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) @@ -123,7 +124,8 @@ static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sbmixer_info_double, \ - .get = snd_sbmixer_get_double, put: snd_sbmixer_put_double, \ + .get = snd_sbmixer_get_double, \ + .put = snd_sbmixer_put_double, \ .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) } static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) @@ -366,7 +368,8 @@ static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .info = snd_sb16mixer_info_input_sw, \ - .get = snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \ + .get = snd_sb16mixer_get_input_sw, \ + .put = snd_sb16mixer_put_input_sw, \ .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index a02b6e8ef8f5..1b6dbaf42cce 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -51,7 +51,6 @@ MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); */ static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97); -static void snd_ac97_proc_done(ac97_t * ac97); typedef struct { unsigned int id; @@ -240,6 +239,18 @@ static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) return 1; } +/** + * snd_ac97_write - write a value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register. This will invoke the write + * callback directly after the register check. + * This function doesn't change the register cache unlike + * #snd_ca97_write_cache(), so use this only when you don't want to + * reflect the change to the suspend/resume state. + */ void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -247,6 +258,17 @@ void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) ac97->write(ac97, reg, value); } +/** + * snd_ac97_read - read a value from the given register + * + * @ac97: the ac97 instance + * @reg: the register to read + * + * Reads a value from the given register. This will invoke the read + * callback directly after the register check. + * + * Returns the read value. + */ unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -254,6 +276,16 @@ unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) return ac97->read(ac97, reg); } +/** + * snd_ac97_write_cache - write a value on the given register and update the cache + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Writes a value on the given register and updates the register + * cache. The cached values are used for the cached-read and the + * suspend/resume. + */ void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value) { if (!snd_ac97_valid_reg(ac97, reg)) @@ -279,6 +311,18 @@ static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned snd_ac97_write_cache(ac97, reg, value); } +/** + * snd_ac97_update - update the value on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @value: the value to set + * + * Compares the value with the register cache and updates the value + * only when the value is changed. + * + * Retruns 1 if the value is changed, 0 if no change, or a negative + * code on failure. + */ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) { int change; @@ -295,7 +339,7 @@ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) return change; } -int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) +static int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) { int change; unsigned short old, new; @@ -312,6 +356,19 @@ int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short return change; } +/** + * snd_ac97_update_bits - update the bits on the given register + * @ac97: the ac97 instance + * @reg: the register to change + * @mask: the bit-mask to change + * @value: the value to set + * + * Updates the masked-bits on the given register onle when the value + * is changed. + * + * Returns 1 if the bits are changed, 0 if no change, or a negative + * code on failure. + */ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) { int change; @@ -321,7 +378,7 @@ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, return change; } -int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) +static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) { int change; unsigned short old, new; @@ -935,6 +992,11 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0), AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0), + /* 4: Analog Input To Surround */ + /* 5: Analog Input To Center/LFE */ + /* 6: Indepedent Master Volume Right */ + /* 7: Indepedent Master Volume Left */ + /* 8: reserved */ AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0), AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), @@ -944,6 +1006,10 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0), AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0), #endif + AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1), + AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), + AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), }; /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ @@ -1098,7 +1164,6 @@ static const snd_kcontrol_new_t snd_ac97_ymf753_controls_spdif[3] = { static int snd_ac97_free(ac97_t *ac97) { if (ac97) { - snd_ac97_proc_done(ac97); if (ac97->private_free) ac97->private_free(ac97); snd_magic_kfree(ac97); @@ -1686,6 +1751,32 @@ static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name) sprintf(name + strlen(name), " (%x)", id & 0xff); } + +/** + * snd_ac97_mixer - create an AC97 codec component + * @card: the card instance + * @_ac97: the template of ac97, including index, callbacks and + * the private data. + * @rac97: the pointer to store the new ac97 instance. + * + * Creates an AC97 codec component. An ac97_t instance is newly + * allocated and initialized from the template (_ac97). The codec + * is then initialized by the standard procedure. + * + * The template must include the valid callbacks (at least read and + * write), the codec number (num) and address (addr), and the private + * data (private_data). The other callbacks, wait and reset, are not + * mandantory. + * + * The clock is set to 48000. If another clock is needed, reset + * ac97->clock manually afterwards. + * + * The ac97 instance is registered as a low-level device, so you don't + * have to release it manually. + * + * Returns zero if sucessful, or a negative error code on failure. + */ + int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) { int err; @@ -1866,7 +1957,7 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) } /* - + * proc interface */ static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) @@ -2089,46 +2180,14 @@ static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97) sprintf(name, "ac97#%d-%d", ac97->addr, ac97->num); else sprintf(name, "ac97#%d", ac97->addr); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ac97; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; - entry->c.text.read = snd_ac97_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ac97->proc_entry = entry; + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); if (ac97->num) sprintf(name, "ac97#%d-%dregs", ac97->addr, ac97->num); else sprintf(name, "ac97#%dregs", ac97->addr); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ac97; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; - entry->c.text.read = snd_ac97_proc_regs_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ac97->proc_regs_entry = entry; -} - -static void snd_ac97_proc_done(ac97_t * ac97) -{ - if (ac97->proc_regs_entry) { - snd_info_unregister(ac97->proc_regs_entry); - ac97->proc_regs_entry = NULL; - } - if (ac97->proc_entry) { - snd_info_unregister(ac97->proc_entry); - ac97->proc_entry = NULL; - } + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); } /* @@ -2174,6 +2233,23 @@ static int set_spdif_rate(ac97_t *ac97, unsigned short rate) return 0; } +/** + * snd_ac97_set_rate - change the rate of the given input/output. + * @ac97: the ac97 instance + * @reg: the register to change + * @rate: the sample rate to set + * + * Changes the rate of the given input/output on the codec. + * If the codec doesn't support VAR, the rate must be 48000 (except + * for SPDIF). + * + * The valid registers are AC97_PMC_MIC_ADC_RATE, + * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE and AC97_SPDIF. + * The SPDIF register is a pseudo-register to change the rate of SPDIF + * (only if supported). + * + * Returns zero if successful, or a negative error code on failure. + */ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) { unsigned short mask; @@ -2213,8 +2289,11 @@ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) #ifdef CONFIG_PM -/* - * general suspend procedure +/** + * snd_ac97_suspend - General suspend function for AC97 codec + * @ac97: the ac97 instance + * + * Suspends the codec, power down the chip. */ void snd_ac97_suspend(ac97_t *ac97) { @@ -2231,8 +2310,12 @@ void snd_ac97_suspend(ac97_t *ac97) snd_ac97_write(ac97, AC97_POWERDOWN, power); } -/* - * general resume procedure +/** + * snd_ac97_resume - General resume function for AC97 codec + * @ac97: the ac97 instance + * + * Do the standard resume procedure, power up and restoring the + * old register values. */ void snd_ac97_resume(ac97_t *ac97) { diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 22afe9f7d82b..18e1731d9bcf 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -33,7 +33,6 @@ MODULE_LICENSE("GPL"); #define chip_t ak4531_t static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); -static void snd_ak4531_proc_done(ak4531_t * ak4531); /* * @@ -314,7 +313,6 @@ AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) static int snd_ak4531_free(ak4531_t *ak4531) { if (ak4531) { - snd_ak4531_proc_done(ak4531); if (ak4531->private_free) ak4531->private_free(ak4531); snd_magic_kfree(ak4531); @@ -425,26 +423,8 @@ static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(card, "ak4531", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ak4531; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_ak4531_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ak4531->proc_entry = entry; -} - -static void snd_ak4531_proc_done(ak4531_t * ak4531) -{ - if (ak4531->proc_entry) { - snd_info_unregister(ak4531->proc_entry); - ak4531->proc_entry = NULL; - } + if (! snd_card_proc_new(card, "ak4531", &entry)) + snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); } EXPORT_SYMBOL(snd_ak4531_mixer); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index ce0a20f9f7e5..b3a02850d812 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -472,7 +472,6 @@ struct snd_stru_cmipci { snd_rawmidi_t *rmidi; spinlock_t reg_lock; - snd_info_entry_t *proc_entry; }; @@ -2746,26 +2745,8 @@ static void __devinit snd_cmipci_proc_init(cmipci_t *cm) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(cm->card, "cmipci", cm->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = cm; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_cmipci_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - cm->proc_entry = entry; -} - -static void snd_cmipci_proc_done(cmipci_t *cm) -{ - if (cm->proc_entry) { - snd_info_unregister(cm->proc_entry); - cm->proc_entry = NULL; - } + if (! snd_card_proc_new(cm->card, "cmipci", &entry)) + snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); } @@ -2832,8 +2813,6 @@ static void __devinit query_chip(cmipci_t *cm) static int snd_cmipci_free(cmipci_t *cm) { - snd_cmipci_proc_done(cm); - if (cm->irq >= 0) { snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); @@ -2949,20 +2928,10 @@ static int __devinit snd_cmipci_create(snd_card_t *card, /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */ switch (pci->device) { - struct list_head *pos; - int txvx; case PCI_DEVICE_ID_CMEDIA_CM8738: case PCI_DEVICE_ID_CMEDIA_CM8738B: - txvx = 1; - list_for_each(pos, &(pci->global_list)) { - struct pci_dev * cur = list_entry(pos, struct pci_dev, global_list); - if (cur->vendor != 0x8086) /* PCI_VENDOR_ID_INTEL */ - continue; - if (cur->device != 0x7030) /* PCI_DEVICE_ID_INTEL_82437VX */ - continue; - txvx = 0; - } - if (txvx) + /* PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX */ + if (! pci_find_device(0x8086, 0x7030, NULL)) snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX); break; default: diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index a9468e9e127c..11207a67b87e 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -499,13 +499,9 @@ struct snd_cs4281 { unsigned int spurious_dhtc_irq; unsigned int spurious_dtc_irq; - void *proc_entry_BA0; - void *proc_entry_BA1; - spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; - snd_info_entry_t *proc_entry; struct snd_cs4281_gameport *gameport; @@ -1251,55 +1247,19 @@ static void __devinit snd_cs4281_proc_init(cs4281_t * chip) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281", chip->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_cs4281_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - chip->proc_entry = entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA0", chip->card->proc_root)) != NULL) { + if (! snd_card_proc_new(chip->card, "cs4281", &entry)) + snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); + if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs4281_proc_ops_BA0; entry->size = CS4281_BA0_SIZE; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } } - chip->proc_entry_BA0 = entry; - if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA1", chip->card->proc_root)) != NULL) { + if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs4281_proc_ops_BA1; entry->size = CS4281_BA1_SIZE; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } - } - chip->proc_entry_BA1 = entry; -} - -static void snd_cs4281_proc_done(cs4281_t * chip) -{ - if (chip->proc_entry_BA1) { - snd_info_unregister(chip->proc_entry_BA1); - chip->proc_entry_BA1 = NULL; - } - if (chip->proc_entry_BA0) { - snd_info_unregister(chip->proc_entry_BA0); - chip->proc_entry_BA0 = NULL; - } - if (chip->proc_entry) { - snd_info_unregister(chip->proc_entry); - chip->proc_entry = NULL; } } @@ -1414,7 +1374,6 @@ static int snd_cs4281_free(cs4281_t *chip) kfree(chip->gameport); } #endif - snd_cs4281_proc_done(chip); if (chip->irq >= 0) synchronize_irq(chip->irq); @@ -2097,9 +2056,8 @@ static void cs4281_suspend(cs4281_t *chip) u32 ulCLK; int i; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -2132,8 +2090,6 @@ static void cs4281_suspend(cs4281_t *chip) snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void cs4281_resume(cs4281_t *chip) @@ -2142,9 +2098,8 @@ static void cs4281_resume(cs4281_t *chip) int i; u32 ulCLK; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); @@ -2169,8 +2124,6 @@ static void cs4281_resume(cs4281_t *chip) snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index d4a97f58c4dd..f8546a2979f1 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -26,7 +26,7 @@ * at Cirrus for have helping me out with the DSP, however we * still dont have sufficient documentation and technical * references to be able to implement all fancy feutures - * supported by the cs46xx DPS's. + * supported by the cs46xx DSP's. * Benny <benny@hostmobility.com> * * This program is free software; you can redistribute it and/or modify @@ -57,6 +57,8 @@ #include <sound/core.h> #include <sound/control.h> #include <sound/info.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/cs46xx.h> #include <asm/io.h> @@ -730,10 +732,12 @@ static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream, snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff; cs46xx_pcm_t * cpcm; - int buffer_size = runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size; cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); + buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift; + diff = runtime->control->appl_ptr - cpcm->appl_ptr; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) @@ -774,7 +778,7 @@ static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr; - int buffer_size = runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = runtime->period_size * CS46XX_FRAGS << chip->capt.shift; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; @@ -830,7 +834,7 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_ size_t ptr; cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); ssize_t bytes; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << cpcm->shift; #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (cpcm->pcm_channel,return -ENXIO); @@ -865,7 +869,7 @@ static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; ssize_t bytes = ptr - chip->capt.hw_io; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4; + int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; if (bytes < 0) bytes += buffer_size; @@ -1071,7 +1075,7 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, int err; cs46xx_t *chip = snd_pcm_substream_chip(substream); int sample_rate = params_rate(hw_params); - int period_size = params_period_size(hw_params); + int period_size = params_period_bytes(hw_params); cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -1090,12 +1094,15 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, return -ENXIO; } - if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size * 4)) { + + if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) { up (&chip->spos_mutex); return -EINVAL; } - snd_printdd ("period_size (%d), periods (%d)\n", - period_size, params_periods(hw_params)); + + snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n", + period_size, params_periods(hw_params), + params_buffer_bytes(hw_params)); #endif if (params_periods(hw_params) == CS46XX_FRAGS) { @@ -1255,12 +1262,10 @@ static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; - int period_size = params_period_size(hw_params); + int period_size = params_period_bytes(hw_params); #ifdef CONFIG_SND_CS46XX_NEW_DSP - snd_printdd ("capture period size (%d)\n",period_size); - - cs46xx_dsp_pcm_ostream_set_period (chip,period_size * 4); + cs46xx_dsp_pcm_ostream_set_period (chip,period_size); #endif if (runtime->periods == CS46XX_FRAGS) { if (runtime->dma_area != chip->capt.hw_area) @@ -1445,7 +1450,7 @@ static snd_pcm_hardware_t snd_cs46xx_capture = .fifo_size = 0, }; -static unsigned int period_sizes[] = { 8, 16, 32, 64, 128, 256, 512 }; +static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; #define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) @@ -1488,8 +1493,11 @@ static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pc cpcm->pcm_channel = NULL; cpcm->pcm_channel_id = pcm_channel_id; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes); + up (&chip->spos_mutex); #else chip->playback_pcm = cpcm; /* HACK */ @@ -1565,7 +1573,8 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) chip->amplifier_ctrl(chip, 1); #ifdef CONFIG_SND_CS46XX_NEW_DSP - snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes); #endif return 0; @@ -2874,19 +2883,13 @@ static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) for (idx = 0; idx < 5; idx++) { snd_cs46xx_region_t *region = &chip->region.idx[idx]; - entry = snd_info_create_card_entry(card, region->name, card->proc_root); - if (entry) { + if (! snd_card_proc_new(card, region->name, &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs46xx_proc_io_ops; entry->size = region->size; entry->mode = S_IFREG | S_IRUSR; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } } - region->proc_entry = entry; } #ifdef CONFIG_SND_CS46XX_NEW_DSP cs46xx_dsp_proc_init(card, chip); @@ -2896,15 +2899,6 @@ static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) static int snd_cs46xx_proc_done(cs46xx_t *chip) { - int idx; - - for (idx = 0; idx < 5; idx++) { - snd_cs46xx_region_t *region = &chip->region.idx[idx]; - if (region->proc_entry) { - snd_info_unregister((snd_info_entry_t *) region->proc_entry); - region->proc_entry = NULL; - } - } #ifdef CONFIG_SND_CS46XX_NEW_DSP cs46xx_dsp_proc_done(chip); #endif @@ -3716,16 +3710,13 @@ void snd_cs46xx_suspend(cs46xx_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); snd_cs46xx_hw_stop(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_cs46xx_resume(cs46xx_t *chip) @@ -3733,9 +3724,8 @@ void snd_cs46xx_resume(cs46xx_t *chip) snd_card_t *card = chip->card; int amp_saved; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); amp_saved = chip->amplifier; @@ -3764,8 +3754,6 @@ void snd_cs46xx_resume(cs46xx_t *chip) chip->active_ctrl(chip, -1); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index fe5c5d0b3698..0e9a18fb235c 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -80,6 +80,7 @@ MODULE_PARM_SYNTAX(enable_ir, SNDRV_ENABLE_DESC); static struct pci_device_id snd_emu10k1_ids[] __devinitdata = { { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ + { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */ { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ { 0, } }; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 48b558a76194..2575010a28ed 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -494,7 +494,6 @@ static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu) static int snd_emu10k1_free(emu10k1_t *emu) { - snd_emu10k1_proc_done(emu); if (emu->res_port != NULL) { /* avoid access to already used hardware */ snd_emu10k1_fx8010_tram_setup(emu, 0); snd_emu10k1_done(emu); diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1885bebc908c..1c3d91f84169 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -365,7 +365,7 @@ static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, snd_util_memblk_t *memblk; if (epcm->memblk != NULL) snd_emu10k1_free_pages(emu, epcm->memblk); - memblk = snd_emu10k1_alloc_pages(emu, (struct snd_sg_buf *)substream->dma_private); + memblk = snd_emu10k1_alloc_pages(emu, substream); if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { epcm->start_addr = 0; return -ENOMEM; diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 1fcade671328..fa0e42af4f6d 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -239,111 +239,43 @@ int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(emu->card, "emu10k1", emu->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = snd_emu10k1_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - emu->proc_entry = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); + + if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_GPR; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_gpr = entry; - entry = NULL; - if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) { + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_TANKMEM_DATA; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_tram_data = entry; - entry = NULL; - if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) { + if (!emu->audigy && ! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_TANKMEM_ADDR; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_tram_addr = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->size = TOTAL_SIZE_CODE; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } - emu->proc_entry_fx8010_code = entry; - entry = NULL; - if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) { + if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; entry->c.text.read_size = 64*1024; entry->c.text.read = snd_emu10k1_proc_acode_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - emu->proc_entry_fx8010_iblocks = entry; - return 0; -} - -int snd_emu10k1_proc_done(emu10k1_t * emu) -{ - if (emu->proc_entry) { - snd_info_unregister(emu->proc_entry); - emu->proc_entry = NULL; - } - if (emu->proc_entry_fx8010_gpr) { - snd_info_unregister(emu->proc_entry_fx8010_gpr); - emu->proc_entry_fx8010_gpr = NULL; - } - if (emu->proc_entry_fx8010_tram_data) { - snd_info_unregister(emu->proc_entry_fx8010_tram_data); - emu->proc_entry_fx8010_tram_data = NULL; - } - if (emu->proc_entry_fx8010_tram_addr) { - snd_info_unregister(emu->proc_entry_fx8010_tram_addr); - emu->proc_entry_fx8010_tram_addr = NULL; - } - if (emu->proc_entry_fx8010_code) { - snd_info_unregister(emu->proc_entry_fx8010_code); - emu->proc_entry_fx8010_code = NULL; - } - if (emu->proc_entry_fx8010_iblocks) { - snd_info_unregister(emu->proc_entry_fx8010_iblocks); - emu->proc_entry_fx8010_iblocks = NULL; } return 0; } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index dc1f597eb70c..5037fb5fdea4 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -25,6 +25,7 @@ #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> +#include <sound/pcm_sgbuf.h> /* page arguments of these two macros are Emu page (4096 bytes), not like * aligned pages in others @@ -288,8 +289,9 @@ int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk) * page allocation for DMA */ snd_util_memblk_t * -snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf) +snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream) { + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return NULL); snd_util_memhdr_t *hdr; emu10k1_memblk_t *blk; int page, err, idx; @@ -306,7 +308,7 @@ snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf) return NULL; } /* fill buffer addresses but pointers are not stored so that - * snd_free_pci_pages() is not called in in synth_free() + * snd_free_pci_page() is not called in in synth_free() */ idx = 0; for (page = blk->first_page; page <= blk->last_page; page++, idx++) { @@ -430,11 +432,11 @@ static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) get_single_page_range(emu->memhdr, blk, &first_page, &last_page); /* allocate kernel pages */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_pages(emu->pci, PAGE_SIZE, &addr); + ptr = snd_malloc_pci_page(emu->pci, &addr); if (ptr == NULL) goto __fail; if (! is_valid_page(addr)) { - snd_free_pci_pages(emu->pci, PAGE_SIZE, ptr, addr); + snd_free_pci_page(emu->pci, ptr, addr); goto __fail; } emu->page_addr_table[page] = addr; @@ -446,7 +448,7 @@ __fail: /* release allocated pages */ last_page = page - 1; for (page = first_page; page <= last_page; page++) { - snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } @@ -464,7 +466,7 @@ static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) get_single_page_range(emu->memhdr, blk, &first_page, &last_page); for (page = first_page; page <= last_page; page++) { if (emu->page_ptr_table[page]) - snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1b3c1f7b3036..d0e112a04a15 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -398,8 +398,6 @@ struct _snd_ensoniq { unsigned int spdif_default; unsigned int spdif_stream; - snd_info_entry_t *proc_entry; - #ifdef CHIP1370 unsigned char *bugbuf; dma_addr_t bugbuf_addr; @@ -1264,9 +1262,10 @@ static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t * Mixer section */ +/* + * ENS1371 mixer (including SPDIF interface) + */ #ifdef CHIP1371 - - static int snd_ens1373_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; @@ -1490,6 +1489,8 @@ static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) #endif /* CHIP1371 */ +/* generic control callbacks for ens1370 and for joystick */ +#if defined(CHIP1370) || defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) #define ENSONIQ_CONTROL(xname, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \ @@ -1517,7 +1518,6 @@ static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value } #ifdef CHIP1370 - static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); @@ -1535,14 +1535,21 @@ static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value spin_unlock_irqrestore(&ensoniq->reg_lock, flags); return change; } +#endif /* CHIP1370 */ +#endif /* CHIP1370 || GAMEPORT */ -#define ES1370_CONTROLS 2 +/* + * ENS1370 mixer + */ +#ifdef CHIP1370 static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = { ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) }; +#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls) + static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) { ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); @@ -1725,26 +1732,8 @@ static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(ensoniq->card, "audiopci", ensoniq->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ensoniq; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_ensoniq_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ensoniq->proc_entry = entry; -} - -static void snd_ensoniq_proc_done(ensoniq_t * ensoniq) -{ - if (ensoniq->proc_entry) { - snd_info_unregister(ensoniq->proc_entry); - ensoniq->proc_entry = NULL; - } + if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); } /* @@ -1757,7 +1746,6 @@ static int snd_ensoniq_free(ensoniq_t *ensoniq) if (ensoniq->ctrl & ES_JYSTK_EN) snd_ensoniq_joy_disable(ensoniq); #endif - snd_ensoniq_proc_done(ensoniq); if (ensoniq->irq < 0) goto __hw_end; #ifdef CHIP1370 diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 0b91028e2768..4de0e08b524c 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -202,8 +202,6 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); typedef struct _snd_es1938 es1938_t; struct _snd_es1938 { - unsigned long dma1size; - unsigned long dma2size; int irq; unsigned long io_port; @@ -840,6 +838,25 @@ static int snd_es1938_capture_copy(snd_pcm_substream_t *substream, return 0; } +/* + * buffer management + */ +static int snd_es1938_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) + +{ + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es1938_pcm_hw_free(snd_pcm_substream_t *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + /* ---------------------------------------------------------------------- * Audio1 Capture (ADC) * ----------------------------------------------------------------------*/ @@ -890,8 +907,6 @@ static int snd_es1938_capture_open(snd_pcm_substream_t * substream) if (chip->playback2_substream) return -EAGAIN; - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->capture_substream = substream; runtime->hw = snd_es1938_capture; snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, @@ -907,15 +922,11 @@ static int snd_es1938_playback_open(snd_pcm_substream_t * substream) switch (substream->number) { case 0: - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->playback1_substream = substream; break; case 1: if (chip->capture_substream) return -EAGAIN; - if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) - return -ENOMEM; chip->playback2_substream = substream; break; default: @@ -963,6 +974,8 @@ static snd_pcm_ops_t snd_es1938_playback_ops = { .open = snd_es1938_playback_open, .close = snd_es1938_playback_close, .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, .prepare = snd_es1938_playback_prepare, .trigger = snd_es1938_playback_trigger, .pointer = snd_es1938_playback_pointer, @@ -972,6 +985,8 @@ static snd_pcm_ops_t snd_es1938_capture_ops = { .open = snd_es1938_capture_open, .close = snd_es1938_capture_close, .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_es1938_pcm_hw_params, + .hw_free = snd_es1938_pcm_hw_free, .prepare = snd_es1938_capture_prepare, .trigger = snd_es1938_capture_trigger, .pointer = snd_es1938_capture_pointer, @@ -1364,8 +1379,6 @@ static int snd_es1938_dev_free(snd_device_t *device) static int __devinit snd_es1938_create(snd_card_t * card, struct pci_dev * pci, - unsigned long dma1size, - unsigned long dma2size, es1938_t ** rchip) { es1938_t *chip; @@ -1393,8 +1406,6 @@ static int __devinit snd_es1938_create(snd_card_t * card, spin_lock_init(&chip->mixer_lock); chip->card = card; chip->pci = pci; - chip->dma1size = dma1size; - chip->dma2size = dma2size; chip->io_port = pci_resource_start(pci, 0); if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { snd_es1938_free(chip); @@ -1608,10 +1619,7 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci, return -ENODEV; } } - if ((err = snd_es1938_create(card, pci, - 64 * 1024, - 64 * 1024, - &chip)) < 0) { + if ((err = snd_es1938_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 29c94cbb229c..d27a8d2a73e4 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2420,15 +2420,12 @@ static void es1968_suspend(es1968_t *chip) if (! chip->do_pm) return; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); snd_es1968_bob_stop(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void es1968_resume(es1968_t *chip) @@ -2438,9 +2435,8 @@ static void es1968_resume(es1968_t *chip) if (! chip->do_pm) return; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* restore all our config */ pci_enable_device(chip->pci); @@ -2459,8 +2455,6 @@ static void es1968_resume(es1968_t *chip) if (atomic_read(&chip->bobclient)) snd_es1968_bob_start(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 8133aa9796c3..5c5120904532 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1681,12 +1681,11 @@ static void snd_ice1712_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, " CCS%02x : 0x%02x\n", idx, inb(ice->port+idx)); for (idx = 0x0; idx < 0x30 ; idx++) snd_iprintf(buffer, " MT%02x : 0x%02x\n", idx, inb(ice->profi_port+idx)); - - } - else { + } else { snd_iprintf(buffer, " PSDOUT03 : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_PSDOUT03))); snd_iprintf(buffer, " CAPTURE : 0x%08x\n", inl(ICEMT(ice, ROUTE_CAPTURE))); snd_iprintf(buffer, " SPDOUT : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT))); + snd_iprintf(buffer, " RATE : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE))); } } @@ -1694,26 +1693,8 @@ static void __devinit snd_ice1712_proc_init(ice1712_t * ice) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = ice; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 2048; - entry->c.text.read = snd_ice1712_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ice->proc_entry = entry; -} - -static void snd_ice1712_proc_done(ice1712_t * ice) -{ - if (ice->proc_entry) { - snd_info_unregister(ice->proc_entry); - ice->proc_entry = NULL; - } + if (! snd_card_proc_new(ice->card, "ice1712", &entry)) + snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); } /* @@ -2689,7 +2670,6 @@ static int snd_ice1712_free(ice1712_t *ice) outb(0xff, ICEREG(ice, IRQMASK)); /* --- */ __hw_end: - snd_ice1712_proc_done(ice); if (ice->irq >= 0) { synchronize_irq(ice->irq); free_irq(ice->irq, (void *) ice); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 392178381d5e..bbfa3c2435b2 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -322,7 +322,6 @@ typedef struct { unsigned char piv_saved; unsigned short picb_saved; #endif - snd_info_entry_t *proc_entry; } ichdev_t; typedef struct _snd_intel8x0 intel8x0_t; @@ -368,7 +367,6 @@ struct _snd_intel8x0 { spinlock_t reg_lock; spinlock_t ac97_lock; - snd_info_entry_t *proc_entry; u32 bdbars_count; u32 *bdbars; @@ -1854,8 +1852,6 @@ static int snd_intel8x0_chip_init(intel8x0_t *chip) return 0; } -static void snd_intel8x0_proc_done(intel8x0_t * chip); - static int snd_intel8x0_free(intel8x0_t *chip) { int i; @@ -1871,7 +1867,6 @@ static int snd_intel8x0_free(intel8x0_t *chip) /* --- */ synchronize_irq(chip->irq); __hw_end: - snd_intel8x0_proc_done(chip); if (chip->bdbars) snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); if (chip->remap_addr) @@ -1900,17 +1895,15 @@ static void intel8x0_suspend(intel8x0_t *chip) { snd_card_t *card = chip->card; - chip->in_suspend = 1; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + if (chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D3hot) + return; + chip->in_suspend = 1; snd_pcm_suspend_all(chip->pcm); if (chip->pcm_mic) snd_pcm_suspend_all(chip->pcm_mic); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void intel8x0_resume(intel8x0_t *chip) @@ -1918,9 +1911,9 @@ static void intel8x0_resume(intel8x0_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + if (! chip->in_suspend || + card->power_state == SNDRV_CTL_POWER_D0) + return; pci_enable_device(chip->pci); snd_intel8x0_chip_init(chip); @@ -1930,8 +1923,6 @@ static void intel8x0_resume(intel8x0_t *chip) chip->in_suspend = 0; snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND @@ -2100,26 +2091,8 @@ static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(chip->card, "intel8x0", chip->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 2048; - entry->c.text.read = snd_intel8x0_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - chip->proc_entry = entry; -} - -static void snd_intel8x0_proc_done(intel8x0_t * chip) -{ - if (chip->proc_entry) { - snd_info_unregister(chip->proc_entry); - chip->proc_entry = NULL; - } + if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) + snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); } static int snd_intel8x0_dev_free(snd_device_t *device) @@ -2493,6 +2466,8 @@ static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = { // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 440MX */ // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SI7012 */ { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE */ + { 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE2 */ + { 0x10de, 0x00db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE3 */ { 0, } }; diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 75f26090ed67..1e6ef1efaf3b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -368,7 +368,6 @@ struct _snd_korg1212 { snd_pcm_substream_t *playback_substream; snd_pcm_substream_t *capture_substream; - snd_info_entry_t * proc_entry; CardState cardState; int running; @@ -1898,26 +1897,8 @@ static void __devinit snd_korg1212_proc_init(korg1212_t *korg1212) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = korg1212; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_korg1212_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - korg1212->proc_entry = entry; -} - -static void snd_korg1212_proc_done(korg1212_t * korg1212) -{ - if (korg1212->proc_entry) { - snd_info_unregister(korg1212->proc_entry); - korg1212->proc_entry = NULL; - } + if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) + snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); } static int __devinit snd_korg1212_create(korg1212_t *korg1212) @@ -2174,8 +2155,6 @@ snd_korg1212_free(void *private_data) return; } - snd_korg1212_proc_done(korg1212); - snd_korg1212_TurnOffIdleMonitor(korg1212); snd_korg1212_DisableCardInterrupts(korg1212); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 18749650cb71..003f7de25dff 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2377,9 +2377,8 @@ static void m3_suspend(m3_t *chip) snd_card_t *card = chip->card; int i, index; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); @@ -2400,8 +2399,6 @@ static void m3_suspend(m3_t *chip) snd_m3_outw(chip, 0xffff, 0x54); snd_m3_outw(chip, 0xffff, 0x56); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void m3_resume(m3_t *chip) @@ -2409,9 +2406,8 @@ static void m3_resume(m3_t *chip) snd_card_t *card = chip->card; int i, index; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* first lets just bring everything back. .*/ snd_m3_outw(chip, 0, 0x54); @@ -2442,8 +2438,6 @@ static void m3_resume(m3_t *chip) snd_m3_amp_enable(chip, 1); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 7fedaab022c6..2336a659f89a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1270,24 +1270,20 @@ static void nm256_suspend(nm256_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); chip->coeffs_current = 0; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void nm256_resume(nm256_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; /* Perform a full reset on the hardware */ pci_enable_device(chip->pci); @@ -1297,8 +1293,6 @@ static void nm256_resume(nm256_t *chip) snd_ac97_resume(chip->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } #ifndef PCI_OLD_SUSPEND diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index b1f061459a3d..bbe85ce76cf4 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -220,7 +220,6 @@ typedef struct snd_rme32 { snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme32_t; @@ -561,18 +560,22 @@ static int snd_rme32_setclockmode(rme32_t * rme32, int mode) { switch (mode) { case RME32_CLOCKMODE_SLAVE: + /* AutoSync */ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_32: + /* Internal 32.0kHz */ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_44: + /* Internal 44.1kHz */ rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1; break; case RME32_CLOCKMODE_MASTER_48: + /* Internal 48.0kHz */ rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1; break; @@ -1240,7 +1243,6 @@ static void snd_rme32_free(void *private_data) if (rme32->irq >= 0) { snd_rme32_playback_stop(rme32); snd_rme32_capture_stop(rme32); - snd_rme32_proc_done(rme32); free_irq(rme32->irq, (void *) rme32); rme32->irq = -1; } @@ -1464,9 +1466,9 @@ snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) snd_rme32_playback_getrate(rme32)); } if (rme32->wcreg & RME32_RCR_KMODE) { - snd_iprintf(buffer, " clock mode: slave\n"); + snd_iprintf(buffer, " sample clock source: AutoSync\n"); } else { - snd_iprintf(buffer, " clock mode: master\n"); + snd_iprintf(buffer, " sample clock source: Internal\n"); } if (rme32->wcreg & RME32_WCR_PRO) { snd_iprintf(buffer, " format: AES/EBU (professional)\n"); @@ -1484,26 +1486,8 @@ static void __devinit snd_rme32_proc_init(rme32_t * rme32) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme32->card, "rme32", rme32->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme32; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme32_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme32->proc_entry = entry; -} - -static void snd_rme32_proc_done(rme32_t * rme32) -{ - if (rme32->proc_entry) { - snd_info_unregister(rme32->proc_entry); - rme32->proc_entry = NULL; - } + if (! snd_card_proc_new(rme32->card, "rme32", &entry)) + snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); } /* @@ -1646,7 +1630,10 @@ static int snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) { - static char *texts[4] = { "Slave", "Master (32.0 kHz)", "Master (44.1 kHz)", "Master (48.0 kHz)" }; + static char *texts[4] = { "AutoSync", + "Internal 32.0kHz", + "Internal 44.1kHz", + "Internal 48.0kHz" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 65279749c7bf..5986e8e9e015 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -257,7 +257,6 @@ typedef struct snd_rme96 { snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme96_t; @@ -308,9 +307,6 @@ snd_rme96_capture_pointer(snd_pcm_substream_t *substream); static void __devinit snd_rme96_proc_init(rme96_t *rme96); -static void -snd_rme96_proc_done(rme96_t *rme96); - static int snd_rme96_create_switches(snd_card_t *card, rme96_t *rme96); @@ -330,6 +326,20 @@ snd_rme96_capture_ptr(rme96_t *rme96) } static int +snd_rme96_ratecode(int rate) +{ + switch (rate) { + case 32000: return SNDRV_PCM_RATE_32000; + case 44100: return SNDRV_PCM_RATE_44100; + case 48000: return SNDRV_PCM_RATE_48000; + case 64000: return SNDRV_PCM_RATE_64000; + case 88200: return SNDRV_PCM_RATE_88200; + case 96000: return SNDRV_PCM_RATE_96000; + } + return 0; +} + +static int snd_rme96_playback_silence(snd_pcm_substream_t *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, @@ -671,8 +681,8 @@ snd_rme96_playback_getrate(rme96_t *rme96) if (!(rme96->wcreg & RME96_WCR_MASTER) && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { - /* slave clock */ - return rate; + /* slave clock */ + return rate; } rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); @@ -974,14 +984,22 @@ static int snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - unsigned long flags; + unsigned long flags; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - int err; + int err, rate, dummy; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) return err; spin_lock_irqsave(&rme96->lock, flags); - if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + if (params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { spin_unlock_irqrestore(&rme96->lock, flags); return err; } @@ -1024,7 +1042,8 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, { unsigned long flags; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - int err, isadat; + snd_pcm_runtime_t *runtime = substream->runtime; + int err, isadat, rate; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) return err; @@ -1040,9 +1059,6 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, spin_unlock_irqrestore(&rme96->lock, flags); return err; } - } else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) { - spin_unlock_irqrestore(&rme96->lock, flags); - return -EBUSY; } snd_rme96_setframelog(rme96, params_channels(params), 0); if (rme96->playback_periodsize != 0) { @@ -1052,7 +1068,18 @@ snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, spin_unlock_irqrestore(&rme96->lock, flags); return -EBUSY; } - } + } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (params_rate(params) != rate) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + if ((isadat && runtime->hw.channels_min == 2) || + (!isadat && runtime->hw.channels_min == 8)) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EIO; + } + } rme96->capture_periodsize = params_period_size(params) << rme96->capture_frlog; snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); @@ -1138,7 +1165,7 @@ snd_rme96_interrupt(int irq, if (rme96->rcreg & RME96_RCR_IRQ) { /* playback */ - snd_pcm_period_elapsed(rme96->playback_substream); + snd_pcm_period_elapsed(rme96->playback_substream); writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); } if (rme96->rcreg & RME96_RCR_IRQ_2) { @@ -1162,12 +1189,17 @@ static int snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) { unsigned long flags; + int rate, dummy; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->wcreg &= ~RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; @@ -1175,7 +1207,15 @@ snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) rme96->playback_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_playback_spdif_info; + runtime->hw = snd_rme96_playback_spdif_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1190,28 +1230,31 @@ static int snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) { unsigned long flags; - int isadat; + int isadat, rate; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (isadat) { - /* ADAT input */ - return -EBUSY; - } snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme96->lock, flags); + runtime->hw = snd_rme96_capture_spdif_info; + if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->capture_substream = substream; rme96->capture_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_capture_spdif_info; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1222,12 +1265,17 @@ static int snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; + int rate, dummy; rme96_t *rme96 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irqsave(&rme96->lock, flags); + if (rme96->playback_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->wcreg |= RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; @@ -1236,6 +1284,14 @@ snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) spin_unlock_irqrestore(&rme96->lock, flags); runtime->hw = snd_rme96_playback_adat_info; + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; @@ -1245,27 +1301,31 @@ static int snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) { unsigned long flags; - int isadat; + int isadat, rate; rme96_t *rme96 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { - /* no input */ - return -EIO; - } - if (!isadat) { - /* S/PDIF input */ - return -EBUSY; - } snd_pcm_set_sync(substream); + runtime->hw = snd_rme96_capture_adat_info; + if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { + if (!isadat) { + return -EIO; + } + runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + spin_lock_irqsave(&rme96->lock, flags); + if (rme96->capture_substream != NULL) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } rme96->capture_substream = substream; rme96->capture_ptr = 0; spin_unlock_irqrestore(&rme96->lock, flags); - runtime->hw = snd_rme96_capture_adat_info; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; @@ -1279,6 +1339,9 @@ snd_rme96_playback_close(snd_pcm_substream_t *substream) int spdif = 0; spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; @@ -1298,6 +1361,9 @@ snd_rme96_capture_close(snd_pcm_substream_t *substream) rme96_t *rme96 = _snd_pcm_substream_chip(substream); spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; spin_unlock_irqrestore(&rme96->lock, flags); @@ -1558,7 +1624,6 @@ snd_rme96_free(void *private_data) snd_rme96_capture_stop(rme96); rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); - snd_rme96_proc_done(rme96); free_irq(rme96->irq, (void *)rme96); rme96->irq = -1; } @@ -1843,27 +1908,8 @@ snd_rme96_proc_init(rme96_t *rme96) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme96; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme96_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme96->proc_entry = entry; -} - -static void -snd_rme96_proc_done(rme96_t * rme96) -{ - if (rme96->proc_entry) { - snd_info_unregister(rme96->proc_entry); - rme96->proc_entry = NULL; - } + if (! snd_card_proc_new(rme96->card, "rme96", &entry)) + snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); } /* diff --git a/sound/pci/rme9652/hammerfall_mem.c b/sound/pci/rme9652/hammerfall_mem.c index 067ce2c18fb0..6981baaa43c5 100644 --- a/sound/pci/rme9652/hammerfall_mem.c +++ b/sound/pci/rme9652/hammerfall_mem.c @@ -25,7 +25,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - $Id: hammerfall_mem.c,v 1.6 2002/12/19 15:59:18 tiwai Exp $ + $Id: hammerfall_mem.c,v 1.7 2003/01/06 14:21:28 perex Exp $ Tue Oct 17 2000 Jaroslav Kysela <perex@suse.cz> @@ -179,7 +179,7 @@ void snd_hammerfall_free_buffer (struct pci_dev *pcidev, void *addr) printk ("Hammerfall memory allocator: unknown buffer address or PCI device ID"); } -static void __exit hammerfall_free_buffers (void) +static void hammerfall_free_buffers (void) { int i; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 320a057bdfe2..8ce7169d419a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -368,7 +368,6 @@ struct _hdsp { snd_card_t *card; snd_pcm_t *pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE]; }; @@ -2076,27 +2075,8 @@ static void __devinit snd_hdsp_proc_init(hdsp_t *hdsp) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(hdsp->card, "hdsp", hdsp->card->proc_root)) != - NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = hdsp; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_hdsp_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - hdsp->proc_entry = entry; -} - -static void snd_hdsp_proc_done(hdsp_t *hdsp) -{ - if (hdsp->proc_entry) { - snd_info_unregister(hdsp->proc_entry); - hdsp->proc_entry = NULL; - } + if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) + snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); } static void snd_hdsp_free_buffers(hdsp_t *hdsp) @@ -3078,7 +3058,6 @@ static int snd_hdsp_free(hdsp_t *hdsp) if (hdsp->irq >= 0) free_irq(hdsp->irq, (void *)hdsp); - snd_hdsp_proc_done(hdsp); snd_hdsp_free_buffers(hdsp); if (hdsp->iobase) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 3c86c0d418f9..646b925100a2 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -263,7 +263,6 @@ typedef struct snd_rme9652 { snd_card_t *card; snd_pcm_t *pcm; struct pci_dev *pci; - snd_info_entry_t *proc_entry; snd_kcontrol_t *spdif_ctl; } rme9652_t; @@ -1803,27 +1802,8 @@ static void __devinit snd_rme9652_proc_init(rme9652_t *rme9652) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) != - NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = rme9652; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_rme9652_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - rme9652->proc_entry = entry; -} - -static void snd_rme9652_proc_done(rme9652_t *rme9652) -{ - if (rme9652->proc_entry) { - snd_info_unregister(rme9652->proc_entry); - rme9652->proc_entry = NULL; - } + if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) + snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); } static void snd_rme9652_free_buffers(rme9652_t *rme9652) @@ -1855,7 +1835,6 @@ static int snd_rme9652_free(rme9652_t *rme9652) { if (rme9652->irq >= 0) rme9652_stop(rme9652); - snd_rme9652_proc_done(rme9652); snd_rme9652_free_buffers(rme9652); if (rme9652->iobase) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 5f1767c27cf0..ecc1be3df90f 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -247,7 +247,6 @@ struct _snd_sonicvibes { snd_hwdep_t *fmsynth; /* S3FM */ spinlock_t reg_lock; - snd_info_entry_t *proc_entry; unsigned int p_dma_size; unsigned int c_dma_size; @@ -1175,26 +1174,8 @@ static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic) { snd_info_entry_t *entry; - if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = sonic; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_sonicvibes_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - sonic->proc_entry = entry; -} - -static void snd_sonicvibes_proc_done(sonicvibes_t * sonic) -{ - if (sonic->proc_entry) { - snd_info_unregister(sonic->proc_entry); - sonic->proc_entry = NULL; - } + if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) + snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); } /* @@ -1210,7 +1191,6 @@ static int snd_sonicvibes_free(sonicvibes_t *sonic) if (sonic->gameport.io) gameport_unregister_port(&sonic->gameport); #endif - snd_sonicvibes_proc_done(sonic); pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); if (sonic->res_sb_port) { diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 0e3496c3c7e0..43b2d84b959f 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -41,6 +41,7 @@ #include <sound/control.h> #include <sound/trident.h> #include <sound/asoundef.h> +#include <sound/pcm_sgbuf.h> #include <asm/io.h> @@ -756,16 +757,19 @@ int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream, snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; int err; - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) - return err; - if (err > 0 && trident->tlb.entries) { - if (voice->memblk) - snd_trident_free_pages(trident, voice->memblk); - spin_lock_irq(&trident->reg_lock); - voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - spin_unlock_irq(&trident->reg_lock); - if (voice->memblk == NULL) - return -ENOMEM; + if (trident->tlb.entries) { + if ((err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = snd_trident_alloc_pages(trident, substream); + if (voice->memblk == NULL) + return -ENOMEM; + } + } else { + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; } return 0; } @@ -792,7 +796,7 @@ int snd_trident_allocate_evoice(snd_pcm_substream_t * substream, /* voice management */ - if (params_buffer_size(hw_params) / 2 != params_buffer_size(hw_params)) { + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { if (evoice == NULL) { evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (evoice == NULL) @@ -851,11 +855,14 @@ static int snd_trident_hw_free(snd_pcm_substream_t * substream) snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_voice_t *evoice = voice ? voice->extra : NULL; - if (trident->tlb.entries && voice && voice->memblk) { - snd_trident_free_pages(trident, voice->memblk); - voice->memblk = NULL; - } - snd_pcm_lib_free_pages(substream); + if (trident->tlb.entries) { + if (voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_sgbuf_free(substream); + } else + snd_pcm_lib_free_pages(substream); if (evoice != NULL) { snd_trident_free_voice(trident, evoice); voice->extra = NULL; @@ -990,10 +997,12 @@ static int snd_trident_capture_prepare(snd_pcm_substream_t * substream) outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); // Set channel buffer Address - voice->LBA = runtime->dma_addr; - outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); + /* FIXME: LEGACY_DMAR0 correctly set? */ if (voice->memblk) voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); // set ESO ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; @@ -1343,6 +1352,7 @@ static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream) voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); /* set Loop Back Address */ + /* FIXME: LBAO?? */ LBAO = runtime->dma_addr; if (voice->memblk) voice->LBA = voice->memblk->offset; @@ -1755,17 +1765,27 @@ static snd_pcm_hardware_t snd_trident_spdif_7018 = .fifo_size = 0, }; +static int snd_trident_sgbuf_init(trident_t *trident, snd_pcm_substream_t *substream) +{ + if (trident->tlb.entries) + return snd_pcm_sgbuf_init(substream, trident->pci, 32); + return 0; +} + +static void snd_trident_sgbuf_delete(trident_t *trident, snd_pcm_substream_t *substream) +{ + if (trident->tlb.entries) + snd_pcm_sgbuf_delete(substream); +} + static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) { - unsigned long flags; snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; trident_t *trident; if (voice) { trident = voice->trident; - spin_lock_irqsave(&trident->reg_lock, flags); snd_trident_free_voice(trident, voice); - spin_unlock_irqrestore(&trident->reg_lock, flags); } } @@ -1774,14 +1794,15 @@ static int snd_trident_playback_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_trident_voice_t *voice; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } - spin_unlock_irq(&trident->reg_lock); snd_trident_pcm_mixer_build(trident, voice, substream); voice->substream = substream; runtime->private_data = voice; @@ -1808,6 +1829,7 @@ static int snd_trident_playback_close(snd_pcm_substream_t * substream) snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_pcm_mixer_free(trident, voice, substream); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1827,11 +1849,13 @@ static int snd_trident_spdif_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } voice->spdif = 1; @@ -1889,6 +1913,7 @@ static int snd_trident_spdif_close(snd_pcm_substream_t * substream) trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1908,15 +1933,16 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) trident_t *trident = snd_pcm_substream_chip(substream); snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; + int err; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } voice->capture = 1; - spin_unlock_irq(&trident->reg_lock); voice->substream = substream; runtime->private_data = voice; runtime->private_free = snd_trident_pcm_free_substream; @@ -1937,6 +1963,8 @@ static int snd_trident_capture_open(snd_pcm_substream_t * substream) ---------------------------------------------------------------------------*/ static int snd_trident_capture_close(snd_pcm_substream_t * substream) { + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -1954,26 +1982,19 @@ static int snd_trident_capture_close(snd_pcm_substream_t * substream) static int snd_trident_foldback_open(snd_pcm_substream_t * substream) { trident_t *trident = snd_pcm_substream_chip(substream); + int err; snd_trident_voice_t *voice; snd_pcm_runtime_t *runtime = substream->runtime; - spin_lock_irq(&trident->reg_lock); + if ((err = snd_trident_sgbuf_init(trident, substream)) < 0) + return err; voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); if (voice == NULL) { - spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return -EAGAIN; } - if (trident->tlb.entries) { - voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - if (voice->memblk == NULL) { - snd_trident_free_voice(trident, voice); - spin_unlock_irq(&trident->reg_lock); - return -ENOMEM; - } - } - voice->substream = substream; voice->foldback_chan = substream->number; - spin_unlock_irq(&trident->reg_lock); + voice->substream = substream; runtime->private_data = voice; runtime->private_free = snd_trident_pcm_free_substream; runtime->hw = snd_trident_foldback; @@ -2001,6 +2022,7 @@ static int snd_trident_foldback_close(snd_pcm_substream_t * substream) spin_lock_irq(&trident->reg_lock); outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); spin_unlock_irq(&trident->reg_lock); + snd_trident_sgbuf_delete(trident, substream); return 0; } @@ -2019,6 +2041,20 @@ static snd_pcm_ops_t snd_trident_playback_ops = { .pointer = snd_trident_playback_pointer, }; +static snd_pcm_ops_t snd_trident_nx_playback_ops = { + .open = snd_trident_playback_open, + .close = snd_trident_playback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_playback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_capture_ops = { .open = snd_trident_capture_open, .close = snd_trident_capture_close, @@ -2030,6 +2066,20 @@ static snd_pcm_ops_t snd_trident_capture_ops = { .pointer = snd_trident_capture_pointer, }; +static snd_pcm_ops_t snd_trident_nx_capture_ops = { + .open = snd_trident_capture_open, + .close = snd_trident_capture_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_capture_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_capture_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_capture_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_si7018_capture_ops = { .open = snd_trident_capture_open, .close = snd_trident_capture_close, @@ -2052,6 +2102,20 @@ static snd_pcm_ops_t snd_trident_foldback_ops = { .pointer = snd_trident_playback_pointer, }; +static snd_pcm_ops_t snd_trident_nx_foldback_ops = { + .open = snd_trident_foldback_open, + .close = snd_trident_foldback_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_foldback_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_playback_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_spdif_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, @@ -2063,6 +2127,20 @@ static snd_pcm_ops_t snd_trident_spdif_ops = { .pointer = snd_trident_spdif_pointer, }; +static snd_pcm_ops_t snd_trident_nx_spdif_ops = { + .open = snd_trident_spdif_open, + .close = snd_trident_spdif_close, + .ioctl = snd_trident_ioctl, + .hw_params = snd_trident_spdif_hw_params, + .hw_free = snd_trident_hw_free, + .prepare = snd_trident_spdif_prepare, + .trigger = snd_trident_trigger, + .pointer = snd_trident_spdif_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + static snd_pcm_ops_t snd_trident_spdif_7018_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, @@ -2088,21 +2166,24 @@ static void snd_trident_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->foldback = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) { trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); trident->spdif = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_free_for_all(pcm); } /*--------------------------------------------------------------------------- @@ -2129,18 +2210,24 @@ int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm pcm->private_data = trident; pcm->private_free = snd_trident_pcm_free; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + if (trident->tlb.entries) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_capture_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, trident->device != TRIDENT_DEVICE_ID_SI7018 ? - &snd_trident_capture_ops : - &snd_trident_si7018_capture_ops); + &snd_trident_capture_ops : + &snd_trident_si7018_capture_ops); + } pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 256*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -2174,7 +2261,10 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_ foldback->private_data = trident; foldback->private_free = snd_trident_foldback_pcm_free; - snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); + if (trident->tlb.entries) + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops); + else + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); foldback->info_flags = 0; strcpy(foldback->name, "Trident 4DWave"); substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; @@ -2189,7 +2279,8 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_ } trident->foldback = foldback; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); if (rpcm) *rpcm = foldback; @@ -2219,7 +2310,9 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t * spdif->private_data = trident; spdif->private_free = snd_trident_spdif_pcm_free; - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if (trident->tlb.entries) { + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_spdif_ops); + } else if (trident->device != TRIDENT_DEVICE_ID_SI7018) { snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); } else { snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); @@ -2228,7 +2321,8 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t * strcpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + if (! trident->tlb.entries) + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); if (rpcm) *rpcm = spdif; @@ -2886,7 +2980,7 @@ static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *v static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) { - ac97_t _ac97, *ac97; + ac97_t _ac97; snd_card_t * card = trident->card; snd_kcontrol_t *kctl; snd_ctl_elem_value_t uctl; @@ -2900,7 +2994,7 @@ static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device _ac97.private_data = trident; trident->ac97_detect = 1; __again: - if ((err = snd_ac97_mixer(trident->card, &_ac97, &ac97)) < 0) { + if ((err = snd_ac97_mixer(trident->card, &_ac97, &trident->ac97)) < 0) { if (trident->device == TRIDENT_DEVICE_ID_SI7018) { if ((err = snd_trident_sis_reset(trident)) < 0) return err; @@ -3077,6 +3171,21 @@ void __devinit snd_trident_gameport(trident_t *chip) #endif /* CONFIG_GAMEPORT */ /* + * delay for 1 tick + */ +inline static void do_delay(trident_t *chip) +{ +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay((1000 + HZ - 1) / HZ); + return; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +} + +/* * SiS reset routine */ @@ -3086,7 +3195,7 @@ static int snd_trident_sis_reset(trident_t *trident) unsigned int i; int r; - r = 2; /* count of retries */ + r = trident->in_suspend ? 0 : 2; /* count of retries */ __si7018_retry: pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */ udelay(100); @@ -3107,15 +3216,13 @@ static int snd_trident_sis_reset(trident_t *trident) do { if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) goto __si7018_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + do_delay(trident); } while (time_after_eq(end_time, jiffies)); snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); if (r-- > 0) { end_time = jiffies + HZ; do { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + do_delay(trident); } while (time_after_eq(end_time, jiffies)); goto __si7018_retry; } @@ -3173,36 +3280,195 @@ static void snd_trident_proc_read(snd_info_entry_t *entry, static void __devinit snd_trident_proc_init(trident_t * trident) { snd_info_entry_t *entry; - char *s = "trident"; + const char *s = "trident"; if (trident->device == TRIDENT_DEVICE_ID_SI7018) s = "sis7018"; - if ((entry = snd_info_create_card_entry(trident->card, s, trident->card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = trident; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 256; - entry->c.text.read = snd_trident_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } + if (! snd_card_proc_new(trident->card, s, &entry)) + snd_info_set_text_ops(entry, trident, snd_trident_proc_read); +} + +static int snd_trident_dev_free(snd_device_t *device) +{ + trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + return snd_trident_free(trident); +} + +/*--------------------------------------------------------------------------- + snd_trident_tlb_alloc + + Description: Allocate and set up the TLB page table on 4D NX. + Each entry has 4 bytes (physical PCI address). + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: 0 or negative error code + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_tlb_alloc(trident_t *trident) +{ + int i; + + /* TLB array must be aligned to 16kB !!! so we allocate + 32kB region and correct offset when necessary */ + + trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); + if (trident->tlb.buffer == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); + return -ENOMEM; + } + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + /* allocate shadow TLB page table (virtual addresses) */ + trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); + if (trident->tlb.shadow_entries == NULL) { + snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n"); + return -ENOMEM; + } + /* allocate and setup silent page and initialise TLB entries */ + trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page == 0UL) { + snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); + return -ENOMEM; } - trident->proc_entry = entry; + memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { + trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + } + + /* use emu memory block manager code to manage tlb page allocation */ + trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); + if (trident->tlb.memhdr == NULL) + return -ENOMEM; + + trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); + return 0; +} + +/* + * initialize 4D DX chip + */ + +static void snd_trident_stop_all_voices(trident_t *trident) +{ + outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); + outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); + outl(0, TRID_REG(trident, T4D_AINTEN_A)); + outl(0, TRID_REG(trident, T4D_AINTEN_B)); +} + +static int snd_trident_4d_dx_init(trident_t *trident) +{ + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + pci_write_config_byte(pci, 0x46, 4); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* DAC on, disable SB IRQ and try to force ADC valid signal */ + trident->ac97_ctrl = 0x0000004a; + outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) + goto __dx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error\n"); + return -EIO; + + __dx_ok: + snd_trident_stop_all_voices(trident); + + return 0; } -static void snd_trident_proc_done(trident_t * trident) +/* + * initialize 4D NX chip + */ +static int snd_trident_4d_nx_init(trident_t *trident) { - if (trident->proc_entry) { - snd_info_unregister(trident->proc_entry); - trident->proc_entry = NULL; + struct pci_dev *pci = trident->pci; + signed long end_time; + + /* reset the legacy configuration and whole audio/wavetable block */ + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + + pci_write_config_byte(pci, 0x46, 1); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) + goto __nx_ok; + do_delay(trident); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); + return -EIO; + + __nx_ok: + /* DAC on */ + trident->ac97_ctrl = 0x00000002; + outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* disable SB IRQ */ + outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + + snd_trident_stop_all_voices(trident); + + if (trident->tlb.entries != NULL) { + unsigned int i; + /* enable virtual addressing via TLB */ + i = trident->tlb.entries_dmaaddr; + i |= 0x00000001; + outl(i, TRID_REG(trident, NX_TLBC)); + } else { + outl(0, TRID_REG(trident, NX_TLBC)); } + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + + return 0; } -static int snd_trident_dev_free(snd_device_t *device) +/* + * initialize sis7018 chip + */ +static int snd_trident_sis_init(trident_t *trident) { - trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); - return snd_trident_free(trident); + int err; + + if ((err = snd_trident_sis_reset(trident)) < 0) + return err; + + snd_trident_stop_all_voices(trident); + + /* initialize S/PDIF */ + outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); + + return 0; } /*--------------------------------------------------------------------------- @@ -3229,9 +3495,7 @@ int __devinit snd_trident_create(snd_card_t * card, trident_t ** rtrident) { trident_t *trident; - unsigned int i; - int err; - signed long end_time; + int i, err; snd_trident_voice_t *voice; snd_trident_pcm_mixer_t *tmix; static snd_device_ops_t ops = { @@ -3290,151 +3554,39 @@ int __devinit snd_trident_create(snd_card_t * card, trident->tlb.entries = NULL; trident->tlb.buffer = NULL; if (trident->device == TRIDENT_DEVICE_ID_NX) { - /* allocate and setup TLB page table */ - /* each entry has 4 bytes (physical PCI address) */ - /* TLB array must be aligned to 16kB !!! so we allocate - 32kB region and correct offset when necessary */ - trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); - if (trident->tlb.buffer == NULL) { - snd_trident_free(trident); - snd_printk("unable to allocate TLB buffer\n"); - return -ENOMEM; - } - trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); - trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); - /* allocate shadow TLB page table (virtual addresses) */ - trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); - if (trident->tlb.shadow_entries == NULL) { + if ((err = snd_trident_tlb_alloc(trident)) < 0) { snd_trident_free(trident); - snd_printk("unable to allocate shadow TLB entries\n"); - return -ENOMEM; - } - /* allocate and setup silent page and initialise TLB entries */ - trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); - if (trident->tlb.silent_page == 0UL) { - snd_trident_free(trident); - snd_printk("unable to allocate silent page\n"); - return -ENOMEM; - } - memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); - for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { - trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); - trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; - } - - /* use emu memory block manager code to manage tlb page allocation */ - trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); - if (trident->tlb.memhdr == NULL) { - snd_trident_free(trident); - return -ENOMEM; + return err; } - trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); } - /* reset the legacy configuration and whole audio/wavetable block */ - if (trident->device == TRIDENT_DEVICE_ID_DX || - trident->device == TRIDENT_DEVICE_ID_NX) { - pci_write_config_dword(pci, 0x40, 0); /* DDMA */ - pci_write_config_byte(pci, 0x44, 0); /* ports */ - pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ - if (trident->device == TRIDENT_DEVICE_ID_DX) { - pci_write_config_byte(pci, 0x46, 4); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - } else /* NX */ { - pci_write_config_byte(pci, 0x46, 1); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - } - } - - /* initialize chip */ + trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + /* initialize chip */ switch (trident->device) { case TRIDENT_DEVICE_ID_DX: - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* DAC on, disable SB IRQ and try to force ADC valid signal */ - trident->ac97_ctrl = 0x0000004a; - outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) - goto __dx_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (end_time - (signed long)jiffies >= 0); - snd_printk("AC'97 codec ready error\n"); - snd_trident_free(trident); - return -EIO; - __dx_ok: + err = snd_trident_4d_dx_init(trident); break; case TRIDENT_DEVICE_ID_NX: - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) - goto __nx_ok; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (end_time - (signed long)jiffies >= 0); - snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); - snd_trident_free(trident); - return -EIO; - __nx_ok: - /* DAC on */ - trident->ac97_ctrl = 0x00000002; - outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* disable SB IRQ */ - outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + err = snd_trident_4d_nx_init(trident); break; case TRIDENT_DEVICE_ID_SI7018: - if ((err = snd_trident_sis_reset(trident)) < 0) { - snd_trident_free(trident); - return err; - } + err = snd_trident_sis_init(trident); + break; + default: + snd_BUG(); + break; + } + if (err < 0) { + snd_trident_free(trident); + return err; } - - outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); - outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); - outl(0, TRID_REG(trident, T4D_AINTEN_A)); - outl(0, TRID_REG(trident, T4D_AINTEN_B)); if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) { snd_trident_free(trident); return err; } - if (trident->device == TRIDENT_DEVICE_ID_NX) { - if (trident->tlb.entries != NULL) { - /* enable virtual addressing via TLB */ - i = trident->tlb.entries_dmaaddr; - i |= 0x00000001; - outl(i, TRID_REG(trident, NX_TLBC)); - } else { - outl(0, TRID_REG(trident, NX_TLBC)); - } - /* initialize S/PDIF */ - trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - } - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - /* initialize S/PDIF */ - trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - } - /* initialise synth voices */ for (i = 0; i < 64; i++) { voice = &trident->synth.voices[i]; @@ -3493,7 +3645,6 @@ int snd_trident_free(trident_t *trident) else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } - snd_trident_proc_done(trident); if (trident->tlb.buffer) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) @@ -3654,6 +3805,7 @@ snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int pvoice->capture = 0; pvoice->spdif = 0; pvoice->memblk = NULL; + pvoice->substream = NULL; spin_unlock_irqrestore(&trident->voice_alloc, flags); return pvoice; } @@ -3732,9 +3884,9 @@ void snd_trident_suspend(trident_t *trident) { snd_card_t *card = trident->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; + trident->in_suspend = 1; snd_pcm_suspend_all(trident->pcm); if (trident->foldback) snd_pcm_suspend_all(trident->foldback); @@ -3748,27 +3900,40 @@ void snd_trident_suspend(trident_t *trident) break; } snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_trident_resume(trident_t *trident) { snd_card_t *card = trident->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; + + pci_enable_device(trident->pci); + pci_set_dma_mask(trident->pci, 0x3fffffff); /* to be sure */ + pci_set_master(trident->pci); /* to be sure */ + switch (trident->device) { case TRIDENT_DEVICE_ID_DX: + snd_trident_4d_dx_init(trident); + break; case TRIDENT_DEVICE_ID_NX: - break; /* TODO */ + snd_trident_4d_nx_init(trident); + break; case TRIDENT_DEVICE_ID_SI7018: + snd_trident_sis_init(trident); break; } + + snd_ac97_resume(trident->ac97); + + /* restore some registers */ + outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + + snd_trident_enable_eso(trident); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); + trident->in_suspend = 0; } static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index d7701b999dd9..fb8a44f283a7 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -28,6 +28,7 @@ #include <linux/time.h> #include <sound/core.h> #include <sound/trident.h> +#include <sound/pcm_sgbuf.h> /* page arguments of these two macros are Trident page (4096 bytes), not like * aligned pages in others @@ -166,9 +167,8 @@ __found_pages: /* * check if the given pointer is valid for pages */ -static int is_valid_page(void *pages) +static int is_valid_page(unsigned long ptr) { - unsigned long ptr = (unsigned long)virt_to_phys(pages); if (ptr & ~0x3fffffffUL) { snd_printk("max memory size is 1GB!!\n"); return 0; @@ -184,33 +184,42 @@ static int is_valid_page(void *pages) * page allocation for DMA */ snd_util_memblk_t * -snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size) +snd_trident_alloc_pages(trident_t *trident, snd_pcm_substream_t *substream) { - unsigned long ptr; snd_util_memhdr_t *hdr; snd_util_memblk_t *blk; - int page; + int idx, page; + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return NULL); snd_assert(trident != NULL, return NULL); - snd_assert(size > 0 && size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + snd_assert(sgbuf->size > 0 && sgbuf->size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); - if (! is_valid_page(pages)) - return NULL; - down(&hdr->block_mutex); - blk = search_empty(hdr, size); + blk = search_empty(hdr, sgbuf->size); if (blk == NULL) { up(&hdr->block_mutex); return NULL; } + if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) { + snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk)); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + /* set TLB entries */ - ptr = (unsigned long)pages; - for (page = firstpg(blk); page <= lastpg(blk); page++) { + idx = 0; + for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { + dma_addr_t addr = sgbuf->table[idx].addr; + unsigned long ptr = (unsigned long)sgbuf->table[idx].buf; + if (! is_valid_page(addr)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } set_tlb_bus(trident, page, ptr, addr); - ptr += ALIGN_PAGE_SIZE; - addr += ALIGN_PAGE_SIZE; } up(&hdr->block_mutex); return blk; @@ -301,7 +310,8 @@ static void clear_tlb(trident_t *trident, int page) void *ptr = page_to_ptr(trident, page); dma_addr_t addr = page_to_addr(trident, page); set_silent_tlb(trident, page); - snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (ptr) + snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); } /* check new allocation range */ @@ -346,7 +356,7 @@ static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); if (ptr == NULL) goto __fail; - if (! is_valid_page(ptr)) { + if (! is_valid_page(addr)) { snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); goto __fail; } diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index b5ce828f1aa2..6f7d49d2fdf0 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1827,26 +1827,8 @@ static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip) { snd_info_entry_t *entry; - entry = snd_info_create_card_entry(card, "ymfpci", card->proc_root); - if (entry) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = snd_ymfpci_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_unregister(entry); - entry = NULL; - } - } - chip->proc_entry = entry; - return 0; -} - -static int snd_ymfpci_proc_done(ymfpci_t *chip) -{ - if (chip->proc_entry) - snd_info_unregister((snd_info_entry_t *) chip->proc_entry); + if (! snd_card_proc_new(card, "ymfpci", &entry)) + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); return 0; } @@ -2037,7 +2019,6 @@ static int snd_ymfpci_free(ymfpci_t *chip) u16 ctrl; snd_assert(chip != NULL, return -EINVAL); - snd_ymfpci_proc_done(chip); if (chip->res_reg_area) { /* don't touch busy hardware */ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); @@ -2127,9 +2108,8 @@ void snd_ymfpci_suspend(ymfpci_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; snd_pcm_suspend_all(chip->pcm); snd_pcm_suspend_all(chip->pcm2); snd_pcm_suspend_all(chip->pcm_spdif); @@ -2140,8 +2120,6 @@ void snd_ymfpci_suspend(ymfpci_t *chip) snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_disable_dsp(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } void snd_ymfpci_resume(ymfpci_t *chip) @@ -2149,10 +2127,8 @@ void snd_ymfpci_resume(ymfpci_t *chip) snd_card_t *card = chip->card; int i; - snd_power_lock(card); - if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; pci_enable_device(chip->pci); pci_set_master(chip->pci); @@ -2174,8 +2150,6 @@ void snd_ymfpci_resume(ymfpci_t *chip) spin_unlock(&chip->reg_lock); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state) diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 190984c2e287..3d443de8f3a5 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -1460,9 +1460,8 @@ static void snd_pmac_suspend(pmac_t *chip) unsigned long flags; snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) - goto __skip; + return; if (chip->suspend) chip->suspend(chip); @@ -1476,17 +1475,14 @@ static void snd_pmac_suspend(pmac_t *chip) disable_irq(chip->rx_irq); snd_pmac_sound_feature(chip, 0); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __skip: - snd_power_unlock(card); } static void snd_pmac_resume(pmac_t *chip) { snd_card_t *card = chip->card; - snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) - goto __skip; + return; snd_pmac_sound_feature(chip, 1); if (chip->resume) @@ -1505,8 +1501,6 @@ static void snd_pmac_resume(pmac_t *chip) enable_irq(chip->rx_irq); snd_power_change_state(card, SNDRV_CTL_POWER_D0); - __skip: - snd_power_unlock(card); } /* the chip is stored statically by snd_pmac_register_sleep_notifier diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 66e2c95adb32..1564ca401970 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -184,7 +184,6 @@ struct snd_usb_stream { int pcm_index; snd_usb_substream_t substream[2]; struct list_head list; - snd_info_entry_t *proc_entry; }; #define chip_t snd_usb_stream_t @@ -931,6 +930,70 @@ static struct audioformat *find_format(snd_usb_substream_t *subs, snd_pcm_runtim /* + * initialize the picth control and sample rate + */ +static int init_usb_pitch(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt) +{ + unsigned int ep; + unsigned char data[1]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { + data[0] = 1; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", + dev->devnum, iface, ep); + return err; + } + } + return 0; +} + +static int init_usb_sample_rate(struct usb_device *dev, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) +{ + unsigned int ep; + unsigned char data[3]; + int err; + + ep = get_endpoint(alts, 0)->bEndpointAddress; + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { + int crate; + data[0] = rate; + data[1] = rate >> 8; + data[2] = rate >> 16; + if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, rate, ep); + return err; + } + if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", + dev->devnum, iface, fmt->altsetting, ep); + return err; + } + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); + // runtime->rate = crate; + } + } + return 0; +} + +/* * find a matching format and set up the interface */ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) @@ -942,7 +1005,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) struct usb_interface *iface; struct audioformat *fmt; unsigned int ep, attr; - unsigned char data[3]; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; int err; @@ -1015,44 +1077,11 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) subs->syncinterval = get_endpoint(alts, 1)->bRefresh; } - ep = get_endpoint(alts, 0)->bEndpointAddress; - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { - data[0] = 1; - if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", - dev->devnum, subs->interface, ep); - return err; - } - } - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { - int crate; - data[0] = runtime->rate; - data[1] = runtime->rate >> 8; - data[2] = runtime->rate >> 16; - if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", - dev->devnum, subs->interface, fmt->altsetting, runtime->rate, ep); - return err; - } - if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", - dev->devnum, subs->interface, fmt->altsetting, ep); - return err; - } - crate = data[0] | (data[1] << 8) | (data[2] << 16); - if (crate != runtime->rate) { - snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, runtime->rate); - // runtime->rate = crate; - } - } + if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0 || + (err = init_usb_sample_rate(dev, subs->interface, alts, fmt, + runtime->rate)) < 0) + return err; + /* always fill max packet size */ if (fmt->attributes & EP_CS_ATTR_FILL_MAX) subs->fill_max = 1; @@ -1426,18 +1455,8 @@ static void proc_pcm_format_add(snd_usb_stream_t *stream) snd_card_t *card = stream->chip->card; sprintf(name, "stream%d", stream->pcm_index); - if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = stream; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 4096; - entry->c.text.read = proc_pcm_format_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - stream->proc_entry = entry; + if (! snd_card_proc_new(card, name, &entry)) + snd_info_set_text_ops(entry, stream, proc_pcm_format_read); } @@ -1492,10 +1511,6 @@ static void free_substream(snd_usb_substream_t *subs) */ static void snd_usb_audio_stream_free(snd_usb_stream_t *stream) { - if (stream->proc_entry) { - snd_info_unregister(stream->proc_entry); - stream->proc_entry = NULL; - } free_substream(&stream->substream[0]); free_substream(&stream->substream[1]); list_del(&stream->list); @@ -1828,6 +1843,10 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, i kfree(fp); return err; } + /* try to set the interface... */ + usb_set_interface(chip->dev, iface_no, i); + init_usb_pitch(chip->dev, iface_no, alts, fp); + init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max); } return 0; } @@ -1890,9 +1909,10 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif, /* skip non-supported classes */ continue; } - parse_audio_endpoints(chip, buffer, buflen, j); - usb_set_interface(dev, j, 0); /* reset the current interface */ - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + if (! parse_audio_endpoints(chip, buffer, buflen, j)) { + usb_set_interface(dev, j, 0); /* reset the current interface */ + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } } return 0; |
