kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
- debugobjects.xml sh.xml regulator.xml \
- alsa-driver-api.xml writing-an-alsa-driver.xml \
+ 80211.xml debugobjects.xml sh.xml regulator.xml \
tracepoint.xml w1.xml \
writing_musb_glue_layer.xml crypto-API.xml iio.xml
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-
-<!-- ****************************************************** -->
-<!-- Header -->
-<!-- ****************************************************** -->
-<book id="ALSA-Driver-API">
- <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>
-
-<toc></toc>
-
- <chapter><title>Management of Cards and Devices</title>
- <sect1><title>Card Management</title>
-!Esound/core/init.c
- </sect1>
- <sect1><title>Device Components</title>
-!Esound/core/device.c
- </sect1>
- <sect1><title>Module requests and Device File Entries</title>
-!Esound/core/sound.c
- </sect1>
- <sect1><title>Memory Management Helpers</title>
-!Esound/core/memory.c
-!Esound/core/memalloc.c
- </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
-!Iinclude/sound/pcm.h
- </sect1>
- <sect1><title>PCM Format Helpers</title>
-!Esound/core/pcm_misc.c
- </sect1>
- <sect1><title>PCM Memory Management</title>
-!Esound/core/pcm_memory.c
- </sect1>
- <sect1><title>PCM DMA Engine API</title>
-!Esound/core/pcm_dmaengine.c
-!Iinclude/sound/dmaengine_pcm.h
- </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
-!Esound/pci/ac97/ac97_pcm.c
- </sect1>
- <sect1><title>Virtual Master Control API</title>
-!Esound/core/vmaster.c
-!Iinclude/sound/control.h
- </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>Compress Offload</title>
- <sect1><title>Compress Offload API</title>
-!Esound/core/compress_offload.c
-!Iinclude/uapi/sound/compress_offload.h
-!Iinclude/uapi/sound/compress_params.h
-!Iinclude/sound/compress_driver.h
- </sect1>
- </chapter>
- <chapter><title>ASoC</title>
- <sect1><title>ASoC Core API</title>
-!Iinclude/sound/soc.h
-!Esound/soc/soc-core.c
-<!-- !Esound/soc/soc-cache.c no docbook comments here -->
-!Esound/soc/soc-devres.c
-!Esound/soc/soc-io.c
-!Esound/soc/soc-pcm.c
-!Esound/soc/soc-ops.c
-!Esound/soc/soc-compress.c
- </sect1>
- <sect1><title>ASoC DAPM API</title>
-!Esound/soc/soc-dapm.c
- </sect1>
- <sect1><title>ASoC DMA Engine API</title>
-!Esound/soc/soc-generic-dmaengine-pcm.c
- </sect1>
- </chapter>
- <chapter><title>Miscellaneous Functions</title>
- <sect1><title>Hardware-Dependent Devices API</title>
-!Esound/core/hwdep.c
- </sect1>
- <sect1><title>Jack Abstraction Layer API</title>
-!Iinclude/sound/jack.h
-!Esound/core/jack.c
-!Esound/soc/soc-jack.c
- </sect1>
- <sect1><title>ISA DMA Helpers</title>
-!Esound/core/isadma.c
- </sect1>
- <sect1><title>Other Helper Macros</title>
-!Iinclude/sound/core.h
- </sect1>
- </chapter>
-
-</book>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-
-<!-- ****************************************************** -->
-<!-- Header -->
-<!-- ****************************************************** -->
-<book id="Writing-an-ALSA-Driver">
- <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>Oct 15, 2007</date>
- <edition>0.3.7</edition>
-
- <abstract>
- <para>
- This document describes how to write an ALSA (Advanced Linux
- Sound Architecture) driver.
- </para>
- </abstract>
-
- <legalnotice>
- <para>
- Copyright (c) 2002-2005 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 PCI soundcards.
- 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>
- This document targets people who already have enough
- C language skills and have basic linux kernel programming
- knowledge. This document doesn't explain the general
- topic of linux kernel coding and doesn't cover low-level
- driver implementation details. It only describes
- 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 API, you
- can check the drivers such as <filename>sound/pci/es1938.c</filename> or
- <filename>sound/pci/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 feedback 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 two ways.
- </para>
-
- <para>
- One is the trees provided as a tarball or via cvs from the
- ALSA's ftp site, and another is the 2.6 (or later) Linux kernel
- tree. To synchronize both, the ALSA driver tree is split into
- two different trees: alsa-kernel and alsa-driver. The former
- contains purely the source code for the Linux 2.6 (or later)
- tree. This tree is designed only for compilation on 2.6 or
- later environment. The latter, alsa-driver, contains many subtle
- files for compiling ALSA drivers outside of the Linux kernel tree,
- 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 (and eventually to the 2.6 kernel tree) 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 which 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 <filename>core/seq/oss</filename> 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 directory 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 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 reasons :)
- </para>
- </section>
-
- <section id="file-tree-drivers-directory">
- <title>drivers directory</title>
- <para>
- This directory contains code shared among different drivers
- on different architectures. They are hence supposed not to be
- architecture-specific.
- For example, the dummy pcm driver and the serial MIDI
- driver are found in this directory. In the sub-directories,
- there is code 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 and opl4</title>
- <para>
- The OPL3 and OPL4 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 has its
- own i2c code for some cards, because the soundcard needs only a
- simple operation and the standard i2c 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
- the <filename>synth/emux</filename> sub-directory.
- </para>
- </section>
-
- <section id="file-tree-pci-directory">
- <title>pci directory</title>
- <para>
- This directory and its sub-directories hold the top-level card modules
- for PCI soundcards and the code specific to the PCI BUS.
- </para>
-
- <para>
- The drivers compiled from a single file are stored directly
- in the pci directory, while the drivers with several source files are
- stored on their own sub-directory (e.g. emu10k1, ice1712).
- </para>
- </section>
-
- <section id="file-tree-isa-directory">
- <title>isa directory</title>
- <para>
- This directory 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>
- They are used for top-level card modules which are
- specific to one of these architectures.
- </para>
- </section>
-
- <section id="file-tree-usb-directory">
- <title>usb directory</title>
- <para>
- This directory contains the USB-audio driver. In the latest version, the
- USB MIDI driver is integrated in the 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 in the pci directory, because their API is identical
- to that of standard PCI cards.
- </para>
- </section>
-
- <section id="file-tree-oss-directory">
- <title>oss directory</title>
- <para>
- The OSS/Lite source files are stored here in Linux 2.6 (or
- later) tree. In the ALSA driver tarball, this directory is 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 for PCI soundcards is as follows:
-
- <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 a <structname>pci_driver</structname> structure
- containing the three pointers above.</para></listitem>
- <listitem><para>create an <function>init()</function> function just calling
- the <function>pci_register_driver()</function> to register the pci_driver table
- defined above.</para></listitem>
- <listitem><para>create an <function>exit()</function> function to call
- the <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
- next sections. The numbers in the comment lines of the
- <function>snd_mychip_probe()</function> function
- refer to details explained in the following section.
-
- <example>
- <title>Basic Flow for PCI Drivers - Example</title>
- <programlisting>
-<![CDATA[
- #include <linux/init.h>
- #include <linux/pci.h>
- #include <linux/slab.h>
- #include <sound/core.h>
- #include <sound/initval.h>
-
- /* module parameters (see "Module Parameters") */
- /* SNDRV_CARDS: maximum number of cards supported by this module */
- static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
- static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
- static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-
- /* definition of the chip-specific record */
- struct mychip {
- struct snd_card *card;
- /* the rest of the implementation will be in section
- * "PCI Resource Management"
- */
- };
-
- /* chip-specific destructor
- * (see "PCI Resource Management")
- */
- static int snd_mychip_free(struct mychip *chip)
- {
- .... /* will be implemented later... */
- }
-
- /* component-destructor
- * (see "Management of Cards and Components")
- */
- static int snd_mychip_dev_free(struct snd_device *device)
- {
- return snd_mychip_free(device->device_data);
- }
-
- /* chip-specific constructor
- * (see "Management of Cards and Components")
- */
- static int snd_mychip_create(struct snd_card *card,
- struct pci_dev *pci,
- struct mychip **rchip)
- {
- struct mychip *chip;
- int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_mychip_dev_free,
- };
-
- *rchip = NULL;
-
- /* check PCI availability here
- * (see "PCI Resource Management")
- */
- ....
-
- /* allocate a chip-specific data with zero filled */
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
-
- chip->card = card;
-
- /* rest of initialization here; will be implemented
- * later, see "PCI Resource Management"
- */
- ....
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_mychip_free(chip);
- return err;
- }
-
- *rchip = chip;
- return 0;
- }
-
- /* constructor -- see "Constructor" sub-section */
- static int snd_mychip_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
- {
- static int dev;
- struct snd_card *card;
- struct mychip *chip;
- int err;
-
- /* (1) */
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev]) {
- dev++;
- return -ENOENT;
- }
-
- /* (2) */
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
- if (err < 0)
- return err;
-
- /* (3) */
- err = snd_mychip_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
-
- /* (4) */
- 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);
-
- /* (5) */
- .... /* implemented later */
-
- /* (6) */
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
-
- /* (7) */
- pci_set_drvdata(pci, card);
- dev++;
- return 0;
- }
-
- /* destructor -- see the "Destructor" sub-section */
- static void snd_mychip_remove(struct pci_dev *pci)
- {
- snd_card_free(pci_get_drvdata(pci));
- 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 the <function>probe</function> callback.
- The <function>probe</function> callback and other component-constructors which are called
- from the <function>probe</function> callback cannot be used with
- the <parameter>__init</parameter> prefix
- because any PCI device could be a hotplug device.
- </para>
-
- <para>
- In the <function>probe</function> 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>
- Each time the <function>probe</function> 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[
- struct snd_card *card;
- int err;
- ....
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The details 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[
- struct mychip *chip;
- ....
- err = snd_mychip_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
-]]>
- </programlisting>
- </informalexample>
-
- The details will be explained in the section <link
- linkend="pci-resource"><citetitle>PCI Resource
- Management</citetitle></link>.
- </para>
- </section>
-
- <section id="basic-flow-constructor-main-component">
- <title>4) 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 used 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
- shown in <filename>/proc/asound/cards</filename>.
- </para>
- </section>
-
- <section id="basic-flow-constructor-create-other">
- <title>5) 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-register-card">
- <title>6) Register the card instance.</title>
- <para>
- <informalexample>
- <programlisting>
-<![CDATA[
- err = snd_card_register(card);
- if (err < 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, card);
- dev++;
- return 0;
-]]>
- </programlisting>
- </informalexample>
-
- In the above, the card record is stored. This pointer is
- used in the remove callback and power-management
- callbacks, too.
- </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 snd_mychip_remove(struct pci_dev *pci)
- {
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
- }
-]]>
- </programlisting>
- </informalexample>
-
- The above code assumes that the card pointer is set to the PCI
- driver data.
- </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 <linux/init.h>
- #include <linux/pci.h>
- #include <linux/slab.h>
- #include <sound/core.h>
- #include <sound/initval.h>
-]]>
- </programlisting>
- </informalexample>
-
- where the last one is necessary only when module options are
- defined in the source file. If the code is split into several
- files, the files without module options don't need them.
- </para>
-
- <para>
- In addition to these headers, you'll need
- <filename><linux/interrupt.h></filename> for interrupt
- handling, and <filename><asm/io.h></filename> for I/O
- access. If you use the <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 the PCM and control APIs are defined in other
- <filename><sound/xxx.h></filename> header files.
- 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 whole list of 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 correct release of resources at
- destruction.
- </para>
-
- <para>
- As mentioned above, to create a card instance, call
- <function>snd_card_new()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_card *card;
- int err;
- err = snd_card_new(&pci->dev, index, id, module, extra_size, &card);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The function takes six arguments: the parent device pointer,
- the card-index number, the id string, the module pointer (usually
- <constant>THIS_MODULE</constant>),
- the size of extra-data space, and the pointer to return the
- card instance. The extra_size argument is used to
- allocate card->private_data for the
- chip-specific data. Note that these data
- are allocated by <function>snd_card_new()</function>.
- </para>
-
- <para>
- The first argument, the pointer of struct
- <structname>device</structname>, specifies the parent device.
- For PCI devices, typically &pci-> is passed there.
- </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. In an ALSA driver, a component is
- represented as a struct <structname>snd_device</structname> object.
- A component can be a PCM instance, a control interface, a raw
- MIDI interface, etc. Each such instance 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 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 (<parameter>chip</parameter> in the
- above example) is used as the identifier for the instance.
- </para>
-
- <para>
- Each pre-defined ALSA component such as ac97 and 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 wish to create your own component, you need to
- set the destructor function to the dev_free callback in
- the <parameter>ops</parameter>, so that it can be released
- automatically via <function>snd_card_free()</function>.
- The next example will show an implementation of chip-specific
- data.
- </para>
- </section>
-
- <section id="card-management-chip-specific">
- <title>Chip-Specific Data</title>
- <para>
- Chip-specific information, e.g. the I/O port address, its
- resource pointer, or the irq number, is stored in the
- chip-specific record.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct mychip {
- ....
- };
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- In general, there are two ways of allocating 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 5th argument of <function>snd_card_new()</function>, i.e.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- sizeof(struct mychip), &card);
-]]>
- </programlisting>
- </informalexample>
-
- struct <structname>mychip</structname> is the type of the chip record.
- </para>
-
- <para>
- In return, the allocated record can be accessed as
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct mychip *chip = card->private_data;
-]]>
- </programlisting>
- </informalexample>
-
- With this method, you don't have to allocate twice.
- The record is released together with the card instance.
- </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>0</constant> on the 4th arg), call
- <function>kzalloc()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_card *card;
- struct mychip *chip;
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
- .....
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The chip record should have the field to hold the card
- pointer at least,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct mychip {
- struct snd_card *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>
- Next, initialize the fields, and register this chip
- record as a low-level device with a specified
- <parameter>ops</parameter>,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static struct snd_device_ops 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(struct snd_device *device)
- {
- return snd_mychip_free(device->device_data);
- }
-]]>
- </programlisting>
- </informalexample>
-
- where <function>snd_mychip_free()</function> is the real destructor.
- </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>. Access
- to the device files is 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 mentioned earlier, all
- components are released automatically by this call.
- </para>
-
- <para>
- For a device which allows hotplugging, you can use
- <function>snd_card_free_when_closed</function>. This one will
- postpone the destruction until all devices are closed.
- </para>
-
- </section>
-
- </chapter>
-
-
-<!-- ****************************************************** -->
-<!-- PCI Resource Management -->
-<!-- ****************************************************** -->
- <chapter id="pci-resource">
- <title>PCI Resource Management</title>
-
- <section id="pci-resource-example">
- <title>Full Code Example</title>
- <para>
- In this section, we'll complete the chip-specific constructor,
- destructor and PCI entries. Example code is shown first,
- below.
-
- <example>
- <title>PCI Resource Management Example</title>
- <programlisting>
-<![CDATA[
- struct mychip {
- struct snd_card *card;
- struct pci_dev *pci;
-
- unsigned long port;
- int irq;
- };
-
- static int snd_mychip_free(struct mychip *chip)
- {
- /* disable hardware here if any */
- .... /* (not implemented in this document) */
-
- /* release the irq */
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- /* release the I/O ports & memory */
- pci_release_regions(chip->pci);
- /* disable the PCI entry */
- pci_disable_device(chip->pci);
- /* release the data */
- kfree(chip);
- return 0;
- }
-
- /* chip-specific constructor */
- static int snd_mychip_create(struct snd_card *card,
- struct pci_dev *pci,
- struct mychip **rchip)
- {
- struct mychip *chip;
- int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_mychip_dev_free,
- };
-
- *rchip = NULL;
-
- /* initialize the PCI entry */
- err = pci_enable_device(pci);
- if (err < 0)
- return err;
- /* check PCI availability (28bit DMA) */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- printk(KERN_ERR "error to set 28bit mask DMA\n");
- pci_disable_device(pci);
- return -ENXIO;
- }
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
- /* initialize the stuff */
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
-
- /* (1) PCI resource allocation */
- err = pci_request_regions(pci, "My Chip");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
- chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_mychip_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, chip)) {
- printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
- snd_mychip_free(chip);
- return -EBUSY;
- }
- chip->irq = pci->irq;
-
- /* (2) initialization of the chip hardware */
- .... /* (not implemented in this document) */
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_mychip_free(chip);
- return err;
- }
-
- *rchip = chip;
- return 0;
- }
-
- /* PCI IDs */
- static struct pci_device_id snd_mychip_ids[] = {
- { 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 = KBUILD_MODNAME,
- .id_table = snd_mychip_ids,
- .probe = snd_mychip_probe,
- .remove = snd_mychip_remove,
- };
-
- /* module initialization */
- static int __init alsa_card_mychip_init(void)
- {
- return pci_register_driver(&driver);
- }
-
- /* module clean up */
- 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 first have to call
- the <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[
- err = pci_enable_device(pci);
- if (err < 0)
- return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- printk(KERN_ERR "error to set 28bit mask DMA\n");
- pci_disable_device(pci);
- return -ENXIO;
- }
-
-]]>
- </programlisting>
- </informalexample>
- </para>
- </section>
-
- <section id="pci-resource-resource-allocation">
- <title>Resource Allocation</title>
- <para>
- The allocation of I/O ports and irqs is 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 to
- allocate (pseudo-)DMA for PCI like in ALSA 0.5.x.
- </para>
-
- <para>
- Now assume that the PCI device has an I/O port with 8 bytes
- and an interrupt. Then struct <structname>mychip</structname> will have the
- following fields:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct mychip {
- struct snd_card *card;
-
- unsigned long 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>kzalloc()</function> automatically, so you
- don't have to take care of resetting them.
- </para>
-
- <para>
- The allocation of an I/O port is done like this:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- err = pci_request_regions(pci, "My Chip");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
- chip->port = pci_resource_start(pci, 0);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- <!-- obsolete -->
- 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 a
- problem with this. This issue will be explained later.
- </para>
-
- <para>
- The allocation of an interrupt source is done like this:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- if (request_irq(pci->irq, snd_mychip_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, chip)) {
- printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
- snd_mychip_free(chip);
- 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, interrupts can be shared. Thus,
- <constant>IRQF_SHARED</constant> is used 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 give details about 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 irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
- {
- struct mychip *chip = dev_id;
- ....
- return IRQ_HANDLED;
- }
-]]>
- </programlisting>
- </informalexample>
- </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 code is not written here.
- </para>
-
- <para>
- To release the resources, the <quote>check-and-release</quote>
- method is a safer way. For the interrupt, do like this:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-]]>
- </programlisting>
- </informalexample>
-
- Since the irq number can start from 0, you should initialize
- chip->irq with a negative value (e.g. -1), so that you can
- check the validity of the irq number as above.
- </para>
-
- <para>
- When you requested I/O ports or memory regions via
- <function>pci_request_region()</function> or
- <function>pci_request_regions()</function> like in this example,
- release the resource(s) using the corresponding function,
- <function>pci_release_region()</function> or
- <function>pci_release_regions()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- pci_release_regions(chip->pci);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- When you requested manually via <function>request_region()</function>
- or <function>request_mem_region</function>, you can release it via
- <function>release_resource()</function>. Suppose that you keep
- the resource pointer returned from <function>request_region()</function>
- in chip->res_port, the release procedure looks like:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- release_and_free_resource(chip->res_port);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Don't forget to call <function>pci_disable_device()</function>
- before the end.
- </para>
-
- <para>
- And finally, release the chip-specific record.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- kfree(chip);
-]]>
- </programlisting>
- </informalexample>
- </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 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 already been released.
- You don't have to stop PCMs, etc. explicitly, but just
- call low-level hardware stopping.
- </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 mychip {
- ....
- unsigned long iobase_phys;
- void __iomem *iobase_virt;
- };
-]]>
- </programlisting>
- </informalexample>
-
- and the allocation would be like below:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- if ((err = pci_request_regions(pci, "My Chip")) < 0) {
- kfree(chip);
- return err;
- }
- chip->iobase_phys = pci_resource_start(pci, 0);
- chip->iobase_virt = ioremap_nocache(chip->iobase_phys,
- pci_resource_len(pci, 0));
-]]>
- </programlisting>
- </informalexample>
-
- and the corresponding destructor would be:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_free(struct mychip *chip)
- {
- ....
- if (chip->iobase_virt)
- iounmap(chip->iobase_virt);
- ....
- pci_release_regions(chip->pci);
- ....
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- </section>
-
- <section id="pci-resource-entries">
- <title>PCI Entries</title>
- <para>
- So far, so good. Let's finish the missing PCI
- stuff. 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[] = {
- { 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
- the <structname>pci_device_id</structname> structure are the vendor and
- device IDs. If you have no reason to filter the matching
- devices, you can leave the remaining fields as above. The last
- field of the <structname>pci_device_id</structname> struct contains
- private data for this entry. You can specify any value here, for
- example, to define specific operations for supported device IDs.
- Such an example is found in the 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 = KBUILD_MODNAME,
- .id_table = snd_mychip_ids,
- .probe = snd_mychip_probe,
- .remove = snd_mychip_remove,
- };
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The <structfield>probe</structfield> and
- <structfield>remove</structfield> functions have already
- been defined in the previous sections.
- 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)
- {
- return pci_register_driver(&driver);
- }
-
- 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.
- </para>
-
- <para>
- Oh, one thing was forgotten. If you have no exported symbols,
- you need to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels).
-
- <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> first. 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 numbers. Once when 64bit device number is
- used, we'll have more pcm instances available.
- </para>
-
- <para>
- A pcm instance consists of pcm playback and capture streams,
- and each pcm stream consists of one or more pcm substreams. Some
- soundcards support multiple playback functions. 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 successful open will either block
- or error with <constant>EAGAIN</constant> according to the
- file open mode. But you don't have to care about such details in your
- driver. The PCM middle layer will take care of such work.
- </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>
- ....
-
- /* hardware definition */
- static struct snd_pcm_hardware 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,
- };
-
- /* hardware definition */
- static struct snd_pcm_hardware snd_mychip_capture_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_playback_open(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->hw = snd_mychip_playback_hw;
- /* more hardware-initialization will be done here */
- ....
- return 0;
- }
-
- /* close callback */
- static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- /* the hardware-specific codes will be here */
- ....
- return 0;
-
- }
-
- /* open callback */
- static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->hw = snd_mychip_capture_hw;
- /* more hardware-initialization will be done here */
- ....
- return 0;
- }
-
- /* close callback */
- static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
- {
- struct mychip *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(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
- {
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- }
-
- /* hw_free callback */
- static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
- {
- return snd_pcm_lib_free_pages(substream);
- }
-
- /* prepare callback */
- static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *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_addr,
- chip->buffer_size,
- chip->period_size);
- return 0;
- }
-
- /* trigger callback */
- static int snd_mychip_pcm_trigger(struct snd_pcm_substream *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(struct snd_pcm_substream *substream)
- {
- struct mychip *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 struct snd_pcm_ops 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,
- };
-
- /* operators */
- static struct snd_pcm_ops snd_mychip_capture_ops = {
- .open = snd_mychip_capture_open,
- .close = snd_mychip_capture_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 snd_mychip_new_pcm(struct mychip *chip)
- {
- struct snd_pcm *pcm;
- int err;
-
- err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
- if (err < 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 */
- /* NOTE: this may fail */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
- return 0;
- }
-]]>
- </programlisting>
- </example>
- </para>
- </section>
-
- <section id="pcm-interface-constructor">
- <title>Constructor</title>
- <para>
- A pcm instance is allocated by the <function>snd_pcm_new()</function>
- function. It would be better to create a constructor for pcm,
- namely,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_new_pcm(struct mychip *chip)
- {
- struct snd_pcm *pcm;
- int err;
-
- err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
- if (err < 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 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. If
- you 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 1 is used for
- both arguments. When no playback or capture substreams are 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
- struct <structname>snd_pcm_substream</structname> data passed to each callback
- as follows:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_pcm_substream *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 struct snd_pcm_ops 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>
-
- All the callbacks are described in the
- <link linkend="pcm-interface-operators"><citetitle>
- Operators</citetitle></link> subsection.
- </para>
-
- <para>
- After setting the operators, you probably will want to
- pre-allocate the buffer. For the pre-allocation, simply call
- the following:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
-]]>
- </programlisting>
- </informalexample>
-
- It will allocate a buffer up to 64kB as default.
- Buffer management details 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 the destructor
- explicitly.
- </para>
-
- <para>
- The destructor would be necessary if you created
- special records internally and needed 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(struct snd_pcm *pcm)
- {
- struct mychip *chip = snd_pcm_chip(pcm);
- /* free your own data */
- kfree(chip->my_private_pcm_data);
- /* do what you like else */
- ....
- }
-
- static int snd_mychip_new_pcm(struct mychip *chip)
- {
- struct snd_pcm *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-runtime">
- <title>Runtime Pointer - The Chest of PCM Information</title>
- <para>
- When the PCM substream is opened, a PCM runtime instance is
- allocated and assigned to the substream. This pointer is
- accessible via <constant>substream->runtime</constant>.
- This runtime pointer holds most information you need
- to control the PCM: the copy of hw_params and sw_params configurations, the buffer
- pointers, mmap records, spinlocks, etc.
- </para>
-
- <para>
- The definition of runtime instance is found in
- <filename><sound/pcm.h></filename>. Here are
- the contents of this file:
- <informalexample>
- <programlisting>
-<![CDATA[
-struct _snd_pcm_runtime {
- /* -- Status -- */
- struct snd_pcm_substream *trigger_master;
- snd_timestamp_t trigger_tstamp; /* trigger timestamp */
- int overrange;
- snd_pcm_uframes_t avail_max;
- snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
- snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/
-
- /* -- HW params -- */
- snd_pcm_access_t access; /* access mode */
- snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */
- snd_pcm_subformat_t subformat; /* subformat */
- unsigned int rate; /* rate in Hz */
- unsigned int channels; /* channels */
- snd_pcm_uframes_t period_size; /* period size */
- unsigned int periods; /* periods */
- snd_pcm_uframes_t buffer_size; /* buffer size */
- unsigned int tick_time; /* tick time */
- snd_pcm_uframes_t min_align; /* Min alignment for the format */
- size_t byte_align;
- unsigned int frame_bits;
- unsigned int sample_bits;
- unsigned int info;
- unsigned int rate_num;
- unsigned int rate_den;
-
- /* -- SW params -- */
- struct timespec tstamp_mode; /* mmap timestamp is updated */
- unsigned int period_step;
- unsigned int sleep_min; /* min ticks to sleep */
- snd_pcm_uframes_t start_threshold;
- snd_pcm_uframes_t stop_threshold;
- snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
- noise is nearest than this */
- snd_pcm_uframes_t silence_size; /* Silence filling size */
- snd_pcm_uframes_t boundary; /* pointers wrap point */
-
- snd_pcm_uframes_t silenced_start;
- snd_pcm_uframes_t silenced_size;
-
- snd_pcm_sync_id_t sync; /* hardware synchronization ID */
-
- /* -- mmap -- */
- volatile struct snd_pcm_mmap_status *status;
- volatile struct snd_pcm_mmap_control *control;
- atomic_t mmap_count;
-
- /* -- locking / scheduling -- */
- spinlock_t lock;
- wait_queue_head_t sleep;
- struct timer_list tick_timer;
- struct fasync_struct *fasync;
-
- /* -- private section -- */
- void *private_data;
- void (*private_free)(struct snd_pcm_runtime *runtime);
-
- /* -- hardware description -- */
- struct snd_pcm_hardware hw;
- struct snd_pcm_hw_constraints hw_constraints;
-
- /* -- timer -- */
- unsigned int timer_resolution; /* timer resolution */
-
- /* -- DMA -- */
- unsigned char *dma_area; /* DMA area */
- dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */
- size_t dma_bytes; /* size of DMA area */
-
- struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
-
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
- /* -- OSS things -- */
- struct snd_pcm_oss_runtime oss;
-#endif
-};
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- For the operators (callbacks) of each sound driver, most of
- these records are supposed to be read-only. Only the PCM
- middle-layer changes / updates them. The exceptions are
- the hardware description (hw) DMA buffer information and the
- private data. Besides, if you use the standard buffer allocation
- method via <function>snd_pcm_lib_malloc_pages()</function>,
- you don't need to set the DMA buffer information by yourself.
- </para>
-
- <para>
- In the sections below, important records are explained.
- </para>
-
- <section id="pcm-interface-runtime-hw">
- <title>Hardware Description</title>
- <para>
- The hardware descriptor (struct <structname>snd_pcm_hardware</structname>)
- contains the definitions of the fundamental hardware
- configuration. Above all, you'll need to define this in
- <link linkend="pcm-interface-operators-open-callback"><citetitle>
- the open callback</citetitle></link>.
- Note that the runtime instance holds the copy of the
- descriptor, not the pointer to the existing descriptor. That
- is, in the open callback, you can modify the copied descriptor
- (<constant>runtime->hw</constant>) as you need. For example, if the maximum
- number of channels is 1 only on some chip models, you can
- still use the same hardware descriptor and change the
- channels_max later:
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_pcm_runtime *runtime = substream->runtime;
- ...
- runtime->hw = snd_mychip_playback_hw; /* common definition */
- if (chip->model == VERY_OLD_ONE)
- runtime->hw.channels_max = 1;
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Typically, you'll have a hardware descriptor as below:
- <informalexample>
- <programlisting>
-<![CDATA[
- static struct snd_pcm_hardware 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>
- <itemizedlist>
- <listitem><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 hardware supports mmap, add the
- <constant>SNDRV_PCM_INFO_MMAP</constant> flag here. When the
- hardware supports the interleaved or the non-interleaved
- formats, <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 the 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 full <quote>suspend/resume</quote> operation.
- If the <constant>PAUSE</constant> flag is set,
- the <structfield>trigger</structfield> callback below
- must handle the corresponding (pause push/release) commands.
- The suspend/resume trigger commands can be defined even without
- the <constant>RESUME</constant> flag. See <link
- linkend="power-management"><citetitle>
- Power Management</citetitle></link> section for details.
- </para>
-
- <para>
- When the PCM substreams can be synchronized (typically,
- synchronized start/stop of a playback and a capture streams),
- you can give <constant>SNDRV_PCM_INFO_SYNC_START</constant>,
- too. In this case, you'll need to check the linked-list of
- PCM substreams in the trigger callback. This will be
- described in the later section.
- </para>
- </listitem>
-
- <listitem>
- <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>
- </listitem>
-
- <listitem>
- <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 provided only for typical
- rates. If your chip supports unconventional rates, you need to add
- the <constant>KNOT</constant> bit and set up the hardware
- constraint manually (explained later).
- </para>
- </listitem>
-
- <listitem>
- <para>
- <structfield>rate_min</structfield> and
- <structfield>rate_max</structfield> define the minimum and
- maximum sample rate. This should correspond somehow to
- <structfield>rates</structfield> bits.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <structfield>channel_min</structfield> and
- <structfield>channel_max</structfield>
- define, as you might already expected, the minimum and maximum
- number of channels.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <structfield>buffer_bytes_max</structfield> defines the
- maximum buffer size in bytes. There is no
- <structfield>buffer_bytes_min</structfield> field, since
- it can be calculated from the minimum period size and the
- minimum number of periods.
- Meanwhile, <structfield>period_bytes_min</structfield> and
- define the minimum and maximum size of the period in bytes.
- <structfield>periods_max</structfield> and
- <structfield>periods_min</structfield> define the maximum and
- minimum number of periods in the buffer.
- </para>
-
- <para>
- The <quote>period</quote> is a term that corresponds to
- a fragment in the OSS world. The period defines the size at
- which a PCM interrupt is generated. This size strongly
- depends on the hardware.
- Generally, the smaller period size will give you more
- interrupts, that is, more controls.
- In the case of capture, this size defines the input latency.
- On the other hand, the whole buffer size defines the
- output latency for the playback direction.
- </para>
- </listitem>
-
- <listitem>
- <para>
- There is also a field <structfield>fifo_size</structfield>.
- This specifies the size of the hardware FIFO, but currently it
- is neither used in the driver nor in the alsa-lib. So, you
- can ignore this field.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </section>
-
- <section id="pcm-interface-runtime-config">
- <title>PCM Configurations</title>
- <para>
- Ok, let's go back again to the PCM runtime records.
- The most frequently referred records in the runtime instance are
- the PCM configurations.
- The PCM configurations are stored in the runtime instance
- after the application sends <type>hw_params</type> data via
- alsa-lib. There are many fields copied from hw_params and
- sw_params structs. For example,
- <structfield>format</structfield> holds the format type
- chosen by the application. This field contains the enum value
- <constant>SNDRV_PCM_FORMAT_XXX</constant>.
- </para>
-
- <para>
- One thing to be noted is that the configured buffer and period
- sizes are stored in <quote>frames</quote> in the runtime.
- In the ALSA world, 1 frame = channels * samples-size.
- For conversion between frames and bytes, you can use the
- <function>frames_to_bytes()</function> and
- <function>bytes_to_frames()</function> helper functions.
- <informalexample>
- <programlisting>
-<![CDATA[
- period_bytes = frames_to_bytes(runtime, runtime->period_size);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Also, many software parameters (sw_params) are
- stored in frames, too. Please check the type of the field.
- <type>snd_pcm_uframes_t</type> is for the frames as unsigned
- integer while <type>snd_pcm_sframes_t</type> is for the frames
- as signed integer.
- </para>
- </section>
-
- <section id="pcm-interface-runtime-dma">
- <title>DMA Buffer Information</title>
- <para>
- The DMA buffer is defined by the following four fields,
- <structfield>dma_area</structfield>,
- <structfield>dma_addr</structfield>,
- <structfield>dma_bytes</structfield> and
- <structfield>dma_private</structfield>.
- The <structfield>dma_area</structfield> holds the buffer
- pointer (the logical address). You can call
- <function>memcpy</function> from/to
- this pointer. Meanwhile, <structfield>dma_addr</structfield>
- holds the physical address of the buffer. This field is
- specified only when the buffer is a linear buffer.
- <structfield>dma_bytes</structfield> holds the size of buffer
- in bytes. <structfield>dma_private</structfield> is used for
- the ALSA DMA allocator.
- </para>
-
- <para>
- If you use a standard ALSA function,
- <function>snd_pcm_lib_malloc_pages()</function>, for
- allocating the buffer, these fields are set by the ALSA middle
- layer, and you should <emphasis>not</emphasis> change them by
- yourself. You can read them but not write them.
- On the other hand, if you want to allocate the buffer by
- yourself, you'll need to manage it in hw_params callback.
- At least, <structfield>dma_bytes</structfield> is mandatory.
- <structfield>dma_area</structfield> is necessary when the
- buffer is mmapped. If your driver doesn't support mmap, this
- field is not necessary. <structfield>dma_addr</structfield>
- is also optional. You can use
- <structfield>dma_private</structfield> as you like, too.
- </para>
- </section>
-
- <section id="pcm-interface-runtime-status">
- <title>Running Status</title>
- <para>
- The running status can be referred via <constant>runtime->status</constant>.
- This is the pointer to the struct <structname>snd_pcm_mmap_status</structname>
- record. For example, you can get the current DMA hardware
- pointer via <constant>runtime->status->hw_ptr</constant>.
- </para>
-
- <para>
- The DMA application pointer can be referred via
- <constant>runtime->control</constant>, which points to the
- struct <structname>snd_pcm_mmap_control</structname> record.
- However, accessing directly to this value is not recommended.
- </para>
- </section>
-
- <section id="pcm-interface-runtime-private">
- <title>Private Data</title>
- <para>
- You can allocate a record for the substream and store it in
- <constant>runtime->private_data</constant>. Usually, this
- is done in
- <link linkend="pcm-interface-operators-open-callback"><citetitle>
- the open callback</citetitle></link>.
- Don't mix this with <constant>pcm->private_data</constant>.
- The <constant>pcm->private_data</constant> usually points to the
- chip instance assigned statically at the creation of PCM, while the
- <constant>runtime->private_data</constant> points to a dynamic
- data structure created at the PCM open callback.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_open(struct snd_pcm_substream *substream)
- {
- struct my_pcm_data *data;
- ....
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- substream->runtime->private_data = data;
- ....
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The allocated object must be released in
- <link linkend="pcm-interface-operators-open-callback"><citetitle>
- the close callback</citetitle></link>.
- </para>
- </section>
-
- </section>
-
- <section id="pcm-interface-operators">
- <title>Operators</title>
- <para>
- OK, now let me give details about each pcm callback
- (<parameter>ops</parameter>). In general, every callback must
- return 0 if successful, or a negative error number
- such as <constant>-EINVAL</constant>. To choose an appropriate
- error number, it is advised to check what value other parts of
- the kernel return when the same kind of request fails.
- </para>
-
- <para>
- The callback function takes at least the argument with
- <structname>snd_pcm_substream</structname> pointer. To retrieve
- the chip record from the given substream instance, you can use the
- following macro.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- int xxx() {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- ....
- }
-]]>
- </programlisting>
- </informalexample>
-
- The macro reads <constant>substream->private_data</constant>,
- which is a copy of <constant>pcm->private_data</constant>.
- You can override the former if you need to assign different data
- records per PCM substream. For example, the cmi8330 driver assigns
- different private_data for playback and capture directions,
- because it uses two different codecs (SB- and AD-compatible) for
- different directions.
- </para>
-
- <section id="pcm-interface-operators-open-callback">
- <title>open callback</title>
- <para>
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_open(struct snd_pcm_substream *substream);
-]]>
- </programlisting>
- </informalexample>
-
- This is called when a pcm substream is opened.
- </para>
-
- <para>
- At least, here you have to initialize the runtime->hw
- record. Typically, this is done by like this:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_open(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *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 description.
- </para>
-
- <para>
- You can allocate a private data in this callback, as described
- in <link linkend="pcm-interface-runtime-private"><citetitle>
- Private Data</citetitle></link> section.
- </para>
-
- <para>
- If the hardware configuration needs more constraints, set the
- hardware constraints here, too.
- See <link linkend="pcm-interface-constraints"><citetitle>
- Constraints</citetitle></link> for more details.
- </para>
- </section>
-
- <section id="pcm-interface-operators-close-callback">
- <title>close callback</title>
- <para>
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_close(struct snd_pcm_substream *substream);
-]]>
- </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(struct snd_pcm_substream *substream)
- {
- ....
- 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 call 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(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params);
-]]>
- </programlisting>
- </informalexample>
- </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 setups 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. To allocate
- 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 be careful not to allocate the same buffers
- many times, which will lead to memory leaks! 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) as default, i.e. when no
- <structfield>nonatomic</structfield> flag set.
- This is important, because the
- <structfield>trigger</structfield> callback
- is atomic (non-schedulable). That is, mutexes or any
- schedule-related functions are not available in
- <structfield>trigger</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(struct snd_pcm_substream *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 function is always called before the close callback is called.
- Also, the 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(struct snd_pcm_substream *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 each
- time
- <function>snd_pcm_prepare()</function> is called, i.e. when
- recovering after underruns, etc.
- </para>
-
- <para>
- Note that this callback is now non-atomic.
- You can use schedule-related functions safely in this callback.
- </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>
- Be careful that this callback will be called many times at
- each setup, too.
- </para>
- </section>
-
- <section id="pcm-interface-operators-trigger-callback">
- <title>trigger callback</title>
- <para>
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_trigger(struct snd_pcm_substream *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,
- the <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 the info
- field of the hardware table), the <constant>PAUSE_PUSH</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,
- regardless of full or partial suspend/resume support,
- the <constant>SUSPEND</constant> and <constant>RESUME</constant>
- commands must be handled, too.
- These commands are issued when the power-management status is
- changed. Obviously, the <constant>SUSPEND</constant> and
- <constant>RESUME</constant> commands
- suspend and resume the pcm substream, and usually, they
- are identical to the <constant>STOP</constant> and
- <constant>START</constant> commands, respectively.
- See the <link linkend="power-management"><citetitle>
- Power Management</citetitle></link> section for details.
- </para>
-
- <para>
- As mentioned, this callback is atomic as default unless
- <structfield>nonatomic</structfield> flag set, and
- you cannot call functions which may sleep.
- The trigger callback should be as minimal as possible,
- just really triggering the DMA. The other stuff should be
- initialized hw_params and prepare callbacks properly
- beforehand.
- </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(struct snd_pcm_substream *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,
- ranging 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 as default.
- </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 in 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-ack">
- <title>ack callback</title>
- <para>
- This callback is also not mandatory. This callback is called
- when the appl_ptr is updated in read or write operations.
- Some drivers like emu10k1-fx and cs46xx need to track the
- current appl_ptr for the internal buffer, and this callback
- is useful only for such a purpose.
- </para>
- <para>
- This callback is atomic as default.
- </para>
- </section>
-
- <section id="pcm-interface-operators-page-callback">
- <title>page callback</title>
-
- <para>
- This callback is optional too. This callback is used
- mainly for non-contiguous buffers. 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 the <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 acquire 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>
- Typical code would be like:
-
- <example>
- <title>Interrupt Handler Case #1</title>
- <programlisting>
-<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
- {
- struct mychip *chip = dev_id;
- 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);
- /* acknowledge the interrupt if necessary */
- }
- ....
- spin_unlock(&chip->lock);
- return IRQ_HANDLED;
- }
-]]>
- </programlisting>
- </example>
- </para>
- </section>
-
- <section id="pcm-interface-interrupt-handler-timer">
- <title>High frequency timer interrupts</title>
- <para>
- This happens when the hardware doesn't generate interrupts
- at the period boundary but issues timer interrupts at a fixed
- timer rate (e.g. es1968 or ymfpci drivers).
- In this case, you need to check the current hardware
- position and accumulate the processed sample length at each
- interrupt. When the accumulated size exceeds the period
- size, call
- <function>snd_pcm_period_elapsed()</function> and reset the
- accumulator.
- </para>
-
- <para>
- Typical code would be like the following.
-
- <example>
- <title>Interrupt Handler Case #2</title>
- <programlisting>
-<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
- {
- struct mychip *chip = dev_id;
- 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);
- }
- /* acknowledge the interrupt if necessary */
- }
- ....
- spin_unlock(&chip->lock);
- return IRQ_HANDLED;
- }
-]]>
- </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) problems
- in kernel programming are race conditions.
- In the Linux kernel, they are usually avoided via spin-locks, mutexes
- or semaphores. In general, if a race condition can happen
- in an interrupt handler, it has to be managed atomically, and you
- have to use a spinlock to protect the critical session. If the
- critical section is not in interrupt handler code and
- if taking a relatively long time to execute is acceptable, you
- should use mutexes or semaphores instead.
- </para>
-
- <para>
- As already seen, some pcm callbacks are atomic and some are
- not. For example, the <parameter>hw_params</parameter> callback is
- non-atomic, while <parameter>trigger</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 choose a locking scheme 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>. Semaphores and mutexes can sleep,
- and hence they cannot be used inside the atomic callbacks
- (e.g. <parameter>trigger</parameter> callback).
- To implement some delay in such a callback, please use
- <function>udelay()</function> or <function>mdelay()</function>.
- </para>
-
- <para>
- All three atomic callbacks (trigger, pointer, and ack) are
- called with local interrupts disabled.
- </para>
-
- <para>
- The recent changes in PCM core code, however, allow all PCM
- operations to be non-atomic. This assumes that the all caller
- sides are in non-atomic contexts. For example, the function
- <function>snd_pcm_period_elapsed()</function> is called
- typically from the interrupt handler. But, if you set up the
- driver to use a threaded interrupt handler, this call can be in
- non-atomic context, too. In such a case, you can set
- <structfield>nonatomic</structfield> filed of
- <structname>snd_pcm</structname> object after creating it.
- When this flag is set, mutex and rwsem are used internally in
- the PCM core instead of spin and rwlocks, so that you can call
- all PCM functions safely in a non-atomic context.
- </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 struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
- };
-
- static int snd_mychip_pcm_open(struct snd_pcm_substream *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.
- Look at <filename>sound/pcm.h</filename> for a complete list.
- You can even define your own constraint rules.
- For example, let's suppose my_chip can manage a substream of 1 channel
- if and only if the format is S16_LE, otherwise it supports any format
- specified in the <structname>snd_pcm_hardware</structname> structure (or in any
- other constraint_list). You can build a rule like this:
-
- <example>
- <title>Example of Hardware Constraints for Channels</title>
- <programlisting>
-<![CDATA[
- static int hw_rule_channels_by_format(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
- {
- struct snd_interval *c = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- struct snd_interval ch;
-
- snd_interval_any(&ch);
- if (f->bits[0] == SNDRV_PCM_FMTBIT_S16_LE) {
- ch.min = ch.max = 1;
- ch.integer = 1;
- return snd_interval_refine(c, &ch);
- }
- return 0;
- }
-]]>
- </programlisting>
- </example>
- </para>
-
- <para>
- Then you need to call this function to add your rule:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The rule function is called when an application sets the PCM
- format, and it refines the number of channels accordingly.
- But an application may set the number of channels before
- setting the format. Thus you also need to define the inverse rule:
-
- <example>
- <title>Example of Hardware Constraints for Formats</title>
- <programlisting>
-<![CDATA[
- static int hw_rule_format_by_channels(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
- {
- struct snd_interval *c = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- struct snd_mask fmt;
-
- snd_mask_any(&fmt); /* Init the struct */
- if (c->min < 2) {
- fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE;
- return snd_mask_refine(f, &fmt);
- }
- return 0;
- }
-]]>
- </programlisting>
- </example>
- </para>
-
- <para>
- ...and in the open callback:
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- I won't give more 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 user-space. Its most
- important use is the mixer interface. In other words, since ALSA
- 0.9.x, all the mixer stuff is implemented on the control kernel API.
- </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 want to add your own controls.
- </para>
- </section>
-
- <section id="control-interface-definition">
- <title>Definition of Controls</title>
- <para>
- To create a new control, you need to define the
- following three
- callbacks: <structfield>info</structfield>,
- <structfield>get</structfield> and
- <structfield>put</structfield>. Then, define a
- struct <structname>snd_kcontrol_new</structname> record, such as:
-
- <example>
- <title>Definition of a Control</title>
- <programlisting>
-<![CDATA[
- static struct snd_kcontrol_new my_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PCM Playback Switch",
- .index = 0,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .private_value = 0xffff,
- .info = my_control_info,
- .get = my_control_get,
- .put = my_control_put
- };
-]]>
- </programlisting>
- </example>
- </para>
-
- <para>
- The <structfield>iface</structfield> field specifies the control
- type, <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>, which
- is usually <constant>MIXER</constant>.
- Use <constant>CARD</constant> for global controls that are not
- logically part of the mixer.
- If the control is closely associated with some specific device on
- the sound card, use <constant>HWDEP</constant>,
- <constant>PCM</constant>, <constant>RAWMIDI</constant>,
- <constant>TIMER</constant>, or <constant>SEQUENCER</constant>, and
- specify the device number with the
- <structfield>device</structfield> and
- <structfield>subdevice</structfield> fields.
- </para>
-
- <para>
- The <structfield>name</structfield> is the name identifier
- string. Since 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 <link linkend="control-interface-control-names"><citetitle>
- Control Names</citetitle></link> subsection.
- </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 details will be explained in
- the <link linkend="control-interface-access-flags"><citetitle>
- Access Flags</citetitle></link> subsection.
- </para>
-
- <para>
- The <structfield>private_value</structfield> field contains
- an arbitrary long integer value for this record. When using
- the 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 <structfield>tlv</structfield> field can be used to provide
- metadata about the control; see the
- <link linkend="control-interface-tlv">
- <citetitle>Metadata</citetitle></link> subsection.
- </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 to define 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> and
- <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>Documentation/sound/alsa/ControlNames.txt</filename>.
- </para>
- </section>
- </section>
-
- <section id="control-interface-access-flags">
- <title>Access Flags</title>
-
- <para>
- The access flag is the bitmask 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
- considered 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
- the <structfield>put</structfield> callback.
- Similarly, when the control is write-only (although it's a rare
- case), you can use the <constant>WRITE</constant> flag instead, and
- you don't need the <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
- the <constant>INACTIVE</constant> flag, too.
- There are <constant>LOCK</constant> and
- <constant>OWNER</constant> flags to change 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
- detailed information on this control. This must store the
- values of the given struct <structname>snd_ctl_elem_info</structname>
- object. For example, for a boolean control with a single
- element:
-
- <example>
- <title>Example of info callback</title>
- <programlisting>
-<![CDATA[
- static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *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 types 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_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- static char *texts[4] = {
- "First", "Second", "Third", "Fourth"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- 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]);
- return 0;
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The above callback can be simplified with a helper function,
- <function>snd_ctl_enum_info</function>. The final code
- looks like below.
- (You can pass ARRAY_SIZE(texts) instead of 4 in the third
- argument; it's a matter of taste.)
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- static char *texts[4] = {
- "First", "Second", "Third", "Fourth"
- };
- return snd_ctl_enum_info(uinfo, 1, 4, texts);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Some common info callbacks are available for your convenience:
- <function>snd_ctl_boolean_mono_info()</function> and
- <function>snd_ctl_boolean_stereo_info()</function>.
- Obviously, the former is an info callback for a mono channel
- boolean item, just like <function>snd_myctl_mono_info</function>
- above, and the latter is for a stereo channel boolean item.
- </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 user-space.
- </para>
-
- <para>
- For example,
-
- <example>
- <title>Example of get callback</title>
- <programlisting>
-<![CDATA[
- static int snd_myctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *chip = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = get_some_value(chip);
- return 0;
- }
-]]>
- </programlisting>
- </example>
- </para>
-
- <para>
- The <structfield>value</structfield> field depends on
- the type of control as well as on the 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> field is set as follows:
- <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(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *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 the <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 user-space.
- </para>
-
- <para>
- For example,
-
- <example>
- <title>Example of put callback</title>
- <programlisting>
-<![CDATA[
- static int snd_myctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *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>
- As in the <structfield>get</structfield> callback,
- when the control has more than one elements,
- all elements 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. To create 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[
- err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
- if (err < 0)
- return err;
-]]>
- </programlisting>
- </informalexample>
-
- where <parameter>my_control</parameter> is the
- struct <structname>snd_kcontrol_new</structname> object defined above, and chip
- is the object pointer to be passed to
- kcontrol->private_data
- which can be referred to in callbacks.
- </para>
-
- <para>
- <function>snd_ctl_new1()</function> allocates a new
- <structname>snd_kcontrol</structname> instance,
- 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 struct <structname>snd_ctl_elem_id</structname>
- to be notified.
- You can find some examples in <filename>es1938.c</filename> or
- <filename>es1968.c</filename> for hardware volume interrupts.
- </para>
- </section>
-
- <section id="control-interface-tlv">
- <title>Metadata</title>
- <para>
- To provide information about the dB values of a mixer control, use
- on of the <constant>DECLARE_TLV_xxx</constant> macros from
- <filename><sound/tlv.h></filename> to define a variable
- containing this information, set the<structfield>tlv.p
- </structfield> field to point to this variable, and include the
- <constant>SNDRV_CTL_ELEM_ACCESS_TLV_READ</constant> flag in the
- <structfield>access</structfield> field; like this:
- <informalexample>
- <programlisting>
-<![CDATA[
- static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);
-
- static struct snd_kcontrol_new my_control = {
- ...
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- ...
- .tlv.p = db_scale_my_control,
- };
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The <function>DECLARE_TLV_DB_SCALE</function> macro defines
- information about a mixer control where each step in the control's
- value changes the dB value by a constant dB amount.
- The first parameter is the name of the variable to be defined.
- The second parameter is the minimum value, in units of 0.01 dB.
- The third parameter is the step size, in units of 0.01 dB.
- Set the fourth parameter to 1 if the minimum value actually mutes
- the control.
- </para>
-
- <para>
- The <function>DECLARE_TLV_DB_LINEAR</function> macro defines
- information about a mixer control where the control's value affects
- the output linearly.
- The first parameter is the name of the variable to be defined.
- The second parameter is the minimum value, in units of 0.01 dB.
- The third parameter is the maximum value, in units of 0.01 dB.
- If the minimum value mutes the control, set the second parameter to
- <constant>TLV_DB_GAIN_MUTE</constant>.
- </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 much code 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 mychip {
- ....
- struct snd_ac97 *ac97;
- ....
- };
-
- static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
- {
- struct mychip *chip = ac97->private_data;
- ....
- /* read a register value here from the codec */
- return the_register_value;
- }
-
- static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
- unsigned short reg, unsigned short val)
- {
- struct mychip *chip = ac97->private_data;
- ....
- /* write the given register value to the codec */
- }
-
- static int snd_mychip_ac97(struct mychip *chip)
- {
- struct snd_ac97_bus *bus;
- struct snd_ac97_template ac97;
- int err;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_mychip_ac97_write,
- .read = snd_mychip_ac97_read,
- };
-
- err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
- if (err < 0)
- return err;
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = chip;
- return snd_ac97_mixer(bus, &ac97, &chip->ac97);
- }
-
-]]>
- </programlisting>
- </example>
- </para>
- </section>
-
- <section id="api-ac97-constructor">
- <title>Constructor</title>
- <para>
- To create an ac97 instance, first call <function>snd_ac97_bus</function>
- with an <type>ac97_bus_ops_t</type> record with callback functions.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_ac97_bus *bus;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_mychip_ac97_write,
- .read = snd_mychip_ac97_read,
- };
-
- snd_ac97_bus(card, 0, &ops, NULL, &pbus);
-]]>
- </programlisting>
- </informalexample>
-
- The bus record is shared among all belonging ac97 instances.
- </para>
-
- <para>
- And then call <function>snd_ac97_mixer()</function> with an
- struct <structname>snd_ac97_template</structname>
- record together with the bus pointer created above.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_ac97_template ac97;
- int err;
-
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = chip;
- snd_ac97_mixer(bus, &ac97, &chip->ac97);
-]]>
- </programlisting>
- </informalexample>
-
- where chip->ac97 is a pointer to a newly created
- <type>ac97_t</type> instance.
- In this case, the chip pointer is set as the private data, so that
- the read/write callback functions can refer to this chip instance.
- This instance is not necessarily stored in the chip
- record. If 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(struct snd_ac97 *ac97,
- unsigned short reg)
- {
- struct mychip *chip = ac97->private_data;
- ....
- 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(struct snd_ac97 *ac97,
- unsigned short reg, unsigned short val)
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- These callbacks are non-atomic like the control API callbacks.
- </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 kind of reset, you can
- define this callback.
- </para>
-
- <para>
- The <structfield>wait</structfield> callback is used to
- add some waiting time in the standard initialization of the codec. If the
- chip requires the extra waiting time, define this callback.
- </para>
-
- <para>
- The <structfield>init</structfield> callback is used for
- additional initialization of the codec.
- </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 difference 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 in 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
- given register such as
- <constant>AC97_PCM_FRONT_DAC_RATE</constant>) when VRA or
- DRA 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 to set 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
- <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>
- In some chips, the clock of the codec isn't 48000 but using a
- PCI clock (to save a quartz!). In this case, change the field
- bus->clock to the corresponding
- value. For example, intel8x0
- and es1968 drivers have their own function to read from 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/codec97#0/ac97#0-0</filename> and
- <filename>ac97#0-0+regs</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_mixer()</function> multiple times with
- ac97.num=1 or greater. The <structfield>num</structfield> field
- specifies the codec number.
- </para>
-
- <para>
- If you set up multiple codecs, you either need to 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 a similar but slightly different
- implementation of mpu401 stuff. For example, emu10k1 has its own
- mpu401 routines.
- </para>
- </section>
-
- <section id="midi-interface-constructor">
- <title>Constructor</title>
- <para>
- To create a rawmidi object, call
- <function>snd_mpu401_uart_new()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_rawmidi *rmidi;
- snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, info_flags,
- irq, &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 have 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>
- The 5th argument is a bitflag for additional information.
- When the I/O port address above is 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 a bit flag
- <constant>MPU401_INFO_INTEGRATED</constant>,
- and the mpu401-uart layer will allocate the I/O ports by itself.
- </para>
-
- <para>
- When the controller supports only the input or output MIDI stream,
- pass the <constant>MPU401_INFO_INPUT</constant> or
- <constant>MPU401_INFO_OUTPUT</constant> bitflag, respectively.
- Then the rawmidi instance is created as a single stream.
- </para>
-
- <para>
- <constant>MPU401_INFO_MMIO</constant> bitflag is used to change
- the access method to MMIO (via readb and writeb) instead of
- iob and outb. In this case, you have to pass the iomapped address
- to <function>snd_mpu401_uart_new()</function>.
- </para>
-
- <para>
- When <constant>MPU401_INFO_TX_IRQ</constant> is set, the output
- stream isn't checked in the default interrupt handler. The driver
- needs to call <function>snd_mpu401_uart_interrupt_tx()</function>
- by itself to start processing the output stream in the irq handler.
- </para>
-
- <para>
- If the MPU-401 interface shares its interrupt with the other logical
- devices on the card, set <constant>MPU401_INFO_IRQ_HOOK</constant>
- (see <link linkend="midi-interface-interrupt-handler"><citetitle>
- below</citetitle></link>).
- </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
- struct <structname>snd_mpu401</structname> manually
- afterward. However, <structname>snd_mpu401</structname> pointer is not
- returned explicitly by
- <function>snd_mpu401_uart_new()</function>. You need to cast
- rmidi->private_data to
- <structname>snd_mpu401</structname> explicitly,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_mpu401 *mpu;
- mpu = 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 ISA irq number that will be
- allocated. If no interrupt is to be allocated (because your
- code is already allocating a shared interrupt, or because the
- device does not use interrupts), pass -1 instead.
- For a MPU-401 device without an interrupt, a polling timer
- will be used instead.
- </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>, an exclusive ISA
- interrupt handler is automatically used, hence you don't have
- anything else to do than creating the mpu401 stuff. Otherwise, you
- have to set <constant>MPU401_INFO_IRQ_HOOK</constant>, and call
- <function>snd_mpu401_uart_interrupt()</function> explicitly from your
- own interrupt handler when it has determined that a UART interrupt
- has occurred.
- </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>
-
-
-<!-- ****************************************************** -->
-<!-- RawMIDI Interface -->
-<!-- ****************************************************** -->
- <chapter id="rawmidi-interface">
- <title>RawMIDI Interface</title>
-
- <section id="rawmidi-interface-overview">
- <title>Overview</title>
-
- <para>
- The raw MIDI interface is used for hardware MIDI ports that can
- be accessed as a byte stream. It is not used for synthesizer
- chips that do not directly understand MIDI.
- </para>
-
- <para>
- ALSA handles file and buffer management. All you have to do is
- to write some code to move data between the buffer and the
- hardware.
- </para>
-
- <para>
- The rawmidi API is defined in
- <filename><sound/rawmidi.h></filename>.
- </para>
- </section>
-
- <section id="rawmidi-interface-constructor">
- <title>Constructor</title>
-
- <para>
- To create a rawmidi device, call the
- <function>snd_rawmidi_new</function> function:
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_rawmidi *rmidi;
- err = snd_rawmidi_new(chip->card, "MyMIDI", 0, outs, ins, &rmidi);
- if (err < 0)
- return err;
- rmidi->private_data = chip;
- strcpy(rmidi->name, "My MIDI");
- rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The first argument is the card pointer, the second argument is
- the ID string.
- </para>
-
- <para>
- The third argument is the index of this component. You can
- create up to 8 rawmidi devices.
- </para>
-
- <para>
- The fourth and fifth arguments are the number of output and
- input substreams, respectively, of this device (a substream is
- the equivalent of a MIDI port).
- </para>
-
- <para>
- Set the <structfield>info_flags</structfield> field to specify
- the capabilities of the device.
- Set <constant>SNDRV_RAWMIDI_INFO_OUTPUT</constant> if there is
- at least one output port,
- <constant>SNDRV_RAWMIDI_INFO_INPUT</constant> if there is at
- least one input port,
- and <constant>SNDRV_RAWMIDI_INFO_DUPLEX</constant> if the device
- can handle output and input at the same time.
- </para>
-
- <para>
- After the rawmidi device is created, you need to set the
- operators (callbacks) for each substream. There are helper
- functions to set the operators for all the substreams of a device:
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mymidi_output_ops);
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mymidi_input_ops);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The operators are usually defined like this:
- <informalexample>
- <programlisting>
-<![CDATA[
- static struct snd_rawmidi_ops snd_mymidi_output_ops = {
- .open = snd_mymidi_output_open,
- .close = snd_mymidi_output_close,
- .trigger = snd_mymidi_output_trigger,
- };
-]]>
- </programlisting>
- </informalexample>
- These callbacks are explained in the <link
- linkend="rawmidi-interface-callbacks"><citetitle>Callbacks</citetitle></link>
- section.
- </para>
-
- <para>
- If there are more than one substream, you should give a
- unique name to each of them:
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_rawmidi_substream *substream;
- list_for_each_entry(substream,
- &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
- list {
- sprintf(substream->name, "My MIDI Port %d", substream->number + 1);
- }
- /* same for SNDRV_RAWMIDI_STREAM_INPUT */
-]]>
- </programlisting>
- </informalexample>
- </para>
- </section>
-
- <section id="rawmidi-interface-callbacks">
- <title>Callbacks</title>
-
- <para>
- In all the callbacks, the private data that you've set for the
- rawmidi device can be accessed as
- substream->rmidi->private_data.
- <!-- <code> isn't available before DocBook 4.3 -->
- </para>
-
- <para>
- If there is more than one port, your callbacks can determine the
- port index from the struct snd_rawmidi_substream data passed to each
- callback:
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_rawmidi_substream *substream;
- int index = substream->number;
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <section id="rawmidi-interface-op-open">
- <title><function>open</function> callback</title>
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_open(struct snd_rawmidi_substream *substream);
-]]>
- </programlisting>
- </informalexample>
-
- <para>
- This is called when a substream is opened.
- You can initialize the hardware here, but you shouldn't
- start transmitting/receiving data yet.
- </para>
- </section>
-
- <section id="rawmidi-interface-op-close">
- <title><function>close</function> callback</title>
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_xxx_close(struct snd_rawmidi_substream *substream);
-]]>
- </programlisting>
- </informalexample>
-
- <para>
- Guess what.
- </para>
-
- <para>
- The <function>open</function> and <function>close</function>
- callbacks of a rawmidi device are serialized with a mutex,
- and can sleep.
- </para>
- </section>
-
- <section id="rawmidi-interface-op-trigger-out">
- <title><function>trigger</function> callback for output
- substreams</title>
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static void snd_xxx_output_trigger(struct snd_rawmidi_substream *substream, int up);
-]]>
- </programlisting>
- </informalexample>
-
- <para>
- This is called with a nonzero <parameter>up</parameter>
- parameter when there is some data in the substream buffer that
- must be transmitted.
- </para>
-
- <para>
- To read data from the buffer, call
- <function>snd_rawmidi_transmit_peek</function>. It will
- return the number of bytes that have been read; this will be
- less than the number of bytes requested when there are no more
- data in the buffer.
- After the data have been transmitted successfully, call
- <function>snd_rawmidi_transmit_ack</function> to remove the
- data from the substream buffer:
- <informalexample>
- <programlisting>
-<![CDATA[
- unsigned char data;
- while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
- if (snd_mychip_try_to_transmit(data))
- snd_rawmidi_transmit_ack(substream, 1);
- else
- break; /* hardware FIFO full */
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- If you know beforehand that the hardware will accept data, you
- can use the <function>snd_rawmidi_transmit</function> function
- which reads some data and removes them from the buffer at once:
- <informalexample>
- <programlisting>
-<![CDATA[
- while (snd_mychip_transmit_possible()) {
- unsigned char data;
- if (snd_rawmidi_transmit(substream, &data, 1) != 1)
- break; /* no more data */
- snd_mychip_transmit(data);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- If you know beforehand how many bytes you can accept, you can
- use a buffer size greater than one with the
- <function>snd_rawmidi_transmit*</function> functions.
- </para>
-
- <para>
- The <function>trigger</function> callback must not sleep. If
- the hardware FIFO is full before the substream buffer has been
- emptied, you have to continue transmitting data later, either
- in an interrupt handler, or with a timer if the hardware
- doesn't have a MIDI transmit interrupt.
- </para>
-
- <para>
- The <function>trigger</function> callback is called with a
- zero <parameter>up</parameter> parameter when the transmission
- of data should be aborted.
- </para>
- </section>
-
- <section id="rawmidi-interface-op-trigger-in">
- <title><function>trigger</function> callback for input
- substreams</title>
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static void snd_xxx_input_trigger(struct snd_rawmidi_substream *substream, int up);
-]]>
- </programlisting>
- </informalexample>
-
- <para>
- This is called with a nonzero <parameter>up</parameter>
- parameter to enable receiving data, or with a zero
- <parameter>up</parameter> parameter do disable receiving data.
- </para>
-
- <para>
- The <function>trigger</function> callback must not sleep; the
- actual reading of data from the device is usually done in an
- interrupt handler.
- </para>
-
- <para>
- When data reception is enabled, your interrupt handler should
- call <function>snd_rawmidi_receive</function> for all received
- data:
- <informalexample>
- <programlisting>
-<![CDATA[
- void snd_mychip_midi_interrupt(...)
- {
- while (mychip_midi_available()) {
- unsigned char data;
- data = mychip_midi_read();
- snd_rawmidi_receive(substream, &data, 1);
- }
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
- </section>
-
- <section id="rawmidi-interface-op-drain">
- <title><function>drain</function> callback</title>
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static void snd_xxx_drain(struct snd_rawmidi_substream *substream);
-]]>
- </programlisting>
- </informalexample>
-
- <para>
- This is only used with output substreams. This function should wait
- until all data read from the substream buffer have been transmitted.
- This ensures that the device can be closed and the driver unloaded
- without losing data.
- </para>
-
- <para>
- This callback is optional. If you do not set
- <structfield>drain</structfield> in the struct snd_rawmidi_ops
- structure, ALSA will simply wait for 50 milliseconds
- instead.
- </para>
- </section>
- </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 in 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 the direct-FM API,
- defined in <filename><sound/asound_fm.h></filename>. In
- ALSA native mode, FM registers are accessed through
- the Hardware-Dependent Device direct-FM extension API, whereas in
- OSS compatible mode, FM registers can be accessed with the OSS
- direct-FM compatible API in <filename>/dev/dmfmX</filename> device.
- </para>
-
- <para>
- To create the OPL3 component, you have two functions to
- call. The first one is a constructor for the <type>opl3_t</type>
- instance.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_opl3 *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, the opl3 module will
- allocate the specified ports by itself.
- </para>
-
- <para>
- When the accessing the hardware requires special method
- instead of the standard I/O access, you can create opl3 instance
- separately with <function>snd_opl3_new()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_opl3 *opl3;
- snd_opl3_new(card, OPL3_HW_OPL3_XXX, &opl3);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Then set <structfield>command</structfield>,
- <structfield>private_data</structfield> and
- <structfield>private_free</structfield> for the private
- access function, the private data and the destructor.
- The l_port and r_port are not necessarily set. Only the
- command must be set properly. You can retrieve the data
- from the opl3->private_data field.
- </para>
-
- <para>
- After creating the opl3 instance via <function>snd_opl3_new()</function>,
- call <function>snd_opl3_init()</function> to initialize the chip to the
- proper state. Note that <function>snd_opl3_create()</function> always
- calls it internally.
- </para>
-
- <para>
- If the opl3 instance is created successfully, then create a
- hwdep device for this opl3.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_hwdep *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 user-space access 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>
- The creation of the <type>hwdep</type> instance is done via
- <function>snd_hwdep_new()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_hwdep *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>.
- If you assign a private data, you should define the
- destructor, too. The destructor function is set in
- the <structfield>private_free</structfield> field.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct mydata *p = kmalloc(sizeof(*p), GFP_KERNEL);
- hw->private_data = p;
- hw->private_free = mydata_free;
-]]>
- </programlisting>
- </informalexample>
-
- and the implementation of the destructor would be:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static void mydata_free(struct snd_hwdep *hw)
- {
- struct mydata *p = hw->private_data;
- kfree(p);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The arbitrary file operations can be defined for this
- instance. The file operators are defined in
- the <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
- the 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 the <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
- the <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
- enough space is found.
- </para>
-
- <para>
- The release the pages, 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
- pcm instance construction time (in the case of PCI bus).
-
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(pci), size, max);
-]]>
- </programlisting>
- </informalexample>
-
- where <parameter>size</parameter> is the byte size to be
- pre-allocated and the <parameter>max</parameter> is the maximum
- size to be changed via the <filename>prealloc</filename> proc file.
- The allocator will try to get an area as large as possible
- within the given size.
- </para>
-
- <para>
- The second argument (type) and the third argument (device pointer)
- are dependent on the bus.
- In the case of the ISA bus, pass <function>snd_dma_isa_data()</function>
- as the third argument with <constant>SNDRV_DMA_TYPE_DEV</constant> type.
- For the continuous buffer unrelated to the bus can be pre-allocated
- with <constant>SNDRV_DMA_TYPE_CONTINUOUS</constant> type and the
- <function>snd_dma_continuous_data(GFP_KERNEL)</function> device pointer,
- where <constant>GFP_KERNEL</constant> is the kernel allocation flag to
- use.
- For the PCI scatter-gather buffers, use
- <constant>SNDRV_DMA_TYPE_DEV_SG</constant> with
- <function>snd_dma_pci_data(pci)</function>
- (see the
- <link linkend="buffer-and-memory-non-contiguous"><citetitle>Non-Contiguous Buffers
- </citetitle></link> section).
- </para>
-
- <para>
- Once 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.
- </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 large
- enough. 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 for mmap on the buffer, although you have
- to handle an interrupt or a tasklet to transfer the data
- from the intermediate buffer to the hardware buffer. You can find an
- example in the vxpocket driver.
- </para>
-
- <para>
- Another case is when 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 the Intel one.
- In non-mmap mode, the data cannot be transferred as in the normal
- way. Thus you need to define the <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 the <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(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count);
- static int capture_copy(struct snd_pcm_substream *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
- playback case, you 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 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 the position and the amount of data 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(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The meanings of arguments are the same as in 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 as in emu10k1 or the
- buffer descriptors as in 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.h></filename>.
- </para>
-
- <para>
- For creating the SG-buffer handler, call
- <function>snd_pcm_lib_preallocate_pages()</function> or
- <function>snd_pcm_lib_preallocate_pages_for_all()</function>
- with <constant>SNDRV_DMA_TYPE_DEV_SG</constant>
- in the PCM constructor like other PCI pre-allocator.
- You need to pass <function>snd_dma_pci_data(pci)</function>,
- where pci is the struct <structname>pci_dev</structname> pointer
- of the chip as well.
- The <type>struct snd_sg_buf</type> instance is created as
- substream->dma_private. You can cast
- the pointer like:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_sg_buf *sgbuf = (struct snd_sg_buf *)substream->dma_private;
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- Then call <function>snd_pcm_lib_malloc_pages()</function>
- in the <structfield>hw_params</structfield> callback
- as well as in the case of normal PCI buffer.
- The SG-buffer handler will allocate the non-contiguous kernel
- pages of the given size and map them onto the virtually contiguous
- memory. The virtual pointer is addressed in runtime->dma_area.
- The physical address (runtime->dma_addr) is set to zero,
- because the buffer is physically non-contiguous.
- The physical address table is set up in sgbuf->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_page</function> as
- the <structfield>page</structfield> callback.
- (See <link linkend="pcm-interface-operators-page-callback">
- <citetitle>page callback section</citetitle></link>.)
- </para>
-
- <para>
- To release the data, call
- <function>snd_pcm_lib_free_pages()</function> in the
- <structfield>hw_free</structfield> callback as usual.
- </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>
-
- /* get the physical page pointer on the given offset */
- static struct page *mychip_page(struct snd_pcm_substream *substream,
- unsigned long offset)
- {
- void *pageptr = substream->runtime->dma_area + offset;
- return vmalloc_to_page(pageptr);
- }
-]]>
- </programlisting>
- </informalexample>
- </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>
- To create a proc file, call
- <function>snd_card_proc_new()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- struct snd_info_entry *entry;
- int err = snd_card_proc_new(card, "my-file", &entry);
-]]>
- </programlisting>
- </informalexample>
-
- where the second argument specifies the name of the proc file 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 in the pointer given in the third argument.
- It is initialized as a text proc file for read only. To use
- 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. The third parameter
- specifies the read buffer size and the fourth
- (<parameter>my_proc_read</parameter>) is the callback function, which
- is defined like
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static void my_proc_read(struct snd_info_entry *entry,
- struct snd_info_buffer *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(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
- {
- struct my_chip *chip = entry->private_data;
-
- snd_iprintf(buffer, "This is my chip!\n");
- snd_iprintf(buffer, "Port = %ld\n", chip->port);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The file permissions can be changed afterwards. As default, it's
- set as read only for all users. If you want to add write
- permission for the user (root as default), do as follows:
-
- <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 = my_proc_write;
-]]>
- </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 as follows:
-
- <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>
-
- For the raw data, <structfield>size</structfield> field must be
- set properly. This specifies the maximum size of the proc file access.
- </para>
-
- <para>
- The read/write callbacks of raw mode are more direct than the text mode.
- You need to use a low-level I/O functions such as
- <function>copy_from/to_user()</function> to transfer the
- data.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static ssize_t my_file_io_read(struct snd_info_entry *entry,
- void *file_private_data,
- struct file *file,
- char *buf,
- size_t count,
- loff_t pos)
- {
- if (copy_to_user(buf, local_data + pos, count))
- return -EFAULT;
- return count;
- }
-]]>
- </programlisting>
- </informalexample>
-
- If the size of the info entry has been set up properly,
- <structfield>count</structfield> and <structfield>pos</structfield> are
- guaranteed to fit within 0 and the given size.
- You don't have to check the range in the callbacks unless any
- other condition is required.
-
- </para>
-
- </chapter>
-
-
-<!-- ****************************************************** -->
-<!-- Power Management -->
-<!-- ****************************************************** -->
- <chapter id="power-management">
- <title>Power Management</title>
- <para>
- If the chip is supposed to work with suspend/resume
- functions, you need to add power-management code to the
- driver. The additional code for power-management should be
- <function>ifdef</function>'ed with
- <constant>CONFIG_PM</constant>.
- </para>
-
- <para>
- If the driver <emphasis>fully</emphasis> supports suspend/resume
- that is, the device can be
- properly resumed to its state when suspend was called,
- you can set the <constant>SNDRV_PCM_INFO_RESUME</constant> flag
- in the pcm info field. Usually, this is possible when the
- registers of the chip can be safely saved and restored to
- RAM. If this is set, the trigger callback is called with
- <constant>SNDRV_PCM_TRIGGER_RESUME</constant> after the resume
- callback completes.
- </para>
-
- <para>
- Even if the driver doesn't support PM fully but
- partial suspend/resume is still possible, it's still worthy to
- implement suspend/resume callbacks. In such a case, applications
- would reset the status by calling
- <function>snd_pcm_prepare()</function> and restart the stream
- appropriately. Hence, you can define suspend/resume callbacks
- below but don't set <constant>SNDRV_PCM_INFO_RESUME</constant>
- info flag to the PCM.
- </para>
-
- <para>
- Note that the trigger with SUSPEND can always be called when
- <function>snd_pcm_suspend_all</function> is called,
- regardless of the <constant>SNDRV_PCM_INFO_RESUME</constant> flag.
- The <constant>RESUME</constant> flag affects only the behavior
- of <function>snd_pcm_resume()</function>.
- (Thus, in theory,
- <constant>SNDRV_PCM_TRIGGER_RESUME</constant> isn't needed
- to be handled in the trigger callback when no
- <constant>SNDRV_PCM_INFO_RESUME</constant> flag is set. But,
- it's better to keep it for compatibility reasons.)
- </para>
- <para>
- In the earlier version of ALSA drivers, a common
- power-management layer was provided, but it has been removed.
- The driver needs to define the suspend/resume hooks according to
- the bus the device is connected to. In the case of PCI drivers, the
- callbacks look like below:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- #ifdef CONFIG_PM
- static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
- {
- .... /* do things for suspend */
- return 0;
- }
- static int snd_my_resume(struct pci_dev *pci)
- {
- .... /* do things for suspend */
- return 0;
- }
- #endif
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The scheme of the real suspend job is as follows.
-
- <orderedlist>
- <listitem><para>Retrieve the card and the chip data.</para></listitem>
- <listitem><para>Call <function>snd_power_change_state()</function> with
- <constant>SNDRV_CTL_POWER_D3hot</constant> to change the
- power status.</para></listitem>
- <listitem><para>Call <function>snd_pcm_suspend_all()</function> to suspend the running PCM streams.</para></listitem>
- <listitem><para>If AC97 codecs are used, call
- <function>snd_ac97_suspend()</function> for each codec.</para></listitem>
- <listitem><para>Save the register values if necessary.</para></listitem>
- <listitem><para>Stop the hardware if necessary.</para></listitem>
- <listitem><para>Disable the PCI device by calling
- <function>pci_disable_device()</function>. Then, call
- <function>pci_save_state()</function> at last.</para></listitem>
- </orderedlist>
- </para>
-
- <para>
- A typical code would be like:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int mychip_suspend(struct pci_dev *pci, pm_message_t state)
- {
- /* (1) */
- struct snd_card *card = pci_get_drvdata(pci);
- struct mychip *chip = card->private_data;
- /* (2) */
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- /* (3) */
- snd_pcm_suspend_all(chip->pcm);
- /* (4) */
- snd_ac97_suspend(chip->ac97);
- /* (5) */
- snd_mychip_save_registers(chip);
- /* (6) */
- snd_mychip_stop_hardware(chip);
- /* (7) */
- pci_disable_device(pci);
- pci_save_state(pci);
- return 0;
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- The scheme of the real resume job is as follows.
-
- <orderedlist>
- <listitem><para>Retrieve the card and the chip data.</para></listitem>
- <listitem><para>Set up PCI. First, call <function>pci_restore_state()</function>.
- Then enable the pci device again by calling <function>pci_enable_device()</function>.
- Call <function>pci_set_master()</function> if necessary, too.</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>Call <function>snd_power_change_state()</function> with
- <constant>SNDRV_CTL_POWER_D0</constant> to notify the processes.</para></listitem>
- </orderedlist>
- </para>
-
- <para>
- A typical code would be like:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int mychip_resume(struct pci_dev *pci)
- {
- /* (1) */
- struct snd_card *card = pci_get_drvdata(pci);
- struct mychip *chip = card->private_data;
- /* (2) */
- pci_restore_state(pci);
- pci_enable_device(pci);
- pci_set_master(pci);
- /* (3) */
- snd_mychip_reinit_chip(chip);
- /* (4) */
- snd_mychip_restore_registers(chip);
- /* (5) */
- snd_ac97_resume(chip->ac97);
- /* (6) */
- snd_mychip_restart_chip(chip);
- /* (7) */
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- As shown in the above, it's better to save registers after
- suspending the PCM operations via
- <function>snd_pcm_suspend_all()</function> or
- <function>snd_pcm_suspend()</function>. It means that the PCM
- streams are already stopped when the register snapshot is
- taken. But, remember that you don't have to restart the PCM
- stream in the resume callback. It'll be restarted via
- trigger call with <constant>SNDRV_PCM_TRIGGER_RESUME</constant>
- when necessary.
- </para>
-
- <para>
- OK, we have all callbacks now. Let's set them up. In the
- initialization of the card, make sure that you can get the chip
- data from the card instance, typically via
- <structfield>private_data</structfield> field, in case you
- created the chip data individually.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
- {
- ....
- struct snd_card *card;
- struct mychip *chip;
- int err;
- ....
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
- ....
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- ....
- card->private_data = chip;
- ....
- }
-]]>
- </programlisting>
- </informalexample>
-
- When you created the chip data with
- <function>snd_card_new()</function>, it's anyway accessible
- via <structfield>private_data</structfield> field.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
- {
- ....
- struct snd_card *card;
- struct mychip *chip;
- int err;
- ....
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- sizeof(struct mychip), &card);
- ....
- chip = card->private_data;
- ....
- }
-]]>
- </programlisting>
- </informalexample>
-
- </para>
-
- <para>
- If you need a space to save the registers, allocate the
- buffer for it here, too, since it would be fatal
- if you cannot allocate a memory in the suspend phase.
- The allocated buffer should be released in the corresponding
- destructor.
- </para>
-
- <para>
- And next, set suspend/resume callbacks to the pci_driver.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static struct pci_driver driver = {
- .name = KBUILD_MODNAME,
- .id_table = snd_my_ids,
- .probe = snd_my_probe,
- .remove = snd_my_remove,
- #ifdef CONFIG_PM
- .suspend = snd_my_suspend,
- .resume = snd_my_resume,
- #endif
- };
-]]>
- </programlisting>
- </informalexample>
- </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 the <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 easier 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 would be better to have a
- dummy option for compatibility.
- </para>
-
- <para>
- The module parameters must be declared with the standard
- <function>module_param()()</function>,
- <function>module_param_array()()</function> and
- <function>MODULE_PARM_DESC()</function> macros.
- </para>
-
- <para>
- The typical coding would be like below:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- #define CARD_NAME "My Chip"
-
- module_param_array(index, int, NULL, 0444);
- MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
- module_param_array(id, charp, NULL, 0444);
- MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
- module_param_array(enable, bool, NULL, 0444);
- MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
-]]>
- </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_LICENSE("GPL");
- MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}");
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- </chapter>
-
-
-<!-- ****************************************************** -->
-<!-- How To Put Your Driver -->
-<!-- ****************************************************** -->
- <chapter id="how-to-put-your-driver">
- <title>How To Put Your Driver Into ALSA Tree</title>
- <section>
- <title>General</title>
- <para>
- So far, you've learned how to write the driver codes.
- And you might have a question now: how to put my own
- driver into the ALSA driver tree?
- Here (finally :) the standard procedure is described briefly.
- </para>
-
- <para>
- Suppose that you create a new PCI driver for the card
- <quote>xyz</quote>. The card module name would be
- snd-xyz. The new driver is usually put into the alsa-driver
- tree, <filename>alsa-driver/pci</filename> directory in
- the case of PCI cards.
- Then the driver is evaluated, audited and tested
- by developers and users. After a certain time, the driver
- will go to the alsa-kernel tree (to the corresponding directory,
- such as <filename>alsa-kernel/pci</filename>) and eventually
- will be integrated into the Linux 2.6 tree (the directory would be
- <filename>linux/sound/pci</filename>).
- </para>
-
- <para>
- In the following sections, the driver code is supposed
- to be put into alsa-driver tree. The two cases are covered:
- a driver consisting of a single source file and one consisting
- of several source files.
- </para>
- </section>
-
- <section>
- <title>Driver with A Single Source File</title>
- <para>
- <orderedlist>
- <listitem>
- <para>
- Modify alsa-driver/pci/Makefile
- </para>
-
- <para>
- Suppose you have a file xyz.c. Add the following
- two lines
- <informalexample>
- <programlisting>
-<![CDATA[
- snd-xyz-objs := xyz.o
- obj-$(CONFIG_SND_XYZ) += snd-xyz.o
-]]>
- </programlisting>
- </informalexample>
- </para>
- </listitem>
-
- <listitem>
- <para>
- Create the Kconfig entry
- </para>
-
- <para>
- Add the new entry of Kconfig for your xyz driver.
- <informalexample>
- <programlisting>
-<![CDATA[
- config SND_XYZ
- tristate "Foobar XYZ"
- depends on SND
- select SND_PCM
- help
- Say Y here to include support for Foobar XYZ soundcard.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-xyz.
-]]>
- </programlisting>
- </informalexample>
-
- the line, select SND_PCM, specifies that the driver xyz supports
- PCM. In addition to SND_PCM, the following components are
- supported for select command:
- SND_RAWMIDI, SND_TIMER, SND_HWDEP, SND_MPU401_UART,
- SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB, SND_AC97_CODEC.
- Add the select command for each supported component.
- </para>
-
- <para>
- Note that some selections imply the lowlevel selections.
- For example, PCM includes TIMER, MPU401_UART includes RAWMIDI,
- AC97_CODEC includes PCM, and OPL3_LIB includes HWDEP.
- You don't need to give the lowlevel selections again.
- </para>
-
- <para>
- For the details of Kconfig script, refer to the kbuild
- documentation.
- </para>
-
- </listitem>
-
- <listitem>
- <para>
- Run cvscompile script to re-generate the configure script and
- build the whole stuff again.
- </para>
- </listitem>
- </orderedlist>
- </para>
- </section>
-
- <section>
- <title>Drivers with Several Source Files</title>
- <para>
- Suppose that the driver snd-xyz have several source files.
- They are located in the new subdirectory,
- pci/xyz.
-
- <orderedlist>
- <listitem>
- <para>
- Add a new directory (<filename>xyz</filename>) in
- <filename>alsa-driver/pci/Makefile</filename> as below
-
- <informalexample>
- <programlisting>
-<![CDATA[
- obj-$(CONFIG_SND) += xyz/
-]]>
- </programlisting>
- </informalexample>
- </para>
- </listitem>
-
- <listitem>
- <para>
- Under the directory <filename>xyz</filename>, create a Makefile
-
- <example>
- <title>Sample Makefile for a driver xyz</title>
- <programlisting>
-<![CDATA[
- ifndef SND_TOPDIR
- SND_TOPDIR=../..
- endif
-
- include $(SND_TOPDIR)/toplevel.config
- include $(SND_TOPDIR)/Makefile.conf
-
- snd-xyz-objs := xyz.o abc.o def.o
-
- obj-$(CONFIG_SND_XYZ) += snd-xyz.o
-
- include $(SND_TOPDIR)/Rules.make
-]]>
- </programlisting>
- </example>
- </para>
- </listitem>
-
- <listitem>
- <para>
- Create the Kconfig entry
- </para>
-
- <para>
- This procedure is as same as in the last section.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Run cvscompile script to re-generate the configure script and
- build the whole stuff again.
- </para>
- </listitem>
- </orderedlist>
- </para>
- </section>
-
- </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 the
- <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_VERBOSE</constant> is set. Please note
- that <constant>CONFIG_SND_DEBUG_VERBOSE</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-bug">
- <title><function>snd_BUG()</function></title>
- <para>
- It shows the <computeroutput>BUG?</computeroutput> message and
- stack trace as well as <function>snd_BUG_ON</function> at the point.
- It's useful to show that a fatal error happens there.
- </para>
- <para>
- When no debug flag is set, this macro is ignored.
- </para>
- </section>
-
- <section id="useful-functions-snd-bug-on">
- <title><function>snd_BUG_ON()</function></title>
- <para>
- <function>snd_BUG_ON()</function> macro is similar with
- <function>WARN_ON()</function> macro. For example,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_BUG_ON(!pointer);
-]]>
- </programlisting>
- </informalexample>
-
- or it can be used as the condition,
- <informalexample>
- <programlisting>
-<![CDATA[
- if (snd_BUG_ON(non_zero_is_bug))
- return -EINVAL;
-]]>
- </programlisting>
- </informalexample>
-
- </para>
-
- <para>
- The macro takes an conditional expression to evaluate.
- When <constant>CONFIG_SND_DEBUG</constant>, is set, if the
- expression is non-zero, it shows the warning message such as
- <computeroutput>BUG? (xxx)</computeroutput>
- normally followed by stack trace.
-
- In both cases it returns the evaluated value.
- </para>
-
- </section>
-
- </chapter>
-
-
-<!-- ****************************************************** -->
-<!-- Acknowledgments -->
-<!-- ****************************************************** -->
- <chapter id="acknowledgments">
- <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>
- <para>
- Giuliano Pochini corrected typos and contributed the example codes
- in the hardware constraints section.
- </para>
- </chapter>
-</book>
and
- phy-handle: See ethernet.txt file in the same directory.
+ - phy-mode: See ethernet.txt file in the same directory.
or
media/index
gpu/index
80211/index
+ sound/index
Indices and tables
==================
Linus, and net-next is where the new code goes for the future release.
You can find the trees here:
- http://git.kernel.org/?p=linux/kernel/git/davem/net.git
- http://git.kernel.org/?p=linux/kernel/git/davem/net-next.git
+ https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
+ https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
Q: How often do changes from these trees make it to the mainline Linus tree?
A: Load the mainline (Linus) page here:
- http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git
+ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
and note the top of the "tags" section. If it is rc1, it is early
in the dev cycle. If it was tagged rc7 a week ago, then a release
It contains the patches which Dave has selected, but not yet handed
off to Greg. If Greg already has the patch, then it will be here:
- http://git.kernel.org/cgit/linux/kernel/git/stable/stable-queue.git
+ https://git.kernel.org/pub/scm/linux/kernel/git/stable/stable-queue.git
A quick way to find whether the patch is in this stable-queue is
to simply clone the repo, and then git grep the mainline commit ID, e.g.
If this option is enabled, the connection tracking code will
provide userspace with connection tracking events via ctnetlink.
-nf_conntrack_events_retry_timeout - INTEGER (seconds)
- default 15
-
- This option is only relevant when "reliable connection tracking
- events" are used. Normally, ctnetlink is "lossy", that is,
- events are normally dropped when userspace listeners can't keep up.
-
- Userspace can request "reliable event mode". When this mode is
- active, the conntrack will only be destroyed after the event was
- delivered. If event delivery fails, the kernel periodically
- re-tries to send the event to userspace.
-
- This is the maximum interval the kernel should use when re-trying
- to deliver the destroy event.
-
- A higher number means there will be fewer delivery retries and it
- will take longer for a backlog to be processed.
-
nf_conntrack_expect_max - INTEGER
Maximum size of expectation table. Default value is
nf_conntrack_buckets / 256. Minimum is 1.
--- /dev/null
+==============================================================
+Advanced Linux Sound Architecture - Driver Configuration guide
+==============================================================
+
+
+Kernel Configuration
+====================
+
+To enable ALSA support you need at least to build the kernel with
+primary sound card support (``CONFIG_SOUND``). Since ALSA can emulate
+OSS, you don't have to choose any of the OSS modules.
+
+Enable "OSS API emulation" (``CONFIG_SND_OSSEMUL``) and both OSS mixer
+and PCM supports if you want to run OSS applications with ALSA.
+
+If you want to support the WaveTable functionality on cards such as
+SB Live! then you need to enable "Sequencer support"
+(``CONFIG_SND_SEQUENCER``).
+
+To make ALSA debug messages more verbose, enable the "Verbose printk"
+and "Debug" options. To check for memory leaks, turn on "Debug memory"
+too. "Debug detection" will add checks for the detection of cards.
+
+Please note that all the ALSA ISA drivers support the Linux isapnp API
+(if the card supports ISA PnP). You don't need to configure the cards
+using isapnptools.
+
+
+Module parameters
+=================
+
+The user can load modules with options. If the module supports more than
+one card and you have more than one card of the same type then you can
+specify multiple values for the option separated by commas.
+
+
+Module snd
+----------
+
+The core ALSA module. It is used by all ALSA card drivers.
+It takes the following options which have global effects.
+
+major
+ major number for sound driver;
+ Default: 116
+cards_limit
+ limiting card index for auto-loading (1-8);
+ Default: 1;
+ For auto-loading more than one card, specify this option
+ together with snd-card-X aliases.
+slots
+ Reserve the slot index for the given driver;
+ This option takes multiple strings.
+ See `Module Autoloading Support`_ section for details.
+debug
+ Specifies the debug message level;
+ (0 = disable debug prints, 1 = normal debug messages,
+ 2 = verbose debug messages);
+ This option appears only when ``CONFIG_SND_DEBUG=y``.
+ This option can be dynamically changed via sysfs
+ /sys/modules/snd/parameters/debug file.
+
+Module snd-pcm-oss
+------------------
+
+The PCM OSS emulation module.
+This module takes options which change the mapping of devices.
+
+dsp_map
+ PCM device number maps assigned to the 1st OSS device;
+ Default: 0
+adsp_map
+ PCM device number maps assigned to the 2st OSS device;
+ Default: 1
+nonblock_open
+ Don't block opening busy PCM devices;
+ Default: 1
+
+For example, when ``dsp_map=2``, /dev/dsp will be mapped to PCM #2 of
+the card #0. Similarly, when ``adsp_map=0``, /dev/adsp will be mapped
+to PCM #0 of the card #0.
+For changing the second or later card, specify the option with
+commas, such like ``dsp_map=0,1``.
+
+``nonblock_open`` option is used to change the behavior of the PCM
+regarding opening the device. When this option is non-zero,
+opening a busy OSS PCM device won't be blocked but return
+immediately with EAGAIN (just like O_NONBLOCK flag).
+
+Module snd-rawmidi
+------------------
+
+This module takes options which change the mapping of devices.
+similar to those of the snd-pcm-oss module.
+
+midi_map
+ MIDI device number maps assigned to the 1st OSS device;
+ Default: 0
+amidi_map
+ MIDI device number maps assigned to the 2st OSS device;
+ Default: 1
+
+Common parameters for top sound card modules
+--------------------------------------------
+
+Each of top level sound card module takes the following options.
+
+index
+ index (slot #) of sound card;
+ Values: 0 through 31 or negative;
+ If nonnegative, assign that index number;
+ if negative, interpret as a bitmask of permissible indices;
+ the first free permitted index is assigned;
+ Default: -1
+id
+ card ID (identifier or name);
+ Can be up to 15 characters long;
+ Default: the card type;
+ A directory by this name is created under /proc/asound/
+ containing information about the card;
+ This ID can be used instead of the index number in
+ identifying the card
+enable
+ enable card;
+ Default: enabled, for PCI and ISA PnP cards
+
+Module snd-adlib
+----------------
+
+Module for AdLib FM cards.
+
+port
+ port # for OPL chip
+
+This module supports multiple cards. It does not support autoprobe, so
+the port must be specified. For actual AdLib FM cards it will be 0x388.
+Note that this card does not have PCM support and no mixer; only FM
+synthesis.
+
+Make sure you have ``sbiload`` from the alsa-tools package available and,
+after loading the module, find out the assigned ALSA sequencer port
+number through ``sbiload -l``.
+
+Example output:
+::
+
+ Port Client name Port name
+ 64:0 OPL2 FM synth OPL2 FM Port
+
+Load the ``std.sb`` and ``drums.sb`` patches also supplied by ``sbiload``:
+::
+
+ sbiload -p 64:0 std.sb drums.sb
+
+If you use this driver to drive an OPL3, you can use ``std.o3`` and ``drums.o3``
+instead. To have the card produce sound, use ``aplaymidi`` from alsa-utils:
+::
+
+ aplaymidi -p 64:0 foo.mid
+
+Module snd-ad1816a
+------------------
+
+Module for sound cards based on Analog Devices AD1816A/AD1815 ISA chips.
+
+clockfreq
+ Clock frequency for AD1816A chip (default = 0, 33000Hz)
+
+This module supports multiple cards, autoprobe and PnP.
+
+Module snd-ad1848
+-----------------
+
+Module for sound cards based on AD1848/AD1847/CS4248 ISA chips.
+
+port
+ port # for AD1848 chip
+irq
+ IRQ # for AD1848 chip
+dma1
+ DMA # for AD1848 chip (0,1,3)
+
+This module supports multiple cards. It does not support autoprobe
+thus main port must be specified!!! Other ports are optional.
+
+The power-management is supported.
+
+Module snd-ad1889
+-----------------
+
+Module for Analog Devices AD1889 chips.
+
+ac97_quirk
+ AC'97 workaround for strange hardware;
+ See the description of intel8x0 module for details.
+
+This module supports multiple cards.
+
+Module snd-ali5451
+------------------
+
+Module for ALi M5451 PCI chip.
+
+pcm_channels
+ Number of hardware channels assigned for PCM
+spdif
+ Support SPDIF I/O;
+ Default: disabled
+
+This module supports one chip and autoprobe.
+
+The power-management is supported.
+
+Module snd-als100
+-----------------
+
+Module for sound cards based on Avance Logic ALS100/ALS120 ISA chips.
+
+This module supports multiple cards, autoprobe and PnP.
+
+The power-management is supported.
+
+Module snd-als300
+-----------------
+
+Module for Avance Logic ALS300 and ALS300+
+
+This module supports multiple cards.
+
+The power-management is supported.
+
+Module snd-als4000
+------------------
+
+Module for sound cards based on Avance Logic ALS4000 PCI chip.
+
+joystick_port
+ port # for legacy joystick support;
+ 0 = disabled (default), 1 = auto-detect
+
+This module supports multiple cards, autoprobe and PnP.
+
+The power-management is supported.
+
+Module snd-asihpi
+-----------------
+
+Module for AudioScience ASI soundcards
+
+enable_hpi_hwdep
+ enable HPI hwdep for AudioScience soundcard
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-atiixp
+-----------------
+
+Module for ATI IXP 150/200/250/400 AC97 controllers.
+
+ac97_clock
+ AC'97 clock (default = 48000)
+ac97_quirk
+ AC'97 workaround for strange hardware;
+ See `AC97 Quirk Option`_ section below.
+ac97_codec
+ Workaround to specify which AC'97 codec instead of probing.
+ If this works for you file a bug with your `lspci -vn` output.
+ (-2 = Force probing, -1 = Default behavior, 0-2 = Use the
+ specified codec.)
+spdif_aclink
+ S/PDIF transfer over AC-link (default = 1)
+
+This module supports one card and autoprobe.
+
+ATI IXP has two different methods to control SPDIF output. One is
+over AC-link and another is over the "direct" SPDIF output. The
+implementation depends on the motherboard, and you'll need to
+choose the correct one via spdif_aclink module option.
+
+The power-management is supported.
+
+Module snd-atiixp-modem
+-----------------------
+
+Module for ATI IXP 150/200/250 AC97 modem controllers.
+
+This module supports one card and autoprobe.
+
+Note: The default index value of this module is -2, i.e. the first
+slot is excluded.
+
+The power-management is supported.
+
+Module snd-au8810, snd-au8820, snd-au8830
+-----------------------------------------
+
+Module for Aureal Vortex, Vortex2 and Advantage device.
+
+pcifix
+ Control PCI workarounds;
+ 0 = Disable all workarounds,
+ 1 = Force the PCI latency of the Aureal card to 0xff,
+ 2 = Force the Extend PCI#2 Internal Master for Efficient
+ Handling of Dummy Requests on the VIA KT133 AGP Bridge,
+ 3 = Force both settings,
+ 255 = Autodetect what is required (default)
+
+This module supports all ADB PCM channels, ac97 mixer, SPDIF, hardware
+EQ, mpu401, gameport. A3D and wavetable support are still in development.
+Development and reverse engineering work is being coordinated at
+http://savannah.nongnu.org/projects/openvortex/
+SPDIF output has a copy of the AC97 codec output, unless you use the
+``spdif`` pcm device, which allows raw data passthru.
+The hardware EQ hardware and SPDIF is only present in the Vortex2 and
+Advantage.
+
+Note: Some ALSA mixer applications don't handle the SPDIF sample rate
+control correctly. If you have problems regarding this, try
+another ALSA compliant mixer (alsamixer works).
+
+Module snd-azt1605
+------------------
+
+Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605
+chipset.
+
+port
+ port # for BASE (0x220,0x240,0x260,0x280)
+wss_port
+ port # for WSS (0x530,0x604,0xe80,0xf40)
+irq
+ IRQ # for WSS (7,9,10,11)
+dma1
+ DMA # for WSS playback (0,1,3)
+dma2
+ DMA # for WSS capture (0,1), -1 = disabled (default)
+mpu_port
+ port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+mpu_irq
+ IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default)
+fm_port
+ port # for OPL3 (0x388), -1 = disabled (default)
+
+This module supports multiple cards. It does not support autoprobe:
+``port``, ``wss_port``, ``irq`` and ``dma1`` have to be specified.
+The other values are optional.
+
+``port`` needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+or the value stored in the card's EEPROM for cards that have an EEPROM and
+their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+be chosen freely from the options enumerated above.
+
+If ``dma2`` is specified and different from ``dma1``, the card will operate in
+full-duplex mode. When ``dma1=3``, only ``dma2=0`` is valid and the only way to
+enable capture since only channels 0 and 1 are available for capture.
+
+Generic settings are ``port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+mpu_port=0x330 mpu_irq=9 fm_port=0x388``.
+
+Whatever IRQ and DMA channels you pick, be sure to reserve them for
+legacy ISA in your BIOS.
+
+Module snd-azt2316
+------------------
+
+Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316
+chipset.
+
+port
+ port # for BASE (0x220,0x240,0x260,0x280)
+wss_port
+ port # for WSS (0x530,0x604,0xe80,0xf40)
+irq
+ IRQ # for WSS (7,9,10,11)
+dma1
+ DMA # for WSS playback (0,1,3)
+dma2
+ DMA # for WSS capture (0,1), -1 = disabled (default)
+mpu_port
+ port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+mpu_irq
+ IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default)
+fm_port
+ port # for OPL3 (0x388), -1 = disabled (default)
+
+This module supports multiple cards. It does not support autoprobe:
+``port``, ``wss_port``, ``irq`` and ``dma1`` have to be specified.
+The other values are optional.
+
+``port`` needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+or the value stored in the card's EEPROM for cards that have an EEPROM and
+their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+be chosen freely from the options enumerated above.
+
+If ``dma2`` is specified and different from ``dma1``, the card will operate in
+full-duplex mode. When ``dma1=3``, only ``dma2=0`` is valid and the only way to
+enable capture since only channels 0 and 1 are available for capture.
+
+Generic settings are ``port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+mpu_port=0x330 mpu_irq=9 fm_port=0x388``.
+
+Whatever IRQ and DMA channels you pick, be sure to reserve them for
+legacy ISA in your BIOS.
+
+Module snd-aw2
+--------------
+
+Module for Audiowerk2 sound card
+
+This module supports multiple cards.
+
+Module snd-azt2320
+------------------
+
+Module for sound cards based on Aztech System AZT2320 ISA chip (PnP only).
+
+This module supports multiple cards, PnP and autoprobe.
+
+The power-management is supported.
+
+Module snd-azt3328
+------------------
+
+Module for sound cards based on Aztech AZF3328 PCI chip.
+
+joystick
+ Enable joystick (default off)
+
+This module supports multiple cards.
+
+Module snd-bt87x
+----------------
+
+Module for video cards based on Bt87x chips.
+
+digital_rate
+ Override the default digital rate (Hz)
+load_all
+ Load the driver even if the card model isn't known
+
+This module supports multiple cards.
+
+Note: The default index value of this module is -2, i.e. the first
+slot is excluded.
+
+Module snd-ca0106
+-----------------
+
+Module for Creative Audigy LS and SB Live 24bit
+
+This module supports multiple cards.
+
+
+Module snd-cmi8330
+------------------
+
+Module for sound cards based on C-Media CMI8330 ISA chips.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+wssport
+ port # for CMI8330 chip (WSS)
+wssirq
+ IRQ # for CMI8330 chip (WSS)
+wssdma
+ first DMA # for CMI8330 chip (WSS)
+sbport
+ port # for CMI8330 chip (SB16)
+sbirq
+ IRQ # for CMI8330 chip (SB16)
+sbdma8
+ 8bit DMA # for CMI8330 chip (SB16)
+sbdma16
+ 16bit DMA # for CMI8330 chip (SB16)
+fmport
+ (optional) OPL3 I/O port
+mpuport
+ (optional) MPU401 I/O port
+mpuirq
+ (optional) MPU401 irq #
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-cmipci
+-----------------
+
+Module for C-Media CMI8338/8738/8768/8770 PCI sound cards.
+
+mpu_port
+ port address of MIDI interface (8338 only):
+ 0x300,0x310,0x320,0x330 = legacy port,
+ 0 = disable (default)
+fm_port
+ port address of OPL-3 FM synthesizer (8x38 only):
+ 0x388 = legacy port,
+ 1 = integrated PCI port (default on 8738),
+ 0 = disable
+soft_ac3
+ Software-conversion of raw SPDIF packets (model 033 only) (default = 1)
+joystick_port
+ Joystick port address (0 = disable, 1 = auto-detect)
+
+This module supports autoprobe and multiple cards.
+
+The power-management is supported.
+
+Module snd-cs4231
+-----------------
+
+Module for sound cards based on CS4231 ISA chips.
+
+port
+ port # for CS4231 chip
+mpu_port
+ port # for MPU-401 UART (optional), -1 = disable
+irq
+ IRQ # for CS4231 chip
+mpu_irq
+ IRQ # for MPU-401 UART
+dma1
+ first DMA # for CS4231 chip
+dma2
+ second DMA # for CS4231 chip
+
+This module supports multiple cards. This module does not support autoprobe
+thus main port must be specified!!! Other ports are optional.
+
+The power-management is supported.
+
+Module snd-cs4236
+-----------------
+
+Module for sound cards based on CS4232/CS4232A,
+CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 ISA chips.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for CS4236 chip (PnP setup - 0x534)
+cport
+ control port # for CS4236 chip (PnP setup - 0x120,0x210,0xf00)
+mpu_port
+ port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
+fm_port
+ FM port # for CS4236 chip (PnP setup - 0x388), -1 = disable
+irq
+ IRQ # for CS4236 chip (5,7,9,11,12,15)
+mpu_irq
+ IRQ # for MPU-401 UART (9,11,12,15)
+dma1
+ first DMA # for CS4236 chip (0,1,3)
+dma2
+ second DMA # for CS4236 chip (0,1,3), -1 = disable
+
+This module supports multiple cards. This module does not support autoprobe
+(if ISA PnP is not used) thus main port and control port must be
+specified!!! Other ports are optional.
+
+The power-management is supported.
+
+This module is aliased as snd-cs4232 since it provides the old
+snd-cs4232 functionality, too.
+
+Module snd-cs4281
+-----------------
+
+Module for Cirrus Logic CS4281 soundchip.
+
+dual_codec
+ Secondary codec ID (0 = disable, default)
+
+This module supports multiple cards.
+
+The power-management is supported.
+
+Module snd-cs46xx
+-----------------
+
+Module for PCI sound cards based on CS4610/CS4612/CS4614/CS4615/CS4622/
+CS4624/CS4630/CS4280 PCI chips.
+
+external_amp
+ Force to enable external amplifier.
+thinkpad
+ Force to enable Thinkpad's CLKRUN control.
+mmap_valid
+ Support OSS mmap mode (default = 0).
+
+This module supports multiple cards and autoprobe.
+Usually external amp and CLKRUN controls are detected automatically
+from PCI sub vendor/device ids. If they don't work, give the options
+above explicitly.
+
+The power-management is supported.
+
+Module snd-cs5530
+-----------------
+
+Module for Cyrix/NatSemi Geode 5530 chip.
+
+Module snd-cs5535audio
+----------------------
+
+Module for multifunction CS5535 companion PCI device
+
+The power-management is supported.
+
+Module snd-ctxfi
+----------------
+
+Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
+
+* Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
+* Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
+* Creative Sound Blaster X-Fi Titanium Professional Audio
+* Creative Sound Blaster X-Fi Titanium
+* Creative Sound Blaster X-Fi Elite Pro
+* Creative Sound Blaster X-Fi Platinum
+* Creative Sound Blaster X-Fi Fatal1ty
+* Creative Sound Blaster X-Fi XtremeGamer
+* Creative Sound Blaster X-Fi XtremeMusic
+
+reference_rate
+ reference sample rate, 44100 or 48000 (default)
+multiple
+ multiple to ref. sample rate, 1 or 2 (default)
+subsystem
+ override the PCI SSID for probing;
+ the value consists of SSVID << 16 | SSDID.
+ The default is zero, which means no override.
+
+This module supports multiple cards.
+
+Module snd-darla20
+------------------
+
+Module for Echoaudio Darla20
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-darla24
+------------------
+
+Module for Echoaudio Darla24
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-dt019x
+-----------------
+
+Module for Diamond Technologies DT-019X / Avance Logic ALS-007 (PnP
+only)
+
+This module supports multiple cards. This module is enabled only with
+ISA PnP support.
+
+The power-management is supported.
+
+Module snd-dummy
+----------------
+
+Module for the dummy sound card. This "card" doesn't do any output
+or input, but you may use this module for any application which
+requires a sound card (like RealPlayer).
+
+pcm_devs
+ Number of PCM devices assigned to each card (default = 1, up to 4)
+pcm_substreams
+ Number of PCM substreams assigned to each PCM (default = 8, up to 128)
+hrtimer
+ Use hrtimer (=1, default) or system timer (=0)
+fake_buffer
+ Fake buffer allocations (default = 1)
+
+When multiple PCM devices are created, snd-dummy gives different
+behavior to each PCM device:
+* 0 = interleaved with mmap support
+* 1 = non-interleaved with mmap support
+* 2 = interleaved without mmap
+* 3 = non-interleaved without mmap
+
+As default, snd-dummy drivers doesn't allocate the real buffers
+but either ignores read/write or mmap a single dummy page to all
+buffer pages, in order to save the resources. If your apps need
+the read/ written buffer data to be consistent, pass fake_buffer=0
+option.
+
+The power-management is supported.
+
+Module snd-echo3g
+-----------------
+
+Module for Echoaudio 3G cards (Gina3G/Layla3G)
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-emu10k1
+------------------
+
+Module for EMU10K1/EMU10k2 based PCI sound cards.
+
+* Sound Blaster Live!
+* Sound Blaster PCI 512
+* Emu APS (partially supported)
+* Sound Blaster Audigy
+
+extin
+ bitmap of available external inputs for FX8010 (see bellow)
+extout
+ bitmap of available external outputs for FX8010 (see bellow)
+seq_ports
+ allocated sequencer ports (4 by default)
+max_synth_voices
+ limit of voices used for wavetable (64 by default)
+max_buffer_size
+ specifies the maximum size of wavetable/pcm buffers given in MB
+ unit. Default value is 128.
+enable_ir
+ enable IR
+
+This module supports multiple cards and autoprobe.
+
+Input & Output configurations [extin/extout]
+* Creative Card wo/Digital out [0x0003/0x1f03]
+* Creative Card w/Digital out [0x0003/0x1f0f]
+* Creative Card w/Digital CD in [0x000f/0x1f0f]
+* Creative Card wo/Digital out + LiveDrive [0x3fc3/0x1fc3]
+* Creative Card w/Digital out + LiveDrive [0x3fc3/0x1fcf]
+* Creative Card w/Digital CD in + LiveDrive [0x3fcf/0x1fcf]
+* Creative Card wo/Digital out + Digital I/O 2 [0x0fc3/0x1f0f]
+* Creative Card w/Digital out + Digital I/O 2 [0x0fc3/0x1f0f]
+* Creative Card w/Digital CD in + Digital I/O 2 [0x0fcf/0x1f0f]
+* Creative Card 5.1/w Digital out + LiveDrive [0x3fc3/0x1fff]
+* Creative Card 5.1 (c) 2003 [0x3fc3/0x7cff]
+* Creative Card all ins and outs [0x3fff/0x7fff]
+
+The power-management is supported.
+
+Module snd-emu10k1x
+-------------------
+
+Module for Creative Emu10k1X (SB Live Dell OEM version)
+
+This module supports multiple cards.
+
+Module snd-ens1370
+------------------
+
+Module for Ensoniq AudioPCI ES1370 PCI sound cards.
+
+* SoundBlaster PCI 64
+* SoundBlaster PCI 128
+
+joystick
+ Enable joystick (default off)
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-ens1371
+------------------
+
+Module for Ensoniq AudioPCI ES1371 PCI sound cards.
+
+* SoundBlaster PCI 64
+* SoundBlaster PCI 128
+* SoundBlaster Vibra PCI
+
+joystick_port
+ port # for joystick (0x200,0x208,0x210,0x218), 0 = disable
+ (default), 1 = auto-detect
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-es1688
+-----------------
+
+Module for ESS AudioDrive ES-1688 and ES-688 sound cards.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+mpu_port
+ port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
+mpu_irq
+ IRQ # for MPU-401 port (5,7,9,10)
+fm_port
+ port # for OPL3 (option; share the same port as default)
+
+with ``isapnp=0``, the following additional options are available:
+
+port
+ port # for ES-1688 chip (0x220,0x240,0x260)
+irq
+ IRQ # for ES-1688 chip (5,7,9,10)
+dma8
+ DMA # for ES-1688 chip (0,1,3)
+
+This module supports multiple cards and autoprobe (without MPU-401 port)
+and PnP with the ES968 chip.
+
+Module snd-es18xx
+-----------------
+
+Module for ESS AudioDrive ES-18xx sound cards.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for ES-18xx chip (0x220,0x240,0x260)
+mpu_port
+ port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
+fm_port
+ port # for FM (optional, not used)
+irq
+ IRQ # for ES-18xx chip (5,7,9,10)
+dma1
+ first DMA # for ES-18xx chip (0,1,3)
+dma2
+ first DMA # for ES-18xx chip (0,1,3)
+
+This module supports multiple cards, ISA PnP and autoprobe (without MPU-401
+port if native ISA PnP routines are not used).
+When ``dma2`` is equal with ``dma1``, the driver works as half-duplex.
+
+The power-management is supported.
+
+Module snd-es1938
+-----------------
+
+Module for sound cards based on ESS Solo-1 (ES1938,ES1946) chips.
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-es1968
+-----------------
+
+Module for sound cards based on ESS Maestro-1/2/2E (ES1968/ES1978) chips.
+
+total_bufsize
+ total buffer size in kB (1-4096kB)
+pcm_substreams_p
+ playback channels (1-8, default=2)
+pcm_substreams_c
+ capture channels (1-8, default=0)
+clock
+ clock (0 = auto-detection)
+use_pm
+ support the power-management (0 = off, 1 = on, 2 = auto (default))
+enable_mpu
+ enable MPU401 (0 = off, 1 = on, 2 = auto (default))
+joystick
+ enable joystick (default off)
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-fm801
+----------------
+
+Module for ForteMedia FM801 based PCI sound cards.
+
+tea575x_tuner
+ Enable TEA575x tuner;
+ 1 = MediaForte 256-PCS,
+ 2 = MediaForte 256-PCPR,
+ 3 = MediaForte 64-PCR
+ High 16-bits are video (radio) device number + 1;
+ example: 0x10002 (MediaForte 256-PCPR, device 1)
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-gina20
+-----------------
+
+Module for Echoaudio Gina20
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-gina24
+-----------------
+
+Module for Echoaudio Gina24
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-gusclassic
+---------------------
+
+Module for Gravis UltraSound Classic sound card.
+
+port
+ port # for GF1 chip (0x220,0x230,0x240,0x250,0x260)
+irq
+ IRQ # for GF1 chip (3,5,9,11,12,15)
+dma1
+ DMA # for GF1 chip (1,3,5,6,7)
+dma2
+ DMA # for GF1 chip (1,3,5,6,7,-1=disable)
+joystick_dac
+ 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+voices
+ GF1 voices limit (14-32)
+pcm_voices
+ reserved PCM voices
+
+This module supports multiple cards and autoprobe.
+
+Module snd-gusextreme
+---------------------
+
+Module for Gravis UltraSound Extreme (Synergy ViperMax) sound card.
+
+port
+ port # for ES-1688 chip (0x220,0x230,0x240,0x250,0x260)
+gf1_port
+ port # for GF1 chip (0x210,0x220,0x230,0x240,0x250,0x260,0x270)
+mpu_port
+ port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable
+irq
+ IRQ # for ES-1688 chip (5,7,9,10)
+gf1_irq
+ IRQ # for GF1 chip (3,5,9,11,12,15)
+mpu_irq
+ IRQ # for MPU-401 port (5,7,9,10)
+dma8
+ DMA # for ES-1688 chip (0,1,3)
+dma1
+ DMA # for GF1 chip (1,3,5,6,7)
+joystick_dac
+ 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+voices
+ GF1 voices limit (14-32)
+pcm_voices
+ reserved PCM voices
+
+This module supports multiple cards and autoprobe (without MPU-401 port).
+
+Module snd-gusmax
+-----------------
+
+Module for Gravis UltraSound MAX sound card.
+
+port
+ port # for GF1 chip (0x220,0x230,0x240,0x250,0x260)
+irq
+ IRQ # for GF1 chip (3,5,9,11,12,15)
+dma1
+ DMA # for GF1 chip (1,3,5,6,7)
+dma2
+ DMA # for GF1 chip (1,3,5,6,7,-1=disable)
+joystick_dac
+ 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+voices
+ GF1 voices limit (14-32)
+pcm_voices
+ reserved PCM voices
+
+This module supports multiple cards and autoprobe.
+
+Module snd-hda-intel
+--------------------
+
+Module for Intel HD Audio (ICH6, ICH6M, ESB2, ICH7, ICH8, ICH9, ICH10,
+PCH, SCH), ATI SB450, SB600, R600, RS600, RS690, RS780, RV610, RV620,
+RV630, RV635, RV670, RV770, VIA VT8251/VT8237A, SIS966, ULI M5461
+
+[Multiple options for each card instance]
+
+model
+ force the model name
+position_fix
+ Fix DMA pointer;
+ -1 = system default: choose appropriate one per controller hardware,
+ 0 = auto: falls back to LPIB when POSBUF doesn't work,
+ 1 = use LPIB,
+ 2 = POSBUF: use position buffer,
+ 3 = VIACOMBO: VIA-specific workaround for capture,
+ 4 = COMBO: use LPIB for playback, auto for capture stream
+probe_mask
+ Bitmask to probe codecs (default = -1, meaning all slots);
+ When the bit 8 (0x100) is set, the lower 8 bits are used
+ as the "fixed" codec slots; i.e. the driver probes the
+ slots regardless what hardware reports back
+probe_only
+ Only probing and no codec initialization (default=off);
+ Useful to check the initial codec status for debugging
+bdl_pos_adj
+ Specifies the DMA IRQ timing delay in samples.
+ Passing -1 will make the driver to choose the appropriate
+ value based on the controller chip.
+patch
+ Specifies the early "patch" files to modify the HD-audio setup
+ before initializing the codecs.
+ This option is available only when ``CONFIG_SND_HDA_PATCH_LOADER=y``
+ is set. See hd-audio/notes.rst for details.
+beep_mode
+ Selects the beep registration mode (0=off, 1=on);
+ default value is set via ``CONFIG_SND_HDA_INPUT_BEEP_MODE`` kconfig.
+
+[Single (global) options]
+
+single_cmd
+ Use single immediate commands to communicate with codecs
+ (for debugging only)
+enable_msi
+ Enable Message Signaled Interrupt (MSI) (default = off)
+power_save
+ Automatic power-saving timeout (in second, 0 = disable)
+power_save_controller
+ Reset HD-audio controller in power-saving mode (default = on)
+align_buffer_size
+ Force rounding of buffer/period sizes to multiples of 128 bytes.
+ This is more efficient in terms of memory access but isn't
+ required by the HDA spec and prevents users from specifying
+ exact period/buffer sizes. (default = on)
+snoop
+ Enable/disable snooping (default = on)
+
+This module supports multiple cards and autoprobe.
+
+See hd-audio/notes.rst for more details about HD-audio driver.
+
+Each codec may have a model table for different configurations.
+If your machine isn't listed there, the default (usually minimal)
+configuration is set up. You can pass ``model=<name>`` option to
+specify a certain model in such a case. There are different
+models depending on the codec chip. The list of available models
+is found in hd-audio/models.rst.
+
+The model name ``generic`` is treated as a special case. When this
+model is given, the driver uses the generic codec parser without
+"codec-patch". It's sometimes good for testing and debugging.
+
+If the default configuration doesn't work and one of the above
+matches with your device, report it together with alsa-info.sh
+output (with ``--no-upload`` option) to kernel bugzilla or alsa-devel
+ML (see the section `Links and Addresses`_).
+
+``power_save`` and ``power_save_controller`` options are for power-saving
+mode. See powersave.txt for details.
+
+Note 2: If you get click noises on output, try the module option
+``position_fix=1`` or ``2``. ``position_fix=1`` will use the SD_LPIB
+register value without FIFO size correction as the current
+DMA pointer. ``position_fix=2`` will make the driver to use
+the position buffer instead of reading SD_LPIB register.
+(Usually SD_LPIB register is more accurate than the
+position buffer.)
+
+``position_fix=3`` is specific to VIA devices. The position
+of the capture stream is checked from both LPIB and POSBUF
+values. ``position_fix=4`` is a combination mode, using LPIB
+for playback and POSBUF for capture.
+
+NB: If you get many ``azx_get_response timeout`` messages at
+loading, it's likely a problem of interrupts (e.g. ACPI irq
+routing). Try to boot with options like ``pci=noacpi``. Also, you
+can try ``single_cmd=1`` module option. This will switch the
+communication method between HDA controller and codecs to the
+single immediate commands instead of CORB/RIRB. Basically, the
+single command mode is provided only for BIOS, and you won't get
+unsolicited events, too. But, at least, this works independently
+from the irq. Remember this is a last resort, and should be
+avoided as much as possible...
+
+MORE NOTES ON ``azx_get_response timeout`` PROBLEMS:
+On some hardware, you may need to add a proper probe_mask option
+to avoid the ``azx_get_response timeout`` problem above, instead.
+This occurs when the access to non-existing or non-working codec slot
+(likely a modem one) causes a stall of the communication via HD-audio
+bus. You can see which codec slots are probed by enabling
+``CONFIG_SND_DEBUG_VERBOSE``, or simply from the file name of the codec
+proc files. Then limit the slots to probe by probe_mask option.
+For example, ``probe_mask=1`` means to probe only the first slot, and
+``probe_mask=4`` means only the third slot.
+
+The power-management is supported.
+
+Module snd-hdsp
+---------------
+
+Module for RME Hammerfall DSP audio interface(s)
+
+This module supports multiple cards.
+
+Note: The firmware data can be automatically loaded via hotplug
+when ``CONFIG_FW_LOADER`` is set. Otherwise, you need to load
+the firmware via hdsploader utility included in alsa-tools
+package.
+The firmware data is found in alsa-firmware package.
+
+Note: snd-page-alloc module does the job which snd-hammerfall-mem
+module did formerly. It will allocate the buffers in advance
+when any HDSP cards are found. To make the buffer
+allocation sure, load snd-page-alloc module in the early
+stage of boot sequence. See `Early Buffer Allocation`_
+section.
+
+Module snd-hdspm
+----------------
+
+Module for RME HDSP MADI board.
+
+precise_ptr
+ Enable precise pointer, or disable.
+line_outs_monitor
+ Send playback streams to analog outs by default.
+enable_monitor
+ Enable Analog Out on Channel 63/64 by default.
+
+See hdspm.txt for details.
+
+Module snd-ice1712
+------------------
+
+Module for Envy24 (ICE1712) based PCI sound cards.
+
+* MidiMan M Audio Delta 1010
+* MidiMan M Audio Delta 1010LT
+* MidiMan M Audio Delta DiO 2496
+* MidiMan M Audio Delta 66
+* MidiMan M Audio Delta 44
+* MidiMan M Audio Delta 410
+* MidiMan M Audio Audiophile 2496
+* TerraTec EWS 88MT
+* TerraTec EWS 88D
+* TerraTec EWX 24/96
+* TerraTec DMX 6Fire
+* TerraTec Phase 88
+* Hoontech SoundTrack DSP 24
+* Hoontech SoundTrack DSP 24 Value
+* Hoontech SoundTrack DSP 24 Media 7.1
+* Event Electronics, EZ8
+* Digigram VX442
+* Lionstracs, Mediastaton
+* Terrasoniq TS 88
+
+model
+ Use the given board model, one of the following:
+ delta1010, dio2496, delta66, delta44, audiophile, delta410,
+ delta1010lt, vx442, ewx2496, ews88mt, ews88mt_new, ews88d,
+ dmx6fire, dsp24, dsp24_value, dsp24_71, ez8,
+ phase88, mediastation
+omni
+ Omni I/O support for MidiMan M-Audio Delta44/66
+cs8427_timeout
+ reset timeout for the CS8427 chip (S/PDIF transceiver) in msec
+ resolution, default value is 500 (0.5 sec)
+
+This module supports multiple cards and autoprobe.
+Note: The consumer part is not used with all Envy24 based cards (for
+example in the MidiMan Delta siree).
+
+Note: The supported board is detected by reading EEPROM or PCI
+SSID (if EEPROM isn't available). You can override the
+model by passing ``model`` module option in case that the
+driver isn't configured properly or you want to try another
+type for testing.
+
+Module snd-ice1724
+------------------
+
+Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards.
+
+* MidiMan M Audio Revolution 5.1
+* MidiMan M Audio Revolution 7.1
+* MidiMan M Audio Audiophile 192
+* AMP Ltd AUDIO2000
+* TerraTec Aureon 5.1 Sky
+* TerraTec Aureon 7.1 Space
+* TerraTec Aureon 7.1 Universe
+* TerraTec Phase 22
+* TerraTec Phase 28
+* AudioTrak Prodigy 7.1
+* AudioTrak Prodigy 7.1 LT
+* AudioTrak Prodigy 7.1 XT
+* AudioTrak Prodigy 7.1 HIFI
+* AudioTrak Prodigy 7.1 HD2
+* AudioTrak Prodigy 192
+* Pontis MS300
+* Albatron K8X800 Pro II
+* Chaintech ZNF3-150
+* Chaintech ZNF3-250
+* Chaintech 9CJS
+* Chaintech AV-710
+* Shuttle SN25P
+* Onkyo SE-90PCI
+* Onkyo SE-200PCI
+* ESI Juli@
+* ESI Maya44
+* Hercules Fortissimo IV
+* EGO-SYS WaveTerminal 192M
+
+model
+ Use the given board model, one of the following:
+ revo51, revo71, amp2000, prodigy71, prodigy71lt,
+ prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
+ juli, aureon51, aureon71, universe, ap192, k8x800,
+ phase22, phase28, ms300, av710, se200pci, se90pci,
+ fortissimo4, sn25p, WT192M, maya44
+
+This module supports multiple cards and autoprobe.
+
+Note: The supported board is detected by reading EEPROM or PCI
+SSID (if EEPROM isn't available). You can override the
+model by passing ``model`` module option in case that the
+driver isn't configured properly or you want to try another
+type for testing.
+
+Module snd-indigo
+-----------------
+
+Module for Echoaudio Indigo
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-indigodj
+-------------------
+
+Module for Echoaudio Indigo DJ
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-indigoio
+-------------------
+
+Module for Echoaudio Indigo IO
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-intel8x0
+-------------------
+
+Module for AC'97 motherboards from Intel and compatibles.
+
+* Intel i810/810E, i815, i820, i830, i84x, MX440 ICH5, ICH6, ICH7,
+ 6300ESB, ESB2
+* SiS 7012 (SiS 735)
+* NVidia NForce, NForce2, NForce3, MCP04, CK804 CK8, CK8S, MCP501
+* AMD AMD768, AMD8111
+* ALi m5455
+
+ac97_clock
+ AC'97 codec clock base (0 = auto-detect)
+ac97_quirk
+ AC'97 workaround for strange hardware;
+ See `AC97 Quirk Option`_ section below.
+buggy_irq
+ Enable workaround for buggy interrupts on some motherboards
+ (default yes on nForce chips, otherwise off)
+buggy_semaphore
+ Enable workaround for hardware with buggy semaphores (e.g. on some
+ ASUS laptops) (default off)
+spdif_aclink
+ Use S/PDIF over AC-link instead of direct connection from the
+ controller chip (0 = off, 1 = on, -1 = default)
+
+This module supports one chip and autoprobe.
+
+Note: the latest driver supports auto-detection of chip clock.
+if you still encounter too fast playback, specify the clock
+explicitly via the module option ``ac97_clock=41194``.
+
+Joystick/MIDI ports are not supported by this driver. If your
+motherboard has these devices, use the ns558 or snd-mpu401
+modules, respectively.
+
+The power-management is supported.
+
+Module snd-intel8x0m
+--------------------
+
+Module for Intel ICH (i8x0) chipset MC97 modems.
+
+* Intel i810/810E, i815, i820, i830, i84x, MX440 ICH5, ICH6, ICH7
+* SiS 7013 (SiS 735)
+* NVidia NForce, NForce2, NForce2s, NForce3
+* AMD AMD8111
+* ALi m5455
+
+ac97_clock
+ AC'97 codec clock base (0 = auto-detect)
+
+This module supports one card and autoprobe.
+
+Note: The default index value of this module is -2, i.e. the first
+slot is excluded.
+
+The power-management is supported.
+
+Module snd-interwave
+--------------------
+
+Module for Gravis UltraSound PnP, Dynasonic 3-D/Pro, STB Sound Rage 32
+and other sound cards based on AMD InterWave (tm) chip.
+
+joystick_dac
+ 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+midi
+ 1 = MIDI UART enable, 0 = MIDI UART disable (default)
+pcm_voices
+ reserved PCM voices for the synthesizer (default 2)
+effect
+ 1 = InterWave effects enable (default 0); requires 8 voices
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+irq
+ IRQ # for InterWave chip (3,5,9,11,12,15)
+dma1
+ DMA # for InterWave chip (0,1,3,5,6,7)
+dma2
+ DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+
+This module supports multiple cards, autoprobe and ISA PnP.
+
+Module snd-interwave-stb
+------------------------
+
+Module for UltraSound 32-Pro (sound card from STB used by Compaq)
+and other sound cards based on AMD InterWave (tm) chip with TEA6330T
+circuit for extended control of bass, treble and master volume.
+
+joystick_dac
+ 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+midi
+ 1 = MIDI UART enable, 0 = MIDI UART disable (default)
+pcm_voices
+ reserved PCM voices for the synthesizer (default 2)
+effect
+ 1 = InterWave effects enable (default 0); requires 8 voices
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+port_tc
+ tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
+irq
+ IRQ # for InterWave chip (3,5,9,11,12,15)
+dma1
+ DMA # for InterWave chip (0,1,3,5,6,7)
+dma2
+ DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+
+This module supports multiple cards, autoprobe and ISA PnP.
+
+Module snd-jazz16
+-------------------
+
+Module for Media Vision Jazz16 chipset. The chipset consists of 3 chips:
+MVD1216 + MVA416 + MVA514.
+
+port
+ port # for SB DSP chip (0x210,0x220,0x230,0x240,0x250,0x260)
+irq
+ IRQ # for SB DSP chip (3,5,7,9,10,15)
+dma8
+ DMA # for SB DSP chip (1,3)
+dma16
+ DMA # for SB DSP chip (5,7)
+mpu_port
+ MPU-401 port # (0x300,0x310,0x320,0x330)
+mpu_irq
+ MPU-401 irq # (2,3,5,7)
+
+This module supports multiple cards.
+
+Module snd-korg1212
+-------------------
+
+Module for Korg 1212 IO PCI card
+
+This module supports multiple cards.
+
+Module snd-layla20
+------------------
+
+Module for Echoaudio Layla20
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-layla24
+------------------
+
+Module for Echoaudio Layla24
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-lola
+---------------
+
+Module for Digigram Lola PCI-e boards
+
+This module supports multiple cards.
+
+Module snd-lx6464es
+-------------------
+
+Module for Digigram LX6464ES boards
+
+This module supports multiple cards.
+
+Module snd-maestro3
+-------------------
+
+Module for Allegro/Maestro3 chips
+
+external_amp
+ enable external amp (enabled by default)
+amp_gpio
+ GPIO pin number for external amp (0-15) or -1 for default pin (8
+ for allegro, 1 for others)
+
+This module supports autoprobe and multiple chips.
+
+Note: the binding of amplifier is dependent on hardware.
+If there is no sound even though all channels are unmuted, try to
+specify other gpio connection via amp_gpio option.
+For example, a Panasonic notebook might need ``amp_gpio=0x0d``
+option.
+
+The power-management is supported.
+
+Module snd-mia
+---------------
+
+Module for Echoaudio Mia
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-miro
+---------------
+
+Module for Miro soundcards: miroSOUND PCM 1 pro, miroSOUND PCM 12,
+miroSOUND PCM 20 Radio.
+
+port
+ Port # (0x530,0x604,0xe80,0xf40)
+irq
+ IRQ # (5,7,9,10,11)
+dma1
+ 1st dma # (0,1,3)
+dma2
+ 2nd dma # (0,1)
+mpu_port
+ MPU-401 port # (0x300,0x310,0x320,0x330)
+mpu_irq
+ MPU-401 irq # (5,7,9,10)
+fm_port
+ FM Port # (0x388)
+wss
+ enable WSS mode
+ide
+ enable onboard ide support
+
+Module snd-mixart
+-----------------
+
+Module for Digigram miXart8 sound cards.
+
+This module supports multiple cards.
+Note: One miXart8 board will be represented as 4 alsa cards.
+See MIXART.txt for details.
+
+When the driver is compiled as a module and the hotplug firmware
+is supported, the firmware data is loaded via hotplug automatically.
+Install the necessary firmware files in alsa-firmware package.
+When no hotplug fw loader is available, you need to load the
+firmware via mixartloader utility in alsa-tools package.
+
+Module snd-mona
+---------------
+
+Module for Echoaudio Mona
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+
+Module snd-mpu401
+-----------------
+
+Module for MPU-401 UART devices.
+
+port
+ port number or -1 (disable)
+irq
+ IRQ number or -1 (disable)
+pnp
+ PnP detection - 0 = disable, 1 = enable (default)
+
+This module supports multiple devices and PnP.
+
+Module snd-msnd-classic
+-----------------------
+
+Module for Turtle Beach MultiSound Classic, Tahiti or Monterey
+soundcards.
+
+io
+ Port # for msnd-classic card
+irq
+ IRQ # for msnd-classic card
+mem
+ Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000)
+write_ndelay
+ enable write ndelay (default = 1)
+calibrate_signal
+ calibrate signal (default = 0)
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+digital
+ Digital daughterboard present (default = 0)
+cfg
+ Config port (0x250, 0x260 or 0x270) default = PnP
+reset
+ Reset all devices
+mpu_io
+ MPU401 I/O port
+mpu_irq
+ MPU401 irq#
+ide_io0
+ IDE port #0
+ide_io1
+ IDE port #1
+ide_irq
+ IDE irq#
+joystick_io
+ Joystick I/O port
+
+The driver requires firmware files ``turtlebeach/msndinit.bin`` and
+``turtlebeach/msndperm.bin`` in the proper firmware directory.
+
+See Documentation/sound/oss/MultiSound for important information
+about this driver. Note that it has been discontinued, but the
+Voyetra Turtle Beach knowledge base entry for it is still available
+at
+http://www.turtlebeach.com
+
+Module snd-msnd-pinnacle
+------------------------
+
+Module for Turtle Beach MultiSound Pinnacle/Fiji soundcards.
+
+io
+ Port # for pinnacle/fiji card
+irq
+ IRQ # for pinnalce/fiji card
+mem
+ Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000)
+write_ndelay
+ enable write ndelay (default = 1)
+calibrate_signal
+ calibrate signal (default = 0)
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+The driver requires firmware files ``turtlebeach/pndspini.bin`` and
+``turtlebeach/pndsperm.bin`` in the proper firmware directory.
+
+Module snd-mtpav
+----------------
+
+Module for MOTU MidiTimePiece AV multiport MIDI (on the parallel
+port).
+
+port
+ I/O port # for MTPAV (0x378,0x278, default=0x378)
+irq
+ IRQ # for MTPAV (7,5, default=7)
+hwports
+ number of supported hardware ports, default=8.
+
+Module supports only 1 card. This module has no enable option.
+
+Module snd-mts64
+----------------
+
+Module for Ego Systems (ESI) Miditerminal 4140
+
+This module supports multiple devices.
+Requires parport (``CONFIG_PARPORT``).
+
+Module snd-nm256
+----------------
+
+Module for NeoMagic NM256AV/ZX chips
+
+playback_bufsize
+ max playback frame size in kB (4-128kB)
+capture_bufsize
+ max capture frame size in kB (4-128kB)
+force_ac97
+ 0 or 1 (disabled by default)
+buffer_top
+ specify buffer top address
+use_cache
+ 0 or 1 (disabled by default)
+vaio_hack
+ alias buffer_top=0x25a800
+reset_workaround
+ enable AC97 RESET workaround for some laptops
+reset_workaround2
+ enable extended AC97 RESET workaround for some other laptops
+
+This module supports one chip and autoprobe.
+
+The power-management is supported.
+
+Note: on some notebooks the buffer address cannot be detected
+automatically, or causes hang-up during initialization.
+In such a case, specify the buffer top address explicitly via
+the buffer_top option.
+For example,
+Sony F250: buffer_top=0x25a800
+Sony F270: buffer_top=0x272800
+The driver supports only ac97 codec. It's possible to force
+to initialize/use ac97 although it's not detected. In such a
+case, use ``force_ac97=1`` option - but *NO* guarantee whether it
+works!
+
+Note: The NM256 chip can be linked internally with non-AC97
+codecs. This driver supports only the AC97 codec, and won't work
+with machines with other (most likely CS423x or OPL3SAx) chips,
+even though the device is detected in lspci. In such a case, try
+other drivers, e.g. snd-cs4232 or snd-opl3sa2. Some has ISA-PnP
+but some doesn't have ISA PnP. You'll need to specify ``isapnp=0``
+and proper hardware parameters in the case without ISA PnP.
+
+Note: some laptops need a workaround for AC97 RESET. For the
+known hardware like Dell Latitude LS and Sony PCG-F305, this
+workaround is enabled automatically. For other laptops with a
+hard freeze, you can try ``reset_workaround=1`` option.
+
+Note: Dell Latitude CSx laptops have another problem regarding
+AC97 RESET. On these laptops, reset_workaround2 option is
+turned on as default. This option is worth to try if the
+previous reset_workaround option doesn't help.
+
+Note: This driver is really crappy. It's a porting from the
+OSS driver, which is a result of black-magic reverse engineering.
+The detection of codec will fail if the driver is loaded *after*
+X-server as described above. You might be able to force to load
+the module, but it may result in hang-up. Hence, make sure that
+you load this module *before* X if you encounter this kind of
+problem.
+
+Module snd-opl3sa2
+------------------
+
+Module for Yamaha OPL3-SA2/SA3 sound cards.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ control port # for OPL3-SA chip (0x370)
+sb_port
+ SB port # for OPL3-SA chip (0x220,0x240)
+wss_port
+ WSS port # for OPL3-SA chip (0x530,0xe80,0xf40,0x604)
+midi_port
+ port # for MPU-401 UART (0x300,0x330), -1 = disable
+fm_port
+ FM port # for OPL3-SA chip (0x388), -1 = disable
+irq
+ IRQ # for OPL3-SA chip (5,7,9,10)
+dma1
+ first DMA # for Yamaha OPL3-SA chip (0,1,3)
+dma2
+ second DMA # for Yamaha OPL3-SA chip (0,1,3), -1 = disable
+
+This module supports multiple cards and ISA PnP. It does not support
+autoprobe (if ISA PnP is not used) thus all ports must be specified!!!
+
+The power-management is supported.
+
+Module snd-opti92x-ad1848
+-------------------------
+
+Module for sound cards based on OPTi 82c92x and Analog Devices AD1848 chips.
+Module works with OAK Mozart cards as well.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for WSS chip (0x530,0xe80,0xf40,0x604)
+mpu_port
+ port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+fm_port
+ port # for OPL3 device (0x388)
+irq
+ IRQ # for WSS chip (5,7,9,10,11)
+mpu_irq
+ IRQ # for MPU-401 UART (5,7,9,10)
+dma1
+ first DMA # for WSS chip (0,1,3)
+
+This module supports only one card, autoprobe and PnP.
+
+Module snd-opti92x-cs4231
+-------------------------
+
+Module for sound cards based on OPTi 82c92x and Crystal CS4231 chips.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for WSS chip (0x530,0xe80,0xf40,0x604)
+mpu_port
+ port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+fm_port
+ port # for OPL3 device (0x388)
+irq
+ IRQ # for WSS chip (5,7,9,10,11)
+mpu_irq
+ IRQ # for MPU-401 UART (5,7,9,10)
+dma1
+ first DMA # for WSS chip (0,1,3)
+dma2
+ second DMA # for WSS chip (0,1,3)
+
+This module supports only one card, autoprobe and PnP.
+
+Module snd-opti93x
+------------------
+
+Module for sound cards based on OPTi 82c93x chips.
+
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with ``isapnp=0``, the following options are available:
+
+port
+ port # for WSS chip (0x530,0xe80,0xf40,0x604)
+mpu_port
+ port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+fm_port
+ port # for OPL3 device (0x388)
+irq
+ IRQ # for WSS chip (5,7,9,10,11)
+mpu_irq
+ IRQ # for MPU-401 UART (5,7,9,10)
+dma1
+ first DMA # for WSS chip (0,1,3)
+dma2
+ second DMA # for WSS chip (0,1,3)
+
+This module supports only one card, autoprobe and PnP.
+
+Module snd-oxygen
+-----------------
+
+Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
+
+* Asound A-8788
+* Asus Xonar DG/DGX
+* AuzenTech X-Meridian
+* AuzenTech X-Meridian 2G
+* Bgears b-Enspirer
+* Club3D Theatron DTS
+* HT-Omega Claro (plus)
+* HT-Omega Claro halo (XT)
+* Kuroutoshikou CMI8787-HG2PCI
+* Razer Barracuda AC-1
+* Sondigo Inferno
+* TempoTec HiFier Fantasia
+* TempoTec HiFier Serenade
+
+This module supports autoprobe and multiple cards.
+
+Module snd-pcsp
+---------------
+
+Module for internal PC-Speaker.
+
+nopcm
+ Disable PC-Speaker PCM sound. Only beeps remain.
+nforce_wa
+ enable NForce chipset workaround. Expect bad sound.
+
+This module supports system beeps, some kind of PCM playback and
+even a few mixer controls.
+
+Module snd-pcxhr
+----------------
+
+Module for Digigram PCXHR boards
+
+This module supports multiple cards.
+
+Module snd-portman2x4
+---------------------
+
+Module for Midiman Portman 2x4 parallel port MIDI interface
+
+This module supports multiple cards.
+
+Module snd-powermac (on ppc only)
+---------------------------------
+
+Module for PowerMac, iMac and iBook on-board soundchips
+
+enable_beep
+ enable beep using PCM (enabled as default)
+
+Module supports autoprobe a chip.
+
+Note: the driver may have problems regarding endianness.
+
+The power-management is supported.
+
+Module snd-pxa2xx-ac97 (on arm only)
+------------------------------------
+
+Module for AC97 driver for the Intel PXA2xx chip
+
+For ARM architecture only.
+
+The power-management is supported.
+
+Module snd-riptide
+------------------
+
+Module for Conexant Riptide chip
+
+joystick_port
+ Joystick port # (default: 0x200)
+mpu_port
+ MPU401 port # (default: 0x330)
+opl3_port
+ OPL3 port # (default: 0x388)
+
+This module supports multiple cards.
+The driver requires the firmware loader support on kernel.
+You need to install the firmware file ``riptide.hex`` to the standard
+firmware path (e.g. /lib/firmware).
+
+Module snd-rme32
+----------------
+
+Module for RME Digi32, Digi32 Pro and Digi32/8 (Sek'd Prodif32,
+Prodif96 and Prodif Gold) sound cards.
+
+This module supports multiple cards.
+
+Module snd-rme96
+----------------
+
+Module for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST sound cards.
+
+This module supports multiple cards.
+
+Module snd-rme9652
+------------------
+
+Module for RME Digi9652 (Hammerfall, Hammerfall-Light) sound cards.
+
+precise_ptr
+ Enable precise pointer (doesn't work reliably). (default = 0)
+
+This module supports multiple cards.
+
+Note: snd-page-alloc module does the job which snd-hammerfall-mem
+module did formerly. It will allocate the buffers in advance
+when any RME9652 cards are found. To make the buffer
+allocation sure, load snd-page-alloc module in the early
+stage of boot sequence. See `Early Buffer Allocation`_
+section.
+
+Module snd-sa11xx-uda1341 (on arm only)
+---------------------------------------
+
+Module for Philips UDA1341TS on Compaq iPAQ H3600 sound card.
+
+Module supports only one card.
+Module has no enable and index options.
+
+The power-management is supported.
+
+Module snd-sb8
+--------------
+
+Module for 8-bit SoundBlaster cards: SoundBlaster 1.0, SoundBlaster 2.0,
+SoundBlaster Pro
+
+port
+ port # for SB DSP chip (0x220,0x240,0x260)
+irq
+ IRQ # for SB DSP chip (5,7,9,10)
+dma8
+ DMA # for SB DSP chip (1,3)
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-sb16 and snd-sbawe
+-----------------------------
+
+Module for 16-bit SoundBlaster cards: SoundBlaster 16 (PnP),
+SoundBlaster AWE 32 (PnP), SoundBlaster AWE 64 PnP
+
+mic_agc
+ Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
+csp
+ ASP/CSP chip support - 0 = disable (default), 1 = enable
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with isapnp=0, the following options are available:
+
+port
+ port # for SB DSP 4.x chip (0x220,0x240,0x260)
+mpu_port
+ port # for MPU-401 UART (0x300,0x330), -1 = disable
+awe_port
+ base port # for EMU8000 synthesizer (0x620,0x640,0x660) (snd-sbawe
+ module only)
+irq
+ IRQ # for SB DSP 4.x chip (5,7,9,10)
+dma8
+ 8-bit DMA # for SB DSP 4.x chip (0,1,3)
+dma16
+ 16-bit DMA # for SB DSP 4.x chip (5,6,7)
+
+This module supports multiple cards, autoprobe and ISA PnP.
+
+Note: To use Vibra16X cards in 16-bit half duplex mode, you must
+disable 16bit DMA with dma16 = -1 module parameter.
+Also, all Sound Blaster 16 type cards can operate in 16-bit
+half duplex mode through 8-bit DMA channel by disabling their
+16-bit DMA channel.
+
+The power-management is supported.
+
+Module snd-sc6000
+-----------------
+
+Module for Gallant SC-6000 soundcard and later models: SC-6600 and
+SC-7000.
+
+port
+ Port # (0x220 or 0x240)
+mss_port
+ MSS Port # (0x530 or 0xe80)
+irq
+ IRQ # (5,7,9,10,11)
+mpu_irq
+ MPU-401 IRQ # (5,7,9,10) ,0 - no MPU-401 irq
+dma
+ DMA # (1,3,0)
+joystick
+ Enable gameport - 0 = disable (default), 1 = enable
+
+This module supports multiple cards.
+
+This card is also known as Audio Excel DSP 16 or Zoltrix AV302.
+
+Module snd-sscape
+-----------------
+
+Module for ENSONIQ SoundScape cards.
+
+port
+ Port # (PnP setup)
+wss_port
+ WSS Port # (PnP setup)
+irq
+ IRQ # (PnP setup)
+mpu_irq
+ MPU-401 IRQ # (PnP setup)
+dma
+ DMA # (PnP setup)
+dma2
+ 2nd DMA # (PnP setup, -1 to disable)
+joystick
+ Enable gameport - 0 = disable (default), 1 = enable
+
+This module supports multiple cards.
+
+The driver requires the firmware loader support on kernel.
+
+Module snd-sun-amd7930 (on sparc only)
+--------------------------------------
+
+Module for AMD7930 sound chips found on Sparcs.
+
+This module supports multiple cards.
+
+Module snd-sun-cs4231 (on sparc only)
+-------------------------------------
+
+Module for CS4231 sound chips found on Sparcs.
+
+This module supports multiple cards.
+
+Module snd-sun-dbri (on sparc only)
+-----------------------------------
+
+Module for DBRI sound chips found on Sparcs.
+
+This module supports multiple cards.
+
+Module snd-wavefront
+--------------------
+
+Module for Turtle Beach Maui, Tropez and Tropez+ sound cards.
+
+use_cs4232_midi
+ Use CS4232 MPU-401 interface
+ (inaccessibly located inside your computer)
+isapnp
+ ISA PnP detection - 0 = disable, 1 = enable (default)
+
+with isapnp=0, the following options are available:
+
+cs4232_pcm_port
+ Port # for CS4232 PCM interface.
+cs4232_pcm_irq
+ IRQ # for CS4232 PCM interface (5,7,9,11,12,15).
+cs4232_mpu_port
+ Port # for CS4232 MPU-401 interface.
+cs4232_mpu_irq
+ IRQ # for CS4232 MPU-401 interface (9,11,12,15).
+ics2115_port
+ Port # for ICS2115
+ics2115_irq
+ IRQ # for ICS2115
+fm_port
+ FM OPL-3 Port #
+dma1
+ DMA1 # for CS4232 PCM interface.
+dma2
+ DMA2 # for CS4232 PCM interface.
+
+The below are options for wavefront_synth features:
+
+wf_raw
+ Assume that we need to boot the OS (default:no);
+ If yes, then during driver loading, the state of the board is
+ ignored, and we reset the board and load the firmware anyway.
+fx_raw
+ Assume that the FX process needs help (default:yes);
+ If false, we'll leave the FX processor in whatever state it is
+ when the driver is loaded. The default is to download the
+ microprogram and associated coefficients to set it up for
+ "default" operation, whatever that means.
+debug_default
+ Debug parameters for card initialization
+wait_usecs
+ How long to wait without sleeping, usecs (default:150);
+ This magic number seems to give pretty optimal throughput
+ based on my limited experimentation.
+ If you want to play around with it and find a better value, be
+ my guest. Remember, the idea is to get a number that causes us
+ to just busy wait for as many WaveFront commands as possible,
+ without coming up with a number so large that we hog the whole
+ CPU.
+ Specifically, with this number, out of about 134,000 status
+ waits, only about 250 result in a sleep.
+sleep_interval
+ How long to sleep when waiting for reply (default: 100)
+sleep_tries
+ How many times to try sleeping during a wait (default: 50)
+ospath
+ Pathname to processed ICS2115 OS firmware (default:wavefront.os);
+ The path name of the ISC2115 OS firmware. In the recent
+ version, it's handled via firmware loader framework, so it
+ must be installed in the proper path, typically,
+ /lib/firmware.
+reset_time
+ How long to wait for a reset to take effect (default:2)
+ramcheck_time
+ How many seconds to wait for the RAM test (default:20)
+osrun_time
+ How many seconds to wait for the ICS2115 OS (default:10)
+
+This module supports multiple cards and ISA PnP.
+
+Note: the firmware file ``wavefront.os`` was located in the earlier
+version in /etc. Now it's loaded via firmware loader, and
+must be in the proper firmware path, such as /lib/firmware.
+Copy (or symlink) the file appropriately if you get an error
+regarding firmware downloading after upgrading the kernel.
+
+Module snd-sonicvibes
+---------------------
+
+Module for S3 SonicVibes PCI sound cards.
+* PINE Schubert 32 PCI
+
+reverb
+ Reverb Enable - 1 = enable, 0 = disable (default);
+ SoundCard must have onboard SRAM for this.
+mge
+ Mic Gain Enable - 1 = enable, 0 = disable (default)
+
+This module supports multiple cards and autoprobe.
+
+Module snd-serial-u16550
+------------------------
+
+Module for UART16550A serial MIDI ports.
+
+port
+ port # for UART16550A chip
+irq
+ IRQ # for UART16550A chip, -1 = poll mode
+speed
+ speed in bauds (9600,19200,38400,57600,115200)
+ 38400 = default
+base
+ base for divisor in bauds (57600,115200,230400,460800)
+ 115200 = default
+outs
+ number of MIDI ports in a serial port (1-4)
+ 1 = default
+adaptor
+ Type of adaptor.
+ 0 = Soundcanvas, 1 = MS-124T, 2 = MS-124W S/A,
+ 3 = MS-124W M/B, 4 = Generic
+
+This module supports multiple cards. This module does not support autoprobe
+thus the main port must be specified!!! Other options are optional.
+
+Module snd-trident
+------------------
+
+Module for Trident 4DWave DX/NX sound cards.
+* Best Union Miss Melody 4DWave PCI
+* HIS 4DWave PCI
+* Warpspeed ONSpeed 4DWave PCI
+* AzTech PCI 64-Q3D
+* Addonics SV 750
+* CHIC True Sound 4Dwave
+* Shark Predator4D-PCI
+* Jaton SonicWave 4D
+* SiS SI7018 PCI Audio
+* Hoontech SoundTrack Digital 4DWave NX
+
+pcm_channels
+ max channels (voices) reserved for PCM
+wavetable_size
+ max wavetable size in kB (4-?kb)
+
+This module supports multiple cards and autoprobe.
+
+The power-management is supported.
+
+Module snd-ua101
+----------------
+
+Module for the Edirol UA-101/UA-1000 audio/MIDI interfaces.
+
+This module supports multiple devices, autoprobe and hotplugging.
+
+Module snd-usb-audio
+--------------------
+
+Module for USB audio and USB MIDI devices.
+
+vid
+ Vendor ID for the device (optional)
+pid
+ Product ID for the device (optional)
+nrpacks
+ Max. number of packets per URB (default: 8)
+device_setup
+ Device specific magic number (optional);
+ Influence depends on the device
+ Default: 0x0000
+ignore_ctl_error
+ Ignore any USB-controller regarding mixer interface (default: no)
+autoclock
+ Enable auto-clock selection for UAC2 devices (default: yes)
+quirk_alias
+ Quirk alias list, pass strings like ``0123abcd:5678beef``, which
+ applies the existing quirk for the device 5678:beef to a new
+ device 0123:abcd.
+
+This module supports multiple devices, autoprobe and hotplugging.
+
+NB: ``nrpacks`` parameter can be modified dynamically via sysfs.
+Don't put the value over 20. Changing via sysfs has no sanity
+check.
+
+NB: ``ignore_ctl_error=1`` may help when you get an error at accessing
+the mixer element such as URB error -22. This happens on some
+buggy USB device or the controller.
+
+NB: quirk_alias option is provided only for testing / development.
+If you want to have a proper support, contact to upstream for
+adding the matching quirk in the driver code statically.
+
+Module snd-usb-caiaq
+--------------------
+
+Module for caiaq UB audio interfaces,
+
+* Native Instruments RigKontrol2
+* Native Instruments Kore Controller
+* Native Instruments Audio Kontrol 1
+* Native Instruments Audio 8 DJ
+
+This module supports multiple devices, autoprobe and hotplugging.
+
+Module snd-usb-usx2y
+--------------------
+
+Module for Tascam USB US-122, US-224 and US-428 devices.
+
+This module supports multiple devices, autoprobe and hotplugging.
+
+Note: you need to load the firmware via ``usx2yloader`` utility included
+in alsa-tools and alsa-firmware packages.
+
+Module snd-via82xx
+------------------
+
+Module for AC'97 motherboards based on VIA 82C686A/686B, 8233, 8233A,
+8233C, 8235, 8237 (south) bridge.
+
+mpu_port
+ 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup
+ [VIA686A/686B only]
+joystick
+ Enable joystick (default off) [VIA686A/686B only]
+ac97_clock
+ AC'97 codec clock base (default 48000Hz)
+dxs_support
+ support DXS channels, 0 = auto (default), 1 = enable, 2 = disable,
+ 3 = 48k only, 4 = no VRA, 5 = enable any sample rate and different
+ sample rates on different channels [VIA8233/C, 8235, 8237 only]
+ac97_quirk
+ AC'97 workaround for strange hardware;
+ See `AC97 Quirk Option`_ section below.
+
+This module supports one chip and autoprobe.
+
+Note: on some SMP motherboards like MSI 694D the interrupts might
+not be generated properly. In such a case, please try to
+set the SMP (or MPS) version on BIOS to 1.1 instead of
+default value 1.4. Then the interrupt number will be
+assigned under 15. You might also upgrade your BIOS.
+
+Note: VIA8233/5/7 (not VIA8233A) can support DXS (direct sound)
+channels as the first PCM. On these channels, up to 4
+streams can be played at the same time, and the controller
+can perform sample rate conversion with separate rates for
+each channel.
+As default (``dxs_support = 0``), 48k fixed rate is chosen
+except for the known devices since the output is often
+noisy except for 48k on some mother boards due to the
+bug of BIOS.
+Please try once ``dxs_support=5`` and if it works on other
+sample rates (e.g. 44.1kHz of mp3 playback), please let us
+know the PCI subsystem vendor/device id's (output of
+``lspci -nv``).
+If ``dxs_support=5`` does not work, try ``dxs_support=4``; if it
+doesn't work too, try dxs_support=1. (dxs_support=1 is
+usually for old motherboards. The correct implemented
+board should work with 4 or 5.) If it still doesn't
+work and the default setting is ok, ``dxs_support=3`` is the
+right choice. If the default setting doesn't work at all,
+try ``dxs_support=2`` to disable the DXS channels.
+In any cases, please let us know the result and the
+subsystem vendor/device ids. See `Links and Addresses`_
+below.
+
+Note: for the MPU401 on VIA823x, use snd-mpu401 driver
+additionally. The mpu_port option is for VIA686 chips only.
+
+The power-management is supported.
+
+Module snd-via82xx-modem
+------------------------
+
+Module for VIA82xx AC97 modem
+
+ac97_clock
+ AC'97 codec clock base (default 48000Hz)
+
+This module supports one card and autoprobe.
+
+Note: The default index value of this module is -2, i.e. the first
+slot is excluded.
+
+The power-management is supported.
+
+Module snd-virmidi
+------------------
+
+Module for virtual rawmidi devices.
+This module creates virtual rawmidi devices which communicate
+to the corresponding ALSA sequencer ports.
+
+midi_devs
+ MIDI devices # (1-4, default=4)
+
+This module supports multiple cards.
+
+Module snd-virtuoso
+-------------------
+
+Module for sound cards based on the Asus AV66/AV100/AV200 chips,
+i.e., Xonar D1, DX, D2, D2X, DS, DSX, Essence ST (Deluxe),
+Essence STX (II), HDAV1.3 (Deluxe), and HDAV1.3 Slim.
+
+This module supports autoprobe and multiple cards.
+
+Module snd-vx222
+----------------
+
+Module for Digigram VX-Pocket VX222, V222 v2 and Mic cards.
+
+mic
+ Enable Microphone on V222 Mic (NYI)
+ibl
+ Capture IBL size. (default = 0, minimum size)
+
+This module supports multiple cards.
+
+When the driver is compiled as a module and the hotplug firmware
+is supported, the firmware data is loaded via hotplug automatically.
+Install the necessary firmware files in alsa-firmware package.
+When no hotplug fw loader is available, you need to load the
+firmware via vxloader utility in alsa-tools package. To invoke
+vxloader automatically, add the following to /etc/modprobe.d/alsa.conf
+
+::
+
+ install snd-vx222 /sbin/modprobe --first-time -i snd-vx222\
+ && /usr/bin/vxloader
+
+(for 2.2/2.4 kernels, add ``post-install /usr/bin/vxloader`` to
+/etc/modules.conf, instead.)
+IBL size defines the interrupts period for PCM. The smaller size
+gives smaller latency but leads to more CPU consumption, too.
+The size is usually aligned to 126. As default (=0), the smallest
+size is chosen. The possible IBL values can be found in
+/proc/asound/cardX/vx-status proc file.
+
+The power-management is supported.
+
+Module snd-vxpocket
+-------------------
+
+Module for Digigram VX-Pocket VX2 and 440 PCMCIA cards.
+
+ibl
+ Capture IBL size. (default = 0, minimum size)
+
+This module supports multiple cards. The module is compiled only when
+PCMCIA is supported on kernel.
+
+With the older 2.6.x kernel, to activate the driver via the card
+manager, you'll need to set up /etc/pcmcia/vxpocket.conf. See the
+sound/pcmcia/vx/vxpocket.c. 2.6.13 or later kernel requires no
+longer require a config file.
+
+When the driver is compiled as a module and the hotplug firmware
+is supported, the firmware data is loaded via hotplug automatically.
+Install the necessary firmware files in alsa-firmware package.
+When no hotplug fw loader is available, you need to load the
+firmware via vxloader utility in alsa-tools package.
+
+About capture IBL, see the description of snd-vx222 module.
+
+Note: snd-vxp440 driver is merged to snd-vxpocket driver since
+ALSA 1.0.10.
+
+The power-management is supported.
+
+Module snd-ymfpci
+-----------------
+
+Module for Yamaha PCI chips (YMF72x, YMF74x & YMF75x).
+
+mpu_port
+ 0x300,0x330,0x332,0x334, 0 (disable) by default,
+ 1 (auto-detect for YMF744/754 only)
+fm_port
+ 0x388,0x398,0x3a0,0x3a8, 0 (disable) by default
+ 1 (auto-detect for YMF744/754 only)
+joystick_port
+ 0x201,0x202,0x204,0x205, 0 (disable) by default,
+ 1 (auto-detect)
+rear_switch
+ enable shared rear/line-in switch (bool)
+
+This module supports autoprobe and multiple chips.
+
+The power-management is supported.
+
+Module snd-pdaudiocf
+--------------------
+
+Module for Sound Core PDAudioCF sound card.
+
+The power-management is supported.
+
+
+AC97 Quirk Option
+=================
+
+The ac97_quirk option is used to enable/override the workaround for
+specific devices on drivers for on-board AC'97 controllers like
+snd-intel8x0. Some hardware have swapped output pins between Master
+and Headphone, or Surround (thanks to confusion of AC'97
+specifications from version to version :-)
+
+The driver provides the auto-detection of known problematic devices,
+but some might be unknown or wrongly detected. In such a case, pass
+the proper value with this option.
+
+The following strings are accepted:
+
+default
+ Don't override the default setting
+none
+ Disable the quirk
+hp_only
+ Bind Master and Headphone controls as a single control
+swap_hp
+ Swap headphone and master controls
+swap_surround
+ Swap master and surround controls
+ad_sharing
+ For AD1985, turn on OMS bit and use headphone
+alc_jack
+ For ALC65x, turn on the jack sense mode
+inv_eapd
+ Inverted EAPD implementation
+mute_led
+ Bind EAPD bit for turning on/off mute LED
+
+For backward compatibility, the corresponding integer value -1, 0, ...
+are accepted, too.
+
+For example, if ``Master`` volume control has no effect on your device
+but only ``Headphone`` does, pass ac97_quirk=hp_only module option.
+
+
+Configuring Non-ISAPNP Cards
+============================
+
+When the kernel is configured with ISA-PnP support, the modules
+supporting the isapnp cards will have module options ``isapnp``.
+If this option is set, *only* the ISA-PnP devices will be probed.
+For probing the non ISA-PnP cards, you have to pass ``isapnp=0`` option
+together with the proper i/o and irq configuration.
+
+When the kernel is configured without ISA-PnP support, isapnp option
+will be not built in.
+
+
+Module Autoloading Support
+==========================
+
+The ALSA drivers can be loaded automatically on demand by defining
+module aliases. The string ``snd-card-%1`` is requested for ALSA native
+devices where ``%i`` is sound card number from zero to seven.
+
+To auto-load an ALSA driver for OSS services, define the string
+``sound-slot-%i`` where ``%i`` means the slot number for OSS, which
+corresponds to the card index of ALSA. Usually, define this
+as the same card module.
+
+An example configuration for a single emu10k1 card is like below:
+::
+
+ ----- /etc/modprobe.d/alsa.conf
+ alias snd-card-0 snd-emu10k1
+ alias sound-slot-0 snd-emu10k1
+ ----- /etc/modprobe.d/alsa.conf
+
+The available number of auto-loaded sound cards depends on the module
+option ``cards_limit`` of snd module. As default it's set to 1.
+To enable the auto-loading of multiple cards, specify the number of
+sound cards in that option.
+
+When multiple cards are available, it'd better to specify the index
+number for each card via module option, too, so that the order of
+cards is kept consistent.
+
+An example configuration for two sound cards is like below:
+::
+
+ ----- /etc/modprobe.d/alsa.conf
+ # ALSA portion
+ options snd cards_limit=2
+ alias snd-card-0 snd-interwave
+ alias snd-card-1 snd-ens1371
+ options snd-interwave index=0
+ options snd-ens1371 index=1
+ # OSS/Free portion
+ alias sound-slot-0 snd-interwave
+ alias sound-slot-1 snd-ens1371
+ ----- /etc/modprobe.d/alsa.conf
+
+In this example, the interwave card is always loaded as the first card
+(index 0) and ens1371 as the second (index 1).
+
+Alternative (and new) way to fixate the slot assignment is to use
+``slots`` option of snd module. In the case above, specify like the
+following:
+::
+
+ options snd slots=snd-interwave,snd-ens1371
+
+Then, the first slot (#0) is reserved for snd-interwave driver, and
+the second (#1) for snd-ens1371. You can omit index option in each
+driver if slots option is used (although you can still have them at
+the same time as long as they don't conflict).
+
+The slots option is especially useful for avoiding the possible
+hot-plugging and the resultant slot conflict. For example, in the
+case above again, the first two slots are already reserved. If any
+other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
+snd-ens1371, it will be assigned to the third or later slot.
+
+When a module name is given with '!', the slot will be given for any
+modules but that name. For example, ``slots=!snd-pcsp`` will reserve
+the first slot for any modules but snd-pcsp.
+
+
+ALSA PCM devices to OSS devices mapping
+=======================================
+::
+
+ /dev/snd/pcmC0D0[c|p] -> /dev/audio0 (/dev/audio) -> minor 4
+ /dev/snd/pcmC0D0[c|p] -> /dev/dsp0 (/dev/dsp) -> minor 3
+ /dev/snd/pcmC0D1[c|p] -> /dev/adsp0 (/dev/adsp) -> minor 12
+ /dev/snd/pcmC1D0[c|p] -> /dev/audio1 -> minor 4+16 = 20
+ /dev/snd/pcmC1D0[c|p] -> /dev/dsp1 -> minor 3+16 = 19
+ /dev/snd/pcmC1D1[c|p] -> /dev/adsp1 -> minor 12+16 = 28
+ /dev/snd/pcmC2D0[c|p] -> /dev/audio2 -> minor 4+32 = 36
+ /dev/snd/pcmC2D0[c|p] -> /dev/dsp2 -> minor 3+32 = 39
+ /dev/snd/pcmC2D1[c|p] -> /dev/adsp2 -> minor 12+32 = 44
+
+The first number from ``/dev/snd/pcmC{X}D{Y}[c|p]`` expression means
+sound card number and second means device number. The ALSA devices
+have either ``c`` or ``p`` suffix indicating the direction, capture and
+playback, respectively.
+
+Please note that the device mapping above may be varied via the module
+options of snd-pcm-oss module.
+
+
+Proc interfaces (/proc/asound)
+==============================
+
+/proc/asound/card#/pcm#[cp]/oss
+-------------------------------
+erase
+ erase all additional information about OSS applications
+
+<app_name> <fragments> <fragment_size> [<options>]
+ <app_name>
+ name of application with (higher priority) or without path
+ <fragments>
+ number of fragments or zero if auto
+ <fragment_size>
+ size of fragment in bytes or zero if auto
+ <options>
+ optional parameters
+
+ disable
+ the application tries to open a pcm device for
+ this channel but does not want to use it.
+ (Cause a bug or mmap needs)
+ It's good for Quake etc...
+ direct
+ don't use plugins
+ block
+ force block mode (rvplayer)
+ non-block
+ force non-block mode
+ whole-frag
+ write only whole fragments (optimization affecting
+ playback only)
+ no-silence
+ do not fill silence ahead to avoid clicks
+ buggy-ptr
+ Returns the whitespace blocks in GETOPTR ioctl
+ instead of filled blocks
+
+Example:
+::
+
+ echo "x11amp 128 16384" > /proc/asound/card0/pcm0p/oss
+ echo "squake 0 0 disable" > /proc/asound/card0/pcm0c/oss
+ echo "rvplayer 0 0 block" > /proc/asound/card0/pcm0p/oss
+
+
+Early Buffer Allocation
+=======================
+
+Some drivers (e.g. hdsp) require the large contiguous buffers, and
+sometimes it's too late to find such spaces when the driver module is
+actually loaded due to memory fragmentation. You can pre-allocate the
+PCM buffers by loading snd-page-alloc module and write commands to its
+proc file in prior, for example, in the early boot stage like
+``/etc/init.d/*.local`` scripts.
+
+Reading the proc file /proc/drivers/snd-page-alloc shows the current
+usage of page allocation. In writing, you can send the following
+commands to the snd-page-alloc driver:
+
+* add VENDOR DEVICE MASK SIZE BUFFERS
+
+VENDOR and DEVICE are PCI vendor and device IDs. They take
+integer numbers (0x prefix is needed for the hex).
+MASK is the PCI DMA mask. Pass 0 if not restricted.
+SIZE is the size of each buffer to allocate. You can pass
+k and m suffix for KB and MB. The max number is 16MB.
+BUFFERS is the number of buffers to allocate. It must be greater
+than 0. The max number is 4.
+
+* erase
+
+This will erase the all pre-allocated buffers which are not in
+use.
+
+
+Links and Addresses
+===================
+
+ALSA project homepage
+ http://www.alsa-project.org
+Kernel Bugzilla
+ http://bugzilla.kernel.org/
+ALSA Developers ML
+ mailto:alsa-devel@alsa-project.org
+alsa-info.sh script
+ http://www.alsa-project.org/alsa-info.sh
+++ /dev/null
-
- Advanced Linux Sound Architecture - Driver
- ==========================================
- Configuration guide
-
-
-Kernel Configuration
-====================
-
-To enable ALSA support you need at least to build the kernel with
-primary sound card support (CONFIG_SOUND). Since ALSA can emulate OSS,
-you don't have to choose any of the OSS modules.
-
-Enable "OSS API emulation" (CONFIG_SND_OSSEMUL) and both OSS mixer and
-PCM supports if you want to run OSS applications with ALSA.
-
-If you want to support the WaveTable functionality on cards such as
-SB Live! then you need to enable "Sequencer support"
-(CONFIG_SND_SEQUENCER).
-
-To make ALSA debug messages more verbose, enable the "Verbose printk"
-and "Debug" options. To check for memory leaks, turn on "Debug memory"
-too. "Debug detection" will add checks for the detection of cards.
-
-Please note that all the ALSA ISA drivers support the Linux isapnp API
-(if the card supports ISA PnP). You don't need to configure the cards
-using isapnptools.
-
-
-Creating ALSA devices
-=====================
-
-This depends on your distribution, but normally you use the /dev/MAKEDEV
-script to create the necessary device nodes. On some systems you use a
-script named 'snddevices'.
-
-
-Module parameters
-=================
-
-The user can load modules with options. If the module supports more than
-one card and you have more than one card of the same type then you can
-specify multiple values for the option separated by commas.
-
-Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
-
- Module snd
- ----------
-
- The core ALSA module. It is used by all ALSA card drivers.
- It takes the following options which have global effects.
-
- major - major number for sound driver
- - Default: 116
- cards_limit
- - limiting card index for auto-loading (1-8)
- - Default: 1
- - For auto-loading more than one card, specify this
- option together with snd-card-X aliases.
- slots - Reserve the slot index for the given driver.
- This option takes multiple strings.
- See "Module Autoloading Support" section for details.
- debug - Specifies the debug message level
- (0 = disable debug prints, 1 = normal debug messages,
- 2 = verbose debug messages)
- This option appears only when CONFIG_SND_DEBUG=y.
- This option can be dynamically changed via sysfs
- /sys/modules/snd/parameters/debug file.
-
- Module snd-pcm-oss
- ------------------
-
- The PCM OSS emulation module.
- This module takes options which change the mapping of devices.
-
- dsp_map - PCM device number maps assigned to the 1st OSS device.
- - Default: 0
- adsp_map - PCM device number maps assigned to the 2st OSS device.
- - Default: 1
- nonblock_open
- - Don't block opening busy PCM devices. Default: 1
-
- For example, when dsp_map=2, /dev/dsp will be mapped to PCM #2 of
- the card #0. Similarly, when adsp_map=0, /dev/adsp will be mapped
- to PCM #0 of the card #0.
- For changing the second or later card, specify the option with
- commas, such like "dsp_map=0,1".
-
- nonblock_open option is used to change the behavior of the PCM
- regarding opening the device. When this option is non-zero,
- opening a busy OSS PCM device won't be blocked but return
- immediately with EAGAIN (just like O_NONBLOCK flag).
-
- Module snd-rawmidi
- ------------------
-
- This module takes options which change the mapping of devices.
- similar to those of the snd-pcm-oss module.
-
- midi_map - MIDI device number maps assigned to the 1st OSS device.
- - Default: 0
- amidi_map - MIDI device number maps assigned to the 2st OSS device.
- - Default: 1
-
- Common parameters for top sound card modules
- --------------------------------------------
-
- Each of top level sound card module takes the following options.
-
- index - index (slot #) of sound card
- - Values: 0 through 31 or negative
- - If nonnegative, assign that index number
- - if negative, interpret as a bitmask of permissible
- indices; the first free permitted index is assigned
- - Default: -1
- id - card ID (identifier or name)
- - Can be up to 15 characters long
- - Default: the card type
- - A directory by this name is created under /proc/asound/
- containing information about the card
- - This ID can be used instead of the index number in
- identifying the card
- enable - enable card
- - Default: enabled, for PCI and ISA PnP cards
-
- Module snd-adlib
- ----------------
-
- Module for AdLib FM cards.
-
- port - port # for OPL chip
-
- This module supports multiple cards. It does not support autoprobe, so
- the port must be specified. For actual AdLib FM cards it will be 0x388.
- Note that this card does not have PCM support and no mixer; only FM
- synthesis.
-
- Make sure you have "sbiload" from the alsa-tools package available and,
- after loading the module, find out the assigned ALSA sequencer port
- number through "sbiload -l". Example output:
-
- Port Client name Port name
- 64:0 OPL2 FM synth OPL2 FM Port
-
- Load the std.sb and drums.sb patches also supplied by sbiload:
-
- sbiload -p 64:0 std.sb drums.sb
-
- If you use this driver to drive an OPL3, you can use std.o3 and drums.o3
- instead. To have the card produce sound, use aplaymidi from alsa-utils:
-
- aplaymidi -p 64:0 foo.mid
-
- Module snd-ad1816a
- ------------------
-
- Module for sound cards based on Analog Devices AD1816A/AD1815 ISA chips.
-
- clockfreq - Clock frequency for AD1816A chip (default = 0, 33000Hz)
-
- This module supports multiple cards, autoprobe and PnP.
-
- Module snd-ad1848
- -----------------
-
- Module for sound cards based on AD1848/AD1847/CS4248 ISA chips.
-
- port - port # for AD1848 chip
- irq - IRQ # for AD1848 chip
- dma1 - DMA # for AD1848 chip (0,1,3)
-
- This module supports multiple cards. It does not support autoprobe
- thus main port must be specified!!! Other ports are optional.
-
- The power-management is supported.
-
- Module snd-ad1889
- -----------------
-
- Module for Analog Devices AD1889 chips.
-
- ac97_quirk - AC'97 workaround for strange hardware
- See the description of intel8x0 module for details.
-
- This module supports multiple cards.
-
- Module snd-ali5451
- ------------------
-
- Module for ALi M5451 PCI chip.
-
- pcm_channels - Number of hardware channels assigned for PCM
- spdif - Support SPDIF I/O
- - Default: disabled
-
- This module supports one chip and autoprobe.
-
- The power-management is supported.
-
- Module snd-als100
- -----------------
-
- Module for sound cards based on Avance Logic ALS100/ALS120 ISA chips.
-
- This module supports multiple cards, autoprobe and PnP.
-
- The power-management is supported.
-
- Module snd-als300
- -----------------
-
- Module for Avance Logic ALS300 and ALS300+
-
- This module supports multiple cards.
-
- The power-management is supported.
-
- Module snd-als4000
- ------------------
-
- Module for sound cards based on Avance Logic ALS4000 PCI chip.
-
- joystick_port - port # for legacy joystick support.
- 0 = disabled (default), 1 = auto-detect
-
- This module supports multiple cards, autoprobe and PnP.
-
- The power-management is supported.
-
- Module snd-asihpi
- -----------------
-
- Module for AudioScience ASI soundcards
-
- enable_hpi_hwdep - enable HPI hwdep for AudioScience soundcard
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-atiixp
- -----------------
-
- Module for ATI IXP 150/200/250/400 AC97 controllers.
-
- ac97_clock - AC'97 clock (default = 48000)
- ac97_quirk - AC'97 workaround for strange hardware
- See "AC97 Quirk Option" section below.
- ac97_codec - Workaround to specify which AC'97 codec
- instead of probing. If this works for you
- file a bug with your `lspci -vn` output.
- -2 -- Force probing.
- -1 -- Default behavior.
- 0-2 -- Use the specified codec.
- spdif_aclink - S/PDIF transfer over AC-link (default = 1)
-
- This module supports one card and autoprobe.
-
- ATI IXP has two different methods to control SPDIF output. One is
- over AC-link and another is over the "direct" SPDIF output. The
- implementation depends on the motherboard, and you'll need to
- choose the correct one via spdif_aclink module option.
-
- The power-management is supported.
-
- Module snd-atiixp-modem
- -----------------------
-
- Module for ATI IXP 150/200/250 AC97 modem controllers.
-
- This module supports one card and autoprobe.
-
- Note: The default index value of this module is -2, i.e. the first
- slot is excluded.
-
- The power-management is supported.
-
- Module snd-au8810, snd-au8820, snd-au8830
- -----------------------------------------
-
- Module for Aureal Vortex, Vortex2 and Advantage device.
-
- pcifix - Control PCI workarounds
- 0 = Disable all workarounds
- 1 = Force the PCI latency of the Aureal card to 0xff
- 2 = Force the Extend PCI#2 Internal Master for Efficient
- Handling of Dummy Requests on the VIA KT133 AGP Bridge
- 3 = Force both settings
- 255 = Autodetect what is required (default)
-
- This module supports all ADB PCM channels, ac97 mixer, SPDIF, hardware
- EQ, mpu401, gameport. A3D and wavetable support are still in development.
- Development and reverse engineering work is being coordinated at
- http://savannah.nongnu.org/projects/openvortex/
- SPDIF output has a copy of the AC97 codec output, unless you use the
- "spdif" pcm device, which allows raw data passthru.
- The hardware EQ hardware and SPDIF is only present in the Vortex2 and
- Advantage.
-
- Note: Some ALSA mixer applications don't handle the SPDIF sample rate
- control correctly. If you have problems regarding this, try
- another ALSA compliant mixer (alsamixer works).
-
- Module snd-azt1605
- ------------------
-
- Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605
- chipset.
-
- port - port # for BASE (0x220,0x240,0x260,0x280)
- wss_port - port # for WSS (0x530,0x604,0xe80,0xf40)
- irq - IRQ # for WSS (7,9,10,11)
- dma1 - DMA # for WSS playback (0,1,3)
- dma2 - DMA # for WSS capture (0,1), -1 = disabled (default)
- mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
- mpu_irq - IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default)
- fm_port - port # for OPL3 (0x388), -1 = disabled (default)
-
- This module supports multiple cards. It does not support autoprobe: port,
- wss_port, irq and dma1 have to be specified. The other values are
- optional.
-
- "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
- or the value stored in the card's EEPROM for cards that have an EEPROM and
- their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
- be chosen freely from the options enumerated above.
-
- If dma2 is specified and different from dma1, the card will operate in
- full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
- enable capture since only channels 0 and 1 are available for capture.
-
- Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
- mpu_port=0x330 mpu_irq=9 fm_port=0x388".
-
- Whatever IRQ and DMA channels you pick, be sure to reserve them for
- legacy ISA in your BIOS.
-
- Module snd-azt2316
- ------------------
-
- Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316
- chipset.
-
- port - port # for BASE (0x220,0x240,0x260,0x280)
- wss_port - port # for WSS (0x530,0x604,0xe80,0xf40)
- irq - IRQ # for WSS (7,9,10,11)
- dma1 - DMA # for WSS playback (0,1,3)
- dma2 - DMA # for WSS capture (0,1), -1 = disabled (default)
- mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
- mpu_irq - IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default)
- fm_port - port # for OPL3 (0x388), -1 = disabled (default)
-
- This module supports multiple cards. It does not support autoprobe: port,
- wss_port, irq and dma1 have to be specified. The other values are
- optional.
-
- "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
- or the value stored in the card's EEPROM for cards that have an EEPROM and
- their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
- be chosen freely from the options enumerated above.
-
- If dma2 is specified and different from dma1, the card will operate in
- full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
- enable capture since only channels 0 and 1 are available for capture.
-
- Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
- mpu_port=0x330 mpu_irq=9 fm_port=0x388".
-
- Whatever IRQ and DMA channels you pick, be sure to reserve them for
- legacy ISA in your BIOS.
-
- Module snd-aw2
- --------------
-
- Module for Audiowerk2 sound card
-
- This module supports multiple cards.
-
- Module snd-azt2320
- ------------------
-
- Module for sound cards based on Aztech System AZT2320 ISA chip (PnP only).
-
- This module supports multiple cards, PnP and autoprobe.
-
- The power-management is supported.
-
- Module snd-azt3328
- ------------------
-
- Module for sound cards based on Aztech AZF3328 PCI chip.
-
- joystick - Enable joystick (default off)
-
- This module supports multiple cards.
-
- Module snd-bt87x
- ----------------
-
- Module for video cards based on Bt87x chips.
-
- digital_rate - Override the default digital rate (Hz)
- load_all - Load the driver even if the card model isn't known
-
- This module supports multiple cards.
-
- Note: The default index value of this module is -2, i.e. the first
- slot is excluded.
-
- Module snd-ca0106
- -----------------
-
- Module for Creative Audigy LS and SB Live 24bit
-
- This module supports multiple cards.
-
-
- Module snd-cmi8330
- ------------------
-
- Module for sound cards based on C-Media CMI8330 ISA chips.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- wssport - port # for CMI8330 chip (WSS)
- wssirq - IRQ # for CMI8330 chip (WSS)
- wssdma - first DMA # for CMI8330 chip (WSS)
- sbport - port # for CMI8330 chip (SB16)
- sbirq - IRQ # for CMI8330 chip (SB16)
- sbdma8 - 8bit DMA # for CMI8330 chip (SB16)
- sbdma16 - 16bit DMA # for CMI8330 chip (SB16)
- fmport - (optional) OPL3 I/O port
- mpuport - (optional) MPU401 I/O port
- mpuirq - (optional) MPU401 irq #
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-cmipci
- -----------------
-
- Module for C-Media CMI8338/8738/8768/8770 PCI sound cards.
-
- mpu_port - port address of MIDI interface (8338 only):
- 0x300,0x310,0x320,0x330 = legacy port,
- 0 = disable (default)
- fm_port - port address of OPL-3 FM synthesizer (8x38 only):
- 0x388 = legacy port,
- 1 = integrated PCI port (default on 8738),
- 0 = disable
- soft_ac3 - Software-conversion of raw SPDIF packets (model 033 only)
- (default = 1)
- joystick_port - Joystick port address (0 = disable, 1 = auto-detect)
-
- This module supports autoprobe and multiple cards.
-
- The power-management is supported.
-
- Module snd-cs4231
- -----------------
-
- Module for sound cards based on CS4231 ISA chips.
-
- port - port # for CS4231 chip
- mpu_port - port # for MPU-401 UART (optional), -1 = disable
- irq - IRQ # for CS4231 chip
- mpu_irq - IRQ # for MPU-401 UART
- dma1 - first DMA # for CS4231 chip
- dma2 - second DMA # for CS4231 chip
-
- This module supports multiple cards. This module does not support autoprobe
- thus main port must be specified!!! Other ports are optional.
-
- The power-management is supported.
-
- Module snd-cs4236
- -----------------
-
- Module for sound cards based on CS4232/CS4232A,
- CS4235/CS4236/CS4236B/CS4237B/
- CS4238B/CS4239 ISA chips.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for CS4236 chip (PnP setup - 0x534)
- cport - control port # for CS4236 chip (PnP setup - 0x120,0x210,0xf00)
- mpu_port - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
- fm_port - FM port # for CS4236 chip (PnP setup - 0x388), -1 = disable
- irq - IRQ # for CS4236 chip (5,7,9,11,12,15)
- mpu_irq - IRQ # for MPU-401 UART (9,11,12,15)
- dma1 - first DMA # for CS4236 chip (0,1,3)
- dma2 - second DMA # for CS4236 chip (0,1,3), -1 = disable
-
- This module supports multiple cards. This module does not support autoprobe
- (if ISA PnP is not used) thus main port and control port must be
- specified!!! Other ports are optional.
-
- The power-management is supported.
-
- This module is aliased as snd-cs4232 since it provides the old
- snd-cs4232 functionality, too.
-
- Module snd-cs4281
- -----------------
-
- Module for Cirrus Logic CS4281 soundchip.
-
- dual_codec - Secondary codec ID (0 = disable, default)
-
- This module supports multiple cards.
-
- The power-management is supported.
-
- Module snd-cs46xx
- -----------------
-
- Module for PCI sound cards based on CS4610/CS4612/CS4614/CS4615/CS4622/
- CS4624/CS4630/CS4280 PCI chips.
-
- external_amp - Force to enable external amplifier.
- thinkpad - Force to enable Thinkpad's CLKRUN control.
- mmap_valid - Support OSS mmap mode (default = 0).
-
- This module supports multiple cards and autoprobe.
- Usually external amp and CLKRUN controls are detected automatically
- from PCI sub vendor/device ids. If they don't work, give the options
- above explicitly.
-
- The power-management is supported.
-
- Module snd-cs5530
- _________________
-
- Module for Cyrix/NatSemi Geode 5530 chip.
-
- Module snd-cs5535audio
- ----------------------
-
- Module for multifunction CS5535 companion PCI device
-
- The power-management is supported.
-
- Module snd-ctxfi
- ----------------
-
- Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
- * Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
- * Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
- * Creative Sound Blaster X-Fi Titanium Professional Audio
- * Creative Sound Blaster X-Fi Titanium
- * Creative Sound Blaster X-Fi Elite Pro
- * Creative Sound Blaster X-Fi Platinum
- * Creative Sound Blaster X-Fi Fatal1ty
- * Creative Sound Blaster X-Fi XtremeGamer
- * Creative Sound Blaster X-Fi XtremeMusic
-
- reference_rate - reference sample rate, 44100 or 48000 (default)
- multiple - multiple to ref. sample rate, 1 or 2 (default)
- subsystem - override the PCI SSID for probing; the value
- consists of SSVID << 16 | SSDID. The default is
- zero, which means no override.
-
- This module supports multiple cards.
-
- Module snd-darla20
- ------------------
-
- Module for Echoaudio Darla20
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-darla24
- ------------------
-
- Module for Echoaudio Darla24
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-dt019x
- -----------------
-
- Module for Diamond Technologies DT-019X / Avance Logic ALS-007 (PnP
- only)
-
- This module supports multiple cards. This module is enabled only with
- ISA PnP support.
-
- The power-management is supported.
-
- Module snd-dummy
- ----------------
-
- Module for the dummy sound card. This "card" doesn't do any output
- or input, but you may use this module for any application which
- requires a sound card (like RealPlayer).
-
- pcm_devs - Number of PCM devices assigned to each card
- (default = 1, up to 4)
- pcm_substreams - Number of PCM substreams assigned to each PCM
- (default = 8, up to 128)
- hrtimer - Use hrtimer (=1, default) or system timer (=0)
- fake_buffer - Fake buffer allocations (default = 1)
-
- When multiple PCM devices are created, snd-dummy gives different
- behavior to each PCM device:
- 0 = interleaved with mmap support
- 1 = non-interleaved with mmap support
- 2 = interleaved without mmap
- 3 = non-interleaved without mmap
-
- As default, snd-dummy drivers doesn't allocate the real buffers
- but either ignores read/write or mmap a single dummy page to all
- buffer pages, in order to save the resources. If your apps need
- the read/ written buffer data to be consistent, pass fake_buffer=0
- option.
-
- The power-management is supported.
-
- Module snd-echo3g
- -----------------
-
- Module for Echoaudio 3G cards (Gina3G/Layla3G)
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-emu10k1
- ------------------
-
- Module for EMU10K1/EMU10k2 based PCI sound cards.
- * Sound Blaster Live!
- * Sound Blaster PCI 512
- * Emu APS (partially supported)
- * Sound Blaster Audigy
-
- extin - bitmap of available external inputs for FX8010 (see bellow)
- extout - bitmap of available external outputs for FX8010 (see bellow)
- seq_ports - allocated sequencer ports (4 by default)
- max_synth_voices - limit of voices used for wavetable (64 by default)
- max_buffer_size - specifies the maximum size of wavetable/pcm buffers
- given in MB unit. Default value is 128.
- enable_ir - enable IR
-
- This module supports multiple cards and autoprobe.
-
- Input & Output configurations [extin/extout]
- * Creative Card wo/Digital out [0x0003/0x1f03]
- * Creative Card w/Digital out [0x0003/0x1f0f]
- * Creative Card w/Digital CD in [0x000f/0x1f0f]
- * Creative Card wo/Digital out + LiveDrive [0x3fc3/0x1fc3]
- * Creative Card w/Digital out + LiveDrive [0x3fc3/0x1fcf]
- * Creative Card w/Digital CD in + LiveDrive [0x3fcf/0x1fcf]
- * Creative Card wo/Digital out + Digital I/O 2 [0x0fc3/0x1f0f]
- * Creative Card w/Digital out + Digital I/O 2 [0x0fc3/0x1f0f]
- * Creative Card w/Digital CD in + Digital I/O 2 [0x0fcf/0x1f0f]
- * Creative Card 5.1/w Digital out + LiveDrive [0x3fc3/0x1fff]
- * Creative Card 5.1 (c) 2003 [0x3fc3/0x7cff]
- * Creative Card all ins and outs [0x3fff/0x7fff]
-
- The power-management is supported.
-
- Module snd-emu10k1x
- -------------------
-
- Module for Creative Emu10k1X (SB Live Dell OEM version)
-
- This module supports multiple cards.
-
- Module snd-ens1370
- ------------------
-
- Module for Ensoniq AudioPCI ES1370 PCI sound cards.
- * SoundBlaster PCI 64
- * SoundBlaster PCI 128
-
- joystick - Enable joystick (default off)
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-ens1371
- ------------------
-
- Module for Ensoniq AudioPCI ES1371 PCI sound cards.
- * SoundBlaster PCI 64
- * SoundBlaster PCI 128
- * SoundBlaster Vibra PCI
-
- joystick_port - port # for joystick (0x200,0x208,0x210,0x218),
- 0 = disable (default), 1 = auto-detect
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-es1688
- -----------------
-
- Module for ESS AudioDrive ES-1688 and ES-688 sound cards.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
- mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
- mpu_irq - IRQ # for MPU-401 port (5,7,9,10)
- fm_port - port # for OPL3 (option; share the same port as default)
-
- with isapnp=0, the following additional options are available:
- port - port # for ES-1688 chip (0x220,0x240,0x260)
- irq - IRQ # for ES-1688 chip (5,7,9,10)
- dma8 - DMA # for ES-1688 chip (0,1,3)
-
- This module supports multiple cards and autoprobe (without MPU-401 port)
- and PnP with the ES968 chip.
-
- Module snd-es18xx
- -----------------
-
- Module for ESS AudioDrive ES-18xx sound cards.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for ES-18xx chip (0x220,0x240,0x260)
- mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
- fm_port - port # for FM (optional, not used)
- irq - IRQ # for ES-18xx chip (5,7,9,10)
- dma1 - first DMA # for ES-18xx chip (0,1,3)
- dma2 - first DMA # for ES-18xx chip (0,1,3)
-
- This module supports multiple cards, ISA PnP and autoprobe (without MPU-401
- port if native ISA PnP routines are not used).
- When dma2 is equal with dma1, the driver works as half-duplex.
-
- The power-management is supported.
-
- Module snd-es1938
- -----------------
-
- Module for sound cards based on ESS Solo-1 (ES1938,ES1946) chips.
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-es1968
- -----------------
-
- Module for sound cards based on ESS Maestro-1/2/2E (ES1968/ES1978) chips.
-
- total_bufsize - total buffer size in kB (1-4096kB)
- pcm_substreams_p - playback channels (1-8, default=2)
- pcm_substreams_c - capture channels (1-8, default=0)
- clock - clock (0 = auto-detection)
- use_pm - support the power-management (0 = off, 1 = on,
- 2 = auto (default))
- enable_mpu - enable MPU401 (0 = off, 1 = on, 2 = auto (default))
- joystick - enable joystick (default off)
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-fm801
- ----------------
-
- Module for ForteMedia FM801 based PCI sound cards.
-
- tea575x_tuner - Enable TEA575x tuner
- - 1 = MediaForte 256-PCS
- - 2 = MediaForte 256-PCPR
- - 3 = MediaForte 64-PCR
- - High 16-bits are video (radio) device number + 1
- - example: 0x10002 (MediaForte 256-PCPR, device 1)
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-gina20
- -----------------
-
- Module for Echoaudio Gina20
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-gina24
- -----------------
-
- Module for Echoaudio Gina24
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-gusclassic
- ---------------------
-
- Module for Gravis UltraSound Classic sound card.
-
- port - port # for GF1 chip (0x220,0x230,0x240,0x250,0x260)
- irq - IRQ # for GF1 chip (3,5,9,11,12,15)
- dma1 - DMA # for GF1 chip (1,3,5,6,7)
- dma2 - DMA # for GF1 chip (1,3,5,6,7,-1=disable)
- joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
- voices - GF1 voices limit (14-32)
- pcm_voices - reserved PCM voices
-
- This module supports multiple cards and autoprobe.
-
- Module snd-gusextreme
- ---------------------
-
- Module for Gravis UltraSound Extreme (Synergy ViperMax) sound card.
-
- port - port # for ES-1688 chip (0x220,0x230,0x240,0x250,0x260)
- gf1_port - port # for GF1 chip (0x210,0x220,0x230,0x240,0x250,0x260,0x270)
- mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable
- irq - IRQ # for ES-1688 chip (5,7,9,10)
- gf1_irq - IRQ # for GF1 chip (3,5,9,11,12,15)
- mpu_irq - IRQ # for MPU-401 port (5,7,9,10)
- dma8 - DMA # for ES-1688 chip (0,1,3)
- dma1 - DMA # for GF1 chip (1,3,5,6,7)
- joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
- voices - GF1 voices limit (14-32)
- pcm_voices - reserved PCM voices
-
- This module supports multiple cards and autoprobe (without MPU-401 port).
-
- Module snd-gusmax
- -----------------
-
- Module for Gravis UltraSound MAX sound card.
-
- port - port # for GF1 chip (0x220,0x230,0x240,0x250,0x260)
- irq - IRQ # for GF1 chip (3,5,9,11,12,15)
- dma1 - DMA # for GF1 chip (1,3,5,6,7)
- dma2 - DMA # for GF1 chip (1,3,5,6,7,-1=disable)
- joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
- voices - GF1 voices limit (14-32)
- pcm_voices - reserved PCM voices
-
- This module supports multiple cards and autoprobe.
-
- Module snd-hda-intel
- --------------------
-
- Module for Intel HD Audio (ICH6, ICH6M, ESB2, ICH7, ICH8, ICH9, ICH10,
- PCH, SCH),
- ATI SB450, SB600, R600, RS600, RS690, RS780, RV610, RV620,
- RV630, RV635, RV670, RV770,
- VIA VT8251/VT8237A,
- SIS966, ULI M5461
-
- [Multiple options for each card instance]
- model - force the model name
- position_fix - Fix DMA pointer
- -1 = system default: choose appropriate one per controller
- hardware
- 0 = auto: falls back to LPIB when POSBUF doesn't work
- 1 = use LPIB
- 2 = POSBUF: use position buffer
- 3 = VIACOMBO: VIA-specific workaround for capture
- 4 = COMBO: use LPIB for playback, auto for capture stream
- probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
- When the bit 8 (0x100) is set, the lower 8 bits are used
- as the "fixed" codec slots; i.e. the driver probes the
- slots regardless what hardware reports back
- probe_only - Only probing and no codec initialization (default=off);
- Useful to check the initial codec status for debugging
- bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
- Passing -1 will make the driver to choose the appropriate
- value based on the controller chip.
- patch - Specifies the early "patch" files to modify the HD-audio
- setup before initializing the codecs. This option is
- available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
- See HD-Audio.txt for details.
- beep_mode - Selects the beep registration mode (0=off, 1=on); default
- value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig.
-
- [Single (global) options]
- single_cmd - Use single immediate commands to communicate with
- codecs (for debugging only)
- enable_msi - Enable Message Signaled Interrupt (MSI) (default = off)
- power_save - Automatic power-saving timeout (in second, 0 =
- disable)
- power_save_controller - Reset HD-audio controller in power-saving mode
- (default = on)
- align_buffer_size - Force rounding of buffer/period sizes to multiples
- of 128 bytes. This is more efficient in terms of memory
- access but isn't required by the HDA spec and prevents
- users from specifying exact period/buffer sizes.
- (default = on)
- snoop - Enable/disable snooping (default = on)
-
- This module supports multiple cards and autoprobe.
-
- See Documentation/sound/alsa/HD-Audio.txt for more details about
- HD-audio driver.
-
- Each codec may have a model table for different configurations.
- If your machine isn't listed there, the default (usually minimal)
- configuration is set up. You can pass "model=<name>" option to
- specify a certain model in such a case. There are different
- models depending on the codec chip. The list of available models
- is found in HD-Audio-Models.txt
-
- The model name "generic" is treated as a special case. When this
- model is given, the driver uses the generic codec parser without
- "codec-patch". It's sometimes good for testing and debugging.
-
- If the default configuration doesn't work and one of the above
- matches with your device, report it together with alsa-info.sh
- output (with --no-upload option) to kernel bugzilla or alsa-devel
- ML (see the section "Links and Addresses").
-
- power_save and power_save_controller options are for power-saving
- mode. See powersave.txt for details.
-
- Note 2: If you get click noises on output, try the module option
- position_fix=1 or 2. position_fix=1 will use the SD_LPIB
- register value without FIFO size correction as the current
- DMA pointer. position_fix=2 will make the driver to use
- the position buffer instead of reading SD_LPIB register.
- (Usually SD_LPIB register is more accurate than the
- position buffer.)
-
- position_fix=3 is specific to VIA devices. The position
- of the capture stream is checked from both LPIB and POSBUF
- values. position_fix=4 is a combination mode, using LPIB
- for playback and POSBUF for capture.
-
- NB: If you get many "azx_get_response timeout" messages at
- loading, it's likely a problem of interrupts (e.g. ACPI irq
- routing). Try to boot with options like "pci=noacpi". Also, you
- can try "single_cmd=1" module option. This will switch the
- communication method between HDA controller and codecs to the
- single immediate commands instead of CORB/RIRB. Basically, the
- single command mode is provided only for BIOS, and you won't get
- unsolicited events, too. But, at least, this works independently
- from the irq. Remember this is a last resort, and should be
- avoided as much as possible...
-
- MORE NOTES ON "azx_get_response timeout" PROBLEMS:
- On some hardware, you may need to add a proper probe_mask option
- to avoid the "azx_get_response timeout" problem above, instead.
- This occurs when the access to non-existing or non-working codec slot
- (likely a modem one) causes a stall of the communication via HD-audio
- bus. You can see which codec slots are probed by enabling
- CONFIG_SND_DEBUG_VERBOSE, or simply from the file name of the codec
- proc files. Then limit the slots to probe by probe_mask option.
- For example, probe_mask=1 means to probe only the first slot, and
- probe_mask=4 means only the third slot.
-
- The power-management is supported.
-
- Module snd-hdsp
- ---------------
-
- Module for RME Hammerfall DSP audio interface(s)
-
- This module supports multiple cards.
-
- Note: The firmware data can be automatically loaded via hotplug
- when CONFIG_FW_LOADER is set. Otherwise, you need to load
- the firmware via hdsploader utility included in alsa-tools
- package.
- The firmware data is found in alsa-firmware package.
-
- Note: snd-page-alloc module does the job which snd-hammerfall-mem
- module did formerly. It will allocate the buffers in advance
- when any HDSP cards are found. To make the buffer
- allocation sure, load snd-page-alloc module in the early
- stage of boot sequence. See "Early Buffer Allocation"
- section.
-
- Module snd-hdspm
- ----------------
-
- Module for RME HDSP MADI board.
-
- precise_ptr - Enable precise pointer, or disable.
- line_outs_monitor - Send playback streams to analog outs by default.
- enable_monitor - Enable Analog Out on Channel 63/64 by default.
-
- See hdspm.txt for details.
-
- Module snd-ice1712
- ------------------
-
- Module for Envy24 (ICE1712) based PCI sound cards.
- * MidiMan M Audio Delta 1010
- * MidiMan M Audio Delta 1010LT
- * MidiMan M Audio Delta DiO 2496
- * MidiMan M Audio Delta 66
- * MidiMan M Audio Delta 44
- * MidiMan M Audio Delta 410
- * MidiMan M Audio Audiophile 2496
- * TerraTec EWS 88MT
- * TerraTec EWS 88D
- * TerraTec EWX 24/96
- * TerraTec DMX 6Fire
- * TerraTec Phase 88
- * Hoontech SoundTrack DSP 24
- * Hoontech SoundTrack DSP 24 Value
- * Hoontech SoundTrack DSP 24 Media 7.1
- * Event Electronics, EZ8
- * Digigram VX442
- * Lionstracs, Mediastaton
- * Terrasoniq TS 88
-
- model - Use the given board model, one of the following:
- delta1010, dio2496, delta66, delta44, audiophile, delta410,
- delta1010lt, vx442, ewx2496, ews88mt, ews88mt_new, ews88d,
- dmx6fire, dsp24, dsp24_value, dsp24_71, ez8,
- phase88, mediastation
- omni - Omni I/O support for MidiMan M-Audio Delta44/66
- cs8427_timeout - reset timeout for the CS8427 chip (S/PDIF transceiver)
- in msec resolution, default value is 500 (0.5 sec)
-
- This module supports multiple cards and autoprobe. Note: The consumer part
- is not used with all Envy24 based cards (for example in the MidiMan Delta
- serie).
-
- Note: The supported board is detected by reading EEPROM or PCI
- SSID (if EEPROM isn't available). You can override the
- model by passing "model" module option in case that the
- driver isn't configured properly or you want to try another
- type for testing.
-
- Module snd-ice1724
- ------------------
-
- Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards.
- * MidiMan M Audio Revolution 5.1
- * MidiMan M Audio Revolution 7.1
- * MidiMan M Audio Audiophile 192
- * AMP Ltd AUDIO2000
- * TerraTec Aureon 5.1 Sky
- * TerraTec Aureon 7.1 Space
- * TerraTec Aureon 7.1 Universe
- * TerraTec Phase 22
- * TerraTec Phase 28
- * AudioTrak Prodigy 7.1
- * AudioTrak Prodigy 7.1 LT
- * AudioTrak Prodigy 7.1 XT
- * AudioTrak Prodigy 7.1 HIFI
- * AudioTrak Prodigy 7.1 HD2
- * AudioTrak Prodigy 192
- * Pontis MS300
- * Albatron K8X800 Pro II
- * Chaintech ZNF3-150
- * Chaintech ZNF3-250
- * Chaintech 9CJS
- * Chaintech AV-710
- * Shuttle SN25P
- * Onkyo SE-90PCI
- * Onkyo SE-200PCI
- * ESI Juli@
- * ESI Maya44
- * Hercules Fortissimo IV
- * EGO-SYS WaveTerminal 192M
-
- model - Use the given board model, one of the following:
- revo51, revo71, amp2000, prodigy71, prodigy71lt,
- prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
- juli, aureon51, aureon71, universe, ap192, k8x800,
- phase22, phase28, ms300, av710, se200pci, se90pci,
- fortissimo4, sn25p, WT192M, maya44
-
- This module supports multiple cards and autoprobe.
-
- Note: The supported board is detected by reading EEPROM or PCI
- SSID (if EEPROM isn't available). You can override the
- model by passing "model" module option in case that the
- driver isn't configured properly or you want to try another
- type for testing.
-
- Module snd-indigo
- -----------------
-
- Module for Echoaudio Indigo
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-indigodj
- -------------------
-
- Module for Echoaudio Indigo DJ
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-indigoio
- -------------------
-
- Module for Echoaudio Indigo IO
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-intel8x0
- -------------------
-
- Module for AC'97 motherboards from Intel and compatibles.
- * Intel i810/810E, i815, i820, i830, i84x, MX440
- ICH5, ICH6, ICH7, 6300ESB, ESB2
- * SiS 7012 (SiS 735)
- * NVidia NForce, NForce2, NForce3, MCP04, CK804
- CK8, CK8S, MCP501
- * AMD AMD768, AMD8111
- * ALi m5455
-
- ac97_clock - AC'97 codec clock base (0 = auto-detect)
- ac97_quirk - AC'97 workaround for strange hardware
- See "AC97 Quirk Option" section below.
- buggy_irq - Enable workaround for buggy interrupts on some
- motherboards (default yes on nForce chips,
- otherwise off)
- buggy_semaphore - Enable workaround for hardware with buggy
- semaphores (e.g. on some ASUS laptops)
- (default off)
- spdif_aclink - Use S/PDIF over AC-link instead of direct connection
- from the controller chip
- (0 = off, 1 = on, -1 = default)
-
- This module supports one chip and autoprobe.
-
- Note: the latest driver supports auto-detection of chip clock.
- if you still encounter too fast playback, specify the clock
- explicitly via the module option "ac97_clock=41194".
-
- Joystick/MIDI ports are not supported by this driver. If your
- motherboard has these devices, use the ns558 or snd-mpu401
- modules, respectively.
-
- The power-management is supported.
-
- Module snd-intel8x0m
- --------------------
-
- Module for Intel ICH (i8x0) chipset MC97 modems.
- * Intel i810/810E, i815, i820, i830, i84x, MX440
- ICH5, ICH6, ICH7
- * SiS 7013 (SiS 735)
- * NVidia NForce, NForce2, NForce2s, NForce3
- * AMD AMD8111
- * ALi m5455
-
- ac97_clock - AC'97 codec clock base (0 = auto-detect)
-
- This module supports one card and autoprobe.
-
- Note: The default index value of this module is -2, i.e. the first
- slot is excluded.
-
- The power-management is supported.
-
- Module snd-interwave
- --------------------
-
- Module for Gravis UltraSound PnP, Dynasonic 3-D/Pro, STB Sound Rage 32
- and other sound cards based on AMD InterWave (tm) chip.
-
- joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
- midi - 1 = MIDI UART enable, 0 = MIDI UART disable (default)
- pcm_voices - reserved PCM voices for the synthesizer (default 2)
- effect - 1 = InterWave effects enable (default 0);
- requires 8 voices
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
- irq - IRQ # for InterWave chip (3,5,9,11,12,15)
- dma1 - DMA # for InterWave chip (0,1,3,5,6,7)
- dma2 - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
-
- This module supports multiple cards, autoprobe and ISA PnP.
-
- Module snd-interwave-stb
- ------------------------
-
- Module for UltraSound 32-Pro (sound card from STB used by Compaq)
- and other sound cards based on AMD InterWave (tm) chip with TEA6330T
- circuit for extended control of bass, treble and master volume.
-
- joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
- midi - 1 = MIDI UART enable, 0 = MIDI UART disable (default)
- pcm_voices - reserved PCM voices for the synthesizer (default 2)
- effect - 1 = InterWave effects enable (default 0);
- requires 8 voices
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
- port_tc - tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
- irq - IRQ # for InterWave chip (3,5,9,11,12,15)
- dma1 - DMA # for InterWave chip (0,1,3,5,6,7)
- dma2 - DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
-
- This module supports multiple cards, autoprobe and ISA PnP.
-
- Module snd-jazz16
- -------------------
-
- Module for Media Vision Jazz16 chipset. The chipset consists of 3 chips:
- MVD1216 + MVA416 + MVA514.
-
- port - port # for SB DSP chip (0x210,0x220,0x230,0x240,0x250,0x260)
- irq - IRQ # for SB DSP chip (3,5,7,9,10,15)
- dma8 - DMA # for SB DSP chip (1,3)
- dma16 - DMA # for SB DSP chip (5,7)
- mpu_port - MPU-401 port # (0x300,0x310,0x320,0x330)
- mpu_irq - MPU-401 irq # (2,3,5,7)
-
- This module supports multiple cards.
-
- Module snd-korg1212
- -------------------
-
- Module for Korg 1212 IO PCI card
-
- This module supports multiple cards.
-
- Module snd-layla20
- ------------------
-
- Module for Echoaudio Layla20
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-layla24
- ------------------
-
- Module for Echoaudio Layla24
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-lola
- ---------------
-
- Module for Digigram Lola PCI-e boards
-
- This module supports multiple cards.
-
- Module snd-lx6464es
- -------------------
-
- Module for Digigram LX6464ES boards
-
- This module supports multiple cards.
-
- Module snd-maestro3
- -------------------
-
- Module for Allegro/Maestro3 chips
-
- external_amp - enable external amp (enabled by default)
- amp_gpio - GPIO pin number for external amp (0-15) or
- -1 for default pin (8 for allegro, 1 for
- others)
-
- This module supports autoprobe and multiple chips.
-
- Note: the binding of amplifier is dependent on hardware.
- If there is no sound even though all channels are unmuted, try to
- specify other gpio connection via amp_gpio option.
- For example, a Panasonic notebook might need "amp_gpio=0x0d"
- option.
-
- The power-management is supported.
-
- Module snd-mia
- ---------------
-
- Module for Echoaudio Mia
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-miro
- ---------------
-
- Module for Miro soundcards: miroSOUND PCM 1 pro,
- miroSOUND PCM 12,
- miroSOUND PCM 20 Radio.
-
- port - Port # (0x530,0x604,0xe80,0xf40)
- irq - IRQ # (5,7,9,10,11)
- dma1 - 1st dma # (0,1,3)
- dma2 - 2nd dma # (0,1)
- mpu_port - MPU-401 port # (0x300,0x310,0x320,0x330)
- mpu_irq - MPU-401 irq # (5,7,9,10)
- fm_port - FM Port # (0x388)
- wss - enable WSS mode
- ide - enable onboard ide support
-
- Module snd-mixart
- -----------------
-
- Module for Digigram miXart8 sound cards.
-
- This module supports multiple cards.
- Note: One miXart8 board will be represented as 4 alsa cards.
- See MIXART.txt for details.
-
- When the driver is compiled as a module and the hotplug firmware
- is supported, the firmware data is loaded via hotplug automatically.
- Install the necessary firmware files in alsa-firmware package.
- When no hotplug fw loader is available, you need to load the
- firmware via mixartloader utility in alsa-tools package.
-
- Module snd-mona
- ---------------
-
- Module for Echoaudio Mona
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
-
- Module snd-mpu401
- -----------------
-
- Module for MPU-401 UART devices.
-
- port - port number or -1 (disable)
- irq - IRQ number or -1 (disable)
- pnp - PnP detection - 0 = disable, 1 = enable (default)
-
- This module supports multiple devices and PnP.
-
- Module snd-msnd-classic
- -----------------------
-
- Module for Turtle Beach MultiSound Classic, Tahiti or Monterey
- soundcards.
-
- io - Port # for msnd-classic card
- irq - IRQ # for msnd-classic card
- mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
- 0xe0000 or 0xe8000)
- write_ndelay - enable write ndelay (default = 1)
- calibrate_signal - calibrate signal (default = 0)
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
- digital - Digital daughterboard present (default = 0)
- cfg - Config port (0x250, 0x260 or 0x270) default = PnP
- reset - Reset all devices
- mpu_io - MPU401 I/O port
- mpu_irq - MPU401 irq#
- ide_io0 - IDE port #0
- ide_io1 - IDE port #1
- ide_irq - IDE irq#
- joystick_io - Joystick I/O port
-
- The driver requires firmware files "turtlebeach/msndinit.bin" and
- "turtlebeach/msndperm.bin" in the proper firmware directory.
-
- See Documentation/sound/oss/MultiSound for important information
- about this driver. Note that it has been discontinued, but the
- Voyetra Turtle Beach knowledge base entry for it is still available
- at
- http://www.turtlebeach.com
-
- Module snd-msnd-pinnacle
- ------------------------
-
- Module for Turtle Beach MultiSound Pinnacle/Fiji soundcards.
-
- io - Port # for pinnacle/fiji card
- irq - IRQ # for pinnalce/fiji card
- mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
- 0xe0000 or 0xe8000)
- write_ndelay - enable write ndelay (default = 1)
- calibrate_signal - calibrate signal (default = 0)
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- The driver requires firmware files "turtlebeach/pndspini.bin" and
- "turtlebeach/pndsperm.bin" in the proper firmware directory.
-
- Module snd-mtpav
- ----------------
-
- Module for MOTU MidiTimePiece AV multiport MIDI (on the parallel
- port).
-
- port - I/O port # for MTPAV (0x378,0x278, default=0x378)
- irq - IRQ # for MTPAV (7,5, default=7)
- hwports - number of supported hardware ports, default=8.
-
- Module supports only 1 card. This module has no enable option.
-
- Module snd-mts64
- ----------------
-
- Module for Ego Systems (ESI) Miditerminal 4140
-
- This module supports multiple devices.
- Requires parport (CONFIG_PARPORT).
-
- Module snd-nm256
- ----------------
-
- Module for NeoMagic NM256AV/ZX chips
-
- playback_bufsize - max playback frame size in kB (4-128kB)
- capture_bufsize - max capture frame size in kB (4-128kB)
- force_ac97 - 0 or 1 (disabled by default)
- buffer_top - specify buffer top address
- use_cache - 0 or 1 (disabled by default)
- vaio_hack - alias buffer_top=0x25a800
- reset_workaround - enable AC97 RESET workaround for some laptops
- reset_workaround2 - enable extended AC97 RESET workaround for some
- other laptops
-
- This module supports one chip and autoprobe.
-
- The power-management is supported.
-
- Note: on some notebooks the buffer address cannot be detected
- automatically, or causes hang-up during initialization.
- In such a case, specify the buffer top address explicitly via
- the buffer_top option.
- For example,
- Sony F250: buffer_top=0x25a800
- Sony F270: buffer_top=0x272800
- The driver supports only ac97 codec. It's possible to force
- to initialize/use ac97 although it's not detected. In such a
- case, use force_ac97=1 option - but *NO* guarantee whether it
- works!
-
- Note: The NM256 chip can be linked internally with non-AC97
- codecs. This driver supports only the AC97 codec, and won't work
- with machines with other (most likely CS423x or OPL3SAx) chips,
- even though the device is detected in lspci. In such a case, try
- other drivers, e.g. snd-cs4232 or snd-opl3sa2. Some has ISA-PnP
- but some doesn't have ISA PnP. You'll need to specify isapnp=0
- and proper hardware parameters in the case without ISA PnP.
-
- Note: some laptops need a workaround for AC97 RESET. For the
- known hardware like Dell Latitude LS and Sony PCG-F305, this
- workaround is enabled automatically. For other laptops with a
- hard freeze, you can try reset_workaround=1 option.
-
- Note: Dell Latitude CSx laptops have another problem regarding
- AC97 RESET. On these laptops, reset_workaround2 option is
- turned on as default. This option is worth to try if the
- previous reset_workaround option doesn't help.
-
- Note: This driver is really crappy. It's a porting from the
- OSS driver, which is a result of black-magic reverse engineering.
- The detection of codec will fail if the driver is loaded *after*
- X-server as described above. You might be able to force to load
- the module, but it may result in hang-up. Hence, make sure that
- you load this module *before* X if you encounter this kind of
- problem.
-
- Module snd-opl3sa2
- ------------------
-
- Module for Yamaha OPL3-SA2/SA3 sound cards.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - control port # for OPL3-SA chip (0x370)
- sb_port - SB port # for OPL3-SA chip (0x220,0x240)
- wss_port - WSS port # for OPL3-SA chip (0x530,0xe80,0xf40,0x604)
- midi_port - port # for MPU-401 UART (0x300,0x330), -1 = disable
- fm_port - FM port # for OPL3-SA chip (0x388), -1 = disable
- irq - IRQ # for OPL3-SA chip (5,7,9,10)
- dma1 - first DMA # for Yamaha OPL3-SA chip (0,1,3)
- dma2 - second DMA # for Yamaha OPL3-SA chip (0,1,3), -1 = disable
-
- This module supports multiple cards and ISA PnP. It does not support
- autoprobe (if ISA PnP is not used) thus all ports must be specified!!!
-
- The power-management is supported.
-
- Module snd-opti92x-ad1848
- -------------------------
-
- Module for sound cards based on OPTi 82c92x and Analog Devices AD1848 chips.
- Module works with OAK Mozart cards as well.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for WSS chip (0x530,0xe80,0xf40,0x604)
- mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
- fm_port - port # for OPL3 device (0x388)
- irq - IRQ # for WSS chip (5,7,9,10,11)
- mpu_irq - IRQ # for MPU-401 UART (5,7,9,10)
- dma1 - first DMA # for WSS chip (0,1,3)
-
- This module supports only one card, autoprobe and PnP.
-
- Module snd-opti92x-cs4231
- -------------------------
-
- Module for sound cards based on OPTi 82c92x and Crystal CS4231 chips.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for WSS chip (0x530,0xe80,0xf40,0x604)
- mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
- fm_port - port # for OPL3 device (0x388)
- irq - IRQ # for WSS chip (5,7,9,10,11)
- mpu_irq - IRQ # for MPU-401 UART (5,7,9,10)
- dma1 - first DMA # for WSS chip (0,1,3)
- dma2 - second DMA # for WSS chip (0,1,3)
-
- This module supports only one card, autoprobe and PnP.
-
- Module snd-opti93x
- ------------------
-
- Module for sound cards based on OPTi 82c93x chips.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for WSS chip (0x530,0xe80,0xf40,0x604)
- mpu_port - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
- fm_port - port # for OPL3 device (0x388)
- irq - IRQ # for WSS chip (5,7,9,10,11)
- mpu_irq - IRQ # for MPU-401 UART (5,7,9,10)
- dma1 - first DMA # for WSS chip (0,1,3)
- dma2 - second DMA # for WSS chip (0,1,3)
-
- This module supports only one card, autoprobe and PnP.
-
- Module snd-oxygen
- -----------------
-
- Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
- * Asound A-8788
- * Asus Xonar DG/DGX
- * AuzenTech X-Meridian
- * AuzenTech X-Meridian 2G
- * Bgears b-Enspirer
- * Club3D Theatron DTS
- * HT-Omega Claro (plus)
- * HT-Omega Claro halo (XT)
- * Kuroutoshikou CMI8787-HG2PCI
- * Razer Barracuda AC-1
- * Sondigo Inferno
- * TempoTec HiFier Fantasia
- * TempoTec HiFier Serenade
-
- This module supports autoprobe and multiple cards.
-
- Module snd-pcsp
- -----------------
-
- Module for internal PC-Speaker.
-
- nopcm - Disable PC-Speaker PCM sound. Only beeps remain.
- nforce_wa - enable NForce chipset workaround. Expect bad sound.
-
- This module supports system beeps, some kind of PCM playback and
- even a few mixer controls.
-
- Module snd-pcxhr
- ----------------
-
- Module for Digigram PCXHR boards
-
- This module supports multiple cards.
-
- Module snd-portman2x4
- ---------------------
-
- Module for Midiman Portman 2x4 parallel port MIDI interface
-
- This module supports multiple cards.
-
- Module snd-powermac (on ppc only)
- ---------------------------------
-
- Module for PowerMac, iMac and iBook on-board soundchips
-
- enable_beep - enable beep using PCM (enabled as default)
-
- Module supports autoprobe a chip.
-
- Note: the driver may have problems regarding endianness.
-
- The power-management is supported.
-
- Module snd-pxa2xx-ac97 (on arm only)
- ------------------------------------
-
- Module for AC97 driver for the Intel PXA2xx chip
-
- For ARM architecture only.
-
- The power-management is supported.
-
- Module snd-riptide
- ------------------
-
- Module for Conexant Riptide chip
-
- joystick_port - Joystick port # (default: 0x200)
- mpu_port - MPU401 port # (default: 0x330)
- opl3_port - OPL3 port # (default: 0x388)
-
- This module supports multiple cards.
- The driver requires the firmware loader support on kernel.
- You need to install the firmware file "riptide.hex" to the standard
- firmware path (e.g. /lib/firmware).
-
- Module snd-rme32
- ----------------
-
- Module for RME Digi32, Digi32 Pro and Digi32/8 (Sek'd Prodif32,
- Prodif96 and Prodif Gold) sound cards.
-
- This module supports multiple cards.
-
- Module snd-rme96
- ----------------
-
- Module for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST sound cards.
-
- This module supports multiple cards.
-
- Module snd-rme9652
- ------------------
-
- Module for RME Digi9652 (Hammerfall, Hammerfall-Light) sound cards.
-
- precise_ptr - Enable precise pointer (doesn't work reliably).
- (default = 0)
-
- This module supports multiple cards.
-
- Note: snd-page-alloc module does the job which snd-hammerfall-mem
- module did formerly. It will allocate the buffers in advance
- when any RME9652 cards are found. To make the buffer
- allocation sure, load snd-page-alloc module in the early
- stage of boot sequence. See "Early Buffer Allocation"
- section.
-
- Module snd-sa11xx-uda1341 (on arm only)
- ---------------------------------------
-
- Module for Philips UDA1341TS on Compaq iPAQ H3600 sound card.
-
- Module supports only one card.
- Module has no enable and index options.
-
- The power-management is supported.
-
- Module snd-sb8
- --------------
-
- Module for 8-bit SoundBlaster cards: SoundBlaster 1.0,
- SoundBlaster 2.0,
- SoundBlaster Pro
-
- port - port # for SB DSP chip (0x220,0x240,0x260)
- irq - IRQ # for SB DSP chip (5,7,9,10)
- dma8 - DMA # for SB DSP chip (1,3)
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-sb16 and snd-sbawe
- -----------------------------
-
- Module for 16-bit SoundBlaster cards: SoundBlaster 16 (PnP),
- SoundBlaster AWE 32 (PnP),
- SoundBlaster AWE 64 PnP
-
- mic_agc - Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
- csp - ASP/CSP chip support - 0 = disable (default), 1 = enable
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for SB DSP 4.x chip (0x220,0x240,0x260)
- mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disable
- awe_port - base port # for EMU8000 synthesizer (0x620,0x640,0x660)
- (snd-sbawe module only)
- irq - IRQ # for SB DSP 4.x chip (5,7,9,10)
- dma8 - 8-bit DMA # for SB DSP 4.x chip (0,1,3)
- dma16 - 16-bit DMA # for SB DSP 4.x chip (5,6,7)
-
- This module supports multiple cards, autoprobe and ISA PnP.
-
- Note: To use Vibra16X cards in 16-bit half duplex mode, you must
- disable 16bit DMA with dma16 = -1 module parameter.
- Also, all Sound Blaster 16 type cards can operate in 16-bit
- half duplex mode through 8-bit DMA channel by disabling their
- 16-bit DMA channel.
-
- The power-management is supported.
-
- Module snd-sc6000
- -----------------
-
- Module for Gallant SC-6000 soundcard and later models: SC-6600
- and SC-7000.
-
- port - Port # (0x220 or 0x240)
- mss_port - MSS Port # (0x530 or 0xe80)
- irq - IRQ # (5,7,9,10,11)
- mpu_irq - MPU-401 IRQ # (5,7,9,10) ,0 - no MPU-401 irq
- dma - DMA # (1,3,0)
- joystick - Enable gameport - 0 = disable (default), 1 = enable
-
- This module supports multiple cards.
-
- This card is also known as Audio Excel DSP 16 or Zoltrix AV302.
-
- Module snd-sscape
- -----------------
-
- Module for ENSONIQ SoundScape cards.
-
- port - Port # (PnP setup)
- wss_port - WSS Port # (PnP setup)
- irq - IRQ # (PnP setup)
- mpu_irq - MPU-401 IRQ # (PnP setup)
- dma - DMA # (PnP setup)
- dma2 - 2nd DMA # (PnP setup, -1 to disable)
- joystick - Enable gameport - 0 = disable (default), 1 = enable
-
- This module supports multiple cards.
-
- The driver requires the firmware loader support on kernel.
-
- Module snd-sun-amd7930 (on sparc only)
- --------------------------------------
-
- Module for AMD7930 sound chips found on Sparcs.
-
- This module supports multiple cards.
-
- Module snd-sun-cs4231 (on sparc only)
- -------------------------------------
-
- Module for CS4231 sound chips found on Sparcs.
-
- This module supports multiple cards.
-
- Module snd-sun-dbri (on sparc only)
- -----------------------------------
-
- Module for DBRI sound chips found on Sparcs.
-
- This module supports multiple cards.
-
- Module snd-wavefront
- --------------------
-
- Module for Turtle Beach Maui, Tropez and Tropez+ sound cards.
-
- use_cs4232_midi - Use CS4232 MPU-401 interface
- (inaccessibly located inside your computer)
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- cs4232_pcm_port - Port # for CS4232 PCM interface.
- cs4232_pcm_irq - IRQ # for CS4232 PCM interface (5,7,9,11,12,15).
- cs4232_mpu_port - Port # for CS4232 MPU-401 interface.
- cs4232_mpu_irq - IRQ # for CS4232 MPU-401 interface (9,11,12,15).
- ics2115_port - Port # for ICS2115
- ics2115_irq - IRQ # for ICS2115
- fm_port - FM OPL-3 Port #
- dma1 - DMA1 # for CS4232 PCM interface.
- dma2 - DMA2 # for CS4232 PCM interface.
-
- The below are options for wavefront_synth features:
- wf_raw - Assume that we need to boot the OS (default:no)
- If yes, then during driver loading, the state of the board is
- ignored, and we reset the board and load the firmware anyway.
- fx_raw - Assume that the FX process needs help (default:yes)
- If false, we'll leave the FX processor in whatever state it is
- when the driver is loaded. The default is to download the
- microprogram and associated coefficients to set it up for
- "default" operation, whatever that means.
- debug_default - Debug parameters for card initialization
- wait_usecs - How long to wait without sleeping, usecs
- (default:150)
- This magic number seems to give pretty optimal throughput
- based on my limited experimentation.
- If you want to play around with it and find a better value, be
- my guest. Remember, the idea is to get a number that causes us
- to just busy wait for as many WaveFront commands as possible,
- without coming up with a number so large that we hog the whole
- CPU.
- Specifically, with this number, out of about 134,000 status
- waits, only about 250 result in a sleep.
- sleep_interval - How long to sleep when waiting for reply
- (default: 100)
- sleep_tries - How many times to try sleeping during a wait
- (default: 50)
- ospath - Pathname to processed ICS2115 OS firmware
- (default:wavefront.os)
- The path name of the ISC2115 OS firmware. In the recent
- version, it's handled via firmware loader framework, so it
- must be installed in the proper path, typically,
- /lib/firmware.
- reset_time - How long to wait for a reset to take effect
- (default:2)
- ramcheck_time - How many seconds to wait for the RAM test
- (default:20)
- osrun_time - How many seconds to wait for the ICS2115 OS
- (default:10)
-
- This module supports multiple cards and ISA PnP.
-
- Note: the firmware file "wavefront.os" was located in the earlier
- version in /etc. Now it's loaded via firmware loader, and
- must be in the proper firmware path, such as /lib/firmware.
- Copy (or symlink) the file appropriately if you get an error
- regarding firmware downloading after upgrading the kernel.
-
- Module snd-sonicvibes
- ---------------------
-
- Module for S3 SonicVibes PCI sound cards.
- * PINE Schubert 32 PCI
-
- reverb - Reverb Enable - 1 = enable, 0 = disable (default)
- - SoundCard must have onboard SRAM for this.
- mge - Mic Gain Enable - 1 = enable, 0 = disable (default)
-
- This module supports multiple cards and autoprobe.
-
- Module snd-serial-u16550
- ------------------------
-
- Module for UART16550A serial MIDI ports.
-
- port - port # for UART16550A chip
- irq - IRQ # for UART16550A chip, -1 = poll mode
- speed - speed in bauds (9600,19200,38400,57600,115200)
- 38400 = default
- base - base for divisor in bauds (57600,115200,230400,460800)
- 115200 = default
- outs - number of MIDI ports in a serial port (1-4)
- 1 = default
- adaptor - Type of adaptor.
- 0 = Soundcanvas, 1 = MS-124T, 2 = MS-124W S/A,
- 3 = MS-124W M/B, 4 = Generic
-
- This module supports multiple cards. This module does not support autoprobe
- thus the main port must be specified!!! Other options are optional.
-
- Module snd-trident
- ------------------
-
- Module for Trident 4DWave DX/NX sound cards.
- * Best Union Miss Melody 4DWave PCI
- * HIS 4DWave PCI
- * Warpspeed ONSpeed 4DWave PCI
- * AzTech PCI 64-Q3D
- * Addonics SV 750
- * CHIC True Sound 4Dwave
- * Shark Predator4D-PCI
- * Jaton SonicWave 4D
- * SiS SI7018 PCI Audio
- * Hoontech SoundTrack Digital 4DWave NX
-
- pcm_channels - max channels (voices) reserved for PCM
- wavetable_size - max wavetable size in kB (4-?kb)
-
- This module supports multiple cards and autoprobe.
-
- The power-management is supported.
-
- Module snd-ua101
- ----------------
-
- Module for the Edirol UA-101/UA-1000 audio/MIDI interfaces.
-
- This module supports multiple devices, autoprobe and hotplugging.
-
- Module snd-usb-audio
- --------------------
-
- Module for USB audio and USB MIDI devices.
-
- vid - Vendor ID for the device (optional)
- pid - Product ID for the device (optional)
- nrpacks - Max. number of packets per URB (default: 8)
- device_setup - Device specific magic number (optional)
- - Influence depends on the device
- - Default: 0x0000
- ignore_ctl_error - Ignore any USB-controller regarding mixer
- interface (default: no)
- autoclock - Enable auto-clock selection for UAC2 devices
- (default: yes)
- quirk_alias - Quirk alias list, pass strings like
- "0123abcd:5678beef", which applies the existing
- quirk for the device 5678:beef to a new device
- 0123:abcd.
-
- This module supports multiple devices, autoprobe and hotplugging.
-
- NB: nrpacks parameter can be modified dynamically via sysfs.
- Don't put the value over 20. Changing via sysfs has no sanity
- check.
- NB: ignore_ctl_error=1 may help when you get an error at accessing
- the mixer element such as URB error -22. This happens on some
- buggy USB device or the controller.
- NB: quirk_alias option is provided only for testing / development.
- If you want to have a proper support, contact to upstream for
- adding the matching quirk in the driver code statically.
-
- Module snd-usb-caiaq
- --------------------
-
- Module for caiaq UB audio interfaces,
- * Native Instruments RigKontrol2
- * Native Instruments Kore Controller
- * Native Instruments Audio Kontrol 1
- * Native Instruments Audio 8 DJ
-
- This module supports multiple devices, autoprobe and hotplugging.
-
- Module snd-usb-usx2y
- --------------------
-
- Module for Tascam USB US-122, US-224 and US-428 devices.
-
- This module supports multiple devices, autoprobe and hotplugging.
-
- Note: you need to load the firmware via usx2yloader utility included
- in alsa-tools and alsa-firmware packages.
-
- Module snd-via82xx
- ------------------
-
- Module for AC'97 motherboards based on VIA 82C686A/686B, 8233,
- 8233A, 8233C, 8235, 8237 (south) bridge.
-
- mpu_port - 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup
- [VIA686A/686B only]
- joystick - Enable joystick (default off) [VIA686A/686B only]
- ac97_clock - AC'97 codec clock base (default 48000Hz)
- dxs_support - support DXS channels,
- 0 = auto (default), 1 = enable, 2 = disable,
- 3 = 48k only, 4 = no VRA, 5 = enable any sample
- rate and different sample rates on different
- channels
- [VIA8233/C, 8235, 8237 only]
- ac97_quirk - AC'97 workaround for strange hardware
- See "AC97 Quirk Option" section below.
-
- This module supports one chip and autoprobe.
-
- Note: on some SMP motherboards like MSI 694D the interrupts might
- not be generated properly. In such a case, please try to
- set the SMP (or MPS) version on BIOS to 1.1 instead of
- default value 1.4. Then the interrupt number will be
- assigned under 15. You might also upgrade your BIOS.
-
- Note: VIA8233/5/7 (not VIA8233A) can support DXS (direct sound)
- channels as the first PCM. On these channels, up to 4
- streams can be played at the same time, and the controller
- can perform sample rate conversion with separate rates for
- each channel.
- As default (dxs_support = 0), 48k fixed rate is chosen
- except for the known devices since the output is often
- noisy except for 48k on some mother boards due to the
- bug of BIOS.
- Please try once dxs_support=5 and if it works on other
- sample rates (e.g. 44.1kHz of mp3 playback), please let us
- know the PCI subsystem vendor/device id's (output of
- "lspci -nv").
- If dxs_support=5 does not work, try dxs_support=4; if it
- doesn't work too, try dxs_support=1. (dxs_support=1 is
- usually for old motherboards. The correct implemented
- board should work with 4 or 5.) If it still doesn't
- work and the default setting is ok, dxs_support=3 is the
- right choice. If the default setting doesn't work at all,
- try dxs_support=2 to disable the DXS channels.
- In any cases, please let us know the result and the
- subsystem vendor/device ids. See "Links and Addresses"
- below.
-
- Note: for the MPU401 on VIA823x, use snd-mpu401 driver
- additionally. The mpu_port option is for VIA686 chips only.
-
- The power-management is supported.
-
- Module snd-via82xx-modem
- ------------------------
-
- Module for VIA82xx AC97 modem
-
- ac97_clock - AC'97 codec clock base (default 48000Hz)
-
- This module supports one card and autoprobe.
-
- Note: The default index value of this module is -2, i.e. the first
- slot is excluded.
-
- The power-management is supported.
-
- Module snd-virmidi
- ------------------
-
- Module for virtual rawmidi devices.
- This module creates virtual rawmidi devices which communicate
- to the corresponding ALSA sequencer ports.
-
- midi_devs - MIDI devices # (1-4, default=4)
-
- This module supports multiple cards.
-
- Module snd-virtuoso
- -------------------
-
- Module for sound cards based on the Asus AV66/AV100/AV200 chips,
- i.e., Xonar D1, DX, D2, D2X, DS, DSX, Essence ST (Deluxe),
- Essence STX (II), HDAV1.3 (Deluxe), and HDAV1.3 Slim.
-
- This module supports autoprobe and multiple cards.
-
- Module snd-vx222
- ----------------
-
- Module for Digigram VX-Pocket VX222, V222 v2 and Mic cards.
-
- mic - Enable Microphone on V222 Mic (NYI)
- ibl - Capture IBL size. (default = 0, minimum size)
-
- This module supports multiple cards.
-
- When the driver is compiled as a module and the hotplug firmware
- is supported, the firmware data is loaded via hotplug automatically.
- Install the necessary firmware files in alsa-firmware package.
- When no hotplug fw loader is available, you need to load the
- firmware via vxloader utility in alsa-tools package. To invoke
- vxloader automatically, add the following to /etc/modprobe.d/alsa.conf
-
- install snd-vx222 /sbin/modprobe --first-time -i snd-vx222 && /usr/bin/vxloader
-
- (for 2.2/2.4 kernels, add "post-install /usr/bin/vxloader" to
- /etc/modules.conf, instead.)
- IBL size defines the interrupts period for PCM. The smaller size
- gives smaller latency but leads to more CPU consumption, too.
- The size is usually aligned to 126. As default (=0), the smallest
- size is chosen. The possible IBL values can be found in
- /proc/asound/cardX/vx-status proc file.
-
- The power-management is supported.
-
- Module snd-vxpocket
- -------------------
-
- Module for Digigram VX-Pocket VX2 and 440 PCMCIA cards.
-
- ibl - Capture IBL size. (default = 0, minimum size)
-
- This module supports multiple cards. The module is compiled only when
- PCMCIA is supported on kernel.
-
- With the older 2.6.x kernel, to activate the driver via the card
- manager, you'll need to set up /etc/pcmcia/vxpocket.conf. See the
- sound/pcmcia/vx/vxpocket.c. 2.6.13 or later kernel requires no
- longer require a config file.
-
- When the driver is compiled as a module and the hotplug firmware
- is supported, the firmware data is loaded via hotplug automatically.
- Install the necessary firmware files in alsa-firmware package.
- When no hotplug fw loader is available, you need to load the
- firmware via vxloader utility in alsa-tools package.
-
- About capture IBL, see the description of snd-vx222 module.
-
- Note: snd-vxp440 driver is merged to snd-vxpocket driver since
- ALSA 1.0.10.
-
- The power-management is supported.
-
- Module snd-ymfpci
- -----------------
-
- Module for Yamaha PCI chips (YMF72x, YMF74x & YMF75x).
-
- mpu_port - 0x300,0x330,0x332,0x334, 0 (disable) by default,
- 1 (auto-detect for YMF744/754 only)
- fm_port - 0x388,0x398,0x3a0,0x3a8, 0 (disable) by default
- 1 (auto-detect for YMF744/754 only)
- joystick_port - 0x201,0x202,0x204,0x205, 0 (disable) by default,
- 1 (auto-detect)
- rear_switch - enable shared rear/line-in switch (bool)
-
- This module supports autoprobe and multiple chips.
-
- The power-management is supported.
-
- Module snd-pdaudiocf
- --------------------
-
- Module for Sound Core PDAudioCF sound card.
-
- The power-management is supported.
-
-
-AC97 Quirk Option
-=================
-
-The ac97_quirk option is used to enable/override the workaround for
-specific devices on drivers for on-board AC'97 controllers like
-snd-intel8x0. Some hardware have swapped output pins between Master
-and Headphone, or Surround (thanks to confusion of AC'97
-specifications from version to version :-)
-
-The driver provides the auto-detection of known problematic devices,
-but some might be unknown or wrongly detected. In such a case, pass
-the proper value with this option.
-
-The following strings are accepted:
- - default Don't override the default setting
- - none Disable the quirk
- - hp_only Bind Master and Headphone controls as a single control
- - swap_hp Swap headphone and master controls
- - swap_surround Swap master and surround controls
- - ad_sharing For AD1985, turn on OMS bit and use headphone
- - alc_jack For ALC65x, turn on the jack sense mode
- - inv_eapd Inverted EAPD implementation
- - mute_led Bind EAPD bit for turning on/off mute LED
-
-For backward compatibility, the corresponding integer value -1, 0,
-... are accepted, too.
-
-For example, if "Master" volume control has no effect on your device
-but only "Headphone" does, pass ac97_quirk=hp_only module option.
-
-
-Configuring Non-ISAPNP Cards
-============================
-
-When the kernel is configured with ISA-PnP support, the modules
-supporting the isapnp cards will have module options "isapnp".
-If this option is set, *only* the ISA-PnP devices will be probed.
-For probing the non ISA-PnP cards, you have to pass "isapnp=0" option
-together with the proper i/o and irq configuration.
-
-When the kernel is configured without ISA-PnP support, isapnp option
-will be not built in.
-
-
-Module Autoloading Support
-==========================
-
-The ALSA drivers can be loaded automatically on demand by defining
-module aliases. The string 'snd-card-%1' is requested for ALSA native
-devices where %i is sound card number from zero to seven.
-
-To auto-load an ALSA driver for OSS services, define the string
-'sound-slot-%i' where %i means the slot number for OSS, which
-corresponds to the card index of ALSA. Usually, define this
-as the same card module.
-
-An example configuration for a single emu10k1 card is like below:
------ /etc/modprobe.d/alsa.conf
-alias snd-card-0 snd-emu10k1
-alias sound-slot-0 snd-emu10k1
------ /etc/modprobe.d/alsa.conf
-
-The available number of auto-loaded sound cards depends on the module
-option "cards_limit" of snd module. As default it's set to 1.
-To enable the auto-loading of multiple cards, specify the number of
-sound cards in that option.
-
-When multiple cards are available, it'd better to specify the index
-number for each card via module option, too, so that the order of
-cards is kept consistent.
-
-An example configuration for two sound cards is like below:
-
------ /etc/modprobe.d/alsa.conf
-# ALSA portion
-options snd cards_limit=2
-alias snd-card-0 snd-interwave
-alias snd-card-1 snd-ens1371
-options snd-interwave index=0
-options snd-ens1371 index=1
-# OSS/Free portion
-alias sound-slot-0 snd-interwave
-alias sound-slot-1 snd-ens1371
------ /etc/modprobe.d/alsa.conf
-
-In this example, the interwave card is always loaded as the first card
-(index 0) and ens1371 as the second (index 1).
-
-Alternative (and new) way to fixate the slot assignment is to use
-"slots" option of snd module. In the case above, specify like the
-following:
-
-options snd slots=snd-interwave,snd-ens1371
-
-Then, the first slot (#0) is reserved for snd-interwave driver, and
-the second (#1) for snd-ens1371. You can omit index option in each
-driver if slots option is used (although you can still have them at
-the same time as long as they don't conflict).
-
-The slots option is especially useful for avoiding the possible
-hot-plugging and the resultant slot conflict. For example, in the
-case above again, the first two slots are already reserved. If any
-other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
-snd-ens1371, it will be assigned to the third or later slot.
-
-When a module name is given with '!', the slot will be given for any
-modules but that name. For example, "slots=!snd-pcsp" will reserve
-the first slot for any modules but snd-pcsp.
-
-
-ALSA PCM devices to OSS devices mapping
-=======================================
-
-/dev/snd/pcmC0D0[c|p] -> /dev/audio0 (/dev/audio) -> minor 4
-/dev/snd/pcmC0D0[c|p] -> /dev/dsp0 (/dev/dsp) -> minor 3
-/dev/snd/pcmC0D1[c|p] -> /dev/adsp0 (/dev/adsp) -> minor 12
-/dev/snd/pcmC1D0[c|p] -> /dev/audio1 -> minor 4+16 = 20
-/dev/snd/pcmC1D0[c|p] -> /dev/dsp1 -> minor 3+16 = 19
-/dev/snd/pcmC1D1[c|p] -> /dev/adsp1 -> minor 12+16 = 28
-/dev/snd/pcmC2D0[c|p] -> /dev/audio2 -> minor 4+32 = 36
-/dev/snd/pcmC2D0[c|p] -> /dev/dsp2 -> minor 3+32 = 39
-/dev/snd/pcmC2D1[c|p] -> /dev/adsp2 -> minor 12+32 = 44
-
-The first number from /dev/snd/pcmC{X}D{Y}[c|p] expression means
-sound card number and second means device number. The ALSA devices
-have either 'c' or 'p' suffix indicating the direction, capture and
-playback, respectively.
-
-Please note that the device mapping above may be varied via the module
-options of snd-pcm-oss module.
-
-
-Proc interfaces (/proc/asound)
-==============================
-
-/proc/asound/card#/pcm#[cp]/oss
--------------------------------
- String "erase" - erase all additional information about OSS applications
- String "<app_name> <fragments> <fragment_size> [<options>]"
-
- <app_name> - name of application with (higher priority) or without path
- <fragments> - number of fragments or zero if auto
- <fragment_size> - size of fragment in bytes or zero if auto
- <options> - optional parameters
- - disable the application tries to open a pcm device for
- this channel but does not want to use it.
- (Cause a bug or mmap needs)
- It's good for Quake etc...
- - direct don't use plugins
- - block force block mode (rvplayer)
- - non-block force non-block mode
- - whole-frag write only whole fragments (optimization affecting
- playback only)
- - no-silence do not fill silence ahead to avoid clicks
- - buggy-ptr Returns the whitespace blocks in GETOPTR ioctl
- instead of filled blocks
-
- Example: echo "x11amp 128 16384" > /proc/asound/card0/pcm0p/oss
- echo "squake 0 0 disable" > /proc/asound/card0/pcm0c/oss
- echo "rvplayer 0 0 block" > /proc/asound/card0/pcm0p/oss
-
-
-Early Buffer Allocation
-=======================
-
-Some drivers (e.g. hdsp) require the large contiguous buffers, and
-sometimes it's too late to find such spaces when the driver module is
-actually loaded due to memory fragmentation. You can pre-allocate the
-PCM buffers by loading snd-page-alloc module and write commands to its
-proc file in prior, for example, in the early boot stage like
-/etc/init.d/*.local scripts.
-
-Reading the proc file /proc/drivers/snd-page-alloc shows the current
-usage of page allocation. In writing, you can send the following
-commands to the snd-page-alloc driver:
-
- - add VENDOR DEVICE MASK SIZE BUFFERS
-
- VENDOR and DEVICE are PCI vendor and device IDs. They take
- integer numbers (0x prefix is needed for the hex).
- MASK is the PCI DMA mask. Pass 0 if not restricted.
- SIZE is the size of each buffer to allocate. You can pass
- k and m suffix for KB and MB. The max number is 16MB.
- BUFFERS is the number of buffers to allocate. It must be greater
- than 0. The max number is 4.
-
- - erase
-
- This will erase the all pre-allocated buffers which are not in
- use.
-
-
-Links and Addresses
-===================
-
- ALSA project homepage
- http://www.alsa-project.org
-
- Kernel Bugzilla
- http://bugzilla.kernel.org/
-
- ALSA Developers ML
- mailto:alsa-devel@alsa-project.org
-
- alsa-info.sh script
- http://www.alsa-project.org/alsa-info.sh
+++ /dev/null
-
- Sound Blaster Audigy mixer / default DSP code
- ===========================================
-
-This is based on SB-Live-mixer.txt.
-
-The EMU10K2 chips have a DSP part which can be programmed to support
-various ways of sample processing, which is described here.
-(This article does not deal with the overall functionality of the
-EMU10K2 chips. See the manuals section for further details.)
-
-The ALSA driver programs this portion of chip by default code
-(can be altered later) which offers the following functionality:
-
-
-1) Digital mixer controls
--------------------------
-
-These controls are built using the DSP instructions. They offer extended
-functionality. Only the default build-in code in the ALSA driver is described
-here. Note that the controls work as attenuators: the maximum value is the
-neutral position leaving the signal unchanged. Note that if the same destination
-is mentioned in multiple controls, the signal is accumulated and can be wrapped
-(set to maximal or minimal value without checking of overflow).
-
-
-Explanation of used abbreviations:
-
-DAC - digital to analog converter
-ADC - analog to digital converter
-I2S - one-way three wire serial bus for digital sound by Philips Semiconductors
- (this standard is used for connecting standalone DAC and ADC converters)
-LFE - low frequency effects (subwoofer signal)
-AC97 - a chip containing an analog mixer, DAC and ADC converters
-IEC958 - S/PDIF
-FX-bus - the EMU10K2 chip has an effect bus containing 64 accumulators.
- Each of the synthesizer voices can feed its output to these accumulators
- and the DSP microcontroller can operate with the resulting sum.
-
-name='PCM Front Playback Volume',index=0
-
-This control is used to attenuate samples for left and right front PCM FX-bus
-accumulators. ALSA uses accumulators 8 and 9 for left and right front PCM
-samples for 5.1 playback. The result samples are forwarded to the front DAC PCM
-slots of the Philips DAC.
-
-name='PCM Surround Playback Volume',index=0
-
-This control is used to attenuate samples for left and right surround PCM FX-bus
-accumulators. ALSA uses accumulators 2 and 3 for left and right surround PCM
-samples for 5.1 playback. The result samples are forwarded to the surround DAC PCM
-slots of the Philips DAC.
-
-name='PCM Center Playback Volume',index=0
-
-This control is used to attenuate samples for center PCM FX-bus accumulator.
-ALSA uses accumulator 6 for center PCM sample for 5.1 playback. The result sample
-is forwarded to the center DAC PCM slot of the Philips DAC.
-
-name='PCM LFE Playback Volume',index=0
-
-This control is used to attenuate sample for LFE PCM FX-bus accumulator.
-ALSA uses accumulator 7 for LFE PCM sample for 5.1 playback. The result sample
-is forwarded to the LFE DAC PCM slot of the Philips DAC.
-
-name='PCM Playback Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for
-stereo playback. The result samples are forwarded to the front DAC PCM slots
-of the Philips DAC.
-
-name='PCM Capture Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Music Playback Volume',index=0
-
-This control is used to attenuate samples for left and right MIDI FX-bus
-accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
-The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
-
-name='Music Capture Volume',index=0
-
-These controls are used to attenuate samples for left and right MIDI FX-bus
-accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Mic Playback Volume',index=0
-
-This control is used to attenuate samples for left and right Mic input.
-For Mic input is used AC97 codec. The result samples are forwarded to
-the front DAC PCM slots of the Philips DAC. Samples are forwarded to Mic
-capture FIFO (device 1 - 16bit/8KHz mono) too without volume control.
-
-name='Mic Capture Volume',index=0
-
-This control is used to attenuate samples for left and right Mic input.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Audigy CD Playback Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 TTL
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the front DAC PCM slots of the Philips DAC.
-
-name='Audigy CD Capture Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 TTL
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
-
-name='IEC958 Optical Playback Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 optical
-digital input. The result samples are forwarded to the front DAC PCM slots
-of the Philips DAC.
-
-name='IEC958 Optical Capture Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 optical
-digital inputs. The result samples are forwarded to the ADC capture FIFO
-(thus to the standard capture PCM device).
-
-name='Line2 Playback Volume',index=0
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC.
-
-name='Line2 Capture Volume',index=1
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
-
-name='Analog Mix Playback Volume',index=0
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs from Philips ADC. The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC. This contains mix from analog sources
-like CD, Line In, Aux, ....
-
-name='Analog Mix Capture Volume',index=1
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs Philips ADC. The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
-
-name='Aux2 Playback Volume',index=0
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC.
-
-name='Aux2 Capture Volume',index=1
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
-
-name='Front Playback Volume',index=0
-
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate samples for left and right front speakers of
-this mix.
-
-name='Surround Playback Volume',index=0
-
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate samples for left and right surround speakers of
-this mix.
-
-name='Center Playback Volume',index=0
-
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate sample for center speaker of this mix.
-
-name='LFE Playback Volume',index=0
-
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate sample for LFE speaker of this mix.
-
-name='Tone Control - Switch',index=0
-
-This control turns the tone control on or off. The samples for front, rear
-and center / LFE outputs are affected.
-
-name='Tone Control - Bass',index=0
-
-This control sets the bass intensity. There is no neutral value!!
-When the tone control code is activated, the samples are always modified.
-The closest value to pure signal is 20.
-
-name='Tone Control - Treble',index=0
-
-This control sets the treble intensity. There is no neutral value!!
-When the tone control code is activated, the samples are always modified.
-The closest value to pure signal is 20.
-
-name='Master Playback Volume',index=0
-
-This control is used to attenuate samples for front, surround, center and
-LFE outputs.
-
-name='IEC958 Optical Raw Playback Switch',index=0
-
-If this switch is on, then the samples for the IEC958 (S/PDIF) digital
-output are taken only from the raw FX8010 PCM, otherwise standard front
-PCM samples are taken.
-
-
-2) PCM stream related controls
-------------------------------
-
-name='EMU10K1 PCM Volume',index 0-31
-
-Channel volume attenuation in range 0-0xffff. The maximum value (no
-attenuation) is default. The channel mapping for three values is
-as follows:
-
- 0 - mono, default 0xffff (no attenuation)
- 1 - left, default 0xffff (no attenuation)
- 2 - right, default 0xffff (no attenuation)
-
-name='EMU10K1 PCM Send Routing',index 0-31
-
-This control specifies the destination - FX-bus accumulators. There 24
-values with this mapping:
-
- 0 - mono, A destination (FX-bus 0-63), default 0
- 1 - mono, B destination (FX-bus 0-63), default 1
- 2 - mono, C destination (FX-bus 0-63), default 2
- 3 - mono, D destination (FX-bus 0-63), default 3
- 4 - mono, E destination (FX-bus 0-63), default 0
- 5 - mono, F destination (FX-bus 0-63), default 0
- 6 - mono, G destination (FX-bus 0-63), default 0
- 7 - mono, H destination (FX-bus 0-63), default 0
- 8 - left, A destination (FX-bus 0-63), default 0
- 9 - left, B destination (FX-bus 0-63), default 1
- 10 - left, C destination (FX-bus 0-63), default 2
- 11 - left, D destination (FX-bus 0-63), default 3
- 12 - left, E destination (FX-bus 0-63), default 0
- 13 - left, F destination (FX-bus 0-63), default 0
- 14 - left, G destination (FX-bus 0-63), default 0
- 15 - left, H destination (FX-bus 0-63), default 0
- 16 - right, A destination (FX-bus 0-63), default 0
- 17 - right, B destination (FX-bus 0-63), default 1
- 18 - right, C destination (FX-bus 0-63), default 2
- 19 - right, D destination (FX-bus 0-63), default 3
- 20 - right, E destination (FX-bus 0-63), default 0
- 21 - right, F destination (FX-bus 0-63), default 0
- 22 - right, G destination (FX-bus 0-63), default 0
- 23 - right, H destination (FX-bus 0-63), default 0
-
-Don't forget that it's illegal to assign a channel to the same FX-bus accumulator
-more than once (it means 0=0 && 1=0 is an invalid combination).
-
-name='EMU10K1 PCM Send Volume',index 0-31
-
-It specifies the attenuation (amount) for given destination in range 0-255.
-The channel mapping is following:
-
- 0 - mono, A destination attn, default 255 (no attenuation)
- 1 - mono, B destination attn, default 255 (no attenuation)
- 2 - mono, C destination attn, default 0 (mute)
- 3 - mono, D destination attn, default 0 (mute)
- 4 - mono, E destination attn, default 0 (mute)
- 5 - mono, F destination attn, default 0 (mute)
- 6 - mono, G destination attn, default 0 (mute)
- 7 - mono, H destination attn, default 0 (mute)
- 8 - left, A destination attn, default 255 (no attenuation)
- 9 - left, B destination attn, default 0 (mute)
- 10 - left, C destination attn, default 0 (mute)
- 11 - left, D destination attn, default 0 (mute)
- 12 - left, E destination attn, default 0 (mute)
- 13 - left, F destination attn, default 0 (mute)
- 14 - left, G destination attn, default 0 (mute)
- 15 - left, H destination attn, default 0 (mute)
- 16 - right, A destination attn, default 0 (mute)
- 17 - right, B destination attn, default 255 (no attenuation)
- 18 - right, C destination attn, default 0 (mute)
- 19 - right, D destination attn, default 0 (mute)
- 20 - right, E destination attn, default 0 (mute)
- 21 - right, F destination attn, default 0 (mute)
- 22 - right, G destination attn, default 0 (mute)
- 23 - right, H destination attn, default 0 (mute)
-
-
-
-4) MANUALS/PATENTS:
--------------------
-
-ftp://opensource.creative.com/pub/doc
--------------------------------------
-
- Files:
- LM4545.pdf AC97 Codec
-
- m2049.pdf The EMU10K1 Digital Audio Processor
-
- hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects
-
-
-WIPO Patents
-------------
- Patent numbers:
- WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999)
- streams
-
- WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999)
-
- WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction
- Execution and Audio Data Sequencing (Jan. 14, 1999)
-
-
-US Patents (http://www.uspto.gov/)
-----------------------------------
-
- US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999)
-
- US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999)
- with a multiport memory onto which multiple asynchronous
- digital sound samples can be concurrently loaded
-
- US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999)
-
- US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000)
-
- US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000)
- system bus with prioritization and modification of bus transfers
- in accordance with loop ends and minimum block sizes
-
- US 6151670 Method for conserving memory storage using a (Nov. 21, 2000)
- pool of short term memory registers
-
- US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001)
- a common interrupt by associating programs to GP registers,
- defining interrupt register, polling GP registers, and invoking
- callback routine associated with defined interrupt register
+++ /dev/null
- Guide to using M-Audio Audiophile USB with ALSA and Jack v1.5
- ========================================================
-
- Thibault Le Meur <Thibault.LeMeur@supelec.fr>
-
-This document is a guide to using the M-Audio Audiophile USB (tm) device with
-ALSA and JACK.
-
-History
-=======
-* v1.4 - Thibault Le Meur (2007-07-11)
- - Added Low Endianness nature of 16bits-modes
- found by Hakan Lennestal <Hakan.Lennestal@brfsodrahamn.se>
- - Modifying document structure
-* v1.5 - Thibault Le Meur (2007-07-12)
- - Added AC3/DTS passthru info
-
-
-1 - Audiophile USB Specs and correct usage
-==========================================
-
-This part is a reminder of important facts about the functions and limitations
-of the device.
-
-The device has 4 audio interfaces, and 2 MIDI ports:
- * Analog Stereo Input (Ai)
- - This port supports 2 pairs of line-level audio inputs (1/4" TS and RCA)
- - When the 1/4" TS (jack) connectors are connected, the RCA connectors
- are disabled
- * Analog Stereo Output (Ao)
- * Digital Stereo Input (Di)
- * Digital Stereo Output (Do)
- * Midi In (Mi)
- * Midi Out (Mo)
-
-The internal DAC/ADC has the following characteristics:
-* sample depth of 16 or 24 bits
-* sample rate from 8kHz to 96kHz
-* Two interfaces can't use different sample depths at the same time.
-Moreover, the Audiophile USB documentation gives the following Warning:
-"Please exit any audio application running before switching between bit depths"
-
-Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be
-activated at the same time depending on the audio mode selected:
- * 16-bit/48kHz ==> 4 channels in + 4 channels out
- - Ai+Ao+Di+Do
- * 24-bit/48kHz ==> 4 channels in + 2 channels out,
- or 2 channels in + 4 channels out
- - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
- * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
- - Ai or Ao or Di or Do
-
-Important facts about the Digital interface:
---------------------------------------------
- * The Do port additionally supports surround-encoded AC-3 and DTS passthrough,
-though I haven't tested it under Linux
- - Note that in this setup only the Do interface can be enabled
- * Apart from recording an audio digital stream, enabling the Di port is a way
-to synchronize the device to an external sample clock
- - As a consequence, the Di port must be enable only if an active Digital
-source is connected
- - Enabling Di when no digital source is connected can result in a
-synchronization error (for instance sound played at an odd sample rate)
-
-
-2 - Audiophile USB MIDI support in ALSA
-=======================================
-
-The Audiophile USB MIDI ports will be automatically supported once the
-following modules have been loaded:
- * snd-usb-audio
- * snd-seq-midi
-
-No additional setting is required.
-
-
-3 - Audiophile USB Audio support in ALSA
-========================================
-
-Audio functions of the Audiophile USB device are handled by the snd-usb-audio
-module. This module can work in a default mode (without any device-specific
-parameter), or in an "advanced" mode with the device-specific parameter called
-"device_setup".
-
-3.1 - Default Alsa driver mode
-------------------------------
-
-The default behavior of the snd-usb-audio driver is to list the device
-capabilities at startup and activate the required mode when required
-by the applications: for instance if the user is recording in a
-24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
-the snd-usb-audio module will reconfigure the device on the fly.
-
-This approach has the advantage to let the driver automatically switch from sample
-rates/depths automatically according to the user's needs. However, those who
-are using the device under windows know that this is not how the device is meant to
-work: under windows applications must be closed before using the m-audio control
-panel to switch the device working mode. Thus as we'll see in next section, this
-Default Alsa driver mode can lead to device misconfigurations.
-
-Let's get back to the Default Alsa driver mode for now. In this case the
-Audiophile interfaces are mapped to alsa pcm devices in the following
-way (I suppose the device's index is 1):
- * hw:1,0 is Ao in playback and Di in capture
- * hw:1,1 is Do in playback and Ai in capture
- * hw:1,2 is Do in AC3/DTS passthrough mode
-
-In this mode, the device uses Big Endian byte-encoding so that
-supported audio format are S16_BE for 16-bit depth modes and S24_3BE for
-24-bits depth mode.
-
-One exception is the hw:1,2 port which was reported to be Little Endian
-compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
-This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface
-is reported to be big endian in this default driver mode.
-
-Examples:
- * playing a S24_3BE encoded raw file to the Ao port
- % aplay -D hw:1,0 -c2 -t raw -r48000 -fS24_3BE test.raw
- * recording a S24_3BE encoded raw file from the Ai port
- % arecord -D hw:1,1 -c2 -t raw -r48000 -fS24_3BE test.raw
- * playing a S16_BE encoded raw file to the Do port
- % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
- * playing an ac3 sample file to the Do port
- % aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
-
-If you're happy with the default Alsa driver mode and don't experience any
-issue with this mode, then you can skip the following chapter.
-
-3.2 - Advanced module setup
----------------------------
-
-Due to the hardware constraints described above, the device initialization made
-by the Alsa driver in default mode may result in a corrupted state of the
-device. For instance, a particularly annoying issue is that the sound captured
-from the Ai interface sounds distorted (as if boosted with an excessive high
-volume gain).
-
-For people having this problem, the snd-usb-audio module has a new module
-parameter called "device_setup" (this parameter was introduced in kernel
-release 2.6.17)
-
-3.2.1 - Initializing the working mode of the Audiophile USB
-
-As far as the Audiophile USB device is concerned, this value let the user
-specify:
- * the sample depth
- * the sample rate
- * whether the Di port is used or not
-
-When initialized with "device_setup=0x00", the snd-usb-audio module has
-the same behaviour as when the parameter is omitted (see paragraph "Default
-Alsa driver mode" above)
-
-Others modes are described in the following subsections.
-
-3.2.1.1 - 16-bit modes
-
-The two supported modes are:
-
- * device_setup=0x01
- - 16bits 48kHz mode with Di disabled
- - Ai,Ao,Do can be used at the same time
- - hw:1,0 is not available in capture mode
- - hw:1,2 is not available
-
- * device_setup=0x11
- - 16bits 48kHz mode with Di enabled
- - Ai,Ao,Di,Do can be used at the same time
- - hw:1,0 is available in capture mode
- - hw:1,2 is not available
-
-In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
-the devices where reported to be Big-Endian when in fact they were Little-Endian
-so that playing a file was a matter of using:
- % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
-where "test_S16_LE.raw" was in fact a little-endian sample file.
-
-Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
-these modes) a fix has been committed (expected in kernel 2.6.23) and
-Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
-using:
- % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
-
-3.2.1.2 - 24-bit modes
-
-The three supported modes are:
-
- * device_setup=0x09
- - 24bits 48kHz mode with Di disabled
- - Ai,Ao,Do can be used at the same time
- - hw:1,0 is not available in capture mode
- - hw:1,2 is not available
-
- * device_setup=0x19
- - 24bits 48kHz mode with Di enabled
- - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
- - hw:1,0 is available in capture mode and an active digital source must be
- connected to Di
- - hw:1,2 is not available
-
- * device_setup=0x0D or 0x10
- - 24bits 96kHz mode
- - Di is enabled by default for this mode but does not need to be connected
- to an active source
- - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
- - hw:1,0 is available in captured mode
- - hw:1,2 is not available
-
-In these modes the device is only Big-Endian compliant (see "Default Alsa driver
-mode" above for an aplay command example)
-
-3.2.1.3 - AC3 w/ DTS passthru mode
-
-Thanks to Hakan Lennestal, I now have a report saying that this mode works.
-
- * device_setup=0x03
- - 16bits 48kHz mode with only the Do port enabled
- - AC3 with DTS passthru
- - Caution with this setup the Do port is mapped to the pcm device hw:1,0
-
-The command line used to playback the AC3/DTS encoded .wav-files in this mode:
- % aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
-
-3.2.2 - How to use the device_setup parameter
-----------------------------------------------
-
-The parameter can be given:
-
- * By manually probing the device (as root):
- # modprobe -r snd-usb-audio
- # modprobe snd-usb-audio index=1 device_setup=0x09
-
- * Or while configuring the modules options in your modules configuration file
- (typically a .conf file in /etc/modprobe.d/ directory:
- alias snd-card-1 snd-usb-audio
- options snd-usb-audio index=1 device_setup=0x09
-
-CAUTION when initializing the device
--------------------------------------
-
- * Correct initialization on the device requires that device_setup is given to
- the module BEFORE the device is turned on. So, if you use the "manual probing"
- method described above, take care to power-on the device AFTER this initialization.
-
- * Failing to respect this will lead to a misconfiguration of the device. In this case
- turn off the device, unprobe the snd-usb-audio module, then probe it again with
- correct device_setup parameter and then (and only then) turn on the device again.
-
- * If you've correctly initialized the device in a valid mode and then want to switch
- to another mode (possibly with another sample-depth), please use also the following
- procedure:
- - first turn off the device
- - de-register the snd-usb-audio module (modprobe -r)
- - change the device_setup parameter by changing the device_setup
- option in /etc/modprobe.d/*.conf
- - turn on the device
- * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
- be enough to ensure the 'stability' of the device initialization.
-
-3.2.3 - Technical details for hackers
--------------------------------------
-This section is for hackers, wanting to understand details about the device
-internals and how Alsa supports it.
-
-3.2.3.1 - Audiophile USB's device_setup structure
-
-If you want to understand the device_setup magic numbers for the Audiophile
-USB, you need some very basic understanding of binary computation. However,
-this is not required to use the parameter and you may skip this section.
-
-The device_setup is one byte long and its structure is the following:
-
- +---+---+---+---+---+---+---+---+
- | b7| b6| b5| b4| b3| b2| b1| b0|
- +---+---+---+---+---+---+---+---+
- | 0 | 0 | 0 | Di|24B|96K|DTS|SET|
- +---+---+---+---+---+---+---+---+
-
-Where:
- * b0 is the "SET" bit
- - it MUST be set if device_setup is initialized
- * b1 is the "DTS" bit
- - it is set only for Digital output with DTS/AC3
- - this setup is not tested
- * b2 is the Rate selection flag
- - When set to "1" the rate range is 48.1-96kHz
- - Otherwise the sample rate range is 8-48kHz
- * b3 is the bit depth selection flag
- - When set to "1" samples are 24bits long
- - Otherwise they are 16bits long
- - Note that b2 implies b3 as the 96kHz mode is only supported for 24 bits
- samples
- * b4 is the Digital input flag
- - When set to "1" the device assumes that an active digital source is
- connected
- - You shouldn't enable Di if no source is seen on the port (this leads to
- synchronization issues)
- - b4 is implied by b2 (since only one port is enabled at a time no synch
- error can occur)
- * b5 to b7 are reserved for future uses, and must be set to "0"
- - might become Ao, Do, Ai, for b7, b6, b4 respectively
-
-Caution:
- * there is no check on the value you will give to device_setup
- - for instance choosing 0x05 (16bits 96kHz) will fail back to 0x09 since
- b2 implies b3. But _there_will_be_no_warning_ in /var/log/messages
- * Hardware constraints due to the USB bus limitation aren't checked
- - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
- only be able to use one at the same time
-
-3.2.3.2 - USB implementation details for this device
-
-You may safely skip this section if you're not interested in driver
-hacking.
-
-This section describes some internal aspects of the device and summarizes the
-data I got by usb-snooping the windows and Linux drivers.
-
-The M-Audio Audiophile USB has 7 USB Interfaces:
-a "USB interface":
- * USB Interface nb.0
- * USB Interface nb.1
- - Audio Control function
- * USB Interface nb.2
- - Analog Output
- * USB Interface nb.3
- - Digital Output
- * USB Interface nb.4
- - Analog Input
- * USB Interface nb.5
- - Digital Input
- * USB Interface nb.6
- - MIDI interface compliant with the MIDIMAN quirk
-
-Each interface has 5 altsettings (AltSet 1,2,3,4,5) except:
- * Interface 3 (Digital Out) has an extra Alset nb.6
- * Interface 5 (Digital In) does not have Alset nb.3 and 5
-
-Here is a short description of the AltSettings capabilities:
- * AltSettings 1 corresponds to
- - 24-bit depth, 48.1-96kHz sample mode
- - Adaptive playback (Ao and Do), Synch capture (Ai), or Asynch capture (Di)
- * AltSettings 2 corresponds to
- - 24-bit depth, 8-48kHz sample mode
- - Asynch capture and playback (Ao,Ai,Do,Di)
- * AltSettings 3 corresponds to
- - 24-bit depth, 8-48kHz sample mode
- - Synch capture (Ai) and Adaptive playback (Ao,Do)
- * AltSettings 4 corresponds to
- - 16-bit depth, 8-48kHz sample mode
- - Asynch capture and playback (Ao,Ai,Do,Di)
- * AltSettings 5 corresponds to
- - 16-bit depth, 8-48kHz sample mode
- - Synch capture (Ai) and Adaptive playback (Ao,Do)
- * AltSettings 6 corresponds to
- - 16-bit depth, 8-48kHz sample mode
- - Synch playback (Do), audio format type III IEC1937_AC-3
-
-In order to ensure a correct initialization of the device, the driver
-_must_know_ how the device will be used:
- * if DTS is chosen, only Interface 2 with AltSet nb.6 must be
- registered
- * if 96KHz only AltSets nb.1 of each interface must be selected
- * if samples are using 24bits/48KHz then AltSet 2 must me used if
- Digital input is connected, and only AltSet nb.3 if Digital input
- is not connected
- * if samples are using 16bits/48KHz then AltSet 4 must me used if
- Digital input is connected, and only AltSet nb.5 if Digital input
- is not connected
-
-When device_setup is given as a parameter to the snd-usb-audio module, the
-parse_audio_endpoints function uses a quirk called
-"audiophile_skip_setting_quirk" in order to prevent AltSettings not
-corresponding to device_setup from being registered in the driver.
-
-4 - Audiophile USB and Jack support
-===================================
-
-This section deals with support of the Audiophile USB device in Jack.
-
-There are 2 main potential issues when using Jackd with the device:
-* support for Big-Endian devices in 24-bit modes
-* support for 4-in / 4-out channels
-
-4.1 - Direct support in Jackd
------------------------------
-
-Jack supports big endian devices only in recent versions (thanks to
-Andreas Steinmetz for his first big-endian patch). I can't remember
-exactly when this support was released into jackd, let's just say that
-with jackd version 0.103.0 it's almost ok (just a small bug is affecting
-16bits Big-Endian devices, but since you've read carefully the above
-paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices
-are now Little Endians ;-) ).
-
-You can run jackd with the following command for playback with Ao and
-record with Ai:
- % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
-
-4.2 - Using Alsa plughw
------------------------
-If you don't have a recent Jackd installed, you can downgrade to using
-the Alsa "plug" converter.
-
-For instance here is one way to run Jack with 2 playback channels on Ao and 2
-capture channels from Ai:
- % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
-
-However you may see the following warning message:
-"You appear to be using the ALSA software "plug" layer, probably a result of
-using the "default" ALSA device. This is less efficient than it could be.
-Consider using a hardware device instead rather than using the plug layer."
-
-4.3 - Getting 2 input and/or output interfaces in Jack
-------------------------------------------------------
-
-As you can see, starting the Jack server this way will only enable 1 stereo
-input (Di or Ai) and 1 stereo output (Ao or Do).
-
-This is due to the following restrictions:
-* Jack can only open one capture device and one playback device at a time
-* The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
- (and optionally hw:1,2)
-
-If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
-combine the Alsa devices into one logical "complex" device.
-
-If you want to give it a try, I recommend reading the information from
-this page: http://www.sound-man.co.uk/linuxaudio/ice1712multi.html
-It is related to another device (ice1712) but can be adapted to suit
-the Audiophile USB.
-
-Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
-* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
-* (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
-* define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
- file
-* start jackd with this device
-
-I had no success in testing this for now, if you have any success with this kind
-of setup, please drop me an email.
+++ /dev/null
-Intro
-=====
-
-You might have noticed that the bt878 grabber cards have actually
-_two_ PCI functions:
-
-$ lspci
-[ ... ]
-00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02)
-00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02)
-[ ... ]
-
-The first does video, it is backward compatible to the bt848. The second
-does audio. snd-bt87x is a driver for the second function. It's a sound
-driver which can be used for recording sound (and _only_ recording, no
-playback). As most TV cards come with a short cable which can be plugged
-into your sound card's line-in you probably don't need this driver if all
-you want to do is just watching TV...
-
-Some cards do not bother to connect anything to the audio input pins of
-the chip, and some other cards use the audio function to transport MPEG
-video data, so it's quite possible that audio recording may not work
-with your card.
-
-
-Driver Status
-=============
-
-The driver is now stable. However, it doesn't know about many TV cards,
-and it refuses to load for cards it doesn't know.
-
-If the driver complains ("Unknown TV card found, the audio driver will
-not load"), you can specify the load_all=1 option to force the driver to
-try to use the audio capture function of your card. If the frequency of
-recorded data is not right, try to specify the digital_rate option with
-other values than the default 32000 (often it's 44100 or 64000).
-
-If you have an unknown card, please mail the ID and board name to
-<alsa-devel@alsa-project.org>, regardless of whether audio capture works
-or not, so that future versions of this driver know about your card.
-
-
-Audio modes
-===========
-
-The chip knows two different modes (digital/analog). snd-bt87x
-registers two PCM devices, one for each mode. They cannot be used at
-the same time.
-
-
-Digital audio mode
-==================
-
-The first device (hw:X,0) gives you 16 bit stereo sound. The sample
-rate depends on the external source which feeds the Bt87x with digital
-sound via I2S interface.
-
-
-Analog audio mode (A/D)
-=======================
-
-The second device (hw:X,1) gives you 8 or 16 bit mono sound. Supported
-sample rates are between 119466 and 448000 Hz (yes, these numbers are
-that high). If you've set the CONFIG_SND_BT87X_OVERCLOCK option, the
-maximum sample rate is 1792000 Hz, but audio data becomes unusable
-beyond 896000 Hz on my card.
-
-The chip has three analog inputs. Consequently you'll get a mixer
-device to control these.
-
-
-Have fun,
-
- Clemens
-
-
-Written by Clemens Ladisch <clemens@ladisch.de>
-big parts copied from btaudio.txt by Gerd Knorr <kraxel@bytesex.org>
+++ /dev/null
- Brief Notes on C-Media 8338/8738/8768/8770 Driver
- =================================================
-
- Takashi Iwai <tiwai@suse.de>
-
-
-Front/Rear Multi-channel Playback
----------------------------------
-
-CM8x38 chip can use ADC as the second DAC so that two different stereo
-channels can be used for front/rear playbacks. Since there are two
-DACs, both streams are handled independently unlike the 4/6ch multi-
-channel playbacks in the section below.
-
-As default, ALSA driver assigns the first PCM device (i.e. hw:0,0 for
-card#0) for front and 4/6ch playbacks, while the second PCM device
-(hw:0,1) is assigned to the second DAC for rear playback.
-
-There are slight differences between the two DACs:
-
-- The first DAC supports U8 and S16LE formats, while the second DAC
- supports only S16LE.
-- The second DAC supports only two channel stereo.
-
-Please note that the CM8x38 DAC doesn't support continuous playback
-rate but only fixed rates: 5512, 8000, 11025, 16000, 22050, 32000,
-44100 and 48000 Hz.
-
-The rear output can be heard only when "Four Channel Mode" switch is
-disabled. Otherwise no signal will be routed to the rear speakers.
-As default it's turned on.
-
-*** WARNING ***
-When "Four Channel Mode" switch is off, the output from rear speakers
-will be FULL VOLUME regardless of Master and PCM volumes.
-This might damage your audio equipment. Please disconnect speakers
-before your turn off this switch.
-*** WARNING ***
-
-[ Well.. I once got the output with correct volume (i.e. same with the
- front one) and was so excited. It was even with "Four Channel" bit
- on and "double DAC" mode. Actually I could hear separate 4 channels
- from front and rear speakers! But.. after reboot, all was gone.
- It's a very pity that I didn't save the register dump at that
- time.. Maybe there is an unknown register to achieve this... ]
-
-If your card has an extra output jack for the rear output, the rear
-playback should be routed there as default. If not, there is a
-control switch in the driver "Line-In As Rear", which you can change
-via alsamixer or somewhat else. When this switch is on, line-in jack
-is used as rear output.
-
-There are two more controls regarding to the rear output.
-The "Exchange DAC" switch is used to exchange front and rear playback
-routes, i.e. the 2nd DAC is output from front output.
-
-
-4/6 Multi-Channel Playback
---------------------------
-
-The recent CM8738 chips support for the 4/6 multi-channel playback
-function. This is useful especially for AC3 decoding.
-
-When the multi-channel is supported, the driver name has a suffix
-"-MC" such like "CMI8738-MC6". You can check this name from
-/proc/asound/cards.
-
-When the 4/6-ch output is enabled, the second DAC accepts up to 6 (or
-4) channels. While the dual DAC supports two different rates or
-formats, the 4/6-ch playback supports only the same condition for all
-channels. Since the multi-channel playback mode uses both DACs, you
-cannot operate with full-duplex.
-
-The 4.0 and 5.1 modes are defined as the pcm "surround40" and "surround51"
-in alsa-lib. For example, you can play a WAV file with 6 channels like
-
- % aplay -Dsurround51 sixchannels.wav
-
-For programming the 4/6 channel playback, you need to specify the PCM
-channels as you like and set the format S16LE. For example, for playback
-with 4 channels,
-
- snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
- // or mmap if you like
- snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE);
- snd_pcm_hw_params_set_channels(pcm, hw, 4);
-
-and use the interleaved 4 channel data.
-
-There are some control switches affecting to the speaker connections:
-
-"Line-In Mode" - an enum control to change the behavior of line-in
- jack. Either "Line-In", "Rear Output" or "Bass Output" can
- be selected. The last item is available only with model 039
- or newer.
- When "Rear Output" is chosen, the surround channels 3 and 4
- are output to line-in jack.
-"Mic-In Mode" - an enum control to change the behavior of mic-in
- jack. Either "Mic-In" or "Center/LFE Output" can be
- selected.
- When "Center/LFE Output" is chosen, the center and bass
- channels (channels 5 and 6) are output to mic-in jack.
-
-Digital I/O
------------
-
-The CM8x38 provides the excellent SPDIF capability with very cheap
-price (yes, that's the reason I bought the card :)
-
-The SPDIF playback and capture are done via the third PCM device
-(hw:0,2). Usually this is assigned to the PCM device "spdif".
-The available rates are 44100 and 48000 Hz.
-For playback with aplay, you can run like below:
-
- % aplay -Dhw:0,2 foo.wav
-
-or
-
- % aplay -Dspdif foo.wav
-
-24bit format is also supported experimentally.
-
-The playback and capture over SPDIF use normal DAC and ADC,
-respectively, so you cannot playback both analog and digital streams
-simultaneously.
-
-To enable SPDIF output, you need to turn on "IEC958 Output Switch"
-control via mixer or alsactl ("IEC958" is the official name of
-so-called S/PDIF). Then you'll see the red light on from the card so
-you know that's working obviously :)
-The SPDIF input is always enabled, so you can hear SPDIF input data
-from line-out with "IEC958 In Monitor" switch at any time (see
-below).
-
-You can play via SPDIF even with the first device (hw:0,0),
-but SPDIF is enabled only when the proper format (S16LE), sample rate
-(441100 or 48000) and channels (2) are used. Otherwise it's turned
-off. (Also don't forget to turn on "IEC958 Output Switch", too.)
-
-
-Additionally there are relevant control switches:
-
-"IEC958 Mix Analog" - Mix analog PCM playback and FM-OPL/3 streams and
- output through SPDIF. This switch appears only on old chip
- models (CM8738 033 and 037).
- Note: without this control you can output PCM to SPDIF.
- This is "mixing" of streams, so e.g. it's not for AC3 output
- (see the next section).
-
-"IEC958 In Select" - Select SPDIF input, the internal CD-in (false)
- and the external input (true).
-
-"IEC958 Loop" - SPDIF input data is loop back into SPDIF
- output (aka bypass)
-
-"IEC958 Copyright" - Set the copyright bit.
-
-"IEC958 5V" - Select 0.5V (coax) or 5V (optical) interface.
- On some cards this doesn't work and you need to change the
- configuration with hardware dip-switch.
-
-"IEC958 In Monitor" - SPDIF input is routed to DAC.
-
-"IEC958 In Phase Inverse" - Set SPDIF input format as inverse.
- [FIXME: this doesn't work on all chips..]
-
-"IEC958 In Valid" - Set input validity flag detection.
-
-Note: When "PCM Playback Switch" is on, you'll hear the digital output
-stream through analog line-out.
-
-
-The AC3 (RAW DIGITAL) OUTPUT
-----------------------------
-
-The driver supports raw digital (typically AC3) i/o over SPDIF. This
-can be toggled via IEC958 playback control, but usually you need to
-access it via alsa-lib. See alsa-lib documents for more details.
-
-On the raw digital mode, the "PCM Playback Switch" is automatically
-turned off so that non-audio data is heard from the analog line-out.
-Similarly the following switches are off: "IEC958 Mix Analog" and
-"IEC958 Loop". The switches are resumed after closing the SPDIF PCM
-device automatically to the previous state.
-
-On the model 033, AC3 is implemented by the software conversion in
-the alsa-lib. If you need to bypass the software conversion of IEC958
-subframes, pass the "soft_ac3=0" module option. This doesn't matter
-on the newer models.
-
-
-ANALOG MIXER INTERFACE
-----------------------
-
-The mixer interface on CM8x38 is similar to SB16.
-There are Master, PCM, Synth, CD, Line, Mic and PC Speaker playback
-volumes. Synth, CD, Line and Mic have playback and capture switches,
-too, as well as SB16.
-
-In addition to the standard SB mixer, CM8x38 provides more functions.
-- PCM playback switch
-- PCM capture switch (to capture the data sent to DAC)
-- Mic Boost switch
-- Mic capture volume
-- Aux playback volume/switch and capture switch
-- 3D control switch
-
-
-MIDI CONTROLLER
----------------
-
-With CMI8338 chips, the MPU401-UART interface is disabled as default.
-You need to set the module option "mpu_port" to a valid I/O port address
-to enable MIDI support. Valid I/O ports are 0x300, 0x310, 0x320 and
-0x330. Choose a value that doesn't conflict with other cards.
-
-With CMI8738 and newer chips, the MIDI interface is enabled by default
-and the driver automatically chooses a port address.
-
-There is _no_ hardware wavetable function on this chip (except for
-OPL3 synth below).
-What's said as MIDI synth on Windows is a software synthesizer
-emulation. On Linux use TiMidity or other softsynth program for
-playing MIDI music.
-
-
-FM OPL/3 Synth
---------------
-
-The FM OPL/3 is also enabled as default only for the first card.
-Set "fm_port" module option for more cards.
-
-The output quality of FM OPL/3 is, however, very weird.
-I don't know why..
-
-CMI8768 and newer chips do not have the FM synth.
-
-
-Joystick and Modem
-------------------
-
-The legacy joystick is supported. To enable the joystick support, pass
-joystick_port=1 module option. The value 1 means the auto-detection.
-If the auto-detection fails, try to pass the exact I/O address.
-
-The modem is enabled dynamically via a card control switch "Modem".
-
-
-Debugging Information
----------------------
-
-The registers are shown in /proc/asound/cardX/cmipci. If you have any
-problem (especially unexpected behavior of mixer), please attach the
-output of this proc file together with the bug report.
+++ /dev/null
-ALSA PCM channel-mapping API
-============================
- Takashi Iwai <tiwai@suse.de>
-
-GENERAL
--------
-
-The channel mapping API allows user to query the possible channel maps
-and the current channel map, also optionally to modify the channel map
-of the current stream.
-
-A channel map is an array of position for each PCM channel.
-Typically, a stereo PCM stream has a channel map of
- { front_left, front_right }
-while a 4.0 surround PCM stream has a channel map of
- { front left, front right, rear left, rear right }.
-
-The problem, so far, was that we had no standard channel map
-explicitly, and applications had no way to know which channel
-corresponds to which (speaker) position. Thus, applications applied
-wrong channels for 5.1 outputs, and you hear suddenly strange sound
-from rear. Or, some devices secretly assume that center/LFE is the
-third/fourth channels while others that C/LFE as 5th/6th channels.
-
-Also, some devices such as HDMI are configurable for different speaker
-positions even with the same number of total channels. However, there
-was no way to specify this because of lack of channel map
-specification. These are the main motivations for the new channel
-mapping API.
-
-
-DESIGN
-------
-
-Actually, "the channel mapping API" doesn't introduce anything new in
-the kernel/user-space ABI perspective. It uses only the existing
-control element features.
-
-As a ground design, each PCM substream may contain a control element
-providing the channel mapping information and configuration. This
-element is specified by:
- iface = SNDRV_CTL_ELEM_IFACE_PCM
- name = "Playback Channel Map" or "Capture Channel Map"
- device = the same device number for the assigned PCM substream
- index = the same index number for the assigned PCM substream
-
-Note the name is different depending on the PCM substream direction.
-
-Each control element provides at least the TLV read operation and the
-read operation. Optionally, the write operation can be provided to
-allow user to change the channel map dynamically.
-
-* TLV
-
-The TLV operation gives the list of available channel
-maps. A list item of a channel map is usually a TLV of
- type data-bytes ch0 ch1 ch2...
-where type is the TLV type value, the second argument is the total
-bytes (not the numbers) of channel values, and the rest are the
-position value for each channel.
-
-As a TLV type, either SNDRV_CTL_TLVT_CHMAP_FIXED,
-SNDRV_CTL_TLV_CHMAP_VAR or SNDRV_CTL_TLVT_CHMAP_PAIRED can be used.
-The _FIXED type is for a channel map with the fixed channel position
-while the latter two are for flexible channel positions. _VAR type is
-for a channel map where all channels are freely swappable and _PAIRED
-type is where pair-wise channels are swappable. For example, when you
-have {FL/FR/RL/RR} channel map, _PAIRED type would allow you to swap
-only {RL/RR/FL/FR} while _VAR type would allow even swapping FL and
-RR.
-
-These new TLV types are defined in sound/tlv.h.
-
-The available channel position values are defined in sound/asound.h,
-here is a cut:
-
-/* channel positions */
-enum {
- SNDRV_CHMAP_UNKNOWN = 0,
- SNDRV_CHMAP_NA, /* N/A, silent */
- SNDRV_CHMAP_MONO, /* mono stream */
- /* this follows the alsa-lib mixer channel value + 3 */
- SNDRV_CHMAP_FL, /* front left */
- SNDRV_CHMAP_FR, /* front right */
- SNDRV_CHMAP_RL, /* rear left */
- SNDRV_CHMAP_RR, /* rear right */
- SNDRV_CHMAP_FC, /* front center */
- SNDRV_CHMAP_LFE, /* LFE */
- SNDRV_CHMAP_SL, /* side left */
- SNDRV_CHMAP_SR, /* side right */
- SNDRV_CHMAP_RC, /* rear center */
- /* new definitions */
- SNDRV_CHMAP_FLC, /* front left center */
- SNDRV_CHMAP_FRC, /* front right center */
- SNDRV_CHMAP_RLC, /* rear left center */
- SNDRV_CHMAP_RRC, /* rear right center */
- SNDRV_CHMAP_FLW, /* front left wide */
- SNDRV_CHMAP_FRW, /* front right wide */
- SNDRV_CHMAP_FLH, /* front left high */
- SNDRV_CHMAP_FCH, /* front center high */
- SNDRV_CHMAP_FRH, /* front right high */
- SNDRV_CHMAP_TC, /* top center */
- SNDRV_CHMAP_TFL, /* top front left */
- SNDRV_CHMAP_TFR, /* top front right */
- SNDRV_CHMAP_TFC, /* top front center */
- SNDRV_CHMAP_TRL, /* top rear left */
- SNDRV_CHMAP_TRR, /* top rear right */
- SNDRV_CHMAP_TRC, /* top rear center */
- SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC,
-};
-
-When a PCM stream can provide more than one channel map, you can
-provide multiple channel maps in a TLV container type. The TLV data
-to be returned will contain such as:
- SNDRV_CTL_TLVT_CONTAINER 96
- SNDRV_CTL_TLVT_CHMAP_FIXED 4 SNDRV_CHMAP_FC
- SNDRV_CTL_TLVT_CHMAP_FIXED 8 SNDRV_CHMAP_FL SNDRV_CHMAP_FR
- SNDRV_CTL_TLVT_CHMAP_FIXED 16 NDRV_CHMAP_FL SNDRV_CHMAP_FR \
- SNDRV_CHMAP_RL SNDRV_CHMAP_RR
-
-The channel position is provided in LSB 16bits. The upper bits are
-used for bit flags.
-
-#define SNDRV_CHMAP_POSITION_MASK 0xffff
-#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
-#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
-
-SNDRV_CHMAP_PHASE_INVERSE indicates the channel is phase inverted,
-(thus summing left and right channels would result in almost silence).
-Some digital mic devices have this.
-
-When SNDRV_CHMAP_DRIVER_SPEC is set, all the channel position values
-don't follow the standard definition above but driver-specific.
-
-* READ OPERATION
-
-The control read operation is for providing the current channel map of
-the given stream. The control element returns an integer array
-containing the position of each channel.
-
-When this is performed before the number of the channel is specified
-(i.e. hw_params is set), it should return all channels set to
-UNKNOWN.
-
-* WRITE OPERATION
-
-The control write operation is optional, and only for devices that can
-change the channel configuration on the fly, such as HDMI. User needs
-to pass an integer value containing the valid channel positions for
-all channels of the assigned PCM substream.
-
-This operation is allowed only at PCM PREPARED state. When called in
-other states, it shall return an error.
+++ /dev/null
-This document describes standard names of mixer controls.
-
-Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION
-
-DIRECTION:
- <nothing> (both directions)
- Playback
- Capture
- Bypass Playback
- Bypass Capture
-
-FUNCTION:
- Switch (on/off switch)
- Volume
- Route (route control, hardware specific)
-
-CHANNEL:
- <nothing> (channel independent, or applies to all channels)
- Front
- Surround (rear left/right in 4.0/5.1 surround)
- CLFE
- Center
- LFE
- Side (side left/right for 7.1 surround)
-
-LOCATION: (physical location of source)
- Front
- Rear
- Dock (docking station)
- Internal
-
-SOURCE:
- Master
- Master Mono
- Hardware Master
- Speaker (internal speaker)
- Bass Speaker (internal LFE speaker)
- Headphone
- Line Out
- Beep (beep generator)
- Phone
- Phone Input
- Phone Output
- Synth
- FM
- Mic
- Headset Mic (mic part of combined headset jack - 4-pin headphone + mic)
- Headphone Mic (mic part of either/or - 3-pin headphone or mic)
- Line (input only, use "Line Out" for output)
- CD
- Video
- Zoom Video
- Aux
- PCM
- PCM Pan
- Loopback
- Analog Loopback (D/A -> A/D loopback)
- Digital Loopback (playback -> capture loopback - without analog path)
- Mono
- Mono Output
- Multi
- ADC
- Wave
- Music
- I2S
- IEC958
- HDMI
- SPDIF (output only)
- SPDIF In
- Digital In
- HDMI/DP (either HDMI or DisplayPort)
-
-Exceptions (deprecated):
- [Analogue|Digital] Capture Source
- [Analogue|Digital] Capture Switch (aka input gain switch)
- [Analogue|Digital] Capture Volume (aka input gain volume)
- [Analogue|Digital] Playback Switch (aka output gain switch)
- [Analogue|Digital] Playback Volume (aka output gain volume)
- Tone Control - Switch
- Tone Control - Bass
- Tone Control - Treble
- 3D Control - Switch
- 3D Control - Center
- 3D Control - Depth
- 3D Control - Wide
- 3D Control - Space
- 3D Control - Level
- Mic Boost [(?dB)]
-
-PCM interface:
-
- Sample Clock Source { "Word", "Internal", "AutoSync" }
- Clock Sync Status { "Lock", "Sync", "No Lock" }
- External Rate /* external capture rate */
- Capture Rate /* capture rate taken from external source */
-
-IEC958 (S/PDIF) interface:
-
- IEC958 [...] [Playback|Capture] Switch /* turn on/off the IEC958 interface */
- IEC958 [...] [Playback|Capture] Volume /* digital volume control */
- IEC958 [...] [Playback|Capture] Default /* default or global value - read/write */
- IEC958 [...] [Playback|Capture] Mask /* consumer and professional mask */
- IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */
- IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */
- IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */
- IEC958 Q-subcode [Playback|Capture] Default /* Q-subcode bits */
- IEC958 Preamble [Playback|Capture] Default /* burst preamble words (4*16bits) */
+++ /dev/null
-This file explains the codec-specific mixer controls.
-
-Realtek codecs
---------------
-
-* Channel Mode
- This is an enum control to change the surround-channel setup,
- appears only when the surround channels are available.
- It gives the number of channels to be used, "2ch", "4ch", "6ch",
- and "8ch". According to the configuration, this also controls the
- jack-retasking of multi-I/O jacks.
-
-* Auto-Mute Mode
- This is an enum control to change the auto-mute behavior of the
- headphone and line-out jacks. If built-in speakers and headphone
- and/or line-out jacks are available on a machine, this controls
- appears.
- When there are only either headphones or line-out jacks, it gives
- "Disabled" and "Enabled" state. When enabled, the speaker is muted
- automatically when a jack is plugged.
-
- When both headphone and line-out jacks are present, it gives
- "Disabled", "Speaker Only" and "Line-Out+Speaker". When
- speaker-only is chosen, plugging into a headphone or a line-out jack
- mutes the speakers, but not line-outs. When line-out+speaker is
- selected, plugging to a headphone jack mutes both speakers and
- line-outs.
-
-
-IDT/Sigmatel codecs
--------------------
-
-* Analog Loopback
- This control enables/disables the analog-loopback circuit. This
- appears only when "loopback" is set to true in a codec hint
- (see HD-Audio.txt). Note that on some codecs the analog-loopback
- and the normal PCM playback are exclusive, i.e. when this is on, you
- won't hear any PCM stream.
-
-* Swap Center/LFE
- Swaps the center and LFE channel order. Normally, the left
- corresponds to the center and the right to the LFE. When this is
- ON, the left to the LFE and the right to the center.
-
-* Headphone as Line Out
- When this control is ON, treat the headphone jacks as line-out
- jacks. That is, the headphone won't auto-mute the other line-outs,
- and no HP-amp is set to the pins.
-
-* Mic Jack Mode, Line Jack Mode, etc
- These enum controls the direction and the bias of the input jack
- pins. Depending on the jack type, it can set as "Mic In" and "Line
- In", for determining the input bias, or it can be set to "Line Out"
- when the pin is a multi-I/O jack for surround channels.
-
-
-VIA codecs
-----------
-
-* Smart 5.1
- An enum control to re-task the multi-I/O jacks for surround outputs.
- When it's ON, the corresponding input jacks (usually a line-in and a
- mic-in) are switched as the surround and the CLFE output jacks.
-
-* Independent HP
- When this enum control is enabled, the headphone output is routed
- from an individual stream (the third PCM such as hw:0,2) instead of
- the primary stream. In the case the headphone DAC is shared with a
- side or a CLFE-channel DAC, the DAC is switched to the headphone
- automatically.
-
-* Loopback Mixing
- An enum control to determine whether the analog-loopback route is
- enabled or not. When it's enabled, the analog-loopback is mixed to
- the front-channel. Also, the same route is used for the headphone
- and speaker outputs. As a side-effect, when this mode is set, the
- individual volume controls will be no longer available for
- headphones and speakers because there is only one DAC connected to a
- mixer widget.
-
-* Dynamic Power-Control
- This control determines whether the dynamic power-control per jack
- detection is enabled or not. When enabled, the widgets power state
- (D0/D3) are changed dynamically depending on the jack plugging
- state for saving power consumptions. However, if your system
- doesn't provide a proper jack-detection, this won't work; in such a
- case, turn this control OFF.
-
-* Jack Detect
- This control is provided only for VT1708 codec which gives no proper
- unsolicited event per jack plug. When this is on, the driver polls
- the jack detection so that the headphone auto-mute can work, while
- turning this off would reduce the power consumption.
-
-
-Conexant codecs
----------------
-
-* Auto-Mute Mode
- See Reatek codecs.
-
-
-Analog codecs
---------------
-
-* Channel Mode
- This is an enum control to change the surround-channel setup,
- appears only when the surround channels are available.
- It gives the number of channels to be used, "2ch", "4ch" and "6ch".
- According to the configuration, this also controls the
- jack-retasking of multi-I/O jacks.
-
-* Independent HP
- When this enum control is enabled, the headphone output is routed
- from an individual stream (the third PCM such as hw:0,2) instead of
- the primary stream.
+++ /dev/null
-To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
-and dynamic pcm assignment.
-
-Virtual pin is an extension of per_pin. The most difference of DP MST
-from legacy is that DP MST introduces device entry. Each pin can contain
-several device entries. Each device entry behaves as a pin.
-
-As each pin may contain several device entries and each codec may contain
-several pins, if we use one pcm per per_pin, there will be many PCMs.
-The new solution is to create a few PCMs and to dynamically bind pcm to
-per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
-the new solution.
-
-PCM
-===
-To be added
-
-
-Jack
-====
-
-Presume:
- - MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
- - NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
-
-So there are the following scenarios:
- a. MST (&& dyn_pcm_assign && acomp)
- b. NON-MST && dyn_pcm_assign && acomp
- c. NON-MST && !dyn_pcm_assign && !acomp
-
-Below discussion will ignore MST and NON-MST difference as it doesn't
-impact on jack handling too much.
-
-Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
-a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
-
-For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
-
-For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
-when monitor is hotplugged.
-
-
-Build Jack
-----------
-
-- dyn_pcm_assign
-Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
-
-- !dyn_pcm_assign
-Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
-
-
-Unsolicited Event Enabling
---------------------------
-Enable unsolicited event if !acomp.
-
-
-Monitor Hotplug Event Handling
-------------------------------
-- acomp
-pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
-sync_eld_via_acomp().
-Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
-both dyn_pcm_assign and !dyn_pcm_assign
-
-- !acomp
-Hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
-hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
-Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
-Use hda_jack mechanism to handle jack events.
-
-
-Others to be added later
-========================
+++ /dev/null
- Model name Description
- ---------- -----------
-ALC880
-======
- 3stack 3-jack in back and a headphone out
- 3stack-digout 3-jack in back, a HP out and a SPDIF out
- 5stack 5-jack in back, 2-jack in front
- 5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
- 6stack 6-jack in back, 2-jack in front
- 6stack-digout 6-jack with a SPDIF out
-
-ALC260
-======
- gpio1 Enable GPIO1
- coef Enable EAPD via COEF table
- fujitsu Quirk for FSC S7020
- fujitsu-jwse Quirk for FSC S7020 with jack modes and HP mic support
-
-ALC262
-======
- inv-dmic Inverted internal mic workaround
-
-ALC267/268
-==========
- inv-dmic Inverted internal mic workaround
- hp-eapd Disable HP EAPD on NID 0x15
-
-ALC22x/23x/25x/269/27x/28x/29x (and vendor-specific ALC3xxx models)
-======
- laptop-amic Laptops with analog-mic input
- laptop-dmic Laptops with digital-mic input
- alc269-dmic Enable ALC269(VA) digital mic workaround
- alc271-dmic Enable ALC271X digital mic workaround
- inv-dmic Inverted internal mic workaround
- headset-mic Indicates a combined headset (headphone+mic) jack
- headset-mode More comprehensive headset support for ALC269 & co
- headset-mode-no-hp-mic Headset mode support without headphone mic
- lenovo-dock Enables docking station I/O for some Lenovos
- hp-gpio-led GPIO LED support on HP laptops
- dell-headset-multi Headset jack, which can also be used as mic-in
- dell-headset-dock Headset jack (without mic-in), and also dock I/O
- alc283-dac-wcaps Fixups for Chromebook with ALC283
- alc283-sense-combo Combo jack sensing on ALC283
- tpt440-dock Pin configs for Lenovo Thinkpad Dock support
-
-ALC66x/67x/892
-==============
- mario Chromebook mario model fixup
- asus-mode1 ASUS
- asus-mode2 ASUS
- asus-mode3 ASUS
- asus-mode4 ASUS
- asus-mode5 ASUS
- asus-mode6 ASUS
- asus-mode7 ASUS
- asus-mode8 ASUS
- inv-dmic Inverted internal mic workaround
- dell-headset-multi Headset jack, which can also be used as mic-in
-
-ALC680
-======
- N/A
-
-ALC88x/898/1150
-======================
- acer-aspire-4930g Acer Aspire 4930G/5930G/6530G/6930G/7730G
- acer-aspire-8930g Acer Aspire 8330G/6935G
- acer-aspire Acer Aspire others
- inv-dmic Inverted internal mic workaround
- no-primary-hp VAIO Z/VGC-LN51JGB workaround (for fixed speaker DAC)
-
-ALC861/660
-==========
- N/A
-
-ALC861VD/660VD
-==============
- N/A
-
-CMI9880
-=======
- minimal 3-jack in back
- min_fp 3-jack in back, 2-jack in front
- full 6-jack in back, 2-jack in front
- full_dig 6-jack in back, 2-jack in front, SPDIF I/O
- allout 5-jack in back, 2-jack in front, SPDIF out
- auto auto-config reading BIOS (default)
-
-AD1882 / AD1882A
-================
- 3stack 3-stack mode
- 3stack-automute 3-stack with automute front HP (default)
- 6stack 6-stack mode
-
-AD1884A / AD1883 / AD1984A / AD1984B
-====================================
- desktop 3-stack desktop (default)
- laptop laptop with HP jack sensing
- mobile mobile devices with HP jack sensing
- thinkpad Lenovo Thinkpad X300
- touchsmart HP Touchsmart
-
-AD1884
-======
- N/A
-
-AD1981
-======
- basic 3-jack (default)
- hp HP nx6320
- thinkpad Lenovo Thinkpad T60/X60/Z60
- toshiba Toshiba U205
-
-AD1983
-======
- N/A
-
-AD1984
-======
- basic default configuration
- thinkpad Lenovo Thinkpad T61/X61
- dell_desktop Dell T3400
-
-AD1986A
-=======
- 3stack 3-stack, shared surrounds
- laptop 2-channel only (FSC V2060, Samsung M50)
- laptop-imic 2-channel with built-in mic
- eapd Turn on EAPD constantly
-
-AD1988/AD1988B/AD1989A/AD1989B
-==============================
- 6stack 6-jack
- 6stack-dig ditto with SPDIF
- 3stack 3-jack
- 3stack-dig ditto with SPDIF
- laptop 3-jack with hp-jack automute
- laptop-dig ditto with SPDIF
- auto auto-config reading BIOS (default)
-
-Conexant 5045
-=============
- laptop-hpsense Laptop with HP sense (old model laptop)
- laptop-micsense Laptop with Mic sense (old model fujitsu)
- laptop-hpmicsense Laptop with HP and Mic senses
- benq Benq R55E
- laptop-hp530 HP 530 laptop
- test for testing/debugging purpose, almost all controls
- can be adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
-
-Conexant 5047
-=============
- laptop Basic Laptop config
- laptop-hp Laptop config for some HP models (subdevice 30A5)
- laptop-eapd Laptop config with EAPD support
- test for testing/debugging purpose, almost all controls
- can be adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
-
-Conexant 5051
-=============
- laptop Basic Laptop config (default)
- hp HP Spartan laptop
- hp-dv6736 HP dv6736
- hp-f700 HP Compaq Presario F700
- ideapad Lenovo IdeaPad laptop
- toshiba Toshiba Satellite M300
-
-Conexant 5066
-=============
- laptop Basic Laptop config (default)
- hp-laptop HP laptops, e g G60
- asus Asus K52JU, Lenovo G560
- dell-laptop Dell laptops
- dell-vostro Dell Vostro
- olpc-xo-1_5 OLPC XO 1.5
- ideapad Lenovo IdeaPad U150
- thinkpad Lenovo Thinkpad
-
-STAC9200
-========
- ref Reference board
- oqo OQO Model 2
- dell-d21 Dell (unknown)
- dell-d22 Dell (unknown)
- dell-d23 Dell (unknown)
- dell-m21 Dell Inspiron 630m, Dell Inspiron 640m
- dell-m22 Dell Latitude D620, Dell Latitude D820
- dell-m23 Dell XPS M1710, Dell Precision M90
- dell-m24 Dell Latitude 120L
- dell-m25 Dell Inspiron E1505n
- dell-m26 Dell Inspiron 1501
- dell-m27 Dell Inspiron E1705/9400
- gateway-m4 Gateway laptops with EAPD control
- gateway-m4-2 Gateway laptops with EAPD control
- panasonic Panasonic CF-74
- auto BIOS setup (default)
-
-STAC9205/9254
-=============
- ref Reference board
- dell-m42 Dell (unknown)
- dell-m43 Dell Precision
- dell-m44 Dell Inspiron
- eapd Keep EAPD on (e.g. Gateway T1616)
- auto BIOS setup (default)
-
-STAC9220/9221
-=============
- ref Reference board
- 3stack D945 3stack
- 5stack D945 5stack + SPDIF
- intel-mac-v1 Intel Mac Type 1
- intel-mac-v2 Intel Mac Type 2
- intel-mac-v3 Intel Mac Type 3
- intel-mac-v4 Intel Mac Type 4
- intel-mac-v5 Intel Mac Type 5
- intel-mac-auto Intel Mac (detect type according to subsystem id)
- macmini Intel Mac Mini (equivalent with type 3)
- macbook Intel Mac Book (eq. type 5)
- macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
- macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
- imac-intel Intel iMac (eq. type 2)
- imac-intel-20 Intel iMac (newer version) (eq. type 3)
- ecs202 ECS/PC chips
- dell-d81 Dell (unknown)
- dell-d82 Dell (unknown)
- dell-m81 Dell (unknown)
- dell-m82 Dell XPS M1210
- auto BIOS setup (default)
-
-STAC9202/9250/9251
-==================
- ref Reference board, base config
- m1 Some Gateway MX series laptops (NX560XL)
- m1-2 Some Gateway MX series laptops (MX6453)
- m2 Some Gateway MX series laptops (M255)
- m2-2 Some Gateway MX series laptops
- m3 Some Gateway MX series laptops
- m5 Some Gateway MX series laptops (MP6954)
- m6 Some Gateway NX series laptops
- auto BIOS setup (default)
-
-STAC9227/9228/9229/927x
-=======================
- ref Reference board
- ref-no-jd Reference board without HP/Mic jack detection
- 3stack D965 3stack
- 5stack D965 5stack + SPDIF
- 5stack-no-fp D965 5stack without front panel
- dell-3stack Dell Dimension E520
- dell-bios Fixes with Dell BIOS setup
- dell-bios-amic Fixes with Dell BIOS setup including analog mic
- volknob Fixes with volume-knob widget 0x24
- auto BIOS setup (default)
-
-STAC92HD71B*
-============
- ref Reference board
- dell-m4-1 Dell desktops
- dell-m4-2 Dell desktops
- dell-m4-3 Dell desktops
- hp-m4 HP mini 1000
- hp-dv5 HP dv series
- hp-hdx HP HDX series
- hp-dv4-1222nr HP dv4-1222nr (with LED support)
- auto BIOS setup (default)
-
-STAC92HD73*
-===========
- ref Reference board
- no-jd BIOS setup but without jack-detection
- intel Intel DG45* mobos
- dell-m6-amic Dell desktops/laptops with analog mics
- dell-m6-dmic Dell desktops/laptops with digital mics
- dell-m6 Dell desktops/laptops with both type of mics
- dell-eq Dell desktops/laptops
- alienware Alienware M17x
- auto BIOS setup (default)
-
-STAC92HD83*
-===========
- ref Reference board
- mic-ref Reference board with power management for ports
- dell-s14 Dell laptop
- dell-vostro-3500 Dell Vostro 3500 laptop
- hp-dv7-4000 HP dv-7 4000
- hp_cNB11_intquad HP CNB models with 4 speakers
- hp-zephyr HP Zephyr
- hp-led HP with broken BIOS for mute LED
- hp-inv-led HP with broken BIOS for inverted mute LED
- hp-mic-led HP with mic-mute LED
- headset-jack Dell Latitude with a 4-pin headset jack
- hp-envy-bass Pin fixup for HP Envy bass speaker (NID 0x0f)
- hp-envy-ts-bass Pin fixup for HP Envy TS bass speaker (NID 0x10)
- hp-bnb13-eq Hardware equalizer setup for HP laptops
- auto BIOS setup (default)
-
-STAC92HD95
-==========
- hp-led LED support for HP laptops
- hp-bass Bass HPF setup for HP Spectre 13
-
-STAC9872
-========
- vaio VAIO laptop without SPDIF
- auto BIOS setup (default)
-
-Cirrus Logic CS4206/4207
-========================
- mbp55 MacBook Pro 5,5
- imac27 IMac 27 Inch
- auto BIOS setup (default)
-
-Cirrus Logic CS4208
-===================
- mba6 MacBook Air 6,1 and 6,2
- gpio0 Enable GPIO 0 amp
- auto BIOS setup (default)
-
-VIA VT17xx/VT18xx/VT20xx
-========================
- auto BIOS setup (default)
+++ /dev/null
-MORE NOTES ON HD-AUDIO DRIVER
-=============================
- Takashi Iwai <tiwai@suse.de>
-
-
-GENERAL
--------
-
-HD-audio is the new standard on-board audio component on modern PCs
-after AC97. Although Linux has been supporting HD-audio since long
-time ago, there are often problems with new machines. A part of the
-problem is broken BIOS, and the rest is the driver implementation.
-This document explains the brief trouble-shooting and debugging
-methods for the HD-audio hardware.
-
-The HD-audio component consists of two parts: the controller chip and
-the codec chips on the HD-audio bus. Linux provides a single driver
-for all controllers, snd-hda-intel. Although the driver name contains
-a word of a well-known hardware vendor, it's not specific to it but for
-all controller chips by other companies. Since the HD-audio
-controllers are supposed to be compatible, the single snd-hda-driver
-should work in most cases. But, not surprisingly, there are known
-bugs and issues specific to each controller type. The snd-hda-intel
-driver has a bunch of workarounds for these as described below.
-
-A controller may have multiple codecs. Usually you have one audio
-codec and optionally one modem codec. In theory, there might be
-multiple audio codecs, e.g. for analog and digital outputs, and the
-driver might not work properly because of conflict of mixer elements.
-This should be fixed in future if such hardware really exists.
-
-The snd-hda-intel driver has several different codec parsers depending
-on the codec. It has a generic parser as a fallback, but this
-functionality is fairly limited until now. Instead of the generic
-parser, usually the codec-specific parser (coded in patch_*.c) is used
-for the codec-specific implementations. The details about the
-codec-specific problems are explained in the later sections.
-
-If you are interested in the deep debugging of HD-audio, read the
-HD-audio specification at first. The specification is found on
-Intel's web page, for example:
-
-- http://www.intel.com/standards/hdaudio/
-
-
-HD-AUDIO CONTROLLER
--------------------
-
-DMA-Position Problem
-~~~~~~~~~~~~~~~~~~~~
-The most common problem of the controller is the inaccurate DMA
-pointer reporting. The DMA pointer for playback and capture can be
-read in two ways, either via a LPIB register or via a position-buffer
-map. As default the driver tries to read from the io-mapped
-position-buffer, and falls back to LPIB if the position-buffer appears
-dead. However, this detection isn't perfect on some devices. In such
-a case, you can change the default method via `position_fix` option.
-
-`position_fix=1` means to use LPIB method explicitly.
-`position_fix=2` means to use the position-buffer.
-`position_fix=3` means to use a combination of both methods, needed
-for some VIA controllers. The capture stream position is corrected
-by comparing both LPIB and position-buffer values.
-`position_fix=4` is another combination available for all controllers,
-and uses LPIB for the playback and the position-buffer for the capture
-streams.
-0 is the default value for all other
-controllers, the automatic check and fallback to LPIB as described in
-the above. If you get a problem of repeated sounds, this option might
-help.
-
-In addition to that, every controller is known to be broken regarding
-the wake-up timing. It wakes up a few samples before actually
-processing the data on the buffer. This caused a lot of problems, for
-example, with ALSA dmix or JACK. Since 2.6.27 kernel, the driver puts
-an artificial delay to the wake up timing. This delay is controlled
-via `bdl_pos_adj` option.
-
-When `bdl_pos_adj` is a negative value (as default), it's assigned to
-an appropriate value depending on the controller chip. For Intel
-chips, it'd be 1 while it'd be 32 for others. Usually this works.
-Only in case it doesn't work and you get warning messages, you should
-change this parameter to other values.
-
-
-Codec-Probing Problem
-~~~~~~~~~~~~~~~~~~~~~
-A less often but a more severe problem is the codec probing. When
-BIOS reports the available codec slots wrongly, the driver gets
-confused and tries to access the non-existing codec slot. This often
-results in the total screw-up, and destructs the further communication
-with the codec chips. The symptom appears usually as error messages
-like:
-------------------------------------------------------------------------
- hda_intel: azx_get_response timeout, switching to polling mode:
- last cmd=0x12345678
- hda_intel: azx_get_response timeout, switching to single_cmd mode:
- last cmd=0x12345678
-------------------------------------------------------------------------
-
-The first line is a warning, and this is usually relatively harmless.
-It means that the codec response isn't notified via an IRQ. The
-driver uses explicit polling method to read the response. It gives
-very slight CPU overhead, but you'd unlikely notice it.
-
-The second line is, however, a fatal error. If this happens, usually
-it means that something is really wrong. Most likely you are
-accessing a non-existing codec slot.
-
-Thus, if the second error message appears, try to narrow the probed
-codec slots via `probe_mask` option. It's a bitmask, and each bit
-corresponds to the codec slot. For example, to probe only the first
-slot, pass `probe_mask=1`. For the first and the third slots, pass
-`probe_mask=5` (where 5 = 1 | 4), and so on.
-
-Since 2.6.29 kernel, the driver has a more robust probing method, so
-this error might happen rarely, though.
-
-On a machine with a broken BIOS, sometimes you need to force the
-driver to probe the codec slots the hardware doesn't report for use.
-In such a case, turn the bit 8 (0x100) of `probe_mask` option on.
-Then the rest 8 bits are passed as the codec slots to probe
-unconditionally. For example, `probe_mask=0x103` will force to probe
-the codec slots 0 and 1 no matter what the hardware reports.
-
-
-Interrupt Handling
-~~~~~~~~~~~~~~~~~~
-HD-audio driver uses MSI as default (if available) since 2.6.33
-kernel as MSI works better on some machines, and in general, it's
-better for performance. However, Nvidia controllers showed bad
-regressions with MSI (especially in a combination with AMD chipset),
-thus we disabled MSI for them.
-
-There seem also still other devices that don't work with MSI. If you
-see a regression wrt the sound quality (stuttering, etc) or a lock-up
-in the recent kernel, try to pass `enable_msi=0` option to disable
-MSI. If it works, you can add the known bad device to the blacklist
-defined in hda_intel.c. In such a case, please report and give the
-patch back to the upstream developer.
-
-
-HD-AUDIO CODEC
---------------
-
-Model Option
-~~~~~~~~~~~~
-The most common problem regarding the HD-audio driver is the
-unsupported codec features or the mismatched device configuration.
-Most of codec-specific code has several preset models, either to
-override the BIOS setup or to provide more comprehensive features.
-
-The driver checks PCI SSID and looks through the static configuration
-table until any matching entry is found. If you have a new machine,
-you may see a message like below:
-------------------------------------------------------------------------
- hda_codec: ALC880: BIOS auto-probing.
-------------------------------------------------------------------------
-Meanwhile, in the earlier versions, you would see a message like:
-------------------------------------------------------------------------
- hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
-------------------------------------------------------------------------
-Even if you see such a message, DON'T PANIC. Take a deep breath and
-keep your towel. First of all, it's an informational message, no
-warning, no error. This means that the PCI SSID of your device isn't
-listed in the known preset model (white-)list. But, this doesn't mean
-that the driver is broken. Many codec-drivers provide the automatic
-configuration mechanism based on the BIOS setup.
-
-The HD-audio codec has usually "pin" widgets, and BIOS sets the default
-configuration of each pin, which indicates the location, the
-connection type, the jack color, etc. The HD-audio driver can guess
-the right connection judging from these default configuration values.
-However -- some codec-support codes, such as patch_analog.c, don't
-support the automatic probing (yet as of 2.6.28). And, BIOS is often,
-yes, pretty often broken. It sets up wrong values and screws up the
-driver.
-
-The preset model (or recently called as "fix-up") is provided
-basically to overcome such a situation. When the matching preset
-model is found in the white-list, the driver assumes the static
-configuration of that preset with the correct pin setup, etc.
-Thus, if you have a newer machine with a slightly different PCI SSID
-(or codec SSID) from the existing one, you may have a good chance to
-re-use the same model. You can pass the `model` option to specify the
-preset model instead of PCI (and codec-) SSID look-up.
-
-What `model` option values are available depends on the codec chip.
-Check your codec chip from the codec proc file (see "Codec Proc-File"
-section below). It will show the vendor/product name of your codec
-chip. Then, see Documentation/sound/alsa/HD-Audio-Models.txt file,
-the section of HD-audio driver. You can find a list of codecs
-and `model` options belonging to each codec. For example, for Realtek
-ALC262 codec chip, pass `model=ultra` for devices that are compatible
-with Samsung Q1 Ultra.
-
-Thus, the first thing you can do for any brand-new, unsupported and
-non-working HD-audio hardware is to check HD-audio codec and several
-different `model` option values. If you have any luck, some of them
-might suit with your device well.
-
-There are a few special model option values:
-- when 'nofixup' is passed, the device-specific fixups in the codec
- parser are skipped.
-- when `generic` is passed, the codec-specific parser is skipped and
- only the generic parser is used.
-
-
-Speaker and Headphone Output
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-One of the most frequent (and obvious) bugs with HD-audio is the
-silent output from either or both of a built-in speaker and a
-headphone jack. In general, you should try a headphone output at
-first. A speaker output often requires more additional controls like
-the external amplifier bits. Thus a headphone output has a slightly
-better chance.
-
-Before making a bug report, double-check whether the mixer is set up
-correctly. The recent version of snd-hda-intel driver provides mostly
-"Master" volume control as well as "Front" volume (where Front
-indicates the front-channels). In addition, there can be individual
-"Headphone" and "Speaker" controls.
-
-Ditto for the speaker output. There can be "External Amplifier"
-switch on some codecs. Turn on this if present.
-
-Another related problem is the automatic mute of speaker output by
-headphone plugging. This feature is implemented in most cases, but
-not on every preset model or codec-support code.
-
-In anyway, try a different model option if you have such a problem.
-Some other models may match better and give you more matching
-functionality. If none of the available models works, send a bug
-report. See the bug report section for details.
-
-If you are masochistic enough to debug the driver problem, note the
-following:
-
-- The speaker (and the headphone, too) output often requires the
- external amplifier. This can be set usually via EAPD verb or a
- certain GPIO. If the codec pin supports EAPD, you have a better
- chance via SET_EAPD_BTL verb (0x70c). On others, GPIO pin (mostly
- it's either GPIO0 or GPIO1) may turn on/off EAPD.
-- Some Realtek codecs require special vendor-specific coefficients to
- turn on the amplifier. See patch_realtek.c.
-- IDT codecs may have extra power-enable/disable controls on each
- analog pin. See patch_sigmatel.c.
-- Very rare but some devices don't accept the pin-detection verb until
- triggered. Issuing GET_PIN_SENSE verb (0xf09) may result in the
- codec-communication stall. Some examples are found in
- patch_realtek.c.
-
-
-Capture Problems
-~~~~~~~~~~~~~~~~
-The capture problems are often because of missing setups of mixers.
-Thus, before submitting a bug report, make sure that you set up the
-mixer correctly. For example, both "Capture Volume" and "Capture
-Switch" have to be set properly in addition to the right "Capture
-Source" or "Input Source" selection. Some devices have "Mic Boost"
-volume or switch.
-
-When the PCM device is opened via "default" PCM (without pulse-audio
-plugin), you'll likely have "Digital Capture Volume" control as well.
-This is provided for the extra gain/attenuation of the signal in
-software, especially for the inputs without the hardware volume
-control such as digital microphones. Unless really needed, this
-should be set to exactly 50%, corresponding to 0dB -- neither extra
-gain nor attenuation. When you use "hw" PCM, i.e., a raw access PCM,
-this control will have no influence, though.
-
-It's known that some codecs / devices have fairly bad analog circuits,
-and the recorded sound contains a certain DC-offset. This is no bug
-of the driver.
-
-Most of modern laptops have no analog CD-input connection. Thus, the
-recording from CD input won't work in many cases although the driver
-provides it as the capture source. Use CDDA instead.
-
-The automatic switching of the built-in and external mic per plugging
-is implemented on some codec models but not on every model. Partly
-because of my laziness but mostly lack of testers. Feel free to
-submit the improvement patch to the author.
-
-
-Direct Debugging
-~~~~~~~~~~~~~~~~
-If no model option gives you a better result, and you are a tough guy
-to fight against evil, try debugging via hitting the raw HD-audio
-codec verbs to the device. Some tools are available: hda-emu and
-hda-analyzer. The detailed description is found in the sections
-below. You'd need to enable hwdep for using these tools. See "Kernel
-Configuration" section.
-
-
-OTHER ISSUES
-------------
-
-Kernel Configuration
-~~~~~~~~~~~~~~~~~~~~
-In general, I recommend you to enable the sound debug option,
-`CONFIG_SND_DEBUG=y`, no matter whether you are debugging or not.
-This enables snd_printd() macro and others, and you'll get additional
-kernel messages at probing.
-
-In addition, you can enable `CONFIG_SND_DEBUG_VERBOSE=y`. But this
-will give you far more messages. Thus turn this on only when you are
-sure to want it.
-
-Don't forget to turn on the appropriate `CONFIG_SND_HDA_CODEC_*`
-options. Note that each of them corresponds to the codec chip, not
-the controller chip. Thus, even if lspci shows the Nvidia controller,
-you may need to choose the option for other vendors. If you are
-unsure, just select all yes.
-
-`CONFIG_SND_HDA_HWDEP` is a useful option for debugging the driver.
-When this is enabled, the driver creates hardware-dependent devices
-(one per each codec), and you have a raw access to the device via
-these device files. For example, `hwC0D2` will be created for the
-codec slot #2 of the first card (#0). For debug-tools such as
-hda-verb and hda-analyzer, the hwdep device has to be enabled.
-Thus, it'd be better to turn this on always.
-
-`CONFIG_SND_HDA_RECONFIG` is a new option, and this depends on the
-hwdep option above. When enabled, you'll have some sysfs files under
-the corresponding hwdep directory. See "HD-audio reconfiguration"
-section below.
-
-`CONFIG_SND_HDA_POWER_SAVE` option enables the power-saving feature.
-See "Power-saving" section below.
-
-
-Codec Proc-File
-~~~~~~~~~~~~~~~
-The codec proc-file is a treasure-chest for debugging HD-audio.
-It shows most of useful information of each codec widget.
-
-The proc file is located in /proc/asound/card*/codec#*, one file per
-each codec slot. You can know the codec vendor, product id and
-names, the type of each widget, capabilities and so on.
-This file, however, doesn't show the jack sensing state, so far. This
-is because the jack-sensing might be depending on the trigger state.
-
-This file will be picked up by the debug tools, and also it can be fed
-to the emulator as the primary codec information. See the debug tools
-section below.
-
-This proc file can be also used to check whether the generic parser is
-used. When the generic parser is used, the vendor/product ID name
-will appear as "Realtek ID 0262", instead of "Realtek ALC262".
-
-
-HD-Audio Reconfiguration
-~~~~~~~~~~~~~~~~~~~~~~~~
-This is an experimental feature to allow you re-configure the HD-audio
-codec dynamically without reloading the driver. The following sysfs
-files are available under each codec-hwdep device directory (e.g.
-/sys/class/sound/hwC0D0):
-
-vendor_id::
- Shows the 32bit codec vendor-id hex number. You can change the
- vendor-id value by writing to this file.
-subsystem_id::
- Shows the 32bit codec subsystem-id hex number. You can change the
- subsystem-id value by writing to this file.
-revision_id::
- Shows the 32bit codec revision-id hex number. You can change the
- revision-id value by writing to this file.
-afg::
- Shows the AFG ID. This is read-only.
-mfg::
- Shows the MFG ID. This is read-only.
-name::
- Shows the codec name string. Can be changed by writing to this
- file.
-modelname::
- Shows the currently set `model` option. Can be changed by writing
- to this file.
-init_verbs::
- The extra verbs to execute at initialization. You can add a verb by
- writing to this file. Pass three numbers: nid, verb and parameter
- (separated with a space).
-hints::
- Shows / stores hint strings for codec parsers for any use.
- Its format is `key = value`. For example, passing `jack_detect = no`
- will disable the jack detection of the machine completely.
-init_pin_configs::
- Shows the initial pin default config values set by BIOS.
-driver_pin_configs::
- Shows the pin default values set by the codec parser explicitly.
- This doesn't show all pin values but only the changed values by
- the parser. That is, if the parser doesn't change the pin default
- config values by itself, this will contain nothing.
-user_pin_configs::
- Shows the pin default config values to override the BIOS setup.
- Writing this (with two numbers, NID and value) appends the new
- value. The given will be used instead of the initial BIOS value at
- the next reconfiguration time. Note that this config will override
- even the driver pin configs, too.
-reconfig::
- Triggers the codec re-configuration. When any value is written to
- this file, the driver re-initialize and parses the codec tree
- again. All the changes done by the sysfs entries above are taken
- into account.
-clear::
- Resets the codec, removes the mixer elements and PCM stuff of the
- specified codec, and clear all init verbs and hints.
-
-For example, when you want to change the pin default configuration
-value of the pin widget 0x14 to 0x9993013f, and let the driver
-re-configure based on that state, run like below:
-------------------------------------------------------------------------
- # echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
- # echo 1 > /sys/class/sound/hwC0D0/reconfig
-------------------------------------------------------------------------
-
-
-Hint Strings
-~~~~~~~~~~~~
-The codec parser have several switches and adjustment knobs for
-matching better with the actual codec or device behavior. Many of
-them can be adjusted dynamically via "hints" strings as mentioned in
-the section above. For example, by passing `jack_detect = no` string
-via sysfs or a patch file, you can disable the jack detection, thus
-the codec parser will skip the features like auto-mute or mic
-auto-switch. As a boolean value, either `yes`, `no`, `true`, `false`,
-`1` or `0` can be passed.
-
-The generic parser supports the following hints:
-
-- jack_detect (bool): specify whether the jack detection is available
- at all on this machine; default true
-- inv_jack_detect (bool): indicates that the jack detection logic is
- inverted
-- trigger_sense (bool): indicates that the jack detection needs the
- explicit call of AC_VERB_SET_PIN_SENSE verb
-- inv_eapd (bool): indicates that the EAPD is implemented in the
- inverted logic
-- pcm_format_first (bool): sets the PCM format before the stream tag
- and channel ID
-- sticky_stream (bool): keep the PCM format, stream tag and ID as long
- as possible; default true
-- spdif_status_reset (bool): reset the SPDIF status bits at each time
- the SPDIF stream is set up
-- pin_amp_workaround (bool): the output pin may have multiple amp
- values
-- single_adc_amp (bool): ADCs can have only single input amps
-- auto_mute (bool): enable/disable the headphone auto-mute feature;
- default true
-- auto_mic (bool): enable/disable the mic auto-switch feature; default
- true
-- line_in_auto_switch (bool): enable/disable the line-in auto-switch
- feature; default false
-- need_dac_fix (bool): limits the DACs depending on the channel count
-- primary_hp (bool): probe headphone jacks as the primary outputs;
- default true
-- multi_io (bool): try probing multi-I/O config (e.g. shared
- line-in/surround, mic/clfe jacks)
-- multi_cap_vol (bool): provide multiple capture volumes
-- inv_dmic_split (bool): provide split internal mic volume/switch for
- phase-inverted digital mics
-- indep_hp (bool): provide the independent headphone PCM stream and
- the corresponding mixer control, if available
-- add_stereo_mix_input (bool): add the stereo mix (analog-loopback
- mix) to the input mux if available
-- add_jack_modes (bool): add "xxx Jack Mode" enum controls to each
- I/O jack for allowing to change the headphone amp and mic bias VREF
- capabilities
-- power_save_node (bool): advanced power management for each widget,
- controlling the power sate (D0/D3) of each widget node depending on
- the actual pin and stream states
-- power_down_unused (bool): power down the unused widgets, a subset of
- power_save_node, and will be dropped in future
-- add_hp_mic (bool): add the headphone to capture source if possible
-- hp_mic_detect (bool): enable/disable the hp/mic shared input for a
- single built-in mic case; default true
-- mixer_nid (int): specifies the widget NID of the analog-loopback
- mixer
-
-
-Early Patching
-~~~~~~~~~~~~~~
-When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a
-firmware file for modifying the HD-audio setup before initializing the
-codec. This can work basically like the reconfiguration via sysfs in
-the above, but it does it before the first codec configuration.
-
-A patch file is a plain text file which looks like below:
-
-------------------------------------------------------------------------
- [codec]
- 0x12345678 0xabcd1234 2
-
- [model]
- auto
-
- [pincfg]
- 0x12 0x411111f0
-
- [verb]
- 0x20 0x500 0x03
- 0x20 0x400 0xff
-
- [hint]
- jack_detect = no
-------------------------------------------------------------------------
-
-The file needs to have a line `[codec]`. The next line should contain
-three numbers indicating the codec vendor-id (0x12345678 in the
-example), the codec subsystem-id (0xabcd1234) and the address (2) of
-the codec. The rest patch entries are applied to this specified codec
-until another codec entry is given. Passing 0 or a negative number to
-the first or the second value will make the check of the corresponding
-field be skipped. It'll be useful for really broken devices that don't
-initialize SSID properly.
-
-The `[model]` line allows to change the model name of the each codec.
-In the example above, it will be changed to model=auto.
-Note that this overrides the module option.
-
-After the `[pincfg]` line, the contents are parsed as the initial
-default pin-configurations just like `user_pin_configs` sysfs above.
-The values can be shown in user_pin_configs sysfs file, too.
-
-Similarly, the lines after `[verb]` are parsed as `init_verbs`
-sysfs entries, and the lines after `[hint]` are parsed as `hints`
-sysfs entries, respectively.
-
-Another example to override the codec vendor id from 0x12345678 to
-0xdeadbeef is like below:
-------------------------------------------------------------------------
- [codec]
- 0x12345678 0xabcd1234 2
-
- [vendor_id]
- 0xdeadbeef
-------------------------------------------------------------------------
-
-In the similar way, you can override the codec subsystem_id via
-`[subsystem_id]`, the revision id via `[revision_id]` line.
-Also, the codec chip name can be rewritten via `[chip_name]` line.
-------------------------------------------------------------------------
- [codec]
- 0x12345678 0xabcd1234 2
-
- [subsystem_id]
- 0xffff1111
-
- [revision_id]
- 0x10
-
- [chip_name]
- My-own NEWS-0002
-------------------------------------------------------------------------
-
-The hd-audio driver reads the file via request_firmware(). Thus,
-a patch file has to be located on the appropriate firmware path,
-typically, /lib/firmware. For example, when you pass the option
-`patch=hda-init.fw`, the file /lib/firmware/hda-init.fw must be
-present.
-
-The patch module option is specific to each card instance, and you
-need to give one file name for each instance, separated by commas.
-For example, if you have two cards, one for an on-board analog and one
-for an HDMI video board, you may pass patch option like below:
-------------------------------------------------------------------------
- options snd-hda-intel patch=on-board-patch,hdmi-patch
-------------------------------------------------------------------------
-
-
-Power-Saving
-~~~~~~~~~~~~
-The power-saving is a kind of auto-suspend of the device. When the
-device is inactive for a certain time, the device is automatically
-turned off to save the power. The time to go down is specified via
-`power_save` module option, and this option can be changed dynamically
-via sysfs.
-
-The power-saving won't work when the analog loopback is enabled on
-some codecs. Make sure that you mute all unneeded signal routes when
-you want the power-saving.
-
-The power-saving feature might cause audible click noises at each
-power-down/up depending on the device. Some of them might be
-solvable, but some are hard, I'm afraid. Some distros such as
-openSUSE enables the power-saving feature automatically when the power
-cable is unplugged. Thus, if you hear noises, suspect first the
-power-saving. See /sys/module/snd_hda_intel/parameters/power_save to
-check the current value. If it's non-zero, the feature is turned on.
-
-The recent kernel supports the runtime PM for the HD-audio controller
-chip, too. It means that the HD-audio controller is also powered up /
-down dynamically. The feature is enabled only for certain controller
-chips like Intel LynxPoint. You can enable/disable this feature
-forcibly by setting `power_save_controller` option, which is also
-available at /sys/module/snd_hda_intel/parameters directory.
-
-
-Tracepoints
-~~~~~~~~~~~
-The hd-audio driver gives a few basic tracepoints.
-`hda:hda_send_cmd` traces each CORB write while `hda:hda_get_response`
-traces the response from RIRB (only when read from the codec driver).
-`hda:hda_bus_reset` traces the bus-reset due to fatal error, etc,
-`hda:hda_unsol_event` traces the unsolicited events, and
-`hda:hda_power_down` and `hda:hda_power_up` trace the power down/up
-via power-saving behavior.
-
-Enabling all tracepoints can be done like
-------------------------------------------------------------------------
- # echo 1 > /sys/kernel/debug/tracing/events/hda/enable
-------------------------------------------------------------------------
-then after some commands, you can traces from
-/sys/kernel/debug/tracing/trace file. For example, when you want to
-trace what codec command is sent, enable the tracepoint like:
-------------------------------------------------------------------------
- # cat /sys/kernel/debug/tracing/trace
- # tracer: nop
- #
- # TASK-PID CPU# TIMESTAMP FUNCTION
- # | | | | |
- <...>-7807 [002] 105147.774889: hda_send_cmd: [0:0] val=e3a019
- <...>-7807 [002] 105147.774893: hda_send_cmd: [0:0] val=e39019
- <...>-7807 [002] 105147.999542: hda_send_cmd: [0:0] val=e3a01a
- <...>-7807 [002] 105147.999543: hda_send_cmd: [0:0] val=e3901a
- <...>-26764 [001] 349222.837143: hda_send_cmd: [0:0] val=e3a019
- <...>-26764 [001] 349222.837148: hda_send_cmd: [0:0] val=e39019
- <...>-26764 [001] 349223.058539: hda_send_cmd: [0:0] val=e3a01a
- <...>-26764 [001] 349223.058541: hda_send_cmd: [0:0] val=e3901a
-------------------------------------------------------------------------
-Here `[0:0]` indicates the card number and the codec address, and
-`val` shows the value sent to the codec, respectively. The value is
-a packed value, and you can decode it via hda-decode-verb program
-included in hda-emu package below. For example, the value e3a019 is
-to set the left output-amp value to 25.
-------------------------------------------------------------------------
- % hda-decode-verb 0xe3a019
- raw value = 0x00e3a019
- cid = 0, nid = 0x0e, verb = 0x3a0, parm = 0x19
- raw value: verb = 0x3a0, parm = 0x19
- verbname = set_amp_gain_mute
- amp raw val = 0xa019
- output, left, idx=0, mute=0, val=25
-------------------------------------------------------------------------
-
-
-Development Tree
-~~~~~~~~~~~~~~~~
-The latest development codes for HD-audio are found on sound git tree:
-
-- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
-
-The master branch or for-next branches can be used as the main
-development branches in general while the development for the current
-and next kernels are found in for-linus and for-next branches,
-respectively.
-
-
-Sending a Bug Report
-~~~~~~~~~~~~~~~~~~~~
-If any model or module options don't work for your device, it's time
-to send a bug report to the developers. Give the following in your
-bug report:
-
-- Hardware vendor, product and model names
-- Kernel version (and ALSA-driver version if you built externally)
-- `alsa-info.sh` output; run with `--no-upload` option. See the
- section below about alsa-info
-
-If it's a regression, at best, send alsa-info outputs of both working
-and non-working kernels. This is really helpful because we can
-compare the codec registers directly.
-
-Send a bug report either the followings:
-
-kernel-bugzilla::
- https://bugzilla.kernel.org/
-alsa-devel ML::
- alsa-devel@alsa-project.org
-
-
-DEBUG TOOLS
------------
-
-This section describes some tools available for debugging HD-audio
-problems.
-
-alsa-info
-~~~~~~~~~
-The script `alsa-info.sh` is a very useful tool to gather the audio
-device information. It's included in alsa-utils package. The latest
-version can be found on git repository:
-
-- git://git.alsa-project.org/alsa-utils.git
-
-The script can be fetched directly from the following URL, too:
-
-- http://www.alsa-project.org/alsa-info.sh
-
-Run this script as root, and it will gather the important information
-such as the module lists, module parameters, proc file contents
-including the codec proc files, mixer outputs and the control
-elements. As default, it will store the information onto a web server
-on alsa-project.org. But, if you send a bug report, it'd be better to
-run with `--no-upload` option, and attach the generated file.
-
-There are some other useful options. See `--help` option output for
-details.
-
-When a probe error occurs or when the driver obviously assigns a
-mismatched model, it'd be helpful to load the driver with
-`probe_only=1` option (at best after the cold reboot) and run
-alsa-info at this state. With this option, the driver won't configure
-the mixer and PCM but just tries to probe the codec slot. After
-probing, the proc file is available, so you can get the raw codec
-information before modified by the driver. Of course, the driver
-isn't usable with `probe_only=1`. But you can continue the
-configuration via hwdep sysfs file if hda-reconfig option is enabled.
-Using `probe_only` mask 2 skips the reset of HDA codecs (use
-`probe_only=3` as module option). The hwdep interface can be used
-to determine the BIOS codec initialization.
-
-
-hda-verb
-~~~~~~~~
-hda-verb is a tiny program that allows you to access the HD-audio
-codec directly. You can execute a raw HD-audio codec verb with this.
-This program accesses the hwdep device, thus you need to enable the
-kernel config `CONFIG_SND_HDA_HWDEP=y` beforehand.
-
-The hda-verb program takes four arguments: the hwdep device file, the
-widget NID, the verb and the parameter. When you access to the codec
-on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
-argument, typically. (However, the real path name depends on the
-system.)
-
-The second parameter is the widget number-id to access. The third
-parameter can be either a hex/digit number or a string corresponding
-to a verb. Similarly, the last parameter is the value to write, or
-can be a string for the parameter type.
-
-------------------------------------------------------------------------
- % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
- nid = 0x12, verb = 0x701, param = 0x2
- value = 0x0
-
- % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
- nid = 0x0, verb = 0xf00, param = 0x0
- value = 0x10ec0262
-
- % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
- nid = 0x2, verb = 0x300, param = 0xb080
- value = 0x0
-------------------------------------------------------------------------
-
-Although you can issue any verbs with this program, the driver state
-won't be always updated. For example, the volume values are usually
-cached in the driver, and thus changing the widget amp value directly
-via hda-verb won't change the mixer value.
-
-The hda-verb program is included now in alsa-tools:
-
-- git://git.alsa-project.org/alsa-tools.git
-
-Also, the old stand-alone package is found in the ftp directory:
-
-- ftp://ftp.suse.com/pub/people/tiwai/misc/
-
-Also a git repository is available:
-
-- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
-
-See README file in the tarball for more details about hda-verb
-program.
-
-
-hda-analyzer
-~~~~~~~~~~~~
-hda-analyzer provides a graphical interface to access the raw HD-audio
-control, based on pyGTK2 binding. It's a more powerful version of
-hda-verb. The program gives you an easy-to-use GUI stuff for showing
-the widget information and adjusting the amp values, as well as the
-proc-compatible output.
-
-The hda-analyzer:
-
-- http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
-
-is a part of alsa.git repository in alsa-project.org:
-
-- git://git.alsa-project.org/alsa.git
-
-Codecgraph
-~~~~~~~~~~
-Codecgraph is a utility program to generate a graph and visualizes the
-codec-node connection of a codec chip. It's especially useful when
-you analyze or debug a codec without a proper datasheet. The program
-parses the given codec proc file and converts to SVG via graphiz
-program.
-
-The tarball and GIT trees are found in the web page at:
-
-- http://helllabs.org/codecgraph/
-
-
-hda-emu
-~~~~~~~
-hda-emu is an HD-audio emulator. The main purpose of this program is
-to debug an HD-audio codec without the real hardware. Thus, it
-doesn't emulate the behavior with the real audio I/O, but it just
-dumps the codec register changes and the ALSA-driver internal changes
-at probing and operating the HD-audio driver.
-
-The program requires a codec proc-file to simulate. Get a proc file
-for the target codec beforehand, or pick up an example codec from the
-codec proc collections in the tarball. Then, run the program with the
-proc file, and the hda-emu program will start parsing the codec file
-and simulates the HD-audio driver:
-
-------------------------------------------------------------------------
- % hda-emu codecs/stac9200-dell-d820-laptop
- # Parsing..
- hda_codec: Unknown model for STAC9200, using BIOS defaults
- hda_codec: pin nid 08 bios pin config 40c003fa
- ....
-------------------------------------------------------------------------
-
-The program gives you only a very dumb command-line interface. You
-can get a proc-file dump at the current state, get a list of control
-(mixer) elements, set/get the control element value, simulate the PCM
-operation, the jack plugging simulation, etc.
-
-The program is found in the git repository below:
-
-- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
-
-See README file in the repository for more details about hda-emu
-program.
-
-
-hda-jack-retask
-~~~~~~~~~~~~~~~
-hda-jack-retask is a user-friendly GUI program to manipulate the
-HD-audio pin control for jack retasking. If you have a problem about
-the jack assignment, try this program and check whether you can get
-useful results. Once when you figure out the proper pin assignment,
-it can be fixed either in the driver code statically or via passing a
-firmware patch file (see "Early Patching" section).
-
-The program is included in alsa-tools now:
-
-- git://git.alsa-project.org/alsa-tools.git
-
+++ /dev/null
-Why we need Jack kcontrols
-==========================
-
-ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...)
-to user space. This means userspace applications like pulseaudio can
-switch off headphones and switch on speakers when no headphones are
-pluged in.
-
-The old ALSA jack code only created input devices for each registered
-jack. These jack input devices are not readable by userspace devices
-that run as non root.
-
-The new jack code creates embedded jack kcontrols for each jack that
-can be read by any process.
-
-This can be combined with UCM to allow userspace to route audio more
-intelligently based on jack insertion or removal events.
-
-Jack Kcontrol Internals
-=======================
-
-Each jack will have a kcontrol list, so that we can create a kcontrol
-and attach it to the jack, at jack creation stage. We can also add a
-kcontrol to an existing jack, at anytime when required.
-
-Those kcontrols will be freed automatically when the Jack is freed.
-
-How to use jack kcontrols
-=========================
-
-In order to keep compatibility, snd_jack_new() has been modified by
-adding two params :-
-
- - @initial_kctl: if true, create a kcontrol and add it to the jack
- list.
- - @phantom_jack: Don't create a input device for phantom jacks.
-
-HDA jacks can set phantom_jack to true in order to create a phantom
-jack and set initial_kctl to true to create an initial kcontrol with
-the correct id.
-
-ASoC jacks should set initial_kctl as false. The pin name will be
-assigned as the jack kcontrol name.
+++ /dev/null
-Analog Joystick Support on ALSA Drivers
-=======================================
- Oct. 14, 2003
- Takashi Iwai <tiwai@suse.de>
-
-General
--------
-
-First of all, you need to enable GAMEPORT support on Linux kernel for
-using a joystick with the ALSA driver. For the details of gameport
-support, refer to Documentation/input/joystick.txt.
-
-The joystick support of ALSA drivers is different between ISA and PCI
-cards. In the case of ISA (PnP) cards, it's usually handled by the
-independent module (ns558). Meanwhile, the ALSA PCI drivers have the
-built-in gameport support. Hence, when the ALSA PCI driver is built
-in the kernel, CONFIG_GAMEPORT must be 'y', too. Otherwise, the
-gameport support on that card will be (silently) disabled.
-
-Some adapter modules probe the physical connection of the device at
-the load time. It'd be safer to plug in the joystick device before
-loading the module.
-
-
-PCI Cards
----------
-
-For PCI cards, the joystick is enabled when the appropriate module
-option is specified. Some drivers don't need options, and the
-joystick support is always enabled. In the former ALSA version, there
-was a dynamic control API for the joystick activation. It was
-changed, however, to the static module options because of the system
-stability and the resource management.
-
-The following PCI drivers support the joystick natively.
-
- Driver Module Option Available Values
- ---------------------------------------------------------------------------
- als4000 joystick_port 0 = disable (default), 1 = auto-detect,
- manual: any address (e.g. 0x200)
- au88x0 N/A N/A
- azf3328 joystick 0 = disable, 1 = enable, -1 = auto (default)
- ens1370 joystick 0 = disable (default), 1 = enable
- ens1371 joystick_port 0 = disable (default), 1 = auto-detect,
- manual: 0x200, 0x208, 0x210, 0x218
- cmipci joystick_port 0 = disable (default), 1 = auto-detect,
- manual: any address (e.g. 0x200)
- cs4281 N/A N/A
- cs46xx N/A N/A
- es1938 N/A N/A
- es1968 joystick 0 = disable (default), 1 = enable
- sonicvibes N/A N/A
- trident N/A N/A
- via82xx(*1) joystick 0 = disable (default), 1 = enable
- ymfpci joystick_port 0 = disable (default), 1 = auto-detect,
- manual: 0x201, 0x202, 0x204, 0x205(*2)
- ---------------------------------------------------------------------------
-
- *1) VIA686A/B only
- *2) With YMF744/754 chips, the port address can be chosen arbitrarily
-
-The following drivers don't support gameport natively, but there are
-additional modules. Load the corresponding module to add the gameport
-support.
-
- Driver Additional Module
- -----------------------------
- emu10k1 emu10k1-gp
- fm801 fm801-gp
- -----------------------------
-
-Note: the "pcigame" and "cs461x" modules are for the OSS drivers only.
- These ALSA drivers (cs46xx, trident and au88x0) have the
- built-in gameport support.
-
-As mentioned above, ALSA PCI drivers have the built-in gameport
-support, so you don't have to load ns558 module. Just load "joydev"
-and the appropriate adapter module (e.g. "analog").
-
-
-ISA Cards
----------
-
-ALSA ISA drivers don't have the built-in gameport support.
-Instead, you need to load "ns558" module in addition to "joydev" and
-the adapter module (e.g. "analog").
+++ /dev/null
- Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards
- Digigram <alsa@digigram.com>
-
-
-GENERAL
-=======
-
-The miXart8 is a multichannel audio processing and mixing soundcard
-that has 4 stereo audio inputs and 4 stereo audio outputs.
-The miXart8AES/EBU is the same with a add-on card that offers further
-4 digital stereo audio inputs and outputs.
-Furthermore the add-on card offers external clock synchronisation
-(AES/EBU, Word Clock, Time Code and Video Synchro)
-
-The mainboard has a PowerPC that offers onboard mpeg encoding and
-decoding, samplerate conversions and various effects.
-
-The driver don't work properly at all until the certain firmwares
-are loaded, i.e. no PCM nor mixer devices will appear.
-Use the mixartloader that can be found in the alsa-tools package.
-
-
-VERSION 0.1.0
-=============
-
-One miXart8 board will be represented as 4 alsa cards, each with 1
-stereo analog capture 'pcm0c' and 1 stereo analog playback 'pcm0p' device.
-With a miXart8AES/EBU there is in addition 1 stereo digital input
-'pcm1c' and 1 stereo digital output 'pcm1p' per card.
-
-Formats
--------
-U8, S16_LE, S16_BE, S24_3LE, S24_3BE, FLOAT_LE, FLOAT_BE
-Sample rates : 8000 - 48000 Hz continuously
-
-Playback
---------
-For instance the playback devices are configured to have max. 4
-substreams performing hardware mixing. This could be changed to a
-maximum of 24 substreams if wished.
-Mono files will be played on the left and right channel. Each channel
-can be muted for each stream to use 8 analog/digital outputs separately.
-
-Capture
--------
-There is one substream per capture device. For instance only stereo
-formats are supported.
-
-Mixer
------
-<Master> and <Master Capture> : analog volume control of playback and capture PCM.
-<PCM 0-3> and <PCM Capture> : digital volume control of each analog substream.
-<AES 0-3> and <AES Capture> : digital volume control of each AES/EBU substream.
-<Monitoring> : Loopback from 'pcm0c' to 'pcm0p' with digital volume
-and mute control.
-
-Rem : for best audio quality try to keep a 0 attenuation on the PCM
-and AES volume controls which is set by 219 in the range from 0 to 255
-(about 86% with alsamixer)
-
-
-NOT YET IMPLEMENTED
-===================
-
-- external clock support (AES/EBU, Word Clock, Time Code, Video Sync)
-- MPEG audio formats
-- mono record
-- on-board effects and samplerate conversions
-- linked streams
-
-
-FIRMWARE
-========
-
-[As of 2.6.11, the firmware can be loaded automatically with hotplug
- when CONFIG_FW_LOADER is set. The mixartloader is necessary only
- for older versions or when you build the driver into kernel.]
-
-For loading the firmware automatically after the module is loaded, use a
-install command. For example, add the following entry to
-/etc/modprobe.d/mixart.conf for miXart driver:
-
- install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \
- /usr/bin/mixartloader
-(for 2.2/2.4 kernels, add "post-install snd-mixart /usr/bin/vxloader" to
- /etc/modules.conf, instead.)
-
-The firmware binaries are installed on /usr/share/alsa/firmware
-(or /usr/local/share/alsa/firmware, depending to the prefix option of
-configure). There will be a miXart.conf file, which define the dsp image
-files.
-
-The firmware files are copyright by Digigram SA
-
-
-COPYRIGHT
-=========
-
-Copyright (c) 2003 Digigram SA <alsa@digigram.com>
-Distributable under GPL.
+++ /dev/null
- NOTES ON KERNEL OSS-EMULATION
- =============================
-
- Jan. 22, 2004 Takashi Iwai <tiwai@suse.de>
-
-
-Modules
-=======
-
-ALSA provides a powerful OSS emulation on the kernel.
-The OSS emulation for PCM, mixer and sequencer devices is implemented
-as add-on kernel modules, snd-pcm-oss, snd-mixer-oss and snd-seq-oss.
-When you need to access the OSS PCM, mixer or sequencer devices, the
-corresponding module has to be loaded.
-
-These modules are loaded automatically when the corresponding service
-is called. The alias is defined sound-service-x-y, where x and y are
-the card number and the minor unit number. Usually you don't have to
-define these aliases by yourself.
-
-Only necessary step for auto-loading of OSS modules is to define the
-card alias in /etc/modprobe.d/alsa.conf, such as
-
- alias sound-slot-0 snd-emu10k1
-
-As the second card, define sound-slot-1 as well.
-Note that you can't use the aliased name as the target name (i.e.
-"alias sound-slot-0 snd-card-0" doesn't work any more like the old
-modutils).
-
-The currently available OSS configuration is shown in
-/proc/asound/oss/sndstat. This shows in the same syntax of
-/dev/sndstat, which is available on the commercial OSS driver.
-On ALSA, you can symlink /dev/sndstat to this proc file.
-
-Please note that the devices listed in this proc file appear only
-after the corresponding OSS-emulation module is loaded. Don't worry
-even if "NOT ENABLED IN CONFIG" is shown in it.
-
-
-Device Mapping
-==============
-
-ALSA supports the following OSS device files:
-
- PCM:
- /dev/dspX
- /dev/adspX
-
- Mixer:
- /dev/mixerX
-
- MIDI:
- /dev/midi0X
- /dev/amidi0X
-
- Sequencer:
- /dev/sequencer
- /dev/sequencer2 (aka /dev/music)
-
-where X is the card number from 0 to 7.
-
-(NOTE: Some distributions have the device files like /dev/midi0 and
- /dev/midi1. They are NOT for OSS but for tclmidi, which is
- a totally different thing.)
-
-Unlike the real OSS, ALSA cannot use the device files more than the
-assigned ones. For example, the first card cannot use /dev/dsp1 or
-/dev/dsp2, but only /dev/dsp0 and /dev/adsp0.
-
-As seen above, PCM and MIDI may have two devices. Usually, the first
-PCM device (hw:0,0 in ALSA) is mapped to /dev/dsp and the secondary
-device (hw:0,1) to /dev/adsp (if available). For MIDI, /dev/midi and
-/dev/amidi, respectively.
-
-You can change this device mapping via the module options of
-snd-pcm-oss and snd-rawmidi. In the case of PCM, the following
-options are available for snd-pcm-oss:
-
- dsp_map PCM device number assigned to /dev/dspX
- (default = 0)
- adsp_map PCM device number assigned to /dev/adspX
- (default = 1)
-
-For example, to map the third PCM device (hw:0,2) to /dev/adsp0,
-define like this:
-
- options snd-pcm-oss adsp_map=2
-
-The options take arrays. For configuring the second card, specify
-two entries separated by comma. For example, to map the third PCM
-device on the second card to /dev/adsp1, define like below:
-
- options snd-pcm-oss adsp_map=0,2
-
-To change the mapping of MIDI devices, the following options are
-available for snd-rawmidi:
-
- midi_map MIDI device number assigned to /dev/midi0X
- (default = 0)
- amidi_map MIDI device number assigned to /dev/amidi0X
- (default = 1)
-
-For example, to assign the third MIDI device on the first card to
-/dev/midi00, define as follows:
-
- options snd-rawmidi midi_map=2
-
-
-PCM Mode
-========
-
-As default, ALSA emulates the OSS PCM with so-called plugin layer,
-i.e. tries to convert the sample format, rate or channels
-automatically when the card doesn't support it natively.
-This will lead to some problems for some applications like quake or
-wine, especially if they use the card only in the MMAP mode.
-
-In such a case, you can change the behavior of PCM per application by
-writing a command to the proc file. There is a proc file for each PCM
-stream, /proc/asound/cardX/pcmY[cp]/oss, where X is the card number
-(zero-based), Y the PCM device number (zero-based), and 'p' is for
-playback and 'c' for capture, respectively. Note that this proc file
-exists only after snd-pcm-oss module is loaded.
-
-The command sequence has the following syntax:
-
- app_name fragments fragment_size [options]
-
-app_name is the name of application with (higher priority) or without
-path.
-fragments specifies the number of fragments or zero if no specific
-number is given.
-fragment_size is the size of fragment in bytes or zero if not given.
-options is the optional parameters. The following options are
-available:
-
- disable the application tries to open a pcm device for
- this channel but does not want to use it.
- direct don't use plugins
- block force block open mode
- non-block force non-block open mode
- partial-frag write also partial fragments (affects playback only)
- no-silence do not fill silence ahead to avoid clicks
-
-The disable option is useful when one stream direction (playback or
-capture) is not handled correctly by the application although the
-hardware itself does support both directions.
-The direct option is used, as mentioned above, to bypass the automatic
-conversion and useful for MMAP-applications.
-For example, to playback the first PCM device without plugins for
-quake, send a command via echo like the following:
-
- % echo "quake 0 0 direct" > /proc/asound/card0/pcm0p/oss
-
-While quake wants only playback, you may append the second command
-to notify driver that only this direction is about to be allocated:
-
- % echo "quake 0 0 disable" > /proc/asound/card0/pcm0c/oss
-
-The permission of proc files depend on the module options of snd.
-As default it's set as root, so you'll likely need to be superuser for
-sending the command above.
-
-The block and non-block options are used to change the behavior of
-opening the device file.
-
-As default, ALSA behaves as original OSS drivers, i.e. does not block
-the file when it's busy. The -EBUSY error is returned in this case.
-
-This blocking behavior can be changed globally via nonblock_open
-module option of snd-pcm-oss. For using the blocking mode as default
-for OSS devices, define like the following:
-
- options snd-pcm-oss nonblock_open=0
-
-The partial-frag and no-silence commands have been added recently.
-Both commands are for optimization use only. The former command
-specifies to invoke the write transfer only when the whole fragment is
-filled. The latter stops writing the silence data ahead
-automatically. Both are disabled as default.
-
-You can check the currently defined configuration by reading the proc
-file. The read image can be sent to the proc file again, hence you
-can save the current configuration
-
- % cat /proc/asound/card0/pcm0p/oss > /somewhere/oss-cfg
-
-and restore it like
-
- % cat /somewhere/oss-cfg > /proc/asound/card0/pcm0p/oss
-
-Also, for clearing all the current configuration, send "erase" command
-as below:
-
- % echo "erase" > /proc/asound/card0/pcm0p/oss
-
-
-Mixer Elements
-==============
-
-Since ALSA has completely different mixer interface, the emulation of
-OSS mixer is relatively complicated. ALSA builds up a mixer element
-from several different ALSA (mixer) controls based on the name
-string. For example, the volume element SOUND_MIXER_PCM is composed
-from "PCM Playback Volume" and "PCM Playback Switch" controls for the
-playback direction and from "PCM Capture Volume" and "PCM Capture
-Switch" for the capture directory (if exists). When the PCM volume of
-OSS is changed, all the volume and switch controls above are adjusted
-automatically.
-
-As default, ALSA uses the following control for OSS volumes:
-
- OSS volume ALSA control Index
- -----------------------------------------------------
- SOUND_MIXER_VOLUME Master 0
- SOUND_MIXER_BASS Tone Control - Bass 0
- SOUND_MIXER_TREBLE Tone Control - Treble 0
- SOUND_MIXER_SYNTH Synth 0
- SOUND_MIXER_PCM PCM 0
- SOUND_MIXER_SPEAKER PC Speaker 0
- SOUND_MIXER_LINE Line 0
- SOUND_MIXER_MIC Mic 0
- SOUND_MIXER_CD CD 0
- SOUND_MIXER_IMIX Monitor Mix 0
- SOUND_MIXER_ALTPCM PCM 1
- SOUND_MIXER_RECLEV (not assigned)
- SOUND_MIXER_IGAIN Capture 0
- SOUND_MIXER_OGAIN Playback 0
- SOUND_MIXER_LINE1 Aux 0
- SOUND_MIXER_LINE2 Aux 1
- SOUND_MIXER_LINE3 Aux 2
- SOUND_MIXER_DIGITAL1 Digital 0
- SOUND_MIXER_DIGITAL2 Digital 1
- SOUND_MIXER_DIGITAL3 Digital 2
- SOUND_MIXER_PHONEIN Phone 0
- SOUND_MIXER_PHONEOUT Phone 1
- SOUND_MIXER_VIDEO Video 0
- SOUND_MIXER_RADIO Radio 0
- SOUND_MIXER_MONITOR Monitor 0
-
-The second column is the base-string of the corresponding ALSA
-control. In fact, the controls with "XXX [Playback|Capture]
-[Volume|Switch]" will be checked in addition.
-
-The current assignment of these mixer elements is listed in the proc
-file, /proc/asound/cardX/oss_mixer, which will be like the following
-
- VOLUME "Master" 0
- BASS "" 0
- TREBLE "" 0
- SYNTH "" 0
- PCM "PCM" 0
- ...
-
-where the first column is the OSS volume element, the second column
-the base-string of the corresponding ALSA control, and the third the
-control index. When the string is empty, it means that the
-corresponding OSS control is not available.
-
-For changing the assignment, you can write the configuration to this
-proc file. For example, to map "Wave Playback" to the PCM volume,
-send the command like the following:
-
- % echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/oss_mixer
-
-The command is exactly as same as listed in the proc file. You can
-change one or more elements, one volume per line. In the last
-example, both "Wave Playback Volume" and "Wave Playback Switch" will
-be affected when PCM volume is changed.
-
-Like the case of PCM proc file, the permission of proc files depend on
-the module options of snd. you'll likely need to be superuser for
-sending the command above.
-
-As well as in the case of PCM proc file, you can save and restore the
-current mixer configuration by reading and writing the whole file
-image.
-
-
-Duplex Streams
-==============
-
-Note that when attempting to use a single device file for playback and
-capture, the OSS API provides no way to set the format, sample rate or
-number of channels different in each direction. Thus
- io_handle = open("device", O_RDWR)
-will only function correctly if the values are the same in each direction.
-
-To use different values in the two directions, use both
- input_handle = open("device", O_RDONLY)
- output_handle = open("device", O_WRONLY)
-and set the values for the corresponding handle.
-
-
-Unsupported Features
-====================
-
-MMAP on ICE1712 driver
-----------------------
-ICE1712 supports only the unconventional format, interleaved
-10-channels 24bit (packed in 32bit) format. Therefore you cannot mmap
-the buffer as the conventional (mono or 2-channels, 8 or 16bit) format
-on OSS.
-
+++ /dev/null
- Proc Files of ALSA Drivers
- ==========================
- Takashi Iwai <tiwai@suse.de>
-
-General
--------
-
-ALSA has its own proc tree, /proc/asound. Many useful information are
-found in this tree. When you encounter a problem and need debugging,
-check the files listed in the following sections.
-
-Each card has its subtree cardX, where X is from 0 to 7. The
-card-specific files are stored in the card* subdirectories.
-
-
-Global Information
-------------------
-
-cards
- Shows the list of currently configured ALSA drivers,
- index, the id string, short and long descriptions.
-
-version
- Shows the version string and compile date.
-
-modules
- Lists the module of each card
-
-devices
- Lists the ALSA native device mappings.
-
-meminfo
- Shows the status of allocated pages via ALSA drivers.
- Appears only when CONFIG_SND_DEBUG=y.
-
-hwdep
- Lists the currently available hwdep devices in format of
- <card>-<device>: <name>
-
-pcm
- Lists the currently available PCM devices in format of
- <card>-<device>: <id>: <name> : <sub-streams>
-
-timer
- Lists the currently available timer devices
-
-
-oss/devices
- Lists the OSS device mappings.
-
-oss/sndstat
- Provides the output compatible with /dev/sndstat.
- You can symlink this to /dev/sndstat.
-
-
-Card Specific Files
--------------------
-
-The card-specific files are found in /proc/asound/card* directories.
-Some drivers (e.g. cmipci) have their own proc entries for the
-register dump, etc (e.g. /proc/asound/card*/cmipci shows the register
-dump). These files would be really helpful for debugging.
-
-When PCM devices are available on this card, you can see directories
-like pcm0p or pcm1c. They hold the PCM information for each PCM
-stream. The number after 'pcm' is the PCM device number from 0, and
-the last 'p' or 'c' means playback or capture direction. The files in
-this subtree is described later.
-
-The status of MIDI I/O is found in midi* files. It shows the device
-name and the received/transmitted bytes through the MIDI device.
-
-When the card is equipped with AC97 codecs, there are codec97#*
-subdirectories (described later).
-
-When the OSS mixer emulation is enabled (and the module is loaded),
-oss_mixer file appears here, too. This shows the current mapping of
-OSS mixer elements to the ALSA control elements. You can change the
-mapping by writing to this device. Read OSS-Emulation.txt for
-details.
-
-
-PCM Proc Files
---------------
-
-card*/pcm*/info
- The general information of this PCM device: card #, device #,
- substreams, etc.
-
-card*/pcm*/xrun_debug
- This file appears when CONFIG_SND_DEBUG=y and
- CONFIG_PCM_XRUN_DEBUG=y.
- This shows the status of xrun (= buffer overrun/xrun) and
- invalid PCM position debug/check of ALSA PCM middle layer.
- It takes an integer value, can be changed by writing to this
- file, such as
-
- # echo 5 > /proc/asound/card0/pcm0p/xrun_debug
-
- The value consists of the following bit flags:
- bit 0 = Enable XRUN/jiffies debug messages
- bit 1 = Show stack trace at XRUN / jiffies check
- bit 2 = Enable additional jiffies check
-
- When the bit 0 is set, the driver will show the messages to
- kernel log when an xrun is detected. The debug message is
- shown also when the invalid H/W pointer is detected at the
- update of periods (usually called from the interrupt
- handler).
-
- When the bit 1 is set, the driver will show the stack trace
- additionally. This may help the debugging.
-
- Since 2.6.30, this option can enable the hwptr check using
- jiffies. This detects spontaneous invalid pointer callback
- values, but can be lead to too much corrections for a (mostly
- buggy) hardware that doesn't give smooth pointer updates.
- This feature is enabled via the bit 2.
-
-card*/pcm*/sub*/info
- The general information of this PCM sub-stream.
-
-card*/pcm*/sub*/status
- The current status of this PCM sub-stream, elapsed time,
- H/W position, etc.
-
-card*/pcm*/sub*/hw_params
- The hardware parameters set for this sub-stream.
-
-card*/pcm*/sub*/sw_params
- The soft parameters set for this sub-stream.
-
-card*/pcm*/sub*/prealloc
- The buffer pre-allocation information.
-
-card*/pcm*/sub*/xrun_injection
- Triggers an XRUN to the running stream when any value is
- written to this proc file. Used for fault injection.
- This entry is write-only.
-
-AC97 Codec Information
-----------------------
-
-card*/codec97#*/ac97#?-?
- Shows the general information of this AC97 codec chip, such as
- name, capabilities, set up.
-
-card*/codec97#0/ac97#?-?+regs
- Shows the AC97 register dump. Useful for debugging.
-
- When CONFIG_SND_DEBUG is enabled, you can write to this file for
- changing an AC97 register directly. Pass two hex numbers.
- For example,
-
- # echo 02 9f1f > /proc/asound/card0/codec97#0/ac97#0-0+regs
-
-
-USB Audio Streams
------------------
-
-card*/stream*
- Shows the assignment and the current status of each audio stream
- of the given card. This information is very useful for debugging.
-
-
-HD-Audio Codecs
----------------
-
-card*/codec#*
- Shows the general codec information and the attribute of each
- widget node.
-
-card*/eld#*
- Available for HDMI or DisplayPort interfaces.
- Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
- and describes its audio capabilities and configurations.
-
- Some ELD fields may be modified by doing `echo name hex_value > eld#*`.
- Only do this if you are sure the HDMI sink provided value is wrong.
- And if that makes your HDMI audio work, please report to us so that we
- can fix it in future kernel releases.
-
-
-Sequencer Information
----------------------
-
-seq/drivers
- Lists the currently available ALSA sequencer drivers.
-
-seq/clients
- Shows the list of currently available sequencer clients and
- ports. The connection status and the running status are shown
- in this file, too.
-
-seq/queues
- Lists the currently allocated/running sequencer queues.
-
-seq/timer
- Lists the currently allocated/running sequencer timers.
-
-seq/oss
- Lists the OSS-compatible sequencer stuffs.
-
-
-Help For Debugging?
--------------------
-
-When the problem is related with PCM, first try to turn on xrun_debug
-mode. This will give you the kernel messages when and where xrun
-happened.
-
-If it's really a bug, report it with the following information:
-
- - the name of the driver/card, show in /proc/asound/cards
- - the register dump, if available (e.g. card*/cmipci)
-
-when it's a PCM problem,
-
- - set-up of PCM, shown in hw_parms, sw_params, and status in the PCM
- sub-stream directory
-
-when it's a mixer problem,
-
- - AC97 proc files, codec97#*/* files
-
-for USB audio/midi,
-
- - output of lsusb -v
- - stream* files in card directory
-
-
-The ALSA bug-tracking system is found at:
-
- https://bugtrack.alsa-project.org/alsa-bug/
+++ /dev/null
-NOTE: The following is the original document of Rainer's patch that the
-current maya44 code based on. Some contents might be obsoleted, but I
-keep here as reference -- tiwai
-
-----------------------------------------------------------------
-
-STATE OF DEVELOPMENT:
-
-This driver is being developed on the initiative of Piotr Makowski (oponek@gmail.com) and financed by Lars Bergmann.
-Development is carried out by Rainer Zimmermann (mail@lightshed.de).
-
-ESI provided a sample Maya44 card for the development work.
-
-However, unfortunately it has turned out difficult to get detailed programming information, so I (Rainer Zimmermann) had to find out some card-specific information by experiment and conjecture. Some information (in particular, several GPIO bits) is still missing.
-
-This is the first testing version of the Maya44 driver released to the alsa-devel mailing list (Feb 5, 2008).
-
-
-The following functions work, as tested by Rainer Zimmermann and Piotr Makowski:
-
-- playback and capture at all sampling rates
-- input/output level
-- crossmixing
-- line/mic switch
-- phantom power switch
-- analogue monitor a.k.a bypass
-
-
-The following functions *should* work, but are not fully tested:
-
-- Channel 3+4 analogue - S/PDIF input switching
-- S/PDIF output
-- all inputs/outputs on the M/IO/DIO extension card
-- internal/external clock selection
-
-
-*In particular, we would appreciate testing of these functions by anyone who has access to an M/IO/DIO extension card.*
-
-
-Things that do not seem to work:
-
-- The level meters ("multi track") in 'alsamixer' do not seem to react to signals in (if this is a bug, it would probably be in the existing ICE1724 code).
-
-- Ardour 2.1 seems to work only via JACK, not using ALSA directly or via OSS. This still needs to be tracked down.
-
-
-DRIVER DETAILS:
-
-the following files were added:
-
-pci/ice1724/maya44.c - Maya44 specific code
-pci/ice1724/maya44.h
-pci/ice1724/ice1724.patch
-pci/ice1724/ice1724.h.patch - PROPOSED patch to ice1724.h (see SAMPLING RATES)
-i2c/other/wm8776.c - low-level access routines for Wolfson WM8776 codecs
-include/wm8776.h
-
-
-Note that the wm8776.c code is meant to be card-independent and does not actually register the codec with the ALSA infrastructure.
-This is done in maya44.c, mainly because some of the WM8776 controls are used in Maya44-specific ways, and should be named appropriately.
-
-
-the following files were created in pci/ice1724, simply #including the corresponding file from the alsa-kernel tree:
-
-wtm.h
-vt1720_mobo.h
-revo.h
-prodigy192.h
-pontis.h
-phase.h
-maya44.h
-juli.h
-aureon.h
-amp.h
-envy24ht.h
-se.h
-prodigy_hifi.h
-
-
-*I hope this is the correct way to do things.*
-
-
-SAMPLING RATES:
-
-The Maya44 card (or more exactly, the Wolfson WM8776 codecs) allow a maximum sampling rate of 192 kHz for playback and 92 kHz for capture.
-
-As the ICE1724 chip only allows one global sampling rate, this is handled as follows:
-
-* setting the sampling rate on any open PCM device on the maya44 card will always set the *global* sampling rate for all playback and capture channels.
-
-* In the current state of the driver, setting rates of up to 192 kHz is permitted even for capture devices.
-
-*AVOID CAPTURING AT RATES ABOVE 96kHz*, even though it may appear to work. The codec cannot actually capture at such rates, meaning poor quality.
-
-
-I propose some additional code for limiting the sampling rate when setting on a capture pcm device. However because of the global sampling rate, this logic would be somewhat problematic.
-
-The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712).
-
-
-SOUND DEVICES:
-
-PCM devices correspond to inputs/outputs as follows (assuming Maya44 is card #0):
-
-hw:0,0 input - stereo, analog input 1+2
-hw:0,0 output - stereo, analog output 1+2
-hw:0,1 input - stereo, analog input 3+4 OR S/PDIF input
-hw:0,1 output - stereo, analog output 3+4 (and SPDIF out)
-
-
-NAMING OF MIXER CONTROLS:
-
-(for more information about the signal flow, please refer to the block diagram on p.24 of the ESI Maya44 manual, or in the ESI windows software).
-
-
-PCM: (digital) output level for channel 1+2
-PCM 1: same for channel 3+4
-
-Mic Phantom+48V: switch for +48V phantom power for electrostatic microphones on input 1/2.
- Make sure this is not turned on while any other source is connected to input 1/2.
- It might damage the source and/or the maya44 card.
-
-Mic/Line input: if switch is on, input jack 1/2 is microphone input (mono), otherwise line input (stereo).
-
-Bypass: analogue bypass from ADC input to output for channel 1+2. Same as "Monitor" in the windows driver.
-Bypass 1: same for channel 3+4.
-
-Crossmix: cross-mixer from channels 1+2 to channels 3+4
-Crossmix 1: cross-mixer from channels 3+4 to channels 1+2
-
-IEC958 Output: switch for S/PDIF output.
- This is not supported by the ESI windows driver.
- S/PDIF should output the same signal as channel 3+4. [untested!]
-
-
-Digitial output selectors:
-
- These switches allow a direct digital routing from the ADCs to the DACs.
- Each switch determines where the digital input data to one of the DACs comes from.
- They are not supported by the ESI windows driver.
- For normal operation, they should all be set to "PCM out".
-
-H/W: Output source channel 1
-H/W 1: Output source channel 2
-H/W 2: Output source channel 3
-H/W 3: Output source channel 4
-
-H/W 4 ... H/W 9: unknown function, left in to enable testing.
- Possibly some of these control S/PDIF output(s).
- If these turn out to be unused, they will go away in later driver versions.
-
-Selectable values for each of the digital output selectors are:
- "PCM out" -> DAC output of the corresponding channel (default setting)
- "Input 1"...
- "Input 4" -> direct routing from ADC output of the selected input channel
-
-
---------
-
-Feb 14, 2008
-Rainer Zimmermann
-mail@lightshed.de
-
+++ /dev/null
-
- Sound Blaster Live mixer / default DSP code
- ===========================================
-
-
-The EMU10K1 chips have a DSP part which can be programmed to support
-various ways of sample processing, which is described here.
-(This article does not deal with the overall functionality of the
-EMU10K1 chips. See the manuals section for further details.)
-
-The ALSA driver programs this portion of chip by default code
-(can be altered later) which offers the following functionality:
-
-
-1) IEC958 (S/PDIF) raw PCM
---------------------------
-
-This PCM device (it's the 4th PCM device (index 3!) and first subdevice
-(index 0) for a given card) allows to forward 48kHz, stereo, 16-bit
-little endian streams without any modifications to the digital output
-(coaxial or optical). The universal interface allows the creation of up
-to 8 raw PCM devices operating at 48kHz, 16-bit little endian. It would
-be easy to add support for multichannel devices to the current code,
-but the conversion routines exist only for stereo (2-channel streams)
-at the time.
-
-Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details.
-
-
-2) Digital mixer controls
--------------------------
-
-These controls are built using the DSP instructions. They offer extended
-functionality. Only the default build-in code in the ALSA driver is described
-here. Note that the controls work as attenuators: the maximum value is the
-neutral position leaving the signal unchanged. Note that if the same destination
-is mentioned in multiple controls, the signal is accumulated and can be wrapped
-(set to maximal or minimal value without checking of overflow).
-
-
-Explanation of used abbreviations:
-
-DAC - digital to analog converter
-ADC - analog to digital converter
-I2S - one-way three wire serial bus for digital sound by Philips Semiconductors
- (this standard is used for connecting standalone DAC and ADC converters)
-LFE - low frequency effects (subwoofer signal)
-AC97 - a chip containing an analog mixer, DAC and ADC converters
-IEC958 - S/PDIF
-FX-bus - the EMU10K1 chip has an effect bus containing 16 accumulators.
- Each of the synthesizer voices can feed its output to these accumulators
- and the DSP microcontroller can operate with the resulting sum.
-
-
-name='Wave Playback Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
-The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
-
-name='Wave Surround Playback Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
-The result samples are forwarded to the rear I2S DACs. These DACs operates
-separately (they are not inside the AC97 codec).
-
-name='Wave Center Playback Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
-The result is mixed to mono signal (single channel) and forwarded to
-the ??rear?? right DAC PCM slot of the AC97 codec.
-
-name='Wave LFE Playback Volume',index=0
-
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulators. ALSA uses accumulators 0 and 1 for left and right PCM.
-The result is mixed to mono signal (single channel) and forwarded to
-the ??rear?? left DAC PCM slot of the AC97 codec.
-
-name='Wave Capture Volume',index=0
-name='Wave Capture Switch',index=0
-
-These controls are used to attenuate samples for left and right PCM FX-bus
-accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Synth Playback Volume',index=0
-
-This control is used to attenuate samples for left and right MIDI FX-bus
-accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
-The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
-
-name='Synth Capture Volume',index=0
-name='Synth Capture Switch',index=0
-
-These controls are used to attenuate samples for left and right MIDI FX-bus
-accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Surround Playback Volume',index=0
-
-This control is used to attenuate samples for left and right rear PCM FX-bus
-accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
-The result samples are forwarded to the rear I2S DACs. These DACs operate
-separately (they are not inside the AC97 codec).
-
-name='Surround Capture Volume',index=0
-name='Surround Capture Switch',index=0
-
-These controls are used to attenuate samples for left and right rear PCM FX-bus
-accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
-
-name='Center Playback Volume',index=0
-
-This control is used to attenuate sample for center PCM FX-bus accumulator.
-ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded
-to the ??rear?? right DAC PCM slot of the AC97 codec.
-
-name='LFE Playback Volume',index=0
-
-This control is used to attenuate sample for center PCM FX-bus accumulator.
-ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded
-to the ??rear?? left DAC PCM slot of the AC97 codec.
-
-name='AC97 Playback Volume',index=0
-
-This control is used to attenuate samples for left and right front ADC PCM slots
-of the AC97 codec. The result samples are forwarded to the front DAC PCM
-slots of the AC97 codec.
-********************************************************************************
-*** Note: This control should be zero for the standard operations, otherwise ***
-*** a digital loopback is activated. ***
-********************************************************************************
-
-name='AC97 Capture Volume',index=0
-
-This control is used to attenuate samples for left and right front ADC PCM slots
-of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to
-the standard capture PCM device).
-********************************************************************************
-*** Note: This control should be 100 (maximal value), otherwise no analog ***
-*** inputs of the AC97 codec can be captured (recorded). ***
-********************************************************************************
-
-name='IEC958 TTL Playback Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 TTL
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the front DAC PCM slots of the AC97 codec.
-
-name='IEC958 TTL Capture Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 TTL
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
-
-name='Zoom Video Playback Volume',index=0
-
-This control is used to attenuate samples from left and right zoom video
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the front DAC PCM slots of the AC97 codec.
-
-name='Zoom Video Capture Volume',index=0
-
-This control is used to attenuate samples from left and right zoom video
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
-
-name='IEC958 LiveDrive Playback Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 optical
-digital input. The result samples are forwarded to the front DAC PCM slots
-of the AC97 codec.
-
-name='IEC958 LiveDrive Capture Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 optical
-digital inputs. The result samples are forwarded to the ADC capture FIFO
-(thus to the standard capture PCM device).
-
-name='IEC958 Coaxial Playback Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 coaxial
-digital inputs. The result samples are forwarded to the front DAC PCM slots
-of the AC97 codec.
-
-name='IEC958 Coaxial Capture Volume',index=0
-
-This control is used to attenuate samples from left and right IEC958 coaxial
-digital inputs. The result samples are forwarded to the ADC capture FIFO
-(thus to the standard capture PCM device).
-
-name='Line LiveDrive Playback Volume',index=0
-name='Line LiveDrive Playback Volume',index=1
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the LiveDrive). The result samples are forwarded to the front
-DAC PCM slots of the AC97 codec.
-
-name='Line LiveDrive Capture Volume',index=1
-name='Line LiveDrive Capture Volume',index=1
-
-This control is used to attenuate samples from left and right I2S ADC
-inputs (on the LiveDrive). The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
-
-name='Tone Control - Switch',index=0
-
-This control turns the tone control on or off. The samples for front, rear
-and center / LFE outputs are affected.
-
-name='Tone Control - Bass',index=0
-
-This control sets the bass intensity. There is no neutral value!!
-When the tone control code is activated, the samples are always modified.
-The closest value to pure signal is 20.
-
-name='Tone Control - Treble',index=0
-
-This control sets the treble intensity. There is no neutral value!!
-When the tone control code is activated, the samples are always modified.
-The closest value to pure signal is 20.
-
-name='IEC958 Optical Raw Playback Switch',index=0
-
-If this switch is on, then the samples for the IEC958 (S/PDIF) digital
-output are taken only from the raw FX8010 PCM, otherwise standard front
-PCM samples are taken.
-
-name='Headphone Playback Volume',index=1
-
-This control attenuates the samples for the headphone output.
-
-name='Headphone Center Playback Switch',index=1
-
-If this switch is on, then the sample for the center PCM is put to the
-left headphone output (useful for SB Live cards without separate center/LFE
-output).
-
-name='Headphone LFE Playback Switch',index=1
-
-If this switch is on, then the sample for the center PCM is put to the
-right headphone output (useful for SB Live cards without separate center/LFE
-output).
-
-
-3) PCM stream related controls
-------------------------------
-
-name='EMU10K1 PCM Volume',index 0-31
-
-Channel volume attenuation in range 0-0xffff. The maximum value (no
-attenuation) is default. The channel mapping for three values is
-as follows:
-
- 0 - mono, default 0xffff (no attenuation)
- 1 - left, default 0xffff (no attenuation)
- 2 - right, default 0xffff (no attenuation)
-
-name='EMU10K1 PCM Send Routing',index 0-31
-
-This control specifies the destination - FX-bus accumulators. There are
-twelve values with this mapping:
-
- 0 - mono, A destination (FX-bus 0-15), default 0
- 1 - mono, B destination (FX-bus 0-15), default 1
- 2 - mono, C destination (FX-bus 0-15), default 2
- 3 - mono, D destination (FX-bus 0-15), default 3
- 4 - left, A destination (FX-bus 0-15), default 0
- 5 - left, B destination (FX-bus 0-15), default 1
- 6 - left, C destination (FX-bus 0-15), default 2
- 7 - left, D destination (FX-bus 0-15), default 3
- 8 - right, A destination (FX-bus 0-15), default 0
- 9 - right, B destination (FX-bus 0-15), default 1
- 10 - right, C destination (FX-bus 0-15), default 2
- 11 - right, D destination (FX-bus 0-15), default 3
-
-Don't forget that it's illegal to assign a channel to the same FX-bus accumulator
-more than once (it means 0=0 && 1=0 is an invalid combination).
-
-name='EMU10K1 PCM Send Volume',index 0-31
-
-It specifies the attenuation (amount) for given destination in range 0-255.
-The channel mapping is following:
-
- 0 - mono, A destination attn, default 255 (no attenuation)
- 1 - mono, B destination attn, default 255 (no attenuation)
- 2 - mono, C destination attn, default 0 (mute)
- 3 - mono, D destination attn, default 0 (mute)
- 4 - left, A destination attn, default 255 (no attenuation)
- 5 - left, B destination attn, default 0 (mute)
- 6 - left, C destination attn, default 0 (mute)
- 7 - left, D destination attn, default 0 (mute)
- 8 - right, A destination attn, default 0 (mute)
- 9 - right, B destination attn, default 255 (no attenuation)
- 10 - right, C destination attn, default 0 (mute)
- 11 - right, D destination attn, default 0 (mute)
-
-
-
-4) MANUALS/PATENTS:
--------------------
-
-ftp://opensource.creative.com/pub/doc
--------------------------------------
-
- Files:
- LM4545.pdf AC97 Codec
-
- m2049.pdf The EMU10K1 Digital Audio Processor
-
- hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects
-
-
-WIPO Patents
-------------
- Patent numbers:
- WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999)
- streams
-
- WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999)
-
- WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction
- Execution and Audio Data Sequencing (Jan. 14, 1999)
-
-
-US Patents (http://www.uspto.gov/)
-----------------------------------
-
- US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999)
-
- US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999)
- with a multiport memory onto which multiple asynchronous
- digital sound samples can be concurrently loaded
-
- US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999)
-
- US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000)
-
- US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000)
- system bus with prioritization and modification of bus transfers
- in accordance with loop ends and minimum block sizes
-
- US 6151670 Method for conserving memory storage using a (Nov. 21, 2000)
- pool of short term memory registers
-
- US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001)
- a common interrupt by associating programs to GP registers,
- defining interrupt register, polling GP registers, and invoking
- callback routine associated with defined interrupt register
+++ /dev/null
-
- VIA82xx mixer
- =============
-
-On many VIA82xx boards, the 'Input Source Select' mixer control does not work.
-Setting it to 'Input2' on such boards will cause recording to hang, or fail
-with EIO (input/output error) via OSS emulation. This control should be left
-at 'Input1' for such cards.
+++ /dev/null
- ALSA Kernel Parameters
- ~~~~~~~~~~~~~~~~~~~~~~
-
-See Documentation/kernel-parameters.txt for general information on
-specifying module parameters.
-
-This document may not be entirely up to date and comprehensive. The command
-"modinfo -p ${modulename}" shows a current list of all parameters of a loadable
-module. Loadable modules, after being loaded into the running kernel, also
-reveal their parameters in /sys/module/${modulename}/parameters/. Some of these
-parameters may be changed at runtime by the command
-"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}".
-
-
- snd-ad1816a= [HW,ALSA]
-
- snd-ad1848= [HW,ALSA]
-
- snd-ali5451= [HW,ALSA]
-
- snd-als100= [HW,ALSA]
-
- snd-als4000= [HW,ALSA]
-
- snd-azt2320= [HW,ALSA]
-
- snd-cmi8330= [HW,ALSA]
-
- snd-cmipci= [HW,ALSA]
-
- snd-cs4231= [HW,ALSA]
-
- snd-cs4232= [HW,ALSA]
-
- snd-cs4236= [HW,ALSA]
-
- snd-cs4281= [HW,ALSA]
-
- snd-cs46xx= [HW,ALSA]
-
- snd-dt019x= [HW,ALSA]
-
- snd-dummy= [HW,ALSA]
-
- snd-emu10k1= [HW,ALSA]
-
- snd-ens1370= [HW,ALSA]
-
- snd-ens1371= [HW,ALSA]
-
- snd-es968= [HW,ALSA]
-
- snd-es1688= [HW,ALSA]
-
- snd-es18xx= [HW,ALSA]
-
- snd-es1938= [HW,ALSA]
-
- snd-es1968= [HW,ALSA]
-
- snd-fm801= [HW,ALSA]
-
- snd-gusclassic= [HW,ALSA]
-
- snd-gusextreme= [HW,ALSA]
-
- snd-gusmax= [HW,ALSA]
-
- snd-hdsp= [HW,ALSA]
-
- snd-ice1712= [HW,ALSA]
-
- snd-intel8x0= [HW,ALSA]
-
- snd-interwave= [HW,ALSA]
-
- snd-interwave-stb=
- [HW,ALSA]
-
- snd-korg1212= [HW,ALSA]
-
- snd-maestro3= [HW,ALSA]
-
- snd-mpu401= [HW,ALSA]
-
- snd-mtpav= [HW,ALSA]
-
- snd-nm256= [HW,ALSA]
-
- snd-opl3sa2= [HW,ALSA]
-
- snd-opti92x-ad1848=
- [HW,ALSA]
-
- snd-opti92x-cs4231=
- [HW,ALSA]
-
- snd-opti93x= [HW,ALSA]
-
- snd-pmac= [HW,ALSA]
-
- snd-rme32= [HW,ALSA]
-
- snd-rme96= [HW,ALSA]
-
- snd-rme9652= [HW,ALSA]
-
- snd-sb8= [HW,ALSA]
-
- snd-sb16= [HW,ALSA]
-
- snd-sbawe= [HW,ALSA]
-
- snd-serial= [HW,ALSA]
-
- snd-sgalaxy= [HW,ALSA]
-
- snd-sonicvibes= [HW,ALSA]
-
- snd-sun-amd7930=
- [HW,ALSA]
-
- snd-sun-cs4231= [HW,ALSA]
-
- snd-trident= [HW,ALSA]
-
- snd-usb-audio= [HW,ALSA,USB]
-
- snd-via82xx= [HW,ALSA]
-
- snd-virmidi= [HW,ALSA]
-
- snd-wavefront= [HW,ALSA]
-
- snd-ymfpci= [HW,ALSA]
+++ /dev/null
- compress_offload.txt
- =====================
- Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com>
- Vinod Koul <vinod.koul@linux.intel.com>
-
-Overview
-
-Since its early days, the ALSA API was defined with PCM support or
-constant bitrates payloads such as IEC61937 in mind. Arguments and
-returned values in frames are the norm, making it a challenge to
-extend the existing API to compressed data streams.
-
-In recent years, audio digital signal processors (DSP) were integrated
-in system-on-chip designs, and DSPs are also integrated in audio
-codecs. Processing compressed data on such DSPs results in a dramatic
-reduction of power consumption compared to host-based
-processing. Support for such hardware has not been very good in Linux,
-mostly because of a lack of a generic API available in the mainline
-kernel.
-
-Rather than requiring a compatibility break with an API change of the
-ALSA PCM interface, a new 'Compressed Data' API is introduced to
-provide a control and data-streaming interface for audio DSPs.
-
-The design of this API was inspired by the 2-year experience with the
-Intel Moorestown SOC, with many corrections required to upstream the
-API in the mainline kernel instead of the staging tree and make it
-usable by others.
-
-Requirements
-
-The main requirements are:
-
-- separation between byte counts and time. Compressed formats may have
- a header per file, per frame, or no header at all. The payload size
- may vary from frame-to-frame. As a result, it is not possible to
- estimate reliably the duration of audio buffers when handling
- compressed data. Dedicated mechanisms are required to allow for
- reliable audio-video synchronization, which requires precise
- reporting of the number of samples rendered at any given time.
-
-- Handling of multiple formats. PCM data only requires a specification
- of the sampling rate, number of channels and bits per sample. In
- contrast, compressed data comes in a variety of formats. Audio DSPs
- may also provide support for a limited number of audio encoders and
- decoders embedded in firmware, or may support more choices through
- dynamic download of libraries.
-
-- Focus on main formats. This API provides support for the most
- popular formats used for audio and video capture and playback. It is
- likely that as audio compression technology advances, new formats
- will be added.
-
-- Handling of multiple configurations. Even for a given format like
- AAC, some implementations may support AAC multichannel but HE-AAC
- stereo. Likewise WMA10 level M3 may require too much memory and cpu
- cycles. The new API needs to provide a generic way of listing these
- formats.
-
-- Rendering/Grabbing only. This API does not provide any means of
- hardware acceleration, where PCM samples are provided back to
- user-space for additional processing. This API focuses instead on
- streaming compressed data to a DSP, with the assumption that the
- decoded samples are routed to a physical output or logical back-end.
-
- - Complexity hiding. Existing user-space multimedia frameworks all
- have existing enums/structures for each compressed format. This new
- API assumes the existence of a platform-specific compatibility layer
- to expose, translate and make use of the capabilities of the audio
- DSP, eg. Android HAL or PulseAudio sinks. By construction, regular
- applications are not supposed to make use of this API.
-
-
-Design
-
-The new API shares a number of concepts with the PCM API for flow
-control. Start, pause, resume, drain and stop commands have the same
-semantics no matter what the content is.
-
-The concept of memory ring buffer divided in a set of fragments is
-borrowed from the ALSA PCM API. However, only sizes in bytes can be
-specified.
-
-Seeks/trick modes are assumed to be handled by the host.
-
-The notion of rewinds/forwards is not supported. Data committed to the
-ring buffer cannot be invalidated, except when dropping all buffers.
-
-The Compressed Data API does not make any assumptions on how the data
-is transmitted to the audio DSP. DMA transfers from main memory to an
-embedded audio cluster or to a SPI interface for external DSPs are
-possible. As in the ALSA PCM case, a core set of routines is exposed;
-each driver implementer will have to write support for a set of
-mandatory routines and possibly make use of optional ones.
-
-The main additions are
-
-- get_caps
-This routine returns the list of audio formats supported. Querying the
-codecs on a capture stream will return encoders, decoders will be
-listed for playback streams.
-
-- get_codec_caps For each codec, this routine returns a list of
-capabilities. The intent is to make sure all the capabilities
-correspond to valid settings, and to minimize the risks of
-configuration failures. For example, for a complex codec such as AAC,
-the number of channels supported may depend on a specific profile. If
-the capabilities were exposed with a single descriptor, it may happen
-that a specific combination of profiles/channels/formats may not be
-supported. Likewise, embedded DSPs have limited memory and cpu cycles,
-it is likely that some implementations make the list of capabilities
-dynamic and dependent on existing workloads. In addition to codec
-settings, this routine returns the minimum buffer size handled by the
-implementation. This information can be a function of the DMA buffer
-sizes, the number of bytes required to synchronize, etc, and can be
-used by userspace to define how much needs to be written in the ring
-buffer before playback can start.
-
-- set_params
-This routine sets the configuration chosen for a specific codec. The
-most important field in the parameters is the codec type; in most
-cases decoders will ignore other fields, while encoders will strictly
-comply to the settings
-
-- get_params
-This routines returns the actual settings used by the DSP. Changes to
-the settings should remain the exception.
-
-- get_timestamp
-The timestamp becomes a multiple field structure. It lists the number
-of bytes transferred, the number of samples processed and the number
-of samples rendered/grabbed. All these values can be used to determine
-the average bitrate, figure out if the ring buffer needs to be
-refilled or the delay due to decoding/encoding/io on the DSP.
-
-Note that the list of codecs/profiles/modes was derived from the
-OpenMAX AL specification instead of reinventing the wheel.
-Modifications include:
-- Addition of FLAC and IEC formats
-- Merge of encoder/decoder capabilities
-- Profiles/modes listed as bitmasks to make descriptors more compact
-- Addition of set_params for decoders (missing in OpenMAX AL)
-- Addition of AMR/AMR-WB encoding modes (missing in OpenMAX AL)
-- Addition of format information for WMA
-- Addition of encoding options when required (derived from OpenMAX IL)
-- Addition of rateControlSupported (missing in OpenMAX AL)
-
-Gapless Playback
-================
-When playing thru an album, the decoders have the ability to skip the encoder
-delay and padding and directly move from one track content to another. The end
-user can perceive this as gapless playback as we don't have silence while
-switching from one track to another
-
-Also, there might be low-intensity noises due to encoding. Perfect gapless is
-difficult to reach with all types of compressed data, but works fine with most
-music content. The decoder needs to know the encoder delay and encoder padding.
-So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers
-and are not present by default in the bitstream, hence the need for a new
-interface to pass this information to the DSP. Also DSP and userspace needs to
-switch from one track to another and start using data for second track.
-
-The main additions are:
-
-- set_metadata
-This routine sets the encoder delay and encoder padding. This can be used by
-decoder to strip the silence. This needs to be set before the data in the track
-is written.
-
-- set_next_track
-This routine tells DSP that metadata and write operation sent after this would
-correspond to subsequent track
-
-- partial drain
-This is called when end of file is reached. The userspace can inform DSP that
-EOF is reached and now DSP can start skipping padding delay. Also next write
-data would belong to next track
-
-Sequence flow for gapless would be:
-- Open
-- Get caps / codec caps
-- Set params
-- Set metadata of the first track
-- Fill data of the first track
-- Trigger start
-- User-space finished sending all,
-- Indicate next track data by sending set_next_track
-- Set metadata of the next track
-- then call partial_drain to flush most of buffer in DSP
-- Fill data of the next track
-- DSP switches to second track
-(note: order for partial_drain and write for next track can be reversed as well)
-
-Not supported:
-
-- Support for VoIP/circuit-switched calls is not the target of this
- API. Support for dynamic bit-rate changes would require a tight
- coupling between the DSP and the host stack, limiting power savings.
-
-- Packet-loss concealment is not supported. This would require an
- additional interface to let the decoder synthesize data when frames
- are lost during transmission. This may be added in the future.
-
-- Volume control/routing is not handled by this API. Devices exposing a
- compressed data interface will be considered as regular ALSA devices;
- volume changes and routing information will be provided with regular
- ALSA kcontrols.
-
-- Embedded audio effects. Such effects should be enabled in the same
- manner, no matter if the input was PCM or compressed.
-
-- multichannel IEC encoding. Unclear if this is required.
-
-- Encoding/decoding acceleration is not supported as mentioned
- above. It is possible to route the output of a decoder to a capture
- stream, or even implement transcoding capabilities. This routing
- would be enabled with ALSA kcontrols.
-
-- Audio policy/resource management. This API does not provide any
- hooks to query the utilization of the audio DSP, nor any preemption
- mechanisms.
-
-- No notion of underrun/overrun. Since the bytes written are compressed
- in nature and data written/read doesn't translate directly to
- rendered output in time, this does not deal with underrun/overrun and
- maybe dealt in user-library
-
-Credits:
-- Mark Brown and Liam Girdwood for discussions on the need for this API
-- Harsha Priya for her work on intel_sst compressed API
-- Rakesh Ughreja for valuable feedback
-- Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for
- demonstrating and quantifying the benefits of audio offload on a
- real platform.
+++ /dev/null
-This document is a guide to using the emu10k1 based devices with JACK for low
-latency, multichannel recording functionality. All of my recent work to allow
-Linux users to use the full capabilities of their hardware has been inspired
-by the kX Project. Without their work I never would have discovered the true
-power of this hardware.
-
- http://www.kxproject.com
- - Lee Revell, 2005.03.30
-
-Low latency, multichannel audio with JACK and the emu10k1/emu10k2
------------------------------------------------------------------
-
-Until recently, emu10k1 users on Linux did not have access to the same low
-latency, multichannel features offered by the "kX ASIO" feature of their
-Windows driver. As of ALSA 1.0.9 this is no more!
-
-For those unfamiliar with kX ASIO, this consists of 16 capture and 16 playback
-channels. With a post 2.6.9 Linux kernel, latencies down to 64 (1.33 ms) or
-even 32 (0.66ms) frames should work well.
-
-The configuration is slightly more involved than on Windows, as you have to
-select the correct device for JACK to use. Actually, for qjackctl users it's
-fairly self explanatory - select Duplex, then for capture and playback select
-the multichannel devices, set the in and out channels to 16, and the sample
-rate to 48000Hz. The command line looks like this:
-
-/usr/local/bin/jackd -R -dalsa -r48000 -p64 -n2 -D -Chw:0,2 -Phw:0,3 -S
-
-This will give you 16 input ports and 16 output ports.
-
-The 16 output ports map onto the 16 FX buses (or the first 16 of 64, for the
-Audigy). The mapping from FX bus to physical output is described in
-SB-Live-mixer.txt (or Audigy-mixer.txt).
-
-The 16 input ports are connected to the 16 physical inputs. Contrary to
-popular belief, all emu10k1 cards are multichannel cards. Which of these
-input channels have physical inputs connected to them depends on the card
-model. Trial and error is highly recommended; the pinout diagrams
-for the card have been reverse engineered by some enterprising kX users and are
-available on the internet. Meterbridge is helpful here, and the kX forums are
-packed with useful information.
-
-Each input port will either correspond to a digital (SPDIF) input, an analog
-input, or nothing. The one exception is the SBLive! 5.1. On these devices,
-the second and third input ports are wired to the center/LFE output. You will
-still see 16 capture channels, but only 14 are available for recording inputs.
-
-This chart, borrowed from kxfxlib/da_asio51.cpp, describes the mapping of JACK
-ports to FXBUS2 (multitrack recording input) and EXTOUT (physical output)
-channels.
-
-/*JACK (& ASIO) mappings on 10k1 5.1 SBLive cards:
---------------------------------------------
-JACK Epilog FXBUS2(nr)
---------------------------------------------
-capture_1 asio14 FXBUS2(0xe)
-capture_2 asio15 FXBUS2(0xf)
-capture_3 asio0 FXBUS2(0x0)
-~capture_4 Center EXTOUT(0x11) // mapped to by Center
-~capture_5 LFE EXTOUT(0x12) // mapped to by LFE
-capture_6 asio3 FXBUS2(0x3)
-capture_7 asio4 FXBUS2(0x4)
-capture_8 asio5 FXBUS2(0x5)
-capture_9 asio6 FXBUS2(0x6)
-capture_10 asio7 FXBUS2(0x7)
-capture_11 asio8 FXBUS2(0x8)
-capture_12 asio9 FXBUS2(0x9)
-capture_13 asio10 FXBUS2(0xa)
-capture_14 asio11 FXBUS2(0xb)
-capture_15 asio12 FXBUS2(0xc)
-capture_16 asio13 FXBUS2(0xd)
-*/
-
-TODO: describe use of ld10k1/qlo10k1 in conjunction with JACK
+++ /dev/null
-Software Interface ALSA-DSP MADI Driver
-
-(translated from German, so no good English ;-),
-2004 - winfried ritsch
-
-
-
- Full functionality has been added to the driver. Since some of
- the Controls and startup-options are ALSA-Standard and only the
- special Controls are described and discussed below.
-
-
- hardware functionality:
-
-
- Audio transmission:
-
- number of channels -- depends on transmission mode
-
- The number of channels chosen is from 1..Nmax. The reason to
- use for a lower number of channels is only resource allocation,
- since unused DMA channels are disabled and less memory is
- allocated. So also the throughput of the PCI system can be
- scaled. (Only important for low performance boards).
-
- Single Speed -- 1..64 channels
-
- (Note: Choosing the 56channel mode for transmission or as
- receiver, only 56 are transmitted/received over the MADI, but
- all 64 channels are available for the mixer, so channel count
- for the driver)
-
- Double Speed -- 1..32 channels
-
- Note: Choosing the 56-channel mode for
- transmission/receive-mode , only 28 are transmitted/received
- over the MADI, but all 32 channels are available for the mixer,
- so channel count for the driver
-
-
- Quad Speed -- 1..16 channels
-
- Note: Choosing the 56-channel mode for
- transmission/receive-mode , only 14 are transmitted/received
- over the MADI, but all 16 channels are available for the mixer,
- so channel count for the driver
-
- Format -- signed 32 Bit Little Endian (SNDRV_PCM_FMTBIT_S32_LE)
-
- Sample Rates --
-
- Single Speed -- 32000, 44100, 48000
-
- Double Speed -- 64000, 88200, 96000 (untested)
-
- Quad Speed -- 128000, 176400, 192000 (untested)
-
- access-mode -- MMAP (memory mapped), Not interleaved
- (PCM_NON-INTERLEAVED)
-
- buffer-sizes -- 64,128,256,512,1024,2048,8192 Samples
-
- fragments -- 2
-
- Hardware-pointer -- 2 Modi
-
-
- The Card supports the readout of the actual Buffer-pointer,
- where DMA reads/writes. Since of the bulk mode of PCI it is only
- 64 Byte accurate. SO it is not really usable for the
- ALSA-mid-level functions (here the buffer-ID gives a better
- result), but if MMAP is used by the application. Therefore it
- can be configured at load-time with the parameter
- precise-pointer.
-
-
- (Hint: Experimenting I found that the pointer is maximum 64 to
- large never to small. So if you subtract 64 you always have a
- safe pointer for writing, which is used on this mode inside
- ALSA. In theory now you can get now a latency as low as 16
- Samples, which is a quarter of the interrupt possibilities.)
-
- Precise Pointer -- off
- interrupt used for pointer-calculation
-
- Precise Pointer -- on
- hardware pointer used.
-
- Controller:
-
-
- Since DSP-MADI-Mixer has 8152 Fader, it does not make sense to
- use the standard mixer-controls, since this would break most of
- (especially graphic) ALSA-Mixer GUIs. So Mixer control has be
- provided by a 2-dimensional controller using the
- hwdep-interface.
-
- Also all 128+256 Peak and RMS-Meter can be accessed via the
- hwdep-interface. Since it could be a performance problem always
- copying and converting Peak and RMS-Levels even if you just need
- one, I decided to export the hardware structure, so that of
- needed some driver-guru can implement a memory-mapping of mixer
- or peak-meters over ioctl, or also to do only copying and no
- conversion. A test-application shows the usage of the controller.
-
- Latency Controls --- not implemented !!!
-
-
- Note: Within the windows-driver the latency is accessible of a
- control-panel, but buffer-sizes are controlled with ALSA from
- hwparams-calls and should not be changed in run-state, I did not
- implement it here.
-
-
- System Clock -- suspended !!!!
-
- Name -- "System Clock Mode"
-
- Access -- Read Write
-
- Values -- "Master" "Slave"
-
-
- !!!! This is a hardware-function but is in conflict with the
- Clock-source controller, which is a kind of ALSA-standard. I
- makes sense to set the card to a special mode (master at some
- frequency or slave), since even not using an Audio-application
- a studio should have working synchronisations setup. So use
- Clock-source-controller instead !!!!
-
- Clock Source
-
- Name -- "Sample Clock Source"
-
- Access -- Read Write
-
- Values -- "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz",
- "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz",
- "Internal 96.0 kHz"
-
- Choose between Master at a specific Frequency and so also the
- Speed-mode or Slave (Autosync). Also see "Preferred Sync Ref"
-
-
- !!!! This is no pure hardware function but was implemented by
- ALSA by some ALSA-drivers before, so I use it also. !!!
-
-
- Preferred Sync Ref
-
- Name -- "Preferred Sync Reference"
-
- Access -- Read Write
-
- Values -- "Word" "MADI"
-
-
- Within the Auto-sync-Mode the preferred Sync Source can be
- chosen. If it is not available another is used if possible.
-
- Note: Since MADI has a much higher bit-rate than word-clock, the
- card should synchronise better in MADI Mode. But since the
- RME-PLL is very good, there are almost no problems with
- word-clock too. I never found a difference.
-
-
- TX 64 channel ---
-
- Name -- "TX 64 channels mode"
-
- Access -- Read Write
-
- Values -- 0 1
-
- Using 64-channel-modus (1) or 56-channel-modus for
- MADI-transmission (0).
-
-
- Note: This control is for output only. Input-mode is detected
- automatically from hardware sending MADI.
-
-
- Clear TMS ---
-
- Name -- "Clear Track Marker"
-
- Access -- Read Write
-
- Values -- 0 1
-
-
- Don't use to lower 5 Audio-bits on AES as additional Bits.
-
-
- Safe Mode oder Auto Input ---
-
- Name -- "Safe Mode"
-
- Access -- Read Write
-
- Values -- 0 1
-
- (default on)
-
- If on (1), then if either the optical or coaxial connection
- has a failure, there is a takeover to the working one, with no
- sample failure. Its only useful if you use the second as a
- backup connection.
-
- Input ---
-
- Name -- "Input Select"
-
- Access -- Read Write
-
- Values -- optical coaxial
-
-
- Choosing the Input, optical or coaxial. If Safe-mode is active,
- this is the preferred Input.
-
--------------- Mixer ----------------------
-
- Mixer
-
- Name -- "Mixer"
-
- Access -- Read Write
-
- Values - <channel-number 0-127> <Value 0-65535>
-
-
- Here as a first value the channel-index is taken to get/set the
- corresponding mixer channel, where 0-63 are the input to output
- fader and 64-127 the playback to outputs fader. Value 0
- is channel muted 0 and 32768 an amplification of 1.
-
- Chn 1-64
-
- fast mixer for the ALSA-mixer utils. The diagonal of the
- mixer-matrix is implemented from playback to output.
-
-
- Line Out
-
- Name -- "Line Out"
-
- Access -- Read Write
-
- Values -- 0 1
-
- Switching on and off the analog out, which has nothing to do
- with mixing or routing. the analog outs reflects channel 63,64.
-
-
---- information (only read access):
-
- Sample Rate
-
- Name -- "System Sample Rate"
-
- Access -- Read-only
-
- getting the sample rate.
-
-
- External Rate measured
-
- Name -- "External Rate"
-
- Access -- Read only
-
-
- Should be "Autosync Rate", but Name used is
- ALSA-Scheme. External Sample frequency liked used on Autosync is
- reported.
-
-
- MADI Sync Status
-
- Name -- "MADI Sync Lock Status"
-
- Access -- Read
-
- Values -- 0,1,2
-
- MADI-Input is 0=Unlocked, 1=Locked, or 2=Synced.
-
-
- Word Clock Sync Status
-
- Name -- "Word Clock Lock Status"
-
- Access -- Read
-
- Values -- 0,1,2
-
- Word Clock Input is 0=Unlocked, 1=Locked, or 2=Synced.
-
- AutoSync
-
- Name -- "AutoSync Reference"
-
- Access -- Read
-
- Values -- "WordClock", "MADI", "None"
-
- Sync-Reference is either "WordClock", "MADI" or none.
-
- RX 64ch --- noch nicht implementiert
-
- MADI-Receiver is in 64 channel mode oder 56 channel mode.
-
-
- AB_inp --- not tested
-
- Used input for Auto-Input.
-
-
- actual Buffer Position --- not implemented
-
- !!! this is a ALSA internal function, so no control is used !!!
-
-
-
-Calling Parameter:
-
- index int array (min = 1, max = 8),
- "Index value for RME HDSPM interface." card-index within ALSA
-
- note: ALSA-standard
-
- id string array (min = 1, max = 8),
- "ID string for RME HDSPM interface."
-
- note: ALSA-standard
-
- enable int array (min = 1, max = 8),
- "Enable/disable specific HDSPM sound-cards."
-
- note: ALSA-standard
-
- precise_ptr int array (min = 1, max = 8),
- "Enable precise pointer, or disable."
-
- note: Use only when the application supports this (which is a special case).
-
- line_outs_monitor int array (min = 1, max = 8),
- "Send playback streams to analog outs by default."
-
-
- note: each playback channel is mixed to the same numbered output
- channel (routed). This is against the ALSA-convention, where all
- channels have to be muted on after loading the driver, but was
- used before on other cards, so i historically use it again)
-
-
-
- enable_monitor int array (min = 1, max = 8),
- "Enable Analog Out on Channel 63/64 by default."
-
- note: here the analog output is enabled (but not routed).
+++ /dev/null
-The Imagination Technologies SPDIF Input controller contains the following
-controls:
-
-name='IEC958 Capture Mask',index=0
-
-This control returns a mask that shows which of the IEC958 status bits
-can be read using the 'IEC958 Capture Default' control.
-
-name='IEC958 Capture Default',index=0
-
-This control returns the status bits contained within the SPDIF stream that
-is being received. The 'IEC958 Capture Mask' shows which bits can be read
-from this control.
-
-name='SPDIF In Multi Frequency Acquire',index=0
-name='SPDIF In Multi Frequency Acquire',index=1
-name='SPDIF In Multi Frequency Acquire',index=2
-name='SPDIF In Multi Frequency Acquire',index=3
-
-This control is used to attempt acquisition of up to four different sample
-rates. The active rate can be obtained by reading the 'SPDIF In Lock Frequency'
-control.
-
-When the value of this control is set to {0,0,0,0}, the rate given to hw_params
-will determine the single rate the block will capture. Else, the rate given to
-hw_params will be ignored, and the block will attempt capture for each of the
-four sample rates set here.
-
-If less than four rates are required, the same rate can be specified more than
-once
-
-name='SPDIF In Lock Frequency',index=0
-
-This control returns the active capture rate, or 0 if a lock has not been
-acquired
-
-name='SPDIF In Lock TRK',index=0
-
-This control is used to modify the locking/jitter rejection characteristics
-of the block. Larger values increase the locking range, but reduce jitter
-rejection.
-
-name='SPDIF In Lock Acquire Threshold',index=0
-
-This control is used to change the threshold at which a lock is acquired.
-
-name='SPDIF In Lock Release Threshold',index=0
-
-This control is used to change the threshold at which a lock is released.
+++ /dev/null
-Notes on Power-Saving Mode
-==========================
-
-AC97 and HD-audio drivers have the automatic power-saving mode.
-This feature is enabled via Kconfig CONFIG_SND_AC97_POWER_SAVE
-and CONFIG_SND_HDA_POWER_SAVE options, respectively.
-
-With the automatic power-saving, the driver turns off the codec power
-appropriately when no operation is required. When no applications use
-the device and/or no analog loopback is set, the power disablement is
-done fully or partially. It'll save a certain power consumption, thus
-good for laptops (even for desktops).
-
-The time-out for automatic power-off can be specified via power_save
-module option of snd-ac97-codec and snd-hda-intel modules. Specify
-the time-out value in seconds. 0 means to disable the automatic
-power-saving. The default value of timeout is given via
-CONFIG_SND_AC97_POWER_SAVE_DEFAULT and
-CONFIG_SND_HDA_POWER_SAVE_DEFAULT Kconfig options. Setting this to 1
-(the minimum value) isn't recommended because many applications try to
-reopen the device frequently. 10 would be a good choice for normal
-operations.
-
-The power_save option is exported as writable. This means you can
-adjust the value via sysfs on the fly. For example, to turn on the
-automatic power-save mode with 10 seconds, write to
-/sys/modules/snd_ac97_codec/parameters/power_save (usually as root):
-
- # echo 10 > /sys/modules/snd_ac97_codec/parameters/power_save
-
-
-Note that you might hear click noise/pop when changing the power
-state. Also, it often takes certain time to wake up from the
-power-down to the active state. These are often hardly to fix, so
-don't report extra bug reports unless you have a fix patch ;-)
-
-For HD-audio interface, there is another module option,
-power_save_controller. This enables/disables the power-save mode of
-the controller side. Setting this on may reduce a bit more power
-consumption, but might result in longer wake-up time and click noise.
-Try to turn it off when you experience such a thing too often.
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML>
-<HEAD>
- <TITLE>OSS Sequencer Emulation on ALSA</TITLE>
-</HEAD>
-<BODY>
-
-<CENTER>
-<H1>
-
-<HR WIDTH="100%"></H1></CENTER>
-
-<CENTER>
-<H1>
-OSS Sequencer Emulation on ALSA</H1></CENTER>
-
-<HR WIDTH="100%">
-<P>Copyright (c) 1998,1999 by Takashi Iwai
-<TT><A HREF="mailto:iwai@ww.uni-erlangen.de"><iwai@ww.uni-erlangen.de></A></TT>
-<P>ver.0.1.8; Nov. 16, 1999
-<H2>
-
-<HR WIDTH="100%"></H2>
-
-<H2>
-1. Description</H2>
-This directory contains the OSS sequencer emulation driver on ALSA. Note
-that this program is still in the development state.
-<P>What this does - it provides the emulation of the OSS sequencer, access
-via
-<TT>/dev/sequencer</TT> and <TT>/dev/music</TT> devices.
-The most of applications using OSS can run if the appropriate ALSA
-sequencer is prepared.
-<P>The following features are emulated by this driver:
-<UL>
-<LI>
-Normal sequencer and MIDI events:</LI>
-
-<BR>They are converted to the ALSA sequencer events, and sent to the corresponding
-port.
-<LI>
-Timer events:</LI>
-
-<BR>The timer is not selectable by ioctl. The control rate is fixed to
-100 regardless of HZ. That is, even on Alpha system, a tick is always
-1/100 second. The base rate and tempo can be changed in <TT>/dev/music</TT>.
-
-<LI>
-Patch loading:</LI>
-
-<BR>It purely depends on the synth drivers whether it's supported since
-the patch loading is realized by callback to the synth driver.
-<LI>
-I/O controls:</LI>
-
-<BR>Most of controls are accepted. Some controls
-are dependent on the synth driver, as well as even on original OSS.</UL>
-Furthermore, you can find the following advanced features:
-<UL>
-<LI>
-Better queue mechanism:</LI>
-
-<BR>The events are queued before processing them.
-<LI>
-Multiple applications:</LI>
-
-<BR>You can run two or more applications simultaneously (even for OSS sequencer)!
-However, each MIDI device is exclusive - that is, if a MIDI device is opened
-once by some application, other applications can't use it. No such a restriction
-in synth devices.
-<LI>
-Real-time event processing:</LI>
-
-<BR>The events can be processed in real time without using out of bound
-ioctl. To switch to real-time mode, send ABSTIME 0 event. The followed
-events will be processed in real-time without queued. To switch off the
-real-time mode, send RELTIME 0 event.
-<LI>
-<TT>/proc</TT> interface:</LI>
-
-<BR>The status of applications and devices can be shown via <TT>/proc/asound/seq/oss</TT>
-at any time. In the later version, configuration will be changed via <TT>/proc</TT>
-interface, too.</UL>
-
-<H2>
-2. Installation</H2>
-Run configure script with both sequencer support (<TT>--with-sequencer=yes</TT>)
-and OSS emulation (<TT>--with-oss=yes</TT>) options. A module <TT>snd-seq-oss.o</TT>
-will be created. If the synth module of your sound card supports for OSS
-emulation (so far, only Emu8000 driver), this module will be loaded automatically.
-Otherwise, you need to load this module manually.
-<P>At beginning, this module probes all the MIDI ports which have been
-already connected to the sequencer. Once after that, the creation and deletion
-of ports are watched by announcement mechanism of ALSA sequencer.
-<P>The available synth and MIDI devices can be found in proc interface.
-Run "<TT>cat /proc/asound/seq/oss</TT>", and check the devices. For example,
-if you use an AWE64 card, you'll see like the following:
-<PRE> OSS sequencer emulation version 0.1.8
- ALSA client number 63
- ALSA receiver port 0
-
- Number of applications: 0
-
- Number of synth devices: 1
-
- synth 0: [EMU8000]
- type 0x1 : subtype 0x20 : voices 32
- capabilties : ioctl enabled / load_patch enabled
-
- Number of MIDI devices: 3
-
- midi 0: [Emu8000 Port-0] ALSA port 65:0
- capability write / opened none
-
- midi 1: [Emu8000 Port-1] ALSA port 65:1
- capability write / opened none
-
- midi 2: [0: MPU-401 (UART)] ALSA port 64:0
- capability read/write / opened none</PRE>
-Note that the device number may be different from the information of
-<TT>/proc/asound/oss-devices</TT>
-or ones of the original OSS driver. Use the device number listed in <TT>/proc/asound/seq/oss</TT>
-to play via OSS sequencer emulation.
-<H2>
-3. Using Synthesizer Devices</H2>
-Run your favorite program. I've tested playmidi-2.4, awemidi-0.4.3, gmod-3.1
-and xmp-1.1.5. You can load samples via <TT>/dev/sequencer</TT> like sfxload,
-too.
-<P>If the lowlevel driver supports multiple access to synth devices (like
-Emu8000 driver), two or more applications are allowed to run at the same
-time.
-<H2>
-4. Using MIDI Devices</H2>
-So far, only MIDI output was tested. MIDI input was not checked at all,
-but hopefully it will work. Use the device number listed in <TT>/proc/asound/seq/oss</TT>.
-Be aware that these numbers are mostly different from the list in
-<TT>/proc/asound/oss-devices</TT>.
-<H2>
-5. Module Options</H2>
-The following module options are available:
-<UL>
-<LI>
-<TT>maxqlen</TT></LI>
-
-<BR>specifies the maximum read/write queue length. This queue is private
-for OSS sequencer, so that it is independent from the queue length of ALSA
-sequencer. Default value is 1024.
-<LI>
-<TT>seq_oss_debug</TT></LI>
-
-<BR>specifies the debug level and accepts zero (= no debug message) or
-positive integer. Default value is 0.</UL>
-
-<H2>
-6. Queue Mechanism</H2>
-OSS sequencer emulation uses an ALSA priority queue. The
-events from <TT>/dev/sequencer</TT> are processed and put onto the queue
-specified by module option.
-<P>All the events from <TT>/dev/sequencer</TT> are parsed at beginning.
-The timing events are also parsed at this moment, so that the events may
-be processed in real-time. Sending an event ABSTIME 0 switches the operation
-mode to real-time mode, and sending an event RELTIME 0 switches it off.
-In the real-time mode, all events are dispatched immediately.
-<P>The queued events are dispatched to the corresponding ALSA sequencer
-ports after scheduled time by ALSA sequencer dispatcher.
-<P>If the write-queue is full, the application sleeps until a certain amount
-(as default one half) becomes empty in blocking mode. The synchronization
-to write timing was implemented, too.
-<P>The input from MIDI devices or echo-back events are stored on read FIFO
-queue. If application reads <TT>/dev/sequencer</TT> in blocking mode, the
-process will be awaked.
-
-<H2>
-7. Interface to Synthesizer Device</H2>
-
-<H3>
-7.1. Registration</H3>
-To register an OSS synthesizer device, use <TT>snd_seq_oss_synth_register</TT>
-function.
-<PRE>int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
- snd_seq_oss_callback_t *oper, void *private_data)</PRE>
-The arguments <TT>name</TT>, <TT>type</TT>, <TT>subtype</TT> and
-<TT>nvoices</TT>
-are used for making the appropriate synth_info structure for ioctl. The
-return value is an index number of this device. This index must be remembered
-for unregister. If registration is failed, -errno will be returned.
-<P>To release this device, call <TT>snd_seq_oss_synth_unregister function</TT>:
-<PRE>int snd_seq_oss_synth_unregister(int index),</PRE>
-where the <TT>index</TT> is the index number returned by register function.
-<H3>
-7.2. Callbacks</H3>
-OSS synthesizer devices have capability for sample downloading and ioctls
-like sample reset. In OSS emulation, these special features are realized
-by using callbacks. The registration argument oper is used to specify these
-callbacks. The following callback functions must be defined:
-<PRE>snd_seq_oss_callback_t:
- int (*open)(snd_seq_oss_arg_t *p, void *closure);
- int (*close)(snd_seq_oss_arg_t *p);
- int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
- int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
- int (*reset)(snd_seq_oss_arg_t *p);
-Except for <TT>open</TT> and <TT>close</TT> callbacks, they are allowed
-to be NULL.
-<P>Each callback function takes the argument type snd_seq_oss_arg_t as the
-first argument.
-<PRE>struct snd_seq_oss_arg_t {
- int app_index;
- int file_mode;
- int seq_mode;
- snd_seq_addr_t addr;
- void *private_data;
- int event_passing;
-};</PRE>
-The first three fields, <TT>app_index</TT>, <TT>file_mode</TT> and
-<TT>seq_mode</TT>
-are initialized by OSS sequencer. The <TT>app_index</TT> is the application
-index which is unique to each application opening OSS sequencer. The
-<TT>file_mode</TT>
-is bit-flags indicating the file operation mode. See
-<TT>seq_oss.h</TT>
-for its meaning. The <TT>seq_mode</TT> is sequencer operation mode. In
-the current version, only <TT>SND_OSSSEQ_MODE_SYNTH</TT> is used.
-<P>The next two fields, <TT>addr</TT> and <TT>private_data</TT>, must be
-filled by the synth driver at open callback. The <TT>addr</TT> contains
-the address of ALSA sequencer port which is assigned to this device. If
-the driver allocates memory for <TT>private_data</TT>, it must be released
-in close callback by itself.
-<P>The last field, <TT>event_passing</TT>, indicates how to translate note-on
-/ off events. In <TT>PROCESS_EVENTS</TT> mode, the note 255 is regarded
-as velocity change, and key pressure event is passed to the port. In <TT>PASS_EVENTS</TT>
-mode, all note on/off events are passed to the port without modified. <TT>PROCESS_KEYPRESS</TT>
-mode checks the note above 128 and regards it as key pressure event (mainly
-for Emu8000 driver).
-<H4>
-7.2.1. Open Callback</H4>
-The <TT>open</TT> is called at each time this device is opened by an application
-using OSS sequencer. This must not be NULL. Typically, the open callback
-does the following procedure:
-<OL>
-<LI>
-Allocate private data record.</LI>
-
-<LI>
-Create an ALSA sequencer port.</LI>
-
-<LI>
-Set the new port address on arg->addr.</LI>
-
-<LI>
-Set the private data record pointer on arg->private_data.</LI>
-</OL>
-Note that the type bit-flags in port_info of this synth port must NOT contain
-<TT>TYPE_MIDI_GENERIC</TT>
-bit. Instead, <TT>TYPE_SPECIFIC</TT> should be used. Also, <TT>CAP_SUBSCRIPTION</TT>
-bit should NOT be included, too. This is necessary to tell it from other
-normal MIDI devices. If the open procedure succeeded, return zero. Otherwise,
-return -errno.
-<H4>
-7.2.2 Ioctl Callback</H4>
-The <TT>ioctl</TT> callback is called when the sequencer receives device-specific
-ioctls. The following two ioctls should be processed by this callback:
-<UL>
-<LI>
-<TT>IOCTL_SEQ_RESET_SAMPLES</TT></LI>
-
-<BR>reset all samples on memory -- return 0
-<LI>
-<TT>IOCTL_SYNTH_MEMAVL</TT></LI>
-
-<BR>return the available memory size
-<LI>
-<TT>FM_4OP_ENABLE</TT></LI>
-
-<BR>can be ignored usually</UL>
-The other ioctls are processed inside the sequencer without passing to
-the lowlevel driver.
-<H4>
-7.2.3 Load_Patch Callback</H4>
-The <TT>load_patch</TT> callback is used for sample-downloading. This callback
-must read the data on user-space and transfer to each device. Return 0
-if succeeded, and -errno if failed. The format argument is the patch key
-in patch_info record. The buf is user-space pointer where patch_info record
-is stored. The offs can be ignored. The count is total data size of this
-sample data.
-<H4>
-7.2.4 Close Callback</H4>
-The <TT>close</TT> callback is called when this device is closed by the
-application. If any private data was allocated in open callback, it must
-be released in the close callback. The deletion of ALSA port should be
-done here, too. This callback must not be NULL.
-<H4>
-7.2.5 Reset Callback</H4>
-The <TT>reset</TT> callback is called when sequencer device is reset or
-closed by applications. The callback should turn off the sounds on the
-relevant port immediately, and initialize the status of the port. If this
-callback is undefined, OSS seq sends a <TT>HEARTBEAT</TT> event to the
-port.
-<H3>
-7.3 Events</H3>
-Most of the events are processed by sequencer and translated to the adequate
-ALSA sequencer events, so that each synth device can receive by input_event
-callback of ALSA sequencer port. The following ALSA events should be implemented
-by the driver:
-<BR>
-<TABLE BORDER WIDTH="75%" NOSAVE >
-<TR NOSAVE>
-<TD NOSAVE><B>ALSA event</B></TD>
-
-<TD><B>Original OSS events</B></TD>
-</TR>
-
-<TR>
-<TD>NOTEON</TD>
-
-<TD>SEQ_NOTEON
-<BR>MIDI_NOTEON</TD>
-</TR>
-
-<TR>
-<TD>NOTE</TD>
-
-<TD>SEQ_NOTEOFF
-<BR>MIDI_NOTEOFF</TD>
-</TR>
-
-<TR NOSAVE>
-<TD NOSAVE>KEYPRESS</TD>
-
-<TD>MIDI_KEY_PRESSURE</TD>
-</TR>
-
-<TR NOSAVE>
-<TD>CHANPRESS</TD>
-
-<TD NOSAVE>SEQ_AFTERTOUCH
-<BR>MIDI_CHN_PRESSURE</TD>
-</TR>
-
-<TR NOSAVE>
-<TD NOSAVE>PGMCHANGE</TD>
-
-<TD NOSAVE>SEQ_PGMCHANGE
-<BR>MIDI_PGM_CHANGE</TD>
-</TR>
-
-<TR>
-<TD>PITCHBEND</TD>
-
-<TD>SEQ_CONTROLLER(CTRL_PITCH_BENDER)
-<BR>MIDI_PITCH_BEND</TD>
-</TR>
-
-<TR>
-<TD>CONTROLLER</TD>
-
-<TD>MIDI_CTL_CHANGE
-<BR>SEQ_BALANCE (with CTL_PAN)</TD>
-</TR>
-
-<TR>
-<TD>CONTROL14</TD>
-
-<TD>SEQ_CONTROLLER</TD>
-</TR>
-
-<TR>
-<TD>REGPARAM</TD>
-
-<TD>SEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)</TD>
-</TR>
-
-<TR>
-<TD>SYSEX</TD>
-
-<TD>SEQ_SYSEX</TD>
-</TR>
-</TABLE>
-
-<P>The most of these behavior can be realized by MIDI emulation driver
-included in the Emu8000 lowlevel driver. In the future release, this module
-will be independent.
-<P>Some OSS events (<TT>SEQ_PRIVATE</TT> and <TT>SEQ_VOLUME</TT> events) are passed as event
-type SND_SEQ_OSS_PRIVATE. The OSS sequencer passes these event 8 byte
-packets without any modification. The lowlevel driver should process these
-events appropriately.
-<H2>
-8. Interface to MIDI Device</H2>
-Since the OSS emulation probes the creation and deletion of ALSA MIDI sequencer
-ports automatically by receiving announcement from ALSA sequencer, the
-MIDI devices don't need to be registered explicitly like synth devices.
-However, the MIDI port_info registered to ALSA sequencer must include a group
-name <TT>SND_SEQ_GROUP_DEVICE</TT> and a capability-bit <TT>CAP_READ</TT> or
-<TT>CAP_WRITE</TT>. Also, subscription capabilities, <TT>CAP_SUBS_READ</TT> or <TT>CAP_SUBS_WRITE</TT>,
-must be defined, too. If these conditions are not satisfied, the port is not
-registered as OSS sequencer MIDI device.
-<P>The events via MIDI devices are parsed in OSS sequencer and converted
-to the corresponding ALSA sequencer events. The input from MIDI sequencer
-is also converted to MIDI byte events by OSS sequencer. This works just
-a reverse way of seq_midi module.
-<H2>
-9. Known Problems / TODO's</H2>
-
-<UL>
-<LI>
-Patch loading via ALSA instrument layer is not implemented yet.</LI>
-</UL>
-
-</BODY>
-</HTML>
+++ /dev/null
-
- Serial UART 16450/16550 MIDI driver
- ===================================
-
-The adaptor module parameter allows you to select either:
-
- 0 - Roland Soundcanvas support (default)
- 1 - Midiator MS-124T support (1)
- 2 - Midiator MS-124W S/A mode (2)
- 3 - MS-124W M/B mode support (3)
- 4 - Generic device with multiple input support (4)
-
-For the Midiator MS-124W, you must set the physical M-S and A-B
-switches on the Midiator to match the driver mode you select.
-
-In Roland Soundcanvas mode, multiple ALSA raw MIDI substreams are supported
-(midiCnD0-midiCnD15). Whenever you write to a different substream, the driver
-sends the nonstandard MIDI command sequence F5 NN, where NN is the substream
-number plus 1. Roland modules use this command to switch between different
-"parts", so this feature lets you treat each part as a distinct raw MIDI
-substream. The driver provides no way to send F5 00 (no selection) or to not
-send the F5 NN command sequence at all; perhaps it ought to.
-
-Usage example for simple serial converter:
-
- /sbin/setserial /dev/ttyS0 uart none
- /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 speed=115200
-
-Usage example for Roland SoundCanvas with 4 MIDI ports:
-
- /sbin/setserial /dev/ttyS0 uart none
- /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 outs=4
-
-In MS-124T mode, one raw MIDI substream is supported (midiCnD0); the outs
-module parameter is automatically set to 1. The driver sends the same data to
-all four MIDI Out connectors. Set the A-B switch and the speed module
-parameter to match (A=19200, B=9600).
-
-Usage example for MS-124T, with A-B switch in A position:
-
- /sbin/setserial /dev/ttyS0 uart none
- /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=1 \
- speed=19200
-
-In MS-124W S/A mode, one raw MIDI substream is supported (midiCnD0);
-the outs module parameter is automatically set to 1. The driver sends
-the same data to all four MIDI Out connectors at full MIDI speed.
-
-Usage example for S/A mode:
-
- /sbin/setserial /dev/ttyS0 uart none
- /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=2
-
-In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI substreams;
-the outs module parameter is automatically set to 16. The substream
-number gives a bitmask of which MIDI Out connectors the data should be
-sent to, with midiCnD1 sending to Out 1, midiCnD2 to Out 2, midiCnD4 to
-Out 3, and midiCnD8 to Out 4. Thus midiCnD15 sends the data to all 4 ports.
-As a special case, midiCnD0 also sends to all ports, since it is not useful
-to send the data to no ports. M/B mode has extra overhead to select the MIDI
-Out for each byte, so the aggregate data rate across all four MIDI Outs is
-at most one byte every 520 us, as compared with the full MIDI data rate of
-one byte every 320 us per port.
-
-Usage example for M/B mode:
-
- /sbin/setserial /dev/ttyS0 uart none
- /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=3
-
-The MS-124W hardware's M/A mode is currently not supported. This mode allows
-the MIDI Outs to act independently at double the aggregate throughput of M/B,
-but does not allow sending the same byte simultaneously to multiple MIDI Outs.
-The M/A protocol requires the driver to twiddle the modem control lines under
-timing constraints, so it would be a bit more complicated to implement than
-the other modes.
-
-Midiator models other than MS-124W and MS-124T are currently not supported.
-Note that the suffix letter is significant; the MS-124 and MS-124B are not
-compatible, nor are the other known models MS-101, MS-101B, MS-103, and MS-114.
-I do have documentation (tim.mann@compaq.com) that partially covers these models,
-but no units to experiment with. The MS-124W support is tested with a real unit.
-The MS-124T support is untested, but should work.
-
-The Generic driver supports multiple input and output substreams over a single
-serial port. Similar to Roland Soundcanvas mode, F5 NN is used to select the
-appropriate input or output stream (depending on the data direction).
-Additionally, the CTS signal is used to regulate the data flow. The number of
-inputs is specified by the ins parameter.
+++ /dev/null
-ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
-SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.
-
-
-AC97
-====
-
- AC97 is a five wire interface commonly found on many PC sound cards. It is
-now also popular in many portable devices. This DAI has a reset line and time
-multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
-The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
-frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
-frame is 21uS long and is divided into 13 time slots.
-
-The AC97 specification can be found at :-
-http://www.intel.com/p/en_US/business/design
-
-
-I2S
-===
-
- I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
-Rx lines are used for audio transmission, whilst the bit clock (BCLK) and
-left/right clock (LRC) synchronise the link. I2S is flexible in that either the
-controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
-usually varies depending on the sample rate and the master system clock
-(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
-ADC and DAC LRCLKs, this allows for simultaneous capture and playback at
-different sample rates.
-
-I2S has several different operating modes:-
-
- o I2S - MSB is transmitted on the falling edge of the first BCLK after LRC
- transition.
-
- o Left Justified - MSB is transmitted on transition of LRC.
-
- o Right Justified - MSB is transmitted sample size BCLKs before LRC
- transition.
-
-PCM
-===
-
-PCM is another 4 wire interface, very similar to I2S, which can support a more
-flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
-to synchronise the link whilst the Tx and Rx lines are used to transmit and
-receive the audio data. Bit clock usually varies depending on sample rate
-whilst sync runs at the sample rate. PCM also supports Time Division
-Multiplexing (TDM) in that several devices can use the bus simultaneously (this
-is sometimes referred to as network mode).
-
-Common PCM operating modes:-
-
- o Mode A - MSB is transmitted on falling edge of first BCLK after FRAME/SYNC.
-
- o Mode B - MSB is transmitted on rising edge of FRAME/SYNC.
+++ /dev/null
-Dynamic PCM
-===========
-
-1. Description
-==============
-
-Dynamic PCM allows an ALSA PCM device to digitally route its PCM audio to
-various digital endpoints during the PCM stream runtime. e.g. PCM0 can route
-digital audio to I2S DAI0, I2S DAI1 or PDM DAI2. This is useful for on SoC DSP
-drivers that expose several ALSA PCMs and can route to multiple DAIs.
-
-The DPCM runtime routing is determined by the ALSA mixer settings in the same
-way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
-graph representing the DSP internal audio paths and uses the mixer settings to
-determine the patch used by each ALSA PCM.
-
-DPCM re-uses all the existing component codec, platform and DAI drivers without
-any modifications.
-
-
-Phone Audio System with SoC based DSP
--------------------------------------
-
-Consider the following phone audio subsystem. This will be used in this
-document for all examples :-
-
-| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
-
- *************
-PCM0 <------------> * * <----DAI0-----> Codec Headset
- * *
-PCM1 <------------> * * <----DAI1-----> Codec Speakers
- * DSP *
-PCM2 <------------> * * <----DAI2-----> MODEM
- * *
-PCM3 <------------> * * <----DAI3-----> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-This diagram shows a simple smart phone audio subsystem. It supports Bluetooth,
-FM digital radio, Speakers, Headset Jack, digital microphones and cellular
-modem. This sound card exposes 4 DSP front end (FE) ALSA PCM devices and
-supports 6 back end (BE) DAIs. Each FE PCM can digitally route audio data to any
-of the BE DAIs. The FE PCM devices can also route audio to more than 1 BE DAI.
-
-
-
-Example - DPCM Switching playback from DAI0 to DAI1
----------------------------------------------------
-
-Audio is being played to the Headset. After a while the user removes the headset
-and audio continues playing on the speakers.
-
-Playback on PCM0 to Headset would look like :-
-
- *************
-PCM0 <============> * * <====DAI0=====> Codec Headset
- * *
-PCM1 <------------> * * <----DAI1-----> Codec Speakers
- * DSP *
-PCM2 <------------> * * <----DAI2-----> MODEM
- * *
-PCM3 <------------> * * <----DAI3-----> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-The headset is removed from the jack by user so the speakers must now be used :-
-
- *************
-PCM0 <============> * * <----DAI0-----> Codec Headset
- * *
-PCM1 <------------> * * <====DAI1=====> Codec Speakers
- * DSP *
-PCM2 <------------> * * <----DAI2-----> MODEM
- * *
-PCM3 <------------> * * <----DAI3-----> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-The audio driver processes this as follows :-
-
- 1) Machine driver receives Jack removal event.
-
- 2) Machine driver OR audio HAL disables the Headset path.
-
- 3) DPCM runs the PCM trigger(stop), hw_free(), shutdown() operations on DAI0
- for headset since the path is now disabled.
-
- 4) Machine driver or audio HAL enables the speaker path.
-
- 5) DPCM runs the PCM ops for startup(), hw_params(), prepapre() and
- trigger(start) for DAI1 Speakers since the path is enabled.
-
-In this example, the machine driver or userspace audio HAL can alter the routing
-and then DPCM will take care of managing the DAI PCM operations to either bring
-the link up or down. Audio playback does not stop during this transition.
-
-
-
-DPCM machine driver
-===================
-
-The DPCM enabled ASoC machine driver is similar to normal machine drivers
-except that we also have to :-
-
- 1) Define the FE and BE DAI links.
-
- 2) Define any FE/BE PCM operations.
-
- 3) Define widget graph connections.
-
-
-1 FE and BE DAI links
----------------------
-
-| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
-
- *************
-PCM0 <------------> * * <----DAI0-----> Codec Headset
- * *
-PCM1 <------------> * * <----DAI1-----> Codec Speakers
- * DSP *
-PCM2 <------------> * * <----DAI2-----> MODEM
- * *
-PCM3 <------------> * * <----DAI3-----> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-For the example above we have to define 4 FE DAI links and 6 BE DAI links. The
-FE DAI links are defined as follows :-
-
-static struct snd_soc_dai_link machine_dais[] = {
- {
- .name = "PCM0 System",
- .stream_name = "System Playback",
- .cpu_dai_name = "System Pin",
- .platform_name = "dsp-audio",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .dynamic = 1,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- .....< other FE and BE DAI links here >
-};
-
-This FE DAI link is pretty similar to a regular DAI link except that we also
-set the DAI link to a DPCM FE with the "dynamic = 1". The supported FE stream
-directions should also be set with the "dpcm_playback" and "dpcm_capture"
-flags. There is also an option to specify the ordering of the trigger call for
-each FE. This allows the ASoC core to trigger the DSP before or after the other
-components (as some DSPs have strong requirements for the ordering DAI/DSP
-start and stop sequences).
-
-The FE DAI above sets the codec and code DAIs to dummy devices since the BE is
-dynamic and will change depending on runtime config.
-
-The BE DAIs are configured as follows :-
-
-static struct snd_soc_dai_link machine_dais[] = {
- .....< FE DAI links here >
- {
- .name = "Codec Headset",
- .cpu_dai_name = "ssp-dai.0",
- .platform_name = "snd-soc-dummy",
- .no_pcm = 1,
- .codec_name = "rt5640.0-001c",
- .codec_dai_name = "rt5640-aif1",
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = hswult_ssp0_fixup,
- .ops = &haswell_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- .....< other BE DAI links here >
-};
-
-This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
-the "no_pcm" flag to mark it has a BE and sets flags for supported stream
-directions using "dpcm_playback" and "dpcm_capture" above.
-
-The BE has also flags set for ignoring suspend and PM down time. This allows
-the BE to work in a hostless mode where the host CPU is not transferring data
-like a BT phone call :-
-
- *************
-PCM0 <------------> * * <----DAI0-----> Codec Headset
- * *
-PCM1 <------------> * * <----DAI1-----> Codec Speakers
- * DSP *
-PCM2 <------------> * * <====DAI2=====> MODEM
- * *
-PCM3 <------------> * * <====DAI3=====> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-This allows the host CPU to sleep whilst the DSP, MODEM DAI and the BT DAI are
-still in operation.
-
-A BE DAI link can also set the codec to a dummy device if the code is a device
-that is managed externally.
-
-Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
-DSP firmware.
-
-
-2 FE/BE PCM operations
-----------------------
-
-The BE above also exports some PCM operations and a "fixup" callback. The fixup
-callback is used by the machine driver to (re)configure the DAI based upon the
-FE hw params. i.e. the DSP may perform SRC or ASRC from the FE to BE.
-
-e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo for
-DAI0. This means all FE hw_params have to be fixed in the machine driver for
-DAI0 so that the DAI is running at desired configuration regardless of the FE
-configuration.
-
-static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The DSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set DAI0 to 16 bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S16_LE);
- return 0;
-}
-
-The other PCM operation are the same as for regular DAI links. Use as necessary.
-
-
-3 Widget graph connections
---------------------------
-
-The BE DAI links will normally be connected to the graph at initialisation time
-by the ASoC DAPM core. However, if the BE codec or BE DAI is a dummy then this
-has to be set explicitly in the driver :-
-
-/* BE for codec Headset - DAI0 is dummy and managed by DSP FW */
-{"DAI0 CODEC IN", NULL, "AIF1 Capture"},
-{"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
-
-
-Writing a DPCM DSP driver
-=========================
-
-The DPCM DSP driver looks much like a standard platform class ASoC driver
-combined with elements from a codec class driver. A DSP platform driver must
-implement :-
-
- 1) Front End PCM DAIs - i.e. struct snd_soc_dai_driver.
-
- 2) DAPM graph showing DSP audio routing from FE DAIs to BEs.
-
- 3) DAPM widgets from DSP graph.
-
- 4) Mixers for gains, routing, etc.
-
- 5) DMA configuration.
-
- 6) BE AIF widgets.
-
-Items 6 is important for routing the audio outside of the DSP. AIF need to be
-defined for each BE and each stream direction. e.g for BE DAI0 above we would
-have :-
-
-SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),
-
-The BE AIF are used to connect the DSP graph to the graphs for the other
-component drivers (e.g. codec graph).
-
-
-Hostless PCM streams
-====================
-
-A hostless PCM stream is a stream that is not routed through the host CPU. An
-example of this would be a phone call from handset to modem.
-
-
- *************
-PCM0 <------------> * * <----DAI0-----> Codec Headset
- * *
-PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic
- * DSP *
-PCM2 <------------> * * <====DAI2=====> MODEM
- * *
-PCM3 <------------> * * <----DAI3-----> BT
- * *
- * * <----DAI4-----> DMIC
- * *
- * * <----DAI5-----> FM
- *************
-
-In this case the PCM data is routed via the DSP. The host CPU in this use case
-is only used for control and can sleep during the runtime of the stream.
-
-The host can control the hostless link either by :-
-
- 1) Configuring the link as a CODEC <-> CODEC style link. In this case the link
- is enabled or disabled by the state of the DAPM graph. This usually means
- there is a mixer control that can be used to connect or disconnect the path
- between both DAIs.
-
- 2) Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM
- graph. Control is then carried out by the FE as regular PCM operations.
- This method gives more control over the DAI links, but requires much more
- userspace code to control the link. Its recommended to use CODEC<->CODEC
- unless your HW needs more fine grained sequencing of the PCM ops.
-
-
-CODEC <-> CODEC link
---------------------
-
-This DAI link is enabled when DAPM detects a valid path within the DAPM graph.
-The machine driver sets some additional parameters to the DAI link i.e.
-
-static const struct snd_soc_pcm_stream dai_params = {
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .rate_min = 8000,
- .rate_max = 8000,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static struct snd_soc_dai_link dais[] = {
- < ... more DAI links above ... >
- {
- .name = "MODEM",
- .stream_name = "MODEM",
- .cpu_dai_name = "dai2",
- .codec_dai_name = "modem-aif1",
- .codec_name = "modem",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .params = &dai_params,
- }
- < ... more DAI links here ... >
-
-These parameters are used to configure the DAI hw_params() when DAPM detects a
-valid path and then calls the PCM operations to start the link. DAPM will also
-call the appropriate PCM operations to disable the DAI when the path is no
-longer valid.
-
-
-Hostless FE
------------
-
-The DAI link(s) are enabled by a FE that does not read or write any PCM data.
-This means creating a new FE that is connected with a virtual path to both
-DAI links. The DAI links will be started when the FE PCM is started and stopped
-when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
-this configuration.
-
-
+++ /dev/null
-Audio Clocking
-==============
-
-This text describes the audio clocking terms in ASoC and digital audio in
-general. Note: Audio clocking can be complex!
-
-
-Master Clock
-------------
-
-Every audio subsystem is driven by a master clock (sometimes referred to as MCLK
-or SYSCLK). This audio master clock can be derived from a number of sources
-(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
-audio playback and capture sample rates.
-
-Some master clocks (e.g. PLLs and CPU based clocks) are configurable in that
-their speed can be altered by software (depending on the system use and to save
-power). Other master clocks are fixed at a set frequency (i.e. crystals).
-
-
-DAI Clocks
-----------
-The Digital Audio Interface is usually driven by a Bit Clock (often referred to
-as BCLK). This clock is used to drive the digital audio data across the link
-between the codec and CPU.
-
-The DAI also has a frame clock to signal the start of each audio frame. This
-clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
-runs at exactly the sample rate (LRC = Rate).
-
-Bit Clock can be generated as follows:-
-
-BCLK = MCLK / x
-
- or
-
-BCLK = LRC * x
-
- or
-
-BCLK = LRC * Channels * Word Size
-
-This relationship depends on the codec or SoC CPU in particular. In general
-it is best to configure BCLK to the lowest possible speed (depending on your
-rate, number of channels and word size) to save on power.
-
-It is also desirable to use the codec (if possible) to drive (or master) the
-audio clocks as it usually gives more accurate sample rates than the CPU.
-
-
-
+++ /dev/null
-ASoC Codec Class Driver
-=======================
-
-The codec class driver is generic and hardware independent code that configures
-the codec, FM, MODEM, BT or external DSP to provide audio capture and playback.
-It should contain no code that is specific to the target platform or machine.
-All platform and machine specific code should be added to the platform and
-machine drivers respectively.
-
-Each codec class driver *must* provide the following features:-
-
- 1) Codec DAI and PCM configuration
- 2) Codec control IO - using RegMap API
- 3) Mixers and audio controls
- 4) Codec audio operations
- 5) DAPM description.
- 6) DAPM event handler.
-
-Optionally, codec drivers can also provide:-
-
- 7) DAC Digital mute control.
-
-Its probably best to use this guide in conjunction with the existing codec
-driver code in sound/soc/codecs/
-
-ASoC Codec driver breakdown
-===========================
-
-1 - Codec DAI and PCM configuration
------------------------------------
-Each codec driver must have a struct snd_soc_dai_driver to define its DAI and
-PCM capabilities and operations. This struct is exported so that it can be
-registered with the core by your machine driver.
-
-e.g.
-
-static struct snd_soc_dai_ops wm8731_dai_ops = {
- .prepare = wm8731_pcm_prepare,
- .hw_params = wm8731_hw_params,
- .shutdown = wm8731_shutdown,
- .digital_mute = wm8731_mute,
- .set_sysclk = wm8731_set_dai_sysclk,
- .set_fmt = wm8731_set_dai_fmt,
-};
-
-struct snd_soc_dai_driver wm8731_dai = {
- .name = "wm8731-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8731_RATES,
- .formats = WM8731_FORMATS,},
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8731_RATES,
- .formats = WM8731_FORMATS,},
- .ops = &wm8731_dai_ops,
- .symmetric_rates = 1,
-};
-
-
-2 - Codec control IO
---------------------
-The codec can usually be controlled via an I2C or SPI style interface
-(AC97 combines control with data in the DAI). The codec driver should use the
-Regmap API for all codec IO. Please see include/linux/regmap.h and existing
-codec drivers for example regmap usage.
-
-
-3 - Mixers and audio controls
------------------------------
-All the codec mixers and audio controls can be defined using the convenience
-macros defined in soc.h.
-
- #define SOC_SINGLE(xname, reg, shift, mask, invert)
-
-Defines a single control as follows:-
-
- xname = Control name e.g. "Playback Volume"
- reg = codec register
- shift = control bit(s) offset in register
- mask = control bit size(s) e.g. mask of 7 = 3 bits
- invert = the control is inverted
-
-Other macros include:-
-
- #define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
-
-A stereo control
-
- #define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
-
-A stereo control spanning 2 registers
-
- #define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
-
-Defines an single enumerated control as follows:-
-
- xreg = register
- xshift = control bit(s) offset in register
- xmask = control bit(s) size
- xtexts = pointer to array of strings that describe each setting
-
- #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
-
-Defines a stereo enumerated control
-
-
-4 - Codec Audio Operations
---------------------------
-The codec driver also supports the following ALSA PCM operations:-
-
-/* SoC audio ops */
-struct snd_soc_ops {
- int (*startup)(struct snd_pcm_substream *);
- void (*shutdown)(struct snd_pcm_substream *);
- int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
- int (*hw_free)(struct snd_pcm_substream *);
- int (*prepare)(struct snd_pcm_substream *);
-};
-
-Please refer to the ALSA driver PCM documentation for details.
-http://www.alsa-project.org/~iwai/writing-an-alsa-driver/
-
-
-5 - DAPM description.
----------------------
-The Dynamic Audio Power Management description describes the codec power
-components and their relationships and registers to the ASoC core.
-Please read dapm.txt for details of building the description.
-
-Please also see the examples in other codec drivers.
-
-
-6 - DAPM event handler
-----------------------
-This function is a callback that handles codec domain PM calls and system
-domain PM calls (e.g. suspend and resume). It is used to put the codec
-to sleep when not in use.
-
-Power states:-
-
- SNDRV_CTL_POWER_D0: /* full On */
- /* vref/mid, clk and osc on, active */
-
- SNDRV_CTL_POWER_D1: /* partial On */
- SNDRV_CTL_POWER_D2: /* partial On */
-
- SNDRV_CTL_POWER_D3hot: /* Off, with power */
- /* everything off except vref/vmid, inactive */
-
- SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
-
-
-7 - Codec DAC digital mute control
-----------------------------------
-Most codecs have a digital mute before the DACs that can be used to
-minimise any system noise. The mute stops any digital data from
-entering the DAC.
-
-A callback can be created that is called by the core for each codec DAI
-when the mute is applied or freed.
-
-i.e.
-
-static int wm8974_mute(struct snd_soc_dai *dai, int mute)
-{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
-
- if (mute)
- snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
- else
- snd_soc_write(codec, WM8974_DAC, mute_reg);
- return 0;
-}
+++ /dev/null
-Dynamic Audio Power Management for Portable Devices
-===================================================
-
-1. Description
-==============
-
-Dynamic Audio Power Management (DAPM) is designed to allow portable
-Linux devices to use the minimum amount of power within the audio
-subsystem at all times. It is independent of other kernel PM and as
-such, can easily co-exist with the other PM systems.
-
-DAPM is also completely transparent to all user space applications as
-all power switching is done within the ASoC core. No code changes or
-recompiling are required for user space applications. DAPM makes power
-switching decisions based upon any audio stream (capture/playback)
-activity and audio mixer settings within the device.
-
-DAPM spans the whole machine. It covers power control within the entire
-audio subsystem, this includes internal codec power blocks and machine
-level power systems.
-
-There are 4 power domains within DAPM
-
- 1. Codec bias domain - VREF, VMID (core codec and audio power)
- Usually controlled at codec probe/remove and suspend/resume, although
- can be set at stream time if power is not needed for sidetone, etc.
-
- 2. Platform/Machine domain - physically connected inputs and outputs
- Is platform/machine and user action specific, is configured by the
- machine driver and responds to asynchronous events e.g when HP
- are inserted
-
- 3. Path domain - audio subsystem signal paths
- Automatically set when mixer and mux settings are changed by the user.
- e.g. alsamixer, amixer.
-
- 4. Stream domain - DACs and ADCs.
- Enabled and disabled when stream playback/capture is started and
- stopped respectively. e.g. aplay, arecord.
-
-All DAPM power switching decisions are made automatically by consulting an audio
-routing map of the whole machine. This map is specific to each machine and
-consists of the interconnections between every audio component (including
-internal codec components). All audio components that effect power are called
-widgets hereafter.
-
-
-2. DAPM Widgets
-===============
-
-Audio DAPM widgets fall into a number of types:-
-
- o Mixer - Mixes several analog signals into a single analog signal.
- o Mux - An analog switch that outputs only one of many inputs.
- o PGA - A programmable gain amplifier or attenuation widget.
- o ADC - Analog to Digital Converter
- o DAC - Digital to Analog Converter
- o Switch - An analog switch
- o Input - A codec input pin
- o Output - A codec output pin
- o Headphone - Headphone (and optional Jack)
- o Mic - Mic (and optional Jack)
- o Line - Line Input/Output (and optional Jack)
- o Speaker - Speaker
- o Supply - Power or clock supply widget used by other widgets.
- o Regulator - External regulator that supplies power to audio components.
- o Clock - External clock that supplies clock to audio components.
- o AIF IN - Audio Interface Input (with TDM slot mask).
- o AIF OUT - Audio Interface Output (with TDM slot mask).
- o Siggen - Signal Generator.
- o DAI IN - Digital Audio Interface Input.
- o DAI OUT - Digital Audio Interface Output.
- o DAI Link - DAI Link between two DAI structures */
- o Pre - Special PRE widget (exec before all others)
- o Post - Special POST widget (exec after all others)
-
-(Widgets are defined in include/sound/soc-dapm.h)
-
-Widgets can be added to the sound card by any of the component driver types.
-There are convenience macros defined in soc-dapm.h that can be used to quickly
-build a list of widgets of the codecs and machines DAPM widgets.
-
-Most widgets have a name, register, shift and invert. Some widgets have extra
-parameters for stream name and kcontrols.
-
-
-2.1 Stream Domain Widgets
--------------------------
-
-Stream Widgets relate to the stream power domain and only consist of ADCs
-(analog to digital converters), DACs (digital to analog converters),
-AIF IN and AIF OUT.
-
-Stream widgets have the following format:-
-
-SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
-SND_SOC_DAPM_AIF_IN(name, stream, slot, reg, shift, invert)
-
-NOTE: the stream name must match the corresponding stream name in your codec
-snd_soc_codec_dai.
-
-e.g. stream widgets for HiFi playback and capture
-
-SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
-SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
-
-e.g. stream widgets for AIF
-
-SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
-
-
-2.2 Path Domain Widgets
------------------------
-
-Path domain widgets have a ability to control or affect the audio signal or
-audio paths within the audio subsystem. They have the following form:-
-
-SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
-
-Any widget kcontrols can be set using the controls and num_controls members.
-
-e.g. Mixer widget (the kcontrols are declared first)
-
-/* Output Mixer */
-static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
-SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
-SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
-SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
-};
-
-SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
- ARRAY_SIZE(wm8731_output_mixer_controls)),
-
-If you don't want the mixer elements prefixed with the name of the mixer widget,
-you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
-as for SND_SOC_DAPM_MIXER.
-
-
-2.3 Machine domain Widgets
---------------------------
-
-Machine widgets are different from codec widgets in that they don't have a
-codec register bit associated with them. A machine widget is assigned to each
-machine audio component (non codec or DSP) that can be independently
-powered. e.g.
-
- o Speaker Amp
- o Microphone Bias
- o Jack connectors
-
-A machine widget can have an optional call back.
-
-e.g. Jack connector widget for an external Mic that enables Mic Bias
-when the Mic is inserted:-
-
-static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
-{
- gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
-
-
-2.4 Codec (BIAS) Domain
------------------------
-
-The codec bias power domain has no widgets and is handled by the codecs DAPM
-event handler. This handler is called when the codec powerstate is changed wrt
-to any stream event or by kernel PM events.
-
-
-2.5 Virtual Widgets
--------------------
-
-Sometimes widgets exist in the codec or machine audio map that don't have any
-corresponding soft power control. In this case it is necessary to create
-a virtual widget - a widget with no control bits e.g.
-
-SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
-
-This can be used to merge to signal paths together in software.
-
-After all the widgets have been defined, they can then be added to the DAPM
-subsystem individually with a call to snd_soc_dapm_new_control().
-
-
-3. Codec/DSP Widget Interconnections
-====================================
-
-Widgets are connected to each other within the codec, platform and machine by
-audio paths (called interconnections). Each interconnection must be defined in
-order to create a map of all audio paths between widgets.
-
-This is easiest with a diagram of the codec or DSP (and schematic of the machine
-audio system), as it requires joining widgets together via their audio signal
-paths.
-
-e.g., from the WM8731 output mixer (wm8731.c)
-
-The WM8731 output mixer has 3 inputs (sources)
-
- 1. Line Bypass Input
- 2. DAC (HiFi playback)
- 3. Mic Sidetone Input
-
-Each input in this example has a kcontrol associated with it (defined in example
-above) and is connected to the output mixer via its kcontrol name. We can now
-connect the destination widget (wrt audio signal) with its source widgets.
-
- /* output mixer */
- {"Output Mixer", "Line Bypass Switch", "Line Input"},
- {"Output Mixer", "HiFi Playback Switch", "DAC"},
- {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
-
-So we have :-
-
- Destination Widget <=== Path Name <=== Source Widget
-
-Or:-
-
- Sink, Path, Source
-
-Or :-
-
- "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch".
-
-When there is no path name connecting widgets (e.g. a direct connection) we
-pass NULL for the path name.
-
-Interconnections are created with a call to:-
-
-snd_soc_dapm_connect_input(codec, sink, path, source);
-
-Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and
-interconnections have been registered with the core. This causes the core to
-scan the codec and machine so that the internal DAPM state matches the
-physical state of the machine.
-
-
-3.1 Machine Widget Interconnections
------------------------------------
-Machine widget interconnections are created in the same way as codec ones and
-directly connect the codec pins to machine level widgets.
-
-e.g. connects the speaker out codec pins to the internal speaker.
-
- /* ext speaker connected to codec pins LOUT2, ROUT2 */
- {"Ext Spk", NULL , "ROUT2"},
- {"Ext Spk", NULL , "LOUT2"},
-
-This allows the DAPM to power on and off pins that are connected (and in use)
-and pins that are NC respectively.
-
-
-4 Endpoint Widgets
-===================
-An endpoint is a start or end point (widget) of an audio signal within the
-machine and includes the codec. e.g.
-
- o Headphone Jack
- o Internal Speaker
- o Internal Mic
- o Mic Jack
- o Codec Pins
-
-Endpoints are added to the DAPM graph so that their usage can be determined in
-order to save power. e.g. NC codecs pins will be switched OFF, unconnected
-jacks can also be switched OFF.
-
-
-5 DAPM Widget Events
-====================
-
-Some widgets can register their interest with the DAPM core in PM events.
-e.g. A Speaker with an amplifier registers a widget so the amplifier can be
-powered only when the spk is in use.
-
-/* turn speaker amplifier on/off depending on use */
-static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
-{
- gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-/* corgi machine dapm widgets */
-static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
- SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
-
-Please see soc-dapm.h for all other widgets that support events.
-
-
-5.1 Event types
----------------
-
-The following event types are supported by event widgets.
-
-/* dapm event types */
-#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
-#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
-#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
-#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
-#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
-#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
+++ /dev/null
-ASoC jack detection
-===================
-
-ALSA has a standard API for representing physical jacks to user space,
-the kernel side of which can be seen in include/sound/jack.h. ASoC
-provides a version of this API adding two additional features:
-
- - It allows more than one jack detection method to work together on one
- user visible jack. In embedded systems it is common for multiple
- to be present on a single jack but handled by separate bits of
- hardware.
-
- - Integration with DAPM, allowing DAPM endpoints to be updated
- automatically based on the detected jack status (eg, turning off the
- headphone outputs if no headphones are present).
-
-This is done by splitting the jacks up into three things working
-together: the jack itself represented by a struct snd_soc_jack, sets of
-snd_soc_jack_pins representing DAPM endpoints to update and blocks of
-code providing jack reporting mechanisms.
-
-For example, a system may have a stereo headset jack with two reporting
-mechanisms, one for the headphone and one for the microphone. Some
-systems won't be able to use their speaker output while a headphone is
-connected and so will want to make sure to update both speaker and
-headphone when the headphone jack status changes.
-
-The jack - struct snd_soc_jack
-==============================
-
-This represents a physical jack on the system and is what is visible to
-user space. The jack itself is completely passive, it is set up by the
-machine driver and updated by jack detection methods.
-
-Jacks are created by the machine driver calling snd_soc_jack_new().
-
-snd_soc_jack_pin
-================
-
-These represent a DAPM pin to update depending on some of the status
-bits supported by the jack. Each snd_soc_jack has zero or more of these
-which are updated automatically. They are created by the machine driver
-and associated with the jack using snd_soc_jack_add_pins(). The status
-of the endpoint may configured to be the opposite of the jack status if
-required (eg, enabling a built in microphone if a microphone is not
-connected via a jack).
-
-Jack detection methods
-======================
-
-Actual jack detection is done by code which is able to monitor some
-input to the system and update a jack by calling snd_soc_jack_report(),
-specifying a subset of bits to update. The jack detection code should
-be set up by the machine driver, taking configuration for the jack to
-update and the set of things to report when the jack is connected.
-
-Often this is done based on the status of a GPIO - a handler for this is
-provided by the snd_soc_jack_add_gpio() function. Other methods are
-also available, for example integrated into CODECs. One example of
-CODEC integrated jack detection can be see in the WM8350 driver.
-
-Each jack may have multiple reporting mechanisms, though it will need at
-least one to be useful.
-
-Machine drivers
-===============
-
-These are all hooked together by the machine driver depending on the
-system hardware. The machine driver will set up the snd_soc_jack and
-the list of pins to update then set up one or more jack detection
-mechanisms to update that jack based on their current status.
+++ /dev/null
-ASoC Machine Driver
-===================
-
-The ASoC machine (or board) driver is the code that glues together all the
-component drivers (e.g. codecs, platforms and DAIs). It also describes the
-relationships between each component which include audio paths, GPIOs,
-interrupts, clocking, jacks and voltage regulators.
-
-The machine driver can contain codec and platform specific code. It registers
-the audio subsystem with the kernel as a platform device and is represented by
-the following struct:-
-
-/* SoC machine */
-struct snd_soc_card {
- char *name;
-
- ...
-
- int (*probe)(struct platform_device *pdev);
- int (*remove)(struct platform_device *pdev);
-
- /* the pre and post PM functions are used to do any PM work before and
- * after the codec and DAIs do any PM work. */
- int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
- int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
- int (*resume_pre)(struct platform_device *pdev);
- int (*resume_post)(struct platform_device *pdev);
-
- ...
-
- /* CPU <--> Codec DAI links */
- struct snd_soc_dai_link *dai_link;
- int num_links;
-
- ...
-};
-
-probe()/remove()
-----------------
-probe/remove are optional. Do any machine specific probe here.
-
-
-suspend()/resume()
-------------------
-The machine driver has pre and post versions of suspend and resume to take care
-of any machine audio tasks that have to be done before or after the codec, DAIs
-and DMA is suspended and resumed. Optional.
-
-
-Machine DAI Configuration
--------------------------
-The machine DAI configuration glues all the codec and CPU DAIs together. It can
-also be used to set up the DAI system clock and for any machine related DAI
-initialisation e.g. the machine audio map can be connected to the codec audio
-map, unconnected codec pins can be set as such.
-
-struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
-
-/* corgi digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link corgi_dai = {
- .name = "WM8731",
- .stream_name = "WM8731",
- .cpu_dai_name = "pxa-is2-dai",
- .codec_dai_name = "wm8731-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8713-codec.0-001a",
- .init = corgi_wm8731_init,
- .ops = &corgi_ops,
-};
-
-struct snd_soc_card then sets up the machine with its DAIs. e.g.
-
-/* corgi audio machine driver */
-static struct snd_soc_card snd_soc_corgi = {
- .name = "Corgi",
- .dai_link = &corgi_dai,
- .num_links = 1,
-};
-
-
-Machine Power Map
------------------
-
-The machine driver can optionally extend the codec power map and to become an
-audio power map of the audio subsystem. This allows for automatic power up/down
-of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack
-sockets in the machine init function.
-
-
-Machine Controls
-----------------
-
-Machine specific audio mixer controls can be added in the DAI init function.
+++ /dev/null
-ALSA SoC Layer
-==============
-
-The overall project goal of the ALSA System on Chip (ASoC) layer is to
-provide better ALSA support for embedded system-on-chip processors (e.g.
-pxa2xx, au1x00, iMX, etc) and portable audio codecs. Prior to the ASoC
-subsystem there was some support in the kernel for SoC audio, however it
-had some limitations:-
-
- * Codec drivers were often tightly coupled to the underlying SoC
- CPU. This is not ideal and leads to code duplication - for example,
- Linux had different wm8731 drivers for 4 different SoC platforms.
-
- * There was no standard method to signal user initiated audio events (e.g.
- Headphone/Mic insertion, Headphone/Mic detection after an insertion
- event). These are quite common events on portable devices and often require
- machine specific code to re-route audio, enable amps, etc., after such an
- event.
-
- * Drivers tended to power up the entire codec when playing (or
- recording) audio. This is fine for a PC, but tends to waste a lot of
- power on portable devices. There was also no support for saving
- power via changing codec oversampling rates, bias currents, etc.
-
-
-ASoC Design
-===========
-
-The ASoC layer is designed to address these issues and provide the following
-features :-
-
- * Codec independence. Allows reuse of codec drivers on other platforms
- and machines.
-
- * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC
- interface and codec registers its audio interface capabilities with the
- core and are subsequently matched and configured when the application
- hardware parameters are known.
-
- * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
- its minimum power state at all times. This includes powering up/down
- internal power blocks depending on the internal codec audio routing and any
- active streams.
-
- * Pop and click reduction. Pops and clicks can be reduced by powering the
- codec up/down in the correct sequence (including using digital mute). ASoC
- signals the codec when to change power states.
-
- * Machine specific controls: Allow machines to add controls to the sound card
- (e.g. volume control for speaker amplifier).
-
-To achieve all this, ASoC basically splits an embedded audio system into
-multiple re-usable component drivers :-
-
- * Codec class drivers: The codec class driver is platform independent and
- contains audio controls, audio interface capabilities, codec DAPM
- definition and codec IO functions. This class extends to BT, FM and MODEM
- ICs if required. Codec class drivers should be generic code that can run
- on any architecture and machine.
-
- * Platform class drivers: The platform class driver includes the audio DMA
- engine driver, digital audio interface (DAI) drivers (e.g. I2S, AC97, PCM)
- and any audio DSP drivers for that platform.
-
- * Machine class driver: The machine driver class acts as the glue that
- describes and binds the other component drivers together to form an ALSA
- "sound card device". It handles any machine specific controls and
- machine level audio events (e.g. turning on an amp at start of playback).
-
-
-Documentation
-=============
-
-The documentation is spilt into the following sections:-
-
-overview.txt: This file.
-
-codec.txt: Codec driver internals.
-
-DAI.txt: Description of Digital Audio Interface standards and how to configure
-a DAI within your codec and CPU DAI drivers.
-
-dapm.txt: Dynamic Audio Power Management
-
-platform.txt: Platform audio DMA and DAI.
-
-machine.txt: Machine driver internals.
-
-pop_clicks.txt: How to minimise audio artifacts.
-
-clocking.txt: ASoC clocking for best power performance.
-
-jack.txt: ASoC jack detection.
-
-DPCM.txt: Dynamic PCM - Describes DPCM with DSP examples.
+++ /dev/null
-ASoC Platform Driver
-====================
-
-An ASoC platform driver class can be divided into audio DMA drivers, SoC DAI
-drivers and DSP drivers. The platform drivers only target the SoC CPU and must
-have no board specific code.
-
-Audio DMA
-=========
-
-The platform DMA driver optionally supports the following ALSA operations:-
-
-/* SoC audio ops */
-struct snd_soc_ops {
- int (*startup)(struct snd_pcm_substream *);
- void (*shutdown)(struct snd_pcm_substream *);
- int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
- int (*hw_free)(struct snd_pcm_substream *);
- int (*prepare)(struct snd_pcm_substream *);
- int (*trigger)(struct snd_pcm_substream *, int);
-};
-
-The platform driver exports its DMA functionality via struct
-snd_soc_platform_driver:-
-
-struct snd_soc_platform_driver {
- char *name;
-
- int (*probe)(struct platform_device *pdev);
- int (*remove)(struct platform_device *pdev);
- int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
- int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
-
- /* pcm creation and destruction */
- int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *);
- void (*pcm_free)(struct snd_pcm *);
-
- /*
- * For platform caused delay reporting.
- * Optional.
- */
- snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
- struct snd_soc_dai *);
-
- /* platform stream ops */
- struct snd_pcm_ops *pcm_ops;
-};
-
-Please refer to the ALSA driver documentation for details of audio DMA.
-http://www.alsa-project.org/~iwai/writing-an-alsa-driver/
-
-An example DMA driver is soc/pxa/pxa2xx-pcm.c
-
-
-SoC DAI Drivers
-===============
-
-Each SoC DAI driver must provide the following features:-
-
- 1) Digital audio interface (DAI) description
- 2) Digital audio interface configuration
- 3) PCM's description
- 4) SYSCLK configuration
- 5) Suspend and resume (optional)
-
-Please see codec.txt for a description of items 1 - 4.
-
-
-SoC DSP Drivers
-===============
-
-Each SoC DSP driver usually supplies the following features :-
-
- 1) DAPM graph
- 2) Mixer controls
- 3) DMA IO to/from DSP buffers (if applicable)
- 4) Definition of DSP front end (FE) PCM devices.
-
-Please see DPCM.txt for a description of item 4.
+++ /dev/null
-Audio Pops and Clicks
-=====================
-
-Pops and clicks are unwanted audio artifacts caused by the powering up and down
-of components within the audio subsystem. This is noticeable on PCs when an
-audio module is either loaded or unloaded (at module load time the sound card is
-powered up and causes a popping noise on the speakers).
-
-Pops and clicks can be more frequent on portable systems with DAPM. This is
-because the components within the subsystem are being dynamically powered
-depending on the audio usage and this can subsequently cause a small pop or
-click every time a component power state is changed.
-
-
-Minimising Playback Pops and Clicks
-===================================
-
-Playback pops in portable audio subsystems cannot be completely eliminated
-currently, however future audio codec hardware will have better pop and click
-suppression. Pops can be reduced within playback by powering the audio
-components in a specific order. This order is different for startup and
-shutdown and follows some basic rules:-
-
- Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
-
- Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC
-
-This assumes that the codec PCM output path from the DAC is via a mixer and then
-a PGA (programmable gain amplifier) before being output to the speakers.
-
-
-Minimising Capture Pops and Clicks
-==================================
-
-Capture artifacts are somewhat easier to get rid as we can delay activating the
-ADC until all the pops have occurred. This follows similar power rules to
-playback in that components are powered in a sequence depending upon stream
-startup or shutdown.
-
- Startup Order - Input PGA --> Mixers --> ADC
-
- Shutdown Order - ADC --> Mixers --> Input PGA
-
-
-Zipper Noise
-============
-An unwanted zipper noise can occur within the audio playback or capture stream
-when a volume control is changed near its maximum gain value. The zipper noise
-is heard when the gain increase or decrease changes the mean audio signal
-amplitude too quickly. It can be minimised by enabling the zero cross setting
-for each volume control. The ZC forces the gain change to occur when the signal
-crosses the zero amplitude line.
+++ /dev/null
-The ALSA API can provide two different system timestamps:
-
-- Trigger_tstamp is the system time snapshot taken when the .trigger
-callback is invoked. This snapshot is taken by the ALSA core in the
-general case, but specific hardware may have synchronization
-capabilities or conversely may only be able to provide a correct
-estimate with a delay. In the latter two cases, the low-level driver
-is responsible for updating the trigger_tstamp at the most appropriate
-and precise moment. Applications should not rely solely on the first
-trigger_tstamp but update their internal calculations if the driver
-provides a refined estimate with a delay.
-
-- tstamp is the current system timestamp updated during the last
-event or application query.
-The difference (tstamp - trigger_tstamp) defines the elapsed time.
-
-The ALSA API provides two basic pieces of information, avail
-and delay, which combined with the trigger and current system
-timestamps allow for applications to keep track of the 'fullness' of
-the ring buffer and the amount of queued samples.
-
-The use of these different pointers and time information depends on
-the application needs:
-
-- 'avail' reports how much can be written in the ring buffer
-- 'delay' reports the time it will take to hear a new sample after all
-queued samples have been played out.
-
-When timestamps are enabled, the avail/delay information is reported
-along with a snapshot of system time. Applications can select from
-CLOCK_REALTIME (NTP corrections including going backwards),
-CLOCK_MONOTONIC (NTP corrections but never going backwards),
-CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode
-dynamically with sw_params
-
-
-The ALSA API also provide an audio_tstamp which reflects the passage
-of time as measured by different components of audio hardware. In
-ascii-art, this could be represented as follows (for the playback
-case):
-
-
---------------------------------------------------------------> time
- ^ ^ ^ ^ ^
- | | | | |
- analog link dma app FullBuffer
- time time time time time
- | | | | |
- |< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
- |<----------------- delay---------------------->| |
- |<----ring buffer length---->|
-
-The analog time is taken at the last stage of the playback, as close
-as possible to the actual transducer
-
-The link time is taken at the output of the SoC/chipset as the samples
-are pushed on a link. The link time can be directly measured if
-supported in hardware by sample counters or wallclocks (e.g. with
-HDAudio 24MHz or PTP clock for networked solutions) or indirectly
-estimated (e.g. with the frame counter in USB).
-
-The DMA time is measured using counters - typically the least reliable
-of all measurements due to the bursty nature of DMA transfers.
-
-The app time corresponds to the time tracked by an application after
-writing in the ring buffer.
-
-The application can query the hardware capabilities, define which
-audio time it wants reported by selecting the relevant settings in
-audio_tstamp_config fields, thus get an estimate of the timestamp
-accuracy. It can also request the delay-to-analog be included in the
-measurement. Direct access to the link time is very interesting on
-platforms that provide an embedded DSP; measuring directly the link
-time with dedicated hardware, possibly synchronized with system time,
-removes the need to keep track of internal DSP processing times and
-latency.
-
-In case the application requests an audio tstamp that is not supported
-in hardware/low-level driver, the type is overridden as DEFAULT and the
-timestamp will report the DMA time based on the hw_pointer value.
-
-For backwards compatibility with previous implementations that did not
-provide timestamp selection, with a zero-valued COMPAT timestamp type
-the results will default to the HDAudio wall clock for playback
-streams and to the DMA time (hw_ptr) in all other cases.
-
-The audio timestamp accuracy can be returned to user-space, so that
-appropriate decisions are made:
-
-- for dma time (default), the granularity of the transfers can be
- inferred from the steps between updates and in turn provide
- information on how much the application pointer can be rewound
- safely.
-
-- the link time can be used to track long-term drifts between audio
- and system time using the (tstamp-trigger_tstamp)/audio_tstamp
- ratio, the precision helps define how much smoothing/low-pass
- filtering is required. The link time can be either reset on startup
- or reported as is (the latter being useful to compare progress of
- different streams - but may require the wallclock to be always
- running and not wrap-around during idle periods). If supported in
- hardware, the absolute link time could also be used to define a
- precise start time (patches WIP)
-
-- including the delay in the audio timestamp may
- counter-intuitively not increase the precision of timestamps, e.g. if a
- codec includes variable-latency DSP processing or a chain of
- hardware components the delay is typically not known with precision.
-
-The accuracy is reported in nanosecond units (using an unsigned 32-bit
-word), which gives a max precision of 4.29s, more than enough for
-audio applications...
-
-Due to the varied nature of timestamping needs, even for a single
-application, the audio_tstamp_config can be changed dynamically. In
-the STATUS ioctl, the parameters are read-only and do not allow for
-any application selection. To work around this limitation without
-impacting legacy applications, a new STATUS_EXT ioctl is introduced
-with read/write parameters. ALSA-lib will be modified to make use of
-STATUS_EXT and effectively deprecate STATUS.
-
-The ALSA API only allows for a single audio timestamp to be reported
-at a time. This is a conscious design decision, reading the audio
-timestamps from hardware registers or from IPC takes time, the more
-timestamps are read the more imprecise the combined measurements
-are. To avoid any interpretation issues, a single (system, audio)
-timestamp is reported. Applications that need different timestamps
-will be required to issue multiple queries and perform an
-interpolation of the results
-
-In some hardware-specific configuration, the system timestamp is
-latched by a low-level audio subsystem, and the information provided
-back to the driver. Due to potential delays in the communication with
-the hardware, there is a risk of misalignment with the avail and delay
-information. To make sure applications are not confused, a
-driver_timestamp field is added in the snd_pcm_status structure; this
-timestamp shows when the information is put together by the driver
-before returning from the STATUS and STATUS_EXT ioctl. in most cases
-this driver_timestamp will be identical to the regular system tstamp.
-
-Examples of typestamping with HDaudio:
-
-1. DMA timestamp, no compensation for DMA+analog delay
-$ ./audio_time -p --ts_type=1
-playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
-playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
-playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
-playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
-playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
-playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
-
-2. DMA timestamp, compensation for DMA+analog delay
-$ ./audio_time -p --ts_type=1 -d
-playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
-playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
-playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
-playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
-playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
-
-3. link timestamp, compensation for DMA+analog delay
-$ ./audio_time -p --ts_type=2 -d
-playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
-playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
-playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
-playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
-playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
-playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
-
-Example 1 shows that the timestamp at the DMA level is close to 1ms
-ahead of the actual playback time (as a side time this sort of
-measurement can help define rewind safeguards). Compensating for the
-DMA-link delay in example 2 helps remove the hardware buffering but
-the information is still very jittery, with up to one sample of
-error. In example 3 where the timestamps are measured with the link
-wallclock, the timestamps show a monotonic behavior and a lower
-dispersion.
-
-Example 3 and 4 are with USB audio class. Example 3 shows a high
-offset between audio time and system time due to buffering. Example 4
-shows how compensating for the delay exposes a 1ms accuracy (due to
-the use of the frame counter by the driver)
-
-Example 3: DMA timestamp, no compensation for delay, delta of ~5ms
-$ ./audio_time -p -Dhw:1 -t1
-playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
-playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
-playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
-playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
-playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
-playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
-playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
-
-Example 4: DMA timestamp, compensation for delay, delay of ~1ms
-$ ./audio_time -p -Dhw:1 -t1 -d
-playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
-playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
-playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
-playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
-playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
-playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847
--- /dev/null
+=============================================
+Sound Blaster Audigy mixer / default DSP code
+=============================================
+
+This is based on sb-live-mixer.rst.
+
+The EMU10K2 chips have a DSP part which can be programmed to support
+various ways of sample processing, which is described here.
+(This article does not deal with the overall functionality of the
+EMU10K2 chips. See the manuals section for further details.)
+
+The ALSA driver programs this portion of chip by default code
+(can be altered later) which offers the following functionality:
+
+
+Digital mixer controls
+======================
+
+These controls are built using the DSP instructions. They offer extended
+functionality. Only the default build-in code in the ALSA driver is described
+here. Note that the controls work as attenuators: the maximum value is the
+neutral position leaving the signal unchanged. Note that if the same destination
+is mentioned in multiple controls, the signal is accumulated and can be wrapped
+(set to maximal or minimal value without checking of overflow).
+
+
+Explanation of used abbreviations:
+
+DAC
+ digital to analog converter
+ADC
+ analog to digital converter
+I2S
+ one-way three wire serial bus for digital sound by Philips Semiconductors
+ (this standard is used for connecting standalone DAC and ADC converters)
+LFE
+ low frequency effects (subwoofer signal)
+AC97
+ a chip containing an analog mixer, DAC and ADC converters
+IEC958
+ S/PDIF
+FX-bus
+ the EMU10K2 chip has an effect bus containing 64 accumulators.
+ Each of the synthesizer voices can feed its output to these accumulators
+ and the DSP microcontroller can operate with the resulting sum.
+
+name='PCM Front Playback Volume',index=0
+----------------------------------------
+This control is used to attenuate samples for left and right front PCM FX-bus
+accumulators. ALSA uses accumulators 8 and 9 for left and right front PCM
+samples for 5.1 playback. The result samples are forwarded to the front DAC PCM
+slots of the Philips DAC.
+
+name='PCM Surround Playback Volume',index=0
+-------------------------------------------
+This control is used to attenuate samples for left and right surround PCM FX-bus
+accumulators. ALSA uses accumulators 2 and 3 for left and right surround PCM
+samples for 5.1 playback. The result samples are forwarded to the surround DAC PCM
+slots of the Philips DAC.
+
+name='PCM Center Playback Volume',index=0
+-----------------------------------------
+This control is used to attenuate samples for center PCM FX-bus accumulator.
+ALSA uses accumulator 6 for center PCM sample for 5.1 playback. The result sample
+is forwarded to the center DAC PCM slot of the Philips DAC.
+
+name='PCM LFE Playback Volume',index=0
+--------------------------------------
+This control is used to attenuate sample for LFE PCM FX-bus accumulator.
+ALSA uses accumulator 7 for LFE PCM sample for 5.1 playback. The result sample
+is forwarded to the LFE DAC PCM slot of the Philips DAC.
+
+name='PCM Playback Volume',index=0
+----------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for
+stereo playback. The result samples are forwarded to the front DAC PCM slots
+of the Philips DAC.
+
+name='PCM Capture Volume',index=0
+---------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+name='Music Playback Volume',index=0
+------------------------------------
+This control is used to attenuate samples for left and right MIDI FX-bus
+accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
+The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
+
+name='Music Capture Volume',index=0
+-----------------------------------
+These controls are used to attenuate samples for left and right MIDI FX-bus
+accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+name='Mic Playback Volume',index=0
+----------------------------------
+This control is used to attenuate samples for left and right Mic input.
+For Mic input is used AC97 codec. The result samples are forwarded to
+the front DAC PCM slots of the Philips DAC. Samples are forwarded to Mic
+capture FIFO (device 1 - 16bit/8KHz mono) too without volume control.
+
+name='Mic Capture Volume',index=0
+---------------------------------
+This control is used to attenuate samples for left and right Mic input.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+name='Audigy CD Playback Volume',index=0
+----------------------------------------
+This control is used to attenuate samples from left and right IEC958 TTL
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the front DAC PCM slots of the Philips DAC.
+
+name='Audigy CD Capture Volume',index=0
+---------------------------------------
+This control is used to attenuate samples from left and right IEC958 TTL
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
+
+name='IEC958 Optical Playback Volume',index=0
+---------------------------------------------
+This control is used to attenuate samples from left and right IEC958 optical
+digital input. The result samples are forwarded to the front DAC PCM slots
+of the Philips DAC.
+
+name='IEC958 Optical Capture Volume',index=0
+--------------------------------------------
+This control is used to attenuate samples from left and right IEC958 optical
+digital inputs. The result samples are forwarded to the ADC capture FIFO
+(thus to the standard capture PCM device).
+
+name='Line2 Playback Volume',index=0
+------------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the AudigyDrive). The result samples are forwarded to the front
+DAC PCM slots of the Philips DAC.
+
+name='Line2 Capture Volume',index=1
+-----------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the AudigyDrive). The result samples are forwarded to the ADC
+capture FIFO (thus to the standard capture PCM device).
+
+name='Analog Mix Playback Volume',index=0
+-----------------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs from Philips ADC. The result samples are forwarded to the front
+DAC PCM slots of the Philips DAC. This contains mix from analog sources
+like CD, Line In, Aux, ....
+
+name='Analog Mix Capture Volume',index=1
+----------------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs Philips ADC. The result samples are forwarded to the ADC
+capture FIFO (thus to the standard capture PCM device).
+
+name='Aux2 Playback Volume',index=0
+-----------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the AudigyDrive). The result samples are forwarded to the front
+DAC PCM slots of the Philips DAC.
+
+name='Aux2 Capture Volume',index=1
+----------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the AudigyDrive). The result samples are forwarded to the ADC
+capture FIFO (thus to the standard capture PCM device).
+
+name='Front Playback Volume',index=0
+------------------------------------
+All stereo signals are mixed together and mirrored to surround, center and LFE.
+This control is used to attenuate samples for left and right front speakers of
+this mix.
+
+name='Surround Playback Volume',index=0
+---------------------------------------
+All stereo signals are mixed together and mirrored to surround, center and LFE.
+This control is used to attenuate samples for left and right surround speakers of
+this mix.
+
+name='Center Playback Volume',index=0
+-------------------------------------
+All stereo signals are mixed together and mirrored to surround, center and LFE.
+This control is used to attenuate sample for center speaker of this mix.
+
+name='LFE Playback Volume',index=0
+----------------------------------
+All stereo signals are mixed together and mirrored to surround, center and LFE.
+This control is used to attenuate sample for LFE speaker of this mix.
+
+name='Tone Control - Switch',index=0
+------------------------------------
+This control turns the tone control on or off. The samples for front, rear
+and center / LFE outputs are affected.
+
+name='Tone Control - Bass',index=0
+----------------------------------
+This control sets the bass intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+name='Tone Control - Treble',index=0
+------------------------------------
+This control sets the treble intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+name='Master Playback Volume',index=0
+-------------------------------------
+This control is used to attenuate samples for front, surround, center and
+LFE outputs.
+
+name='IEC958 Optical Raw Playback Switch',index=0
+-------------------------------------------------
+If this switch is on, then the samples for the IEC958 (S/PDIF) digital
+output are taken only from the raw FX8010 PCM, otherwise standard front
+PCM samples are taken.
+
+
+PCM stream related controls
+===========================
+
+name='EMU10K1 PCM Volume',index 0-31
+------------------------------------
+Channel volume attenuation in range 0-0xffff. The maximum value (no
+attenuation) is default. The channel mapping for three values is
+as follows:
+
+* 0 - mono, default 0xffff (no attenuation)
+* 1 - left, default 0xffff (no attenuation)
+* 2 - right, default 0xffff (no attenuation)
+
+name='EMU10K1 PCM Send Routing',index 0-31
+------------------------------------------
+This control specifies the destination - FX-bus accumulators. There 24
+values with this mapping:
+
+* 0 - mono, A destination (FX-bus 0-63), default 0
+* 1 - mono, B destination (FX-bus 0-63), default 1
+* 2 - mono, C destination (FX-bus 0-63), default 2
+* 3 - mono, D destination (FX-bus 0-63), default 3
+* 4 - mono, E destination (FX-bus 0-63), default 0
+* 5 - mono, F destination (FX-bus 0-63), default 0
+* 6 - mono, G destination (FX-bus 0-63), default 0
+* 7 - mono, H destination (FX-bus 0-63), default 0
+* 8 - left, A destination (FX-bus 0-63), default 0
+* 9 - left, B destination (FX-bus 0-63), default 1
+* 10 - left, C destination (FX-bus 0-63), default 2
+* 11 - left, D destination (FX-bus 0-63), default 3
+* 12 - left, E destination (FX-bus 0-63), default 0
+* 13 - left, F destination (FX-bus 0-63), default 0
+* 14 - left, G destination (FX-bus 0-63), default 0
+* 15 - left, H destination (FX-bus 0-63), default 0
+* 16 - right, A destination (FX-bus 0-63), default 0
+* 17 - right, B destination (FX-bus 0-63), default 1
+* 18 - right, C destination (FX-bus 0-63), default 2
+* 19 - right, D destination (FX-bus 0-63), default 3
+* 20 - right, E destination (FX-bus 0-63), default 0
+* 21 - right, F destination (FX-bus 0-63), default 0
+* 22 - right, G destination (FX-bus 0-63), default 0
+* 23 - right, H destination (FX-bus 0-63), default 0
+
+Don't forget that it's illegal to assign a channel to the same FX-bus accumulator
+more than once (it means 0=0 && 1=0 is an invalid combination).
+
+name='EMU10K1 PCM Send Volume',index 0-31
+-----------------------------------------
+It specifies the attenuation (amount) for given destination in range 0-255.
+The channel mapping is following:
+
+* 0 - mono, A destination attn, default 255 (no attenuation)
+* 1 - mono, B destination attn, default 255 (no attenuation)
+* 2 - mono, C destination attn, default 0 (mute)
+* 3 - mono, D destination attn, default 0 (mute)
+* 4 - mono, E destination attn, default 0 (mute)
+* 5 - mono, F destination attn, default 0 (mute)
+* 6 - mono, G destination attn, default 0 (mute)
+* 7 - mono, H destination attn, default 0 (mute)
+* 8 - left, A destination attn, default 255 (no attenuation)
+* 9 - left, B destination attn, default 0 (mute)
+* 10 - left, C destination attn, default 0 (mute)
+* 11 - left, D destination attn, default 0 (mute)
+* 12 - left, E destination attn, default 0 (mute)
+* 13 - left, F destination attn, default 0 (mute)
+* 14 - left, G destination attn, default 0 (mute)
+* 15 - left, H destination attn, default 0 (mute)
+* 16 - right, A destination attn, default 0 (mute)
+* 17 - right, B destination attn, default 255 (no attenuation)
+* 18 - right, C destination attn, default 0 (mute)
+* 19 - right, D destination attn, default 0 (mute)
+* 20 - right, E destination attn, default 0 (mute)
+* 21 - right, F destination attn, default 0 (mute)
+* 22 - right, G destination attn, default 0 (mute)
+* 23 - right, H destination attn, default 0 (mute)
+
+
+
+MANUALS/PATENTS
+===============
+
+ftp://opensource.creative.com/pub/doc
+-------------------------------------
+
+LM4545.pdf
+ AC97 Codec
+
+m2049.pdf
+ The EMU10K1 Digital Audio Processor
+
+hog63.ps
+ FX8010 - A DSP Chip Architecture for Audio Effects
+
+
+WIPO Patents
+------------
+
+WO 9901813 (A1)
+ Audio Effects Processor with multiple asynchronous streams
+ (Jan. 14, 1999)
+
+WO 9901814 (A1)
+ Processor with Instruction Set for Audio Effects (Jan. 14, 1999)
+
+WO 9901953 (A1)
+ Audio Effects Processor having Decoupled Instruction
+ Execution and Audio Data Sequencing (Jan. 14, 1999)
+
+
+US Patents (http://www.uspto.gov/)
+----------------------------------
+
+US 5925841
+ Digital Sampling Instrument employing cache memory (Jul. 20, 1999)
+
+US 5928342
+ Audio Effects Processor integrated on a single chip
+ with a multiport memory onto which multiple asynchronous
+ digital sound samples can be concurrently loaded
+ (Jul. 27, 1999)
+
+US 5930158
+ Processor with Instruction Set for Audio Effects (Jul. 27, 1999)
+
+US 6032235
+ Memory initialization circuit (Tram) (Feb. 29, 2000)
+
+US 6138207
+ Interpolation looping of audio samples in cache connected to
+ system bus with prioritization and modification of bus transfers
+ in accordance with loop ends and minimum block sizes
+ (Oct. 24, 2000)
+
+US 6151670
+ Method for conserving memory storage using a
+ pool of short term memory registers
+ (Nov. 21, 2000)
+
+US 6195715
+ Interrupt control for multiple programs communicating with
+ a common interrupt by associating programs to GP registers,
+ defining interrupt register, polling GP registers, and invoking
+ callback routine associated with defined interrupt register
+ (Feb. 27, 2001)
--- /dev/null
+========================================================
+Guide to using M-Audio Audiophile USB with ALSA and Jack
+========================================================
+
+v1.5
+
+Thibault Le Meur <Thibault.LeMeur@supelec.fr>
+
+This document is a guide to using the M-Audio Audiophile USB (tm) device with
+ALSA and JACK.
+
+History
+=======
+
+* v1.4 - Thibault Le Meur (2007-07-11)
+
+ - Added Low Endianness nature of 16bits-modes
+ found by Hakan Lennestal <Hakan.Lennestal@brfsodrahamn.se>
+ - Modifying document structure
+
+* v1.5 - Thibault Le Meur (2007-07-12)
+ - Added AC3/DTS passthru info
+
+
+Audiophile USB Specs and correct usage
+======================================
+
+This part is a reminder of important facts about the functions and limitations
+of the device.
+
+The device has 4 audio interfaces, and 2 MIDI ports:
+
+ * Analog Stereo Input (Ai)
+
+ - This port supports 2 pairs of line-level audio inputs (1/4" TS and RCA)
+ - When the 1/4" TS (jack) connectors are connected, the RCA connectors
+ are disabled
+
+ * Analog Stereo Output (Ao)
+ * Digital Stereo Input (Di)
+ * Digital Stereo Output (Do)
+ * Midi In (Mi)
+ * Midi Out (Mo)
+
+The internal DAC/ADC has the following characteristics:
+
+* sample depth of 16 or 24 bits
+* sample rate from 8kHz to 96kHz
+* Two interfaces can't use different sample depths at the same time.
+
+Moreover, the Audiophile USB documentation gives the following Warning:
+ Please exit any audio application running before switching between bit depths
+
+Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be
+activated at the same time depending on the audio mode selected:
+
+ * 16-bit/48kHz ==> 4 channels in + 4 channels out
+
+ - Ai+Ao+Di+Do
+
+ * 24-bit/48kHz ==> 4 channels in + 2 channels out,
+ or 2 channels in + 4 channels out
+
+ - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
+
+ * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
+
+ - Ai or Ao or Di or Do
+
+Important facts about the Digital interface:
+--------------------------------------------
+
+ * The Do port additionally supports surround-encoded AC-3 and DTS passthrough,
+ though I haven't tested it under Linux
+
+ - Note that in this setup only the Do interface can be enabled
+
+ * Apart from recording an audio digital stream, enabling the Di port is a way
+ to synchronize the device to an external sample clock
+
+ - As a consequence, the Di port must be enable only if an active Digital
+ source is connected
+ - Enabling Di when no digital source is connected can result in a
+ synchronization error (for instance sound played at an odd sample rate)
+
+
+Audiophile USB MIDI support in ALSA
+===================================
+
+The Audiophile USB MIDI ports will be automatically supported once the
+following modules have been loaded:
+
+ * snd-usb-audio
+ * snd-seq-midi
+
+No additional setting is required.
+
+
+Audiophile USB Audio support in ALSA
+====================================
+
+Audio functions of the Audiophile USB device are handled by the snd-usb-audio
+module. This module can work in a default mode (without any device-specific
+parameter), or in an "advanced" mode with the device-specific parameter called
+``device_setup``.
+
+Default Alsa driver mode
+------------------------
+
+The default behavior of the snd-usb-audio driver is to list the device
+capabilities at startup and activate the required mode when required
+by the applications: for instance if the user is recording in a
+24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
+the snd-usb-audio module will reconfigure the device on the fly.
+
+This approach has the advantage to let the driver automatically switch from sample
+rates/depths automatically according to the user's needs. However, those who
+are using the device under windows know that this is not how the device is meant to
+work: under windows applications must be closed before using the m-audio control
+panel to switch the device working mode. Thus as we'll see in next section, this
+Default Alsa driver mode can lead to device misconfigurations.
+
+Let's get back to the Default Alsa driver mode for now. In this case the
+Audiophile interfaces are mapped to alsa pcm devices in the following
+way (I suppose the device's index is 1):
+
+ * hw:1,0 is Ao in playback and Di in capture
+ * hw:1,1 is Do in playback and Ai in capture
+ * hw:1,2 is Do in AC3/DTS passthrough mode
+
+In this mode, the device uses Big Endian byte-encoding so that
+supported audio format are S16_BE for 16-bit depth modes and S24_3BE for
+24-bits depth mode.
+
+One exception is the hw:1,2 port which was reported to be Little Endian
+compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
+This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface
+is reported to be big endian in this default driver mode.
+
+Examples:
+
+ * playing a S24_3BE encoded raw file to the Ao port::
+
+ % aplay -D hw:1,0 -c2 -t raw -r48000 -fS24_3BE test.raw
+
+ * recording a S24_3BE encoded raw file from the Ai port::
+
+ % arecord -D hw:1,1 -c2 -t raw -r48000 -fS24_3BE test.raw
+
+ * playing a S16_BE encoded raw file to the Do port::
+
+ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
+
+ * playing an ac3 sample file to the Do port::
+
+ % aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
+
+If you're happy with the default Alsa driver mode and don't experience any
+issue with this mode, then you can skip the following chapter.
+
+Advanced module setup
+---------------------
+
+Due to the hardware constraints described above, the device initialization made
+by the Alsa driver in default mode may result in a corrupted state of the
+device. For instance, a particularly annoying issue is that the sound captured
+from the Ai interface sounds distorted (as if boosted with an excessive high
+volume gain).
+
+For people having this problem, the snd-usb-audio module has a new module
+parameter called ``device_setup`` (this parameter was introduced in kernel
+release 2.6.17)
+
+Initializing the working mode of the Audiophile USB
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As far as the Audiophile USB device is concerned, this value let the user
+specify:
+
+ * the sample depth
+ * the sample rate
+ * whether the Di port is used or not
+
+When initialized with ``device_setup=0x00``, the snd-usb-audio module has
+the same behaviour as when the parameter is omitted (see paragraph "Default
+Alsa driver mode" above)
+
+Others modes are described in the following subsections.
+
+16-bit modes
+~~~~~~~~~~~~
+
+The two supported modes are:
+
+ * ``device_setup=0x01``
+
+ - 16bits 48kHz mode with Di disabled
+ - Ai,Ao,Do can be used at the same time
+ - hw:1,0 is not available in capture mode
+ - hw:1,2 is not available
+
+ * ``device_setup=0x11``
+
+ - 16bits 48kHz mode with Di enabled
+ - Ai,Ao,Di,Do can be used at the same time
+ - hw:1,0 is available in capture mode
+ - hw:1,2 is not available
+
+In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
+the devices where reported to be Big-Endian when in fact they were Little-Endian
+so that playing a file was a matter of using:
+::
+
+ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
+
+where "test_S16_LE.raw" was in fact a little-endian sample file.
+
+Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
+these modes) a fix has been committed (expected in kernel 2.6.23) and
+Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
+using:
+::
+
+ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
+
+
+24-bit modes
+~~~~~~~~~~~~
+
+The three supported modes are:
+
+ * ``device_setup=0x09``
+
+ - 24bits 48kHz mode with Di disabled
+ - Ai,Ao,Do can be used at the same time
+ - hw:1,0 is not available in capture mode
+ - hw:1,2 is not available
+
+ * ``device_setup=0x19``
+
+ - 24bits 48kHz mode with Di enabled
+ - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
+ - hw:1,0 is available in capture mode and an active digital source must be
+ connected to Di
+ - hw:1,2 is not available
+
+ * ``device_setup=0x0D`` or ``0x10``
+
+ - 24bits 96kHz mode
+ - Di is enabled by default for this mode but does not need to be connected
+ to an active source
+ - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
+ - hw:1,0 is available in captured mode
+ - hw:1,2 is not available
+
+In these modes the device is only Big-Endian compliant (see "Default Alsa driver
+mode" above for an aplay command example)
+
+AC3 w/ DTS passthru mode
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Thanks to Hakan Lennestal, I now have a report saying that this mode works.
+
+ * ``device_setup=0x03``
+
+ - 16bits 48kHz mode with only the Do port enabled
+ - AC3 with DTS passthru
+ - Caution with this setup the Do port is mapped to the pcm device hw:1,0
+
+The command line used to playback the AC3/DTS encoded .wav-files in this mode:
+::
+
+ % aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
+
+How to use the ``device_setup`` parameter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The parameter can be given:
+
+ * By manually probing the device (as root):::
+
+ # modprobe -r snd-usb-audio
+ # modprobe snd-usb-audio index=1 device_setup=0x09
+
+ * Or while configuring the modules options in your modules configuration file
+ (typically a .conf file in /etc/modprobe.d/ directory:::
+
+ alias snd-card-1 snd-usb-audio
+ options snd-usb-audio index=1 device_setup=0x09
+
+CAUTION when initializing the device
+-------------------------------------
+
+ * Correct initialization on the device requires that device_setup is given to
+ the module BEFORE the device is turned on. So, if you use the "manual probing"
+ method described above, take care to power-on the device AFTER this initialization.
+
+ * Failing to respect this will lead to a misconfiguration of the device. In this case
+ turn off the device, unprobe the snd-usb-audio module, then probe it again with
+ correct device_setup parameter and then (and only then) turn on the device again.
+
+ * If you've correctly initialized the device in a valid mode and then want to switch
+ to another mode (possibly with another sample-depth), please use also the following
+ procedure:
+
+ - first turn off the device
+ - de-register the snd-usb-audio module (modprobe -r)
+ - change the device_setup parameter by changing the device_setup
+ option in ``/etc/modprobe.d/*.conf``
+ - turn on the device
+
+ * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
+ be enough to ensure the 'stability' of the device initialization.
+
+Technical details for hackers
+-----------------------------
+
+This section is for hackers, wanting to understand details about the device
+internals and how Alsa supports it.
+
+Audiophile USB's ``device_setup`` structure
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to understand the device_setup magic numbers for the Audiophile
+USB, you need some very basic understanding of binary computation. However,
+this is not required to use the parameter and you may skip this section.
+
+The device_setup is one byte long and its structure is the following:
+::
+
+ +---+---+---+---+---+---+---+---+
+ | b7| b6| b5| b4| b3| b2| b1| b0|
+ +---+---+---+---+---+---+---+---+
+ | 0 | 0 | 0 | Di|24B|96K|DTS|SET|
+ +---+---+---+---+---+---+---+---+
+
+Where:
+
+ * b0 is the ``SET`` bit
+
+ - it MUST be set if device_setup is initialized
+
+ * b1 is the ``DTS`` bit
+
+ - it is set only for Digital output with DTS/AC3
+ - this setup is not tested
+
+ * b2 is the Rate selection flag
+
+ - When set to ``1`` the rate range is 48.1-96kHz
+ - Otherwise the sample rate range is 8-48kHz
+
+ * b3 is the bit depth selection flag
+
+ - When set to ``1`` samples are 24bits long
+ - Otherwise they are 16bits long
+ - Note that b2 implies b3 as the 96kHz mode is only supported for 24 bits
+ samples
+
+ * b4 is the Digital input flag
+
+ - When set to ``1`` the device assumes that an active digital source is
+ connected
+ - You shouldn't enable Di if no source is seen on the port (this leads to
+ synchronization issues)
+ - b4 is implied by b2 (since only one port is enabled at a time no synch
+ error can occur)
+
+ * b5 to b7 are reserved for future uses, and must be set to ``0``
+
+ - might become Ao, Do, Ai, for b7, b6, b4 respectively
+
+Caution:
+
+ * there is no check on the value you will give to device_setup
+
+ - for instance choosing 0x05 (16bits 96kHz) will fail back to 0x09 since
+ b2 implies b3. But _there_will_be_no_warning_ in /var/log/messages
+
+ * Hardware constraints due to the USB bus limitation aren't checked
+
+ - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
+ only be able to use one at the same time
+
+USB implementation details for this device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You may safely skip this section if you're not interested in driver
+hacking.
+
+This section describes some internal aspects of the device and summarizes the
+data I got by usb-snooping the windows and Linux drivers.
+
+The M-Audio Audiophile USB has 7 USB Interfaces:
+a "USB interface":
+
+ * USB Interface nb.0
+ * USB Interface nb.1
+
+ - Audio Control function
+
+ * USB Interface nb.2
+
+ - Analog Output
+
+ * USB Interface nb.3
+
+ - Digital Output
+
+ * USB Interface nb.4
+
+ - Analog Input
+
+ * USB Interface nb.5
+
+ - Digital Input
+
+ * USB Interface nb.6
+
+ - MIDI interface compliant with the MIDIMAN quirk
+
+Each interface has 5 altsettings (AltSet 1,2,3,4,5) except:
+
+ * Interface 3 (Digital Out) has an extra Alset nb.6
+ * Interface 5 (Digital In) does not have Alset nb.3 and 5
+
+Here is a short description of the AltSettings capabilities:
+
+* AltSettings 1 corresponds to
+
+ - 24-bit depth, 48.1-96kHz sample mode
+ - Adaptive playback (Ao and Do), Synch capture (Ai), or Asynch capture (Di)
+
+* AltSettings 2 corresponds to
+
+ - 24-bit depth, 8-48kHz sample mode
+ - Asynch capture and playback (Ao,Ai,Do,Di)
+
+* AltSettings 3 corresponds to
+
+ - 24-bit depth, 8-48kHz sample mode
+ - Synch capture (Ai) and Adaptive playback (Ao,Do)
+
+* AltSettings 4 corresponds to
+
+ - 16-bit depth, 8-48kHz sample mode
+ - Asynch capture and playback (Ao,Ai,Do,Di)
+
+* AltSettings 5 corresponds to
+
+ - 16-bit depth, 8-48kHz sample mode
+ - Synch capture (Ai) and Adaptive playback (Ao,Do)
+
+* AltSettings 6 corresponds to
+
+ - 16-bit depth, 8-48kHz sample mode
+ - Synch playback (Do), audio format type III IEC1937_AC-3
+
+In order to ensure a correct initialization of the device, the driver
+*must* *know* how the device will be used:
+
+ * if DTS is chosen, only Interface 2 with AltSet nb.6 must be
+ registered
+ * if 96KHz only AltSets nb.1 of each interface must be selected
+ * if samples are using 24bits/48KHz then AltSet 2 must me used if
+ Digital input is connected, and only AltSet nb.3 if Digital input
+ is not connected
+ * if samples are using 16bits/48KHz then AltSet 4 must me used if
+ Digital input is connected, and only AltSet nb.5 if Digital input
+ is not connected
+
+When device_setup is given as a parameter to the snd-usb-audio module, the
+parse_audio_endpoints function uses a quirk called
+``audiophile_skip_setting_quirk`` in order to prevent AltSettings not
+corresponding to device_setup from being registered in the driver.
+
+Audiophile USB and Jack support
+===============================
+
+This section deals with support of the Audiophile USB device in Jack.
+
+There are 2 main potential issues when using Jackd with the device:
+
+* support for Big-Endian devices in 24-bit modes
+* support for 4-in / 4-out channels
+
+Direct support in Jackd
+-----------------------
+
+Jack supports big endian devices only in recent versions (thanks to
+Andreas Steinmetz for his first big-endian patch). I can't remember
+exactly when this support was released into jackd, let's just say that
+with jackd version 0.103.0 it's almost ok (just a small bug is affecting
+16bits Big-Endian devices, but since you've read carefully the above
+paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices
+are now Little Endians ;-) ).
+
+You can run jackd with the following command for playback with Ao and
+record with Ai:
+::
+
+ % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
+
+Using Alsa plughw
+-----------------
+
+If you don't have a recent Jackd installed, you can downgrade to using
+the Alsa ``plug`` converter.
+
+For instance here is one way to run Jack with 2 playback channels on Ao and 2
+capture channels from Ai:
+::
+
+ % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
+
+However you may see the following warning message:
+ You appear to be using the ALSA software "plug" layer, probably a result of
+ using the "default" ALSA device. This is less efficient than it could be.
+ Consider using a hardware device instead rather than using the plug layer.
+
+Getting 2 input and/or output interfaces in Jack
+------------------------------------------------
+
+As you can see, starting the Jack server this way will only enable 1 stereo
+input (Di or Ai) and 1 stereo output (Ao or Do).
+
+This is due to the following restrictions:
+
+* Jack can only open one capture device and one playback device at a time
+* The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
+ (and optionally hw:1,2)
+
+If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
+combine the Alsa devices into one logical "complex" device.
+
+If you want to give it a try, I recommend reading the information from
+this page: http://www.sound-man.co.uk/linuxaudio/ice1712multi.html
+It is related to another device (ice1712) but can be adapted to suit
+the Audiophile USB.
+
+Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
+
+* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
+* (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
+* define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
+ file
+* start jackd with this device
+
+I had no success in testing this for now, if you have any success with this kind
+of setup, please drop me an email.
--- /dev/null
+=================
+ALSA BT87x Driver
+=================
+
+Intro
+=====
+
+You might have noticed that the bt878 grabber cards have actually
+*two* PCI functions:
+::
+
+ $ lspci
+ [ ... ]
+ 00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02)
+ 00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02)
+ [ ... ]
+
+The first does video, it is backward compatible to the bt848. The second
+does audio. snd-bt87x is a driver for the second function. It's a sound
+driver which can be used for recording sound (and *only* recording, no
+playback). As most TV cards come with a short cable which can be plugged
+into your sound card's line-in you probably don't need this driver if all
+you want to do is just watching TV...
+
+Some cards do not bother to connect anything to the audio input pins of
+the chip, and some other cards use the audio function to transport MPEG
+video data, so it's quite possible that audio recording may not work
+with your card.
+
+
+Driver Status
+=============
+
+The driver is now stable. However, it doesn't know about many TV cards,
+and it refuses to load for cards it doesn't know.
+
+If the driver complains ("Unknown TV card found, the audio driver will
+not load"), you can specify the ``load_all=1`` option to force the driver to
+try to use the audio capture function of your card. If the frequency of
+recorded data is not right, try to specify the ``digital_rate`` option with
+other values than the default 32000 (often it's 44100 or 64000).
+
+If you have an unknown card, please mail the ID and board name to
+<alsa-devel@alsa-project.org>, regardless of whether audio capture works
+or not, so that future versions of this driver know about your card.
+
+
+Audio modes
+===========
+
+The chip knows two different modes (digital/analog). snd-bt87x
+registers two PCM devices, one for each mode. They cannot be used at
+the same time.
+
+
+Digital audio mode
+==================
+
+The first device (hw:X,0) gives you 16 bit stereo sound. The sample
+rate depends on the external source which feeds the Bt87x with digital
+sound via I2S interface.
+
+
+Analog audio mode (A/D)
+=======================
+
+The second device (hw:X,1) gives you 8 or 16 bit mono sound. Supported
+sample rates are between 119466 and 448000 Hz (yes, these numbers are
+that high). If you've set the CONFIG_SND_BT87X_OVERCLOCK option, the
+maximum sample rate is 1792000 Hz, but audio data becomes unusable
+beyond 896000 Hz on my card.
+
+The chip has three analog inputs. Consequently you'll get a mixer
+device to control these.
+
+
+Have fun,
+
+ Clemens
+
+
+Written by Clemens Ladisch <clemens@ladisch.de>
+big parts copied from btaudio.txt by Gerd Knorr <kraxel@bytesex.org>
--- /dev/null
+=================================================
+Brief Notes on C-Media 8338/8738/8768/8770 Driver
+=================================================
+
+Takashi Iwai <tiwai@suse.de>
+
+
+Front/Rear Multi-channel Playback
+---------------------------------
+
+CM8x38 chip can use ADC as the second DAC so that two different stereo
+channels can be used for front/rear playbacks. Since there are two
+DACs, both streams are handled independently unlike the 4/6ch multi-
+channel playbacks in the section below.
+
+As default, ALSA driver assigns the first PCM device (i.e. hw:0,0 for
+card#0) for front and 4/6ch playbacks, while the second PCM device
+(hw:0,1) is assigned to the second DAC for rear playback.
+
+There are slight differences between the two DACs:
+
+- The first DAC supports U8 and S16LE formats, while the second DAC
+ supports only S16LE.
+- The second DAC supports only two channel stereo.
+
+Please note that the CM8x38 DAC doesn't support continuous playback
+rate but only fixed rates: 5512, 8000, 11025, 16000, 22050, 32000,
+44100 and 48000 Hz.
+
+The rear output can be heard only when "Four Channel Mode" switch is
+disabled. Otherwise no signal will be routed to the rear speakers.
+As default it's turned on.
+
+.. WARNING::
+ When "Four Channel Mode" switch is off, the output from rear speakers
+ will be FULL VOLUME regardless of Master and PCM volumes [#]_.
+ This might damage your audio equipment. Please disconnect speakers
+ before your turn off this switch.
+
+
+.. [#]
+ Well.. I once got the output with correct volume (i.e. same with the
+ front one) and was so excited. It was even with "Four Channel" bit
+ on and "double DAC" mode. Actually I could hear separate 4 channels
+ from front and rear speakers! But.. after reboot, all was gone.
+ It's a very pity that I didn't save the register dump at that
+ time.. Maybe there is an unknown register to achieve this...
+
+If your card has an extra output jack for the rear output, the rear
+playback should be routed there as default. If not, there is a
+control switch in the driver "Line-In As Rear", which you can change
+via alsamixer or somewhat else. When this switch is on, line-in jack
+is used as rear output.
+
+There are two more controls regarding to the rear output.
+The "Exchange DAC" switch is used to exchange front and rear playback
+routes, i.e. the 2nd DAC is output from front output.
+
+
+4/6 Multi-Channel Playback
+--------------------------
+
+The recent CM8738 chips support for the 4/6 multi-channel playback
+function. This is useful especially for AC3 decoding.
+
+When the multi-channel is supported, the driver name has a suffix
+"-MC" such like "CMI8738-MC6". You can check this name from
+/proc/asound/cards.
+
+When the 4/6-ch output is enabled, the second DAC accepts up to 6 (or
+4) channels. While the dual DAC supports two different rates or
+formats, the 4/6-ch playback supports only the same condition for all
+channels. Since the multi-channel playback mode uses both DACs, you
+cannot operate with full-duplex.
+
+The 4.0 and 5.1 modes are defined as the pcm "surround40" and "surround51"
+in alsa-lib. For example, you can play a WAV file with 6 channels like
+::
+
+ % aplay -Dsurround51 sixchannels.wav
+
+For programming the 4/6 channel playback, you need to specify the PCM
+channels as you like and set the format S16LE. For example, for playback
+with 4 channels,
+::
+
+ snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
+ // or mmap if you like
+ snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_channels(pcm, hw, 4);
+
+and use the interleaved 4 channel data.
+
+There are some control switches affecting to the speaker connections:
+
+Line-In Mode
+ an enum control to change the behavior of line-in
+ jack. Either "Line-In", "Rear Output" or "Bass Output" can
+ be selected. The last item is available only with model 039
+ or newer.
+ When "Rear Output" is chosen, the surround channels 3 and 4
+ are output to line-in jack.
+Mic-In Mode
+ an enum control to change the behavior of mic-in
+ jack. Either "Mic-In" or "Center/LFE Output" can be
+ selected.
+ When "Center/LFE Output" is chosen, the center and bass
+ channels (channels 5 and 6) are output to mic-in jack.
+
+Digital I/O
+-----------
+
+The CM8x38 provides the excellent SPDIF capability with very cheap
+price (yes, that's the reason I bought the card :)
+
+The SPDIF playback and capture are done via the third PCM device
+(hw:0,2). Usually this is assigned to the PCM device "spdif".
+The available rates are 44100 and 48000 Hz.
+For playback with aplay, you can run like below:
+::
+
+ % aplay -Dhw:0,2 foo.wav
+
+or
+
+::
+
+ % aplay -Dspdif foo.wav
+
+24bit format is also supported experimentally.
+
+The playback and capture over SPDIF use normal DAC and ADC,
+respectively, so you cannot playback both analog and digital streams
+simultaneously.
+
+To enable SPDIF output, you need to turn on "IEC958 Output Switch"
+control via mixer or alsactl ("IEC958" is the official name of
+so-called S/PDIF). Then you'll see the red light on from the card so
+you know that's working obviously :)
+The SPDIF input is always enabled, so you can hear SPDIF input data
+from line-out with "IEC958 In Monitor" switch at any time (see
+below).
+
+You can play via SPDIF even with the first device (hw:0,0),
+but SPDIF is enabled only when the proper format (S16LE), sample rate
+(441100 or 48000) and channels (2) are used. Otherwise it's turned
+off. (Also don't forget to turn on "IEC958 Output Switch", too.)
+
+
+Additionally there are relevant control switches:
+
+IEC958 Mix Analog
+ Mix analog PCM playback and FM-OPL/3 streams and
+ output through SPDIF. This switch appears only on old chip
+ models (CM8738 033 and 037).
+
+ Note: without this control you can output PCM to SPDIF.
+ This is "mixing" of streams, so e.g. it's not for AC3 output
+ (see the next section).
+
+IEC958 In Select
+ Select SPDIF input, the internal CD-in (false)
+ and the external input (true).
+
+IEC958 Loop
+ SPDIF input data is loop back into SPDIF
+ output (aka bypass)
+
+IEC958 Copyright
+ Set the copyright bit.
+
+IEC958 5V
+ Select 0.5V (coax) or 5V (optical) interface.
+ On some cards this doesn't work and you need to change the
+ configuration with hardware dip-switch.
+
+IEC958 In Monitor
+ SPDIF input is routed to DAC.
+
+IEC958 In Phase Inverse
+ Set SPDIF input format as inverse.
+ [FIXME: this doesn't work on all chips..]
+
+IEC958 In Valid
+ Set input validity flag detection.
+
+Note: When "PCM Playback Switch" is on, you'll hear the digital output
+stream through analog line-out.
+
+
+The AC3 (RAW DIGITAL) OUTPUT
+----------------------------
+
+The driver supports raw digital (typically AC3) i/o over SPDIF. This
+can be toggled via IEC958 playback control, but usually you need to
+access it via alsa-lib. See alsa-lib documents for more details.
+
+On the raw digital mode, the "PCM Playback Switch" is automatically
+turned off so that non-audio data is heard from the analog line-out.
+Similarly the following switches are off: "IEC958 Mix Analog" and
+"IEC958 Loop". The switches are resumed after closing the SPDIF PCM
+device automatically to the previous state.
+
+On the model 033, AC3 is implemented by the software conversion in
+the alsa-lib. If you need to bypass the software conversion of IEC958
+subframes, pass the "soft_ac3=0" module option. This doesn't matter
+on the newer models.
+
+
+ANALOG MIXER INTERFACE
+----------------------
+
+The mixer interface on CM8x38 is similar to SB16.
+There are Master, PCM, Synth, CD, Line, Mic and PC Speaker playback
+volumes. Synth, CD, Line and Mic have playback and capture switches,
+too, as well as SB16.
+
+In addition to the standard SB mixer, CM8x38 provides more functions.
+- PCM playback switch
+- PCM capture switch (to capture the data sent to DAC)
+- Mic Boost switch
+- Mic capture volume
+- Aux playback volume/switch and capture switch
+- 3D control switch
+
+
+MIDI CONTROLLER
+---------------
+
+With CMI8338 chips, the MPU401-UART interface is disabled as default.
+You need to set the module option "mpu_port" to a valid I/O port address
+to enable MIDI support. Valid I/O ports are 0x300, 0x310, 0x320 and
+0x330. Choose a value that doesn't conflict with other cards.
+
+With CMI8738 and newer chips, the MIDI interface is enabled by default
+and the driver automatically chooses a port address.
+
+There is *no* hardware wavetable function on this chip (except for
+OPL3 synth below).
+What's said as MIDI synth on Windows is a software synthesizer
+emulation. On Linux use TiMidity or other softsynth program for
+playing MIDI music.
+
+
+FM OPL/3 Synth
+--------------
+
+The FM OPL/3 is also enabled as default only for the first card.
+Set "fm_port" module option for more cards.
+
+The output quality of FM OPL/3 is, however, very weird.
+I don't know why..
+
+CMI8768 and newer chips do not have the FM synth.
+
+
+Joystick and Modem
+------------------
+
+The legacy joystick is supported. To enable the joystick support, pass
+joystick_port=1 module option. The value 1 means the auto-detection.
+If the auto-detection fails, try to pass the exact I/O address.
+
+The modem is enabled dynamically via a card control switch "Modem".
+
+
+Debugging Information
+---------------------
+
+The registers are shown in /proc/asound/cardX/cmipci. If you have any
+problem (especially unexpected behavior of mixer), please attach the
+output of this proc file together with the bug report.
--- /dev/null
+=================================================================
+Low latency, multichannel audio with JACK and the emu10k1/emu10k2
+=================================================================
+
+This document is a guide to using the emu10k1 based devices with JACK for low
+latency, multichannel recording functionality. All of my recent work to allow
+Linux users to use the full capabilities of their hardware has been inspired
+by the kX Project. Without their work I never would have discovered the true
+power of this hardware.
+
+ http://www.kxproject.com
+ - Lee Revell, 2005.03.30
+
+
+Until recently, emu10k1 users on Linux did not have access to the same low
+latency, multichannel features offered by the "kX ASIO" feature of their
+Windows driver. As of ALSA 1.0.9 this is no more!
+
+For those unfamiliar with kX ASIO, this consists of 16 capture and 16 playback
+channels. With a post 2.6.9 Linux kernel, latencies down to 64 (1.33 ms) or
+even 32 (0.66ms) frames should work well.
+
+The configuration is slightly more involved than on Windows, as you have to
+select the correct device for JACK to use. Actually, for qjackctl users it's
+fairly self explanatory - select Duplex, then for capture and playback select
+the multichannel devices, set the in and out channels to 16, and the sample
+rate to 48000Hz. The command line looks like this:
+::
+
+ /usr/local/bin/jackd -R -dalsa -r48000 -p64 -n2 -D -Chw:0,2 -Phw:0,3 -S
+
+This will give you 16 input ports and 16 output ports.
+
+The 16 output ports map onto the 16 FX buses (or the first 16 of 64, for the
+Audigy). The mapping from FX bus to physical output is described in
+sb-live-mixer.rst (or audigy-mixer.rst).
+
+The 16 input ports are connected to the 16 physical inputs. Contrary to
+popular belief, all emu10k1 cards are multichannel cards. Which of these
+input channels have physical inputs connected to them depends on the card
+model. Trial and error is highly recommended; the pinout diagrams
+for the card have been reverse engineered by some enterprising kX users and are
+available on the internet. Meterbridge is helpful here, and the kX forums are
+packed with useful information.
+
+Each input port will either correspond to a digital (SPDIF) input, an analog
+input, or nothing. The one exception is the SBLive! 5.1. On these devices,
+the second and third input ports are wired to the center/LFE output. You will
+still see 16 capture channels, but only 14 are available for recording inputs.
+
+This chart, borrowed from kxfxlib/da_asio51.cpp, describes the mapping of JACK
+ports to FXBUS2 (multitrack recording input) and EXTOUT (physical output)
+channels.
+
+JACK (& ASIO) mappings on 10k1 5.1 SBLive cards:
+
+============== ======== ============
+JACK Epilog FXBUS2(nr)
+============== ======== ============
+capture_1 asio14 FXBUS2(0xe)
+capture_2 asio15 FXBUS2(0xf)
+capture_3 asio0 FXBUS2(0x0)
+~capture_4 Center EXTOUT(0x11) // mapped to by Center
+~capture_5 LFE EXTOUT(0x12) // mapped to by LFE
+capture_6 asio3 FXBUS2(0x3)
+capture_7 asio4 FXBUS2(0x4)
+capture_8 asio5 FXBUS2(0x5)
+capture_9 asio6 FXBUS2(0x6)
+capture_10 asio7 FXBUS2(0x7)
+capture_11 asio8 FXBUS2(0x8)
+capture_12 asio9 FXBUS2(0x9)
+capture_13 asio10 FXBUS2(0xa)
+capture_14 asio11 FXBUS2(0xb)
+capture_15 asio12 FXBUS2(0xc)
+capture_16 asio13 FXBUS2(0xd)
+============== ======== ============
+
+TODO: describe use of ld10k1/qlo10k1 in conjunction with JACK
--- /dev/null
+=======================================
+Software Interface ALSA-DSP MADI Driver
+=======================================
+
+(translated from German, so no good English ;-),
+
+2004 - winfried ritsch
+
+
+Full functionality has been added to the driver. Since some of
+the Controls and startup-options are ALSA-Standard and only the
+special Controls are described and discussed below.
+
+
+Hardware functionality
+======================
+
+Audio transmission
+------------------
+
+* number of channels -- depends on transmission mode
+
+ The number of channels chosen is from 1..Nmax. The reason to
+ use for a lower number of channels is only resource allocation,
+ since unused DMA channels are disabled and less memory is
+ allocated. So also the throughput of the PCI system can be
+ scaled. (Only important for low performance boards).
+
+* Single Speed -- 1..64 channels
+
+.. note::
+ (Note: Choosing the 56channel mode for transmission or as
+ receiver, only 56 are transmitted/received over the MADI, but
+ all 64 channels are available for the mixer, so channel count
+ for the driver)
+
+* Double Speed -- 1..32 channels
+
+.. note::
+ Note: Choosing the 56-channel mode for
+ transmission/receive-mode , only 28 are transmitted/received
+ over the MADI, but all 32 channels are available for the mixer,
+ so channel count for the driver
+
+
+* Quad Speed -- 1..16 channels
+
+.. note::
+ Choosing the 56-channel mode for
+ transmission/receive-mode , only 14 are transmitted/received
+ over the MADI, but all 16 channels are available for the mixer,
+ so channel count for the driver
+
+* Format -- signed 32 Bit Little Endian (SNDRV_PCM_FMTBIT_S32_LE)
+
+* Sample Rates --
+
+ Single Speed -- 32000, 44100, 48000
+
+ Double Speed -- 64000, 88200, 96000 (untested)
+
+ Quad Speed -- 128000, 176400, 192000 (untested)
+
+* access-mode -- MMAP (memory mapped), Not interleaved (PCM_NON-INTERLEAVED)
+
+* buffer-sizes -- 64,128,256,512,1024,2048,8192 Samples
+
+* fragments -- 2
+
+* Hardware-pointer -- 2 Modi
+
+
+ The Card supports the readout of the actual Buffer-pointer,
+ where DMA reads/writes. Since of the bulk mode of PCI it is only
+ 64 Byte accurate. SO it is not really usable for the
+ ALSA-mid-level functions (here the buffer-ID gives a better
+ result), but if MMAP is used by the application. Therefore it
+ can be configured at load-time with the parameter
+ precise-pointer.
+
+
+.. hint::
+ (Hint: Experimenting I found that the pointer is maximum 64 to
+ large never to small. So if you subtract 64 you always have a
+ safe pointer for writing, which is used on this mode inside
+ ALSA. In theory now you can get now a latency as low as 16
+ Samples, which is a quarter of the interrupt possibilities.)
+
+ * Precise Pointer -- off
+ interrupt used for pointer-calculation
+
+ * Precise Pointer -- on
+ hardware pointer used.
+
+Controller
+----------
+
+Since DSP-MADI-Mixer has 8152 Fader, it does not make sense to
+use the standard mixer-controls, since this would break most of
+(especially graphic) ALSA-Mixer GUIs. So Mixer control has be
+provided by a 2-dimensional controller using the
+hwdep-interface.
+
+Also all 128+256 Peak and RMS-Meter can be accessed via the
+hwdep-interface. Since it could be a performance problem always
+copying and converting Peak and RMS-Levels even if you just need
+one, I decided to export the hardware structure, so that of
+needed some driver-guru can implement a memory-mapping of mixer
+or peak-meters over ioctl, or also to do only copying and no
+conversion. A test-application shows the usage of the controller.
+
+* Latency Controls --- not implemented !!!
+
+.. note::
+ Note: Within the windows-driver the latency is accessible of a
+ control-panel, but buffer-sizes are controlled with ALSA from
+ hwparams-calls and should not be changed in run-state, I did not
+ implement it here.
+
+
+* System Clock -- suspended !!!!
+
+ * Name -- "System Clock Mode"
+
+ * Access -- Read Write
+
+ * Values -- "Master" "Slave"
+
+.. note::
+ !!!! This is a hardware-function but is in conflict with the
+ Clock-source controller, which is a kind of ALSA-standard. I
+ makes sense to set the card to a special mode (master at some
+ frequency or slave), since even not using an Audio-application
+ a studio should have working synchronisations setup. So use
+ Clock-source-controller instead !!!!
+
+* Clock Source
+
+ * Name -- "Sample Clock Source"
+
+ * Access -- Read Write
+
+ * Values -- "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz",
+ "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz",
+ "Internal 96.0 kHz"
+
+ Choose between Master at a specific Frequency and so also the
+ Speed-mode or Slave (Autosync). Also see "Preferred Sync Ref"
+
+.. warning::
+ !!!! This is no pure hardware function but was implemented by
+ ALSA by some ALSA-drivers before, so I use it also. !!!
+
+
+* Preferred Sync Ref
+
+ * Name -- "Preferred Sync Reference"
+
+ * Access -- Read Write
+
+ * Values -- "Word" "MADI"
+
+
+ Within the Auto-sync-Mode the preferred Sync Source can be
+ chosen. If it is not available another is used if possible.
+
+.. note::
+ Note: Since MADI has a much higher bit-rate than word-clock, the
+ card should synchronise better in MADI Mode. But since the
+ RME-PLL is very good, there are almost no problems with
+ word-clock too. I never found a difference.
+
+
+* TX 64 channel
+
+ * Name -- "TX 64 channels mode"
+
+ * Access -- Read Write
+
+ * Values -- 0 1
+
+ Using 64-channel-modus (1) or 56-channel-modus for
+ MADI-transmission (0).
+
+
+.. note::
+ Note: This control is for output only. Input-mode is detected
+ automatically from hardware sending MADI.
+
+
+* Clear TMS
+
+ * Name -- "Clear Track Marker"
+
+ * Access -- Read Write
+
+ * Values -- 0 1
+
+
+ Don't use to lower 5 Audio-bits on AES as additional Bits.
+
+
+* Safe Mode oder Auto Input
+
+ * Name -- "Safe Mode"
+
+ * Access -- Read Write
+
+ * Values -- 0 1 (default on)
+
+ If on (1), then if either the optical or coaxial connection
+ has a failure, there is a takeover to the working one, with no
+ sample failure. Its only useful if you use the second as a
+ backup connection.
+
+* Input
+
+ * Name -- "Input Select"
+
+ * Access -- Read Write
+
+ * Values -- optical coaxial
+
+
+ Choosing the Input, optical or coaxial. If Safe-mode is active,
+ this is the preferred Input.
+
+Mixer
+-----
+
+* Mixer
+
+ * Name -- "Mixer"
+
+ * Access -- Read Write
+
+ * Values - <channel-number 0-127> <Value 0-65535>
+
+
+ Here as a first value the channel-index is taken to get/set the
+ corresponding mixer channel, where 0-63 are the input to output
+ fader and 64-127 the playback to outputs fader. Value 0
+ is channel muted 0 and 32768 an amplification of 1.
+
+* Chn 1-64
+
+ fast mixer for the ALSA-mixer utils. The diagonal of the
+ mixer-matrix is implemented from playback to output.
+
+
+* Line Out
+
+ * Name -- "Line Out"
+
+ * Access -- Read Write
+
+ * Values -- 0 1
+
+ Switching on and off the analog out, which has nothing to do
+ with mixing or routing. the analog outs reflects channel 63,64.
+
+
+Information (only read access)
+------------------------------
+
+* Sample Rate
+
+ * Name -- "System Sample Rate"
+
+ * Access -- Read-only
+
+ getting the sample rate.
+
+
+* External Rate measured
+
+ * Name -- "External Rate"
+
+ * Access -- Read only
+
+
+ Should be "Autosync Rate", but Name used is
+ ALSA-Scheme. External Sample frequency liked used on Autosync is
+ reported.
+
+
+* MADI Sync Status
+
+ * Name -- "MADI Sync Lock Status"
+
+ * Access -- Read
+
+ * Values -- 0,1,2
+
+ MADI-Input is 0=Unlocked, 1=Locked, or 2=Synced.
+
+
+* Word Clock Sync Status
+
+ * Name -- "Word Clock Lock Status"
+
+ * Access -- Read
+
+ * Values -- 0,1,2
+
+ Word Clock Input is 0=Unlocked, 1=Locked, or 2=Synced.
+
+* AutoSync
+
+ * Name -- "AutoSync Reference"
+
+ * Access -- Read
+
+ * Values -- "WordClock", "MADI", "None"
+
+ Sync-Reference is either "WordClock", "MADI" or none.
+
+* RX 64ch --- noch nicht implementiert
+
+ MADI-Receiver is in 64 channel mode oder 56 channel mode.
+
+
+* AB_inp --- not tested
+
+ Used input for Auto-Input.
+
+
+* actual Buffer Position --- not implemented
+
+ !!! this is a ALSA internal function, so no control is used !!!
+
+
+
+Calling Parameter
+=================
+
+* index int array (min = 1, max = 8)
+
+ Index value for RME HDSPM interface. card-index within ALSA
+
+ note: ALSA-standard
+
+* id string array (min = 1, max = 8)
+
+ ID string for RME HDSPM interface.
+
+ note: ALSA-standard
+
+* enable int array (min = 1, max = 8)
+
+ Enable/disable specific HDSPM sound-cards.
+
+ note: ALSA-standard
+
+* precise_ptr int array (min = 1, max = 8)
+
+ Enable precise pointer, or disable.
+
+.. note::
+ note: Use only when the application supports this (which is a special case).
+
+* line_outs_monitor int array (min = 1, max = 8)
+
+ Send playback streams to analog outs by default.
+
+.. note::
+ note: each playback channel is mixed to the same numbered output
+ channel (routed). This is against the ALSA-convention, where all
+ channels have to be muted on after loading the driver, but was
+ used before on other cards, so i historically use it again)
+
+
+
+* enable_monitor int array (min = 1, max = 8)
+
+ Enable Analog Out on Channel 63/64 by default.
+
+.. note ::
+ note: here the analog output is enabled (but not routed).
--- /dev/null
+================================================
+Imagination Technologies SPDIF Input Controllers
+================================================
+
+The Imagination Technologies SPDIF Input controller contains the following
+controls:
+
+* name='IEC958 Capture Mask',index=0
+
+This control returns a mask that shows which of the IEC958 status bits
+can be read using the 'IEC958 Capture Default' control.
+
+* name='IEC958 Capture Default',index=0
+
+This control returns the status bits contained within the SPDIF stream that
+is being received. The 'IEC958 Capture Mask' shows which bits can be read
+from this control.
+
+* name='SPDIF In Multi Frequency Acquire',index=0
+* name='SPDIF In Multi Frequency Acquire',index=1
+* name='SPDIF In Multi Frequency Acquire',index=2
+* name='SPDIF In Multi Frequency Acquire',index=3
+
+This control is used to attempt acquisition of up to four different sample
+rates. The active rate can be obtained by reading the 'SPDIF In Lock Frequency'
+control.
+
+When the value of this control is set to {0,0,0,0}, the rate given to hw_params
+will determine the single rate the block will capture. Else, the rate given to
+hw_params will be ignored, and the block will attempt capture for each of the
+four sample rates set here.
+
+If less than four rates are required, the same rate can be specified more than
+once
+
+* name='SPDIF In Lock Frequency',index=0
+
+This control returns the active capture rate, or 0 if a lock has not been
+acquired
+
+* name='SPDIF In Lock TRK',index=0
+
+This control is used to modify the locking/jitter rejection characteristics
+of the block. Larger values increase the locking range, but reduce jitter
+rejection.
+
+* name='SPDIF In Lock Acquire Threshold',index=0
+
+This control is used to change the threshold at which a lock is acquired.
+
+* name='SPDIF In Lock Release Threshold',index=0
+
+This control is used to change the threshold at which a lock is released.
--- /dev/null
+Card-Specific Information
+=========================
+
+.. toctree::
+ :maxdepth: 2
+
+ joystick
+ cmipci
+ sb-live-mixer
+ audigy-mixer
+ emu10k1-jack
+ via82xx-mixer
+ audiophile-usb
+ mixart
+ bt87x
+ maya44
+ hdspm
+ serial-u16550
+ img-spdif-in
--- /dev/null
+=======================================
+Analog Joystick Support on ALSA Drivers
+=======================================
+
+Oct. 14, 2003
+
+Takashi Iwai <tiwai@suse.de>
+
+General
+-------
+
+First of all, you need to enable GAMEPORT support on Linux kernel for
+using a joystick with the ALSA driver. For the details of gameport
+support, refer to Documentation/input/joystick.txt.
+
+The joystick support of ALSA drivers is different between ISA and PCI
+cards. In the case of ISA (PnP) cards, it's usually handled by the
+independent module (ns558). Meanwhile, the ALSA PCI drivers have the
+built-in gameport support. Hence, when the ALSA PCI driver is built
+in the kernel, CONFIG_GAMEPORT must be 'y', too. Otherwise, the
+gameport support on that card will be (silently) disabled.
+
+Some adapter modules probe the physical connection of the device at
+the load time. It'd be safer to plug in the joystick device before
+loading the module.
+
+
+PCI Cards
+---------
+
+For PCI cards, the joystick is enabled when the appropriate module
+option is specified. Some drivers don't need options, and the
+joystick support is always enabled. In the former ALSA version, there
+was a dynamic control API for the joystick activation. It was
+changed, however, to the static module options because of the system
+stability and the resource management.
+
+The following PCI drivers support the joystick natively.
+
+============== ============= ============================================
+Driver Module Option Available Values
+============== ============= ============================================
+als4000 joystick_port 0 = disable (default), 1 = auto-detect,
+ manual: any address (e.g. 0x200)
+au88x0 N/A N/A
+azf3328 joystick 0 = disable, 1 = enable, -1 = auto (default)
+ens1370 joystick 0 = disable (default), 1 = enable
+ens1371 joystick_port 0 = disable (default), 1 = auto-detect,
+ manual: 0x200, 0x208, 0x210, 0x218
+cmipci joystick_port 0 = disable (default), 1 = auto-detect,
+ manual: any address (e.g. 0x200)
+cs4281 N/A N/A
+cs46xx N/A N/A
+es1938 N/A N/A
+es1968 joystick 0 = disable (default), 1 = enable
+sonicvibes N/A N/A
+trident N/A N/A
+via82xx [#f1]_ joystick 0 = disable (default), 1 = enable
+ymfpci joystick_port 0 = disable (default), 1 = auto-detect,
+ manual: 0x201, 0x202, 0x204, 0x205 [#f2]_
+============== ============= ============================================
+
+.. [#f1] VIA686A/B only
+.. [#f2] With YMF744/754 chips, the port address can be chosen arbitrarily
+
+The following drivers don't support gameport natively, but there are
+additional modules. Load the corresponding module to add the gameport
+support.
+
+======= =================
+Driver Additional Module
+======= =================
+emu10k1 emu10k1-gp
+fm801 fm801-gp
+======= =================
+
+Note: the "pcigame" and "cs461x" modules are for the OSS drivers only.
+These ALSA drivers (cs46xx, trident and au88x0) have the
+built-in gameport support.
+
+As mentioned above, ALSA PCI drivers have the built-in gameport
+support, so you don't have to load ns558 module. Just load "joydev"
+and the appropriate adapter module (e.g. "analog").
+
+
+ISA Cards
+---------
+
+ALSA ISA drivers don't have the built-in gameport support.
+Instead, you need to load "ns558" module in addition to "joydev" and
+the adapter module (e.g. "analog").
--- /dev/null
+=================================
+Notes on Maya44 USB Audio Support
+=================================
+
+.. note::
+ The following is the original document of Rainer's patch that the
+ current maya44 code based on. Some contents might be obsoleted, but I
+ keep here as reference -- tiwai
+
+Feb 14, 2008
+
+Rainer Zimmermann <mail@lightshed.de>
+
+STATE OF DEVELOPMENT
+====================
+
+This driver is being developed on the initiative of Piotr Makowski (oponek@gmail.com) and financed by Lars Bergmann.
+Development is carried out by Rainer Zimmermann (mail@lightshed.de).
+
+ESI provided a sample Maya44 card for the development work.
+
+However, unfortunately it has turned out difficult to get detailed programming information, so I (Rainer Zimmermann) had to find out some card-specific information by experiment and conjecture. Some information (in particular, several GPIO bits) is still missing.
+
+This is the first testing version of the Maya44 driver released to the alsa-devel mailing list (Feb 5, 2008).
+
+
+The following functions work, as tested by Rainer Zimmermann and Piotr Makowski:
+
+- playback and capture at all sampling rates
+- input/output level
+- crossmixing
+- line/mic switch
+- phantom power switch
+- analogue monitor a.k.a bypass
+
+
+The following functions *should* work, but are not fully tested:
+
+- Channel 3+4 analogue - S/PDIF input switching
+- S/PDIF output
+- all inputs/outputs on the M/IO/DIO extension card
+- internal/external clock selection
+
+
+*In particular, we would appreciate testing of these functions by anyone who has access to an M/IO/DIO extension card.*
+
+
+Things that do not seem to work:
+
+- The level meters ("multi track") in 'alsamixer' do not seem to react to signals in (if this is a bug, it would probably be in the existing ICE1724 code).
+
+- Ardour 2.1 seems to work only via JACK, not using ALSA directly or via OSS. This still needs to be tracked down.
+
+
+DRIVER DETAILS
+==============
+
+the following files were added:
+
+* pci/ice1724/maya44.c - Maya44 specific code
+* pci/ice1724/maya44.h
+* pci/ice1724/ice1724.patch
+* pci/ice1724/ice1724.h.patch - PROPOSED patch to ice1724.h (see SAMPLING RATES)
+* i2c/other/wm8776.c - low-level access routines for Wolfson WM8776 codecs
+* include/wm8776.h
+
+
+Note that the wm8776.c code is meant to be card-independent and does not actually register the codec with the ALSA infrastructure.
+This is done in maya44.c, mainly because some of the WM8776 controls are used in Maya44-specific ways, and should be named appropriately.
+
+
+the following files were created in pci/ice1724, simply #including the corresponding file from the alsa-kernel tree:
+
+* wtm.h
+* vt1720_mobo.h
+* revo.h
+* prodigy192.h
+* pontis.h
+* phase.h
+* maya44.h
+* juli.h
+* aureon.h
+* amp.h
+* envy24ht.h
+* se.h
+* prodigy_hifi.h
+
+
+*I hope this is the correct way to do things.*
+
+
+SAMPLING RATES
+==============
+
+The Maya44 card (or more exactly, the Wolfson WM8776 codecs) allow a maximum sampling rate of 192 kHz for playback and 92 kHz for capture.
+
+As the ICE1724 chip only allows one global sampling rate, this is handled as follows:
+
+* setting the sampling rate on any open PCM device on the maya44 card will always set the *global* sampling rate for all playback and capture channels.
+
+* In the current state of the driver, setting rates of up to 192 kHz is permitted even for capture devices.
+
+*AVOID CAPTURING AT RATES ABOVE 96kHz*, even though it may appear to work. The codec cannot actually capture at such rates, meaning poor quality.
+
+
+I propose some additional code for limiting the sampling rate when setting on a capture pcm device. However because of the global sampling rate, this logic would be somewhat problematic.
+
+The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712).
+
+
+SOUND DEVICES
+=============
+
+PCM devices correspond to inputs/outputs as follows (assuming Maya44 is card #0):
+
+* hw:0,0 input - stereo, analog input 1+2
+* hw:0,0 output - stereo, analog output 1+2
+* hw:0,1 input - stereo, analog input 3+4 OR S/PDIF input
+* hw:0,1 output - stereo, analog output 3+4 (and SPDIF out)
+
+
+NAMING OF MIXER CONTROLS
+========================
+
+(for more information about the signal flow, please refer to the block diagram on p.24 of the ESI Maya44 manual, or in the ESI windows software).
+
+
+PCM
+ (digital) output level for channel 1+2
+PCM 1
+ same for channel 3+4
+
+Mic Phantom+48V
+ switch for +48V phantom power for electrostatic microphones on input 1/2.
+
+ Make sure this is not turned on while any other source is connected to input 1/2.
+ It might damage the source and/or the maya44 card.
+
+Mic/Line input
+ if switch is on, input jack 1/2 is microphone input (mono), otherwise line input (stereo).
+
+Bypass
+ analogue bypass from ADC input to output for channel 1+2. Same as "Monitor" in the windows driver.
+Bypass 1
+ same for channel 3+4.
+
+Crossmix
+ cross-mixer from channels 1+2 to channels 3+4
+Crossmix 1
+ cross-mixer from channels 3+4 to channels 1+2
+
+IEC958 Output
+ switch for S/PDIF output.
+
+ This is not supported by the ESI windows driver.
+ S/PDIF should output the same signal as channel 3+4. [untested!]
+
+
+Digitial output selectors
+ These switches allow a direct digital routing from the ADCs to the DACs.
+ Each switch determines where the digital input data to one of the DACs comes from.
+ They are not supported by the ESI windows driver.
+ For normal operation, they should all be set to "PCM out".
+
+H/W
+ Output source channel 1
+H/W 1
+ Output source channel 2
+H/W 2
+ Output source channel 3
+H/W 3
+ Output source channel 4
+
+H/W 4 ... H/W 9
+ unknown function, left in to enable testing.
+
+ Possibly some of these control S/PDIF output(s).
+ If these turn out to be unused, they will go away in later driver versions.
+
+Selectable values for each of the digital output selectors are:
+
+PCM out
+ DAC output of the corresponding channel (default setting)
+Input 1 ... Input 4
+ direct routing from ADC output of the selected input channel
+
--- /dev/null
+==============================================================
+Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards
+==============================================================
+
+Digigram <alsa@digigram.com>
+
+
+GENERAL
+=======
+
+The miXart8 is a multichannel audio processing and mixing soundcard
+that has 4 stereo audio inputs and 4 stereo audio outputs.
+The miXart8AES/EBU is the same with a add-on card that offers further
+4 digital stereo audio inputs and outputs.
+Furthermore the add-on card offers external clock synchronisation
+(AES/EBU, Word Clock, Time Code and Video Synchro)
+
+The mainboard has a PowerPC that offers onboard mpeg encoding and
+decoding, samplerate conversions and various effects.
+
+The driver don't work properly at all until the certain firmwares
+are loaded, i.e. no PCM nor mixer devices will appear.
+Use the mixartloader that can be found in the alsa-tools package.
+
+
+VERSION 0.1.0
+=============
+
+One miXart8 board will be represented as 4 alsa cards, each with 1
+stereo analog capture 'pcm0c' and 1 stereo analog playback 'pcm0p' device.
+With a miXart8AES/EBU there is in addition 1 stereo digital input
+'pcm1c' and 1 stereo digital output 'pcm1p' per card.
+
+Formats
+-------
+U8, S16_LE, S16_BE, S24_3LE, S24_3BE, FLOAT_LE, FLOAT_BE
+Sample rates : 8000 - 48000 Hz continuously
+
+Playback
+--------
+For instance the playback devices are configured to have max. 4
+substreams performing hardware mixing. This could be changed to a
+maximum of 24 substreams if wished.
+Mono files will be played on the left and right channel. Each channel
+can be muted for each stream to use 8 analog/digital outputs separately.
+
+Capture
+-------
+There is one substream per capture device. For instance only stereo
+formats are supported.
+
+Mixer
+-----
+<Master> and <Master Capture>
+ analog volume control of playback and capture PCM.
+<PCM 0-3> and <PCM Capture>
+ digital volume control of each analog substream.
+<AES 0-3> and <AES Capture>
+ digital volume control of each AES/EBU substream.
+<Monitoring>
+ Loopback from 'pcm0c' to 'pcm0p' with digital volume
+ and mute control.
+
+Rem : for best audio quality try to keep a 0 attenuation on the PCM
+and AES volume controls which is set by 219 in the range from 0 to 255
+(about 86% with alsamixer)
+
+
+NOT YET IMPLEMENTED
+===================
+
+- external clock support (AES/EBU, Word Clock, Time Code, Video Sync)
+- MPEG audio formats
+- mono record
+- on-board effects and samplerate conversions
+- linked streams
+
+
+FIRMWARE
+========
+
+[As of 2.6.11, the firmware can be loaded automatically with hotplug
+ when CONFIG_FW_LOADER is set. The mixartloader is necessary only
+ for older versions or when you build the driver into kernel.]
+
+For loading the firmware automatically after the module is loaded, use a
+install command. For example, add the following entry to
+/etc/modprobe.d/mixart.conf for miXart driver:
+::
+
+ install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \
+ /usr/bin/mixartloader
+
+
+(for 2.2/2.4 kernels, add "post-install snd-mixart /usr/bin/vxloader" to
+/etc/modules.conf, instead.)
+
+The firmware binaries are installed on /usr/share/alsa/firmware
+(or /usr/local/share/alsa/firmware, depending to the prefix option of
+configure). There will be a miXart.conf file, which define the dsp image
+files.
+
+The firmware files are copyright by Digigram SA
+
+
+COPYRIGHT
+=========
+
+Copyright (c) 2003 Digigram SA <alsa@digigram.com>
+Distributable under GPL.
--- /dev/null
+===========================================
+Sound Blaster Live mixer / default DSP code
+===========================================
+
+
+The EMU10K1 chips have a DSP part which can be programmed to support
+various ways of sample processing, which is described here.
+(This article does not deal with the overall functionality of the
+EMU10K1 chips. See the manuals section for further details.)
+
+The ALSA driver programs this portion of chip by default code
+(can be altered later) which offers the following functionality:
+
+
+IEC958 (S/PDIF) raw PCM
+=======================
+
+This PCM device (it's the 4th PCM device (index 3!) and first subdevice
+(index 0) for a given card) allows to forward 48kHz, stereo, 16-bit
+little endian streams without any modifications to the digital output
+(coaxial or optical). The universal interface allows the creation of up
+to 8 raw PCM devices operating at 48kHz, 16-bit little endian. It would
+be easy to add support for multichannel devices to the current code,
+but the conversion routines exist only for stereo (2-channel streams)
+at the time.
+
+Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details.
+
+
+Digital mixer controls
+======================
+
+These controls are built using the DSP instructions. They offer extended
+functionality. Only the default build-in code in the ALSA driver is described
+here. Note that the controls work as attenuators: the maximum value is the
+neutral position leaving the signal unchanged. Note that if the same destination
+is mentioned in multiple controls, the signal is accumulated and can be wrapped
+(set to maximal or minimal value without checking of overflow).
+
+
+Explanation of used abbreviations:
+
+DAC
+ digital to analog converter
+ADC
+ analog to digital converter
+I2S
+ one-way three wire serial bus for digital sound by Philips Semiconductors
+ (this standard is used for connecting standalone DAC and ADC converters)
+LFE
+ low frequency effects (subwoofer signal)
+AC97
+ a chip containing an analog mixer, DAC and ADC converters
+IEC958
+ S/PDIF
+FX-bus
+ the EMU10K1 chip has an effect bus containing 16 accumulators.
+ Each of the synthesizer voices can feed its output to these accumulators
+ and the DSP microcontroller can operate with the resulting sum.
+
+
+``name='Wave Playback Volume',index=0``
+---------------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
+The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
+
+``name='Wave Surround Playback Volume',index=0``
+------------------------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
+The result samples are forwarded to the rear I2S DACs. These DACs operates
+separately (they are not inside the AC97 codec).
+
+``name='Wave Center Playback Volume',index=0``
+----------------------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
+The result is mixed to mono signal (single channel) and forwarded to
+the ??rear?? right DAC PCM slot of the AC97 codec.
+
+``name='Wave LFE Playback Volume',index=0``
+-------------------------------------------
+This control is used to attenuate samples for left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM.
+The result is mixed to mono signal (single channel) and forwarded to
+the ??rear?? left DAC PCM slot of the AC97 codec.
+
+``name='Wave Capture Volume',index=0``, ``name='Wave Capture Switch',index=0``
+------------------------------------------------------------------------------
+These controls are used to attenuate samples for left and right PCM FX-bus
+accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+``name='Synth Playback Volume',index=0``
+----------------------------------------
+This control is used to attenuate samples for left and right MIDI FX-bus
+accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
+The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
+
+``name='Synth Capture Volume',index=0``, ``name='Synth Capture Switch',index=0``
+--------------------------------------------------------------------------------
+These controls are used to attenuate samples for left and right MIDI FX-bus
+accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+``name='Surround Playback Volume',index=0``
+-------------------------------------------
+This control is used to attenuate samples for left and right rear PCM FX-bus
+accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
+The result samples are forwarded to the rear I2S DACs. These DACs operate
+separately (they are not inside the AC97 codec).
+
+``name='Surround Capture Volume',index=0``, ``name='Surround Capture Switch',index=0``
+--------------------------------------------------------------------------------------
+These controls are used to attenuate samples for left and right rear PCM FX-bus
+accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
+The result is forwarded to the ADC capture FIFO (thus to the standard capture
+PCM device).
+
+``name='Center Playback Volume',index=0``
+-----------------------------------------
+This control is used to attenuate sample for center PCM FX-bus accumulator.
+ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded
+to the ??rear?? right DAC PCM slot of the AC97 codec.
+
+``name='LFE Playback Volume',index=0``
+--------------------------------------
+This control is used to attenuate sample for center PCM FX-bus accumulator.
+ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded
+to the ??rear?? left DAC PCM slot of the AC97 codec.
+
+``name='AC97 Playback Volume',index=0``
+---------------------------------------
+This control is used to attenuate samples for left and right front ADC PCM slots
+of the AC97 codec. The result samples are forwarded to the front DAC PCM
+slots of the AC97 codec.
+
+.. note::
+ This control should be zero for the standard operations, otherwise
+ a digital loopback is activated.
+
+
+``name='AC97 Capture Volume',index=0``
+--------------------------------------
+This control is used to attenuate samples for left and right front ADC PCM slots
+of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to
+the standard capture PCM device).
+
+.. note::
+ This control should be 100 (maximal value), otherwise no analog
+ inputs of the AC97 codec can be captured (recorded).
+
+``name='IEC958 TTL Playback Volume',index=0``
+---------------------------------------------
+This control is used to attenuate samples from left and right IEC958 TTL
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the front DAC PCM slots of the AC97 codec.
+
+``name='IEC958 TTL Capture Volume',index=0``
+--------------------------------------------
+This control is used to attenuate samples from left and right IEC958 TTL
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
+
+``name='Zoom Video Playback Volume',index=0``
+---------------------------------------------
+This control is used to attenuate samples from left and right zoom video
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the front DAC PCM slots of the AC97 codec.
+
+``name='Zoom Video Capture Volume',index=0``
+--------------------------------------------
+This control is used to attenuate samples from left and right zoom video
+digital inputs (usually used by a CDROM drive). The result samples are
+forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
+
+``name='IEC958 LiveDrive Playback Volume',index=0``
+---------------------------------------------------
+This control is used to attenuate samples from left and right IEC958 optical
+digital input. The result samples are forwarded to the front DAC PCM slots
+of the AC97 codec.
+
+``name='IEC958 LiveDrive Capture Volume',index=0``
+--------------------------------------------------
+This control is used to attenuate samples from left and right IEC958 optical
+digital inputs. The result samples are forwarded to the ADC capture FIFO
+(thus to the standard capture PCM device).
+
+``name='IEC958 Coaxial Playback Volume',index=0``
+-------------------------------------------------
+This control is used to attenuate samples from left and right IEC958 coaxial
+digital inputs. The result samples are forwarded to the front DAC PCM slots
+of the AC97 codec.
+
+``name='IEC958 Coaxial Capture Volume',index=0``
+------------------------------------------------
+This control is used to attenuate samples from left and right IEC958 coaxial
+digital inputs. The result samples are forwarded to the ADC capture FIFO
+(thus to the standard capture PCM device).
+
+``name='Line LiveDrive Playback Volume',index=0``, ``name='Line LiveDrive Playback Volume',index=1``
+----------------------------------------------------------------------------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the LiveDrive). The result samples are forwarded to the front
+DAC PCM slots of the AC97 codec.
+
+``name='Line LiveDrive Capture Volume',index=1``, ``name='Line LiveDrive Capture Volume',index=1``
+--------------------------------------------------------------------------------------------------
+This control is used to attenuate samples from left and right I2S ADC
+inputs (on the LiveDrive). The result samples are forwarded to the ADC
+capture FIFO (thus to the standard capture PCM device).
+
+``name='Tone Control - Switch',index=0``
+----------------------------------------
+This control turns the tone control on or off. The samples for front, rear
+and center / LFE outputs are affected.
+
+``name='Tone Control - Bass',index=0``
+--------------------------------------
+This control sets the bass intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+``name='Tone Control - Treble',index=0``
+----------------------------------------
+This control sets the treble intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+``name='IEC958 Optical Raw Playback Switch',index=0``
+-----------------------------------------------------
+If this switch is on, then the samples for the IEC958 (S/PDIF) digital
+output are taken only from the raw FX8010 PCM, otherwise standard front
+PCM samples are taken.
+
+``name='Headphone Playback Volume',index=1``
+--------------------------------------------
+This control attenuates the samples for the headphone output.
+
+``name='Headphone Center Playback Switch',index=1``
+---------------------------------------------------
+If this switch is on, then the sample for the center PCM is put to the
+left headphone output (useful for SB Live cards without separate center/LFE
+output).
+
+``name='Headphone LFE Playback Switch',index=1``
+------------------------------------------------
+If this switch is on, then the sample for the center PCM is put to the
+right headphone output (useful for SB Live cards without separate center/LFE
+output).
+
+
+PCM stream related controls
+===========================
+
+``name='EMU10K1 PCM Volume',index 0-31``
+----------------------------------------
+Channel volume attenuation in range 0-0xffff. The maximum value (no
+attenuation) is default. The channel mapping for three values is
+as follows:
+
+* 0 - mono, default 0xffff (no attenuation)
+* 1 - left, default 0xffff (no attenuation)
+* 2 - right, default 0xffff (no attenuation)
+
+``name='EMU10K1 PCM Send Routing',index 0-31``
+----------------------------------------------
+This control specifies the destination - FX-bus accumulators. There are
+twelve values with this mapping:
+
+* 0 - mono, A destination (FX-bus 0-15), default 0
+* 1 - mono, B destination (FX-bus 0-15), default 1
+* 2 - mono, C destination (FX-bus 0-15), default 2
+* 3 - mono, D destination (FX-bus 0-15), default 3
+* 4 - left, A destination (FX-bus 0-15), default 0
+* 5 - left, B destination (FX-bus 0-15), default 1
+* 6 - left, C destination (FX-bus 0-15), default 2
+* 7 - left, D destination (FX-bus 0-15), default 3
+* 8 - right, A destination (FX-bus 0-15), default 0
+* 9 - right, B destination (FX-bus 0-15), default 1
+* 10 - right, C destination (FX-bus 0-15), default 2
+* 11 - right, D destination (FX-bus 0-15), default 3
+
+Don't forget that it's illegal to assign a channel to the same FX-bus accumulator
+more than once (it means 0=0 && 1=0 is an invalid combination).
+
+``name='EMU10K1 PCM Send Volume',index 0-31``
+---------------------------------------------
+It specifies the attenuation (amount) for given destination in range 0-255.
+The channel mapping is following:
+
+* 0 - mono, A destination attn, default 255 (no attenuation)
+* 1 - mono, B destination attn, default 255 (no attenuation)
+* 2 - mono, C destination attn, default 0 (mute)
+* 3 - mono, D destination attn, default 0 (mute)
+* 4 - left, A destination attn, default 255 (no attenuation)
+* 5 - left, B destination attn, default 0 (mute)
+* 6 - left, C destination attn, default 0 (mute)
+* 7 - left, D destination attn, default 0 (mute)
+* 8 - right, A destination attn, default 0 (mute)
+* 9 - right, B destination attn, default 255 (no attenuation)
+* 10 - right, C destination attn, default 0 (mute)
+* 11 - right, D destination attn, default 0 (mute)
+
+
+
+MANUALS/PATENTS
+===============
+
+ftp://opensource.creative.com/pub/doc
+-------------------------------------
+
+LM4545.pdf
+ AC97 Codec
+m2049.pdf
+ The EMU10K1 Digital Audio Processor
+hog63.ps
+ FX8010 - A DSP Chip Architecture for Audio Effects
+
+
+WIPO Patents
+------------
+
+WO 9901813 (A1)
+ Audio Effects Processor with multiple asynchronous streams
+ (Jan. 14, 1999)
+
+WO 9901814 (A1)
+ Processor with Instruction Set for Audio Effects (Jan. 14, 1999)
+
+WO 9901953 (A1)
+ Audio Effects Processor having Decoupled Instruction
+ Execution and Audio Data Sequencing (Jan. 14, 1999)
+
+
+US Patents (http://www.uspto.gov/)
+----------------------------------
+
+US 5925841
+ Digital Sampling Instrument employing cache memory (Jul. 20, 1999)
+
+US 5928342
+ Audio Effects Processor integrated on a single chip
+ with a multiport memory onto which multiple asynchronous
+ digital sound samples can be concurrently loaded
+ (Jul. 27, 1999)
+
+US 5930158
+ Processor with Instruction Set for Audio Effects (Jul. 27, 1999)
+
+US 6032235
+ Memory initialization circuit (Tram) (Feb. 29, 2000)
+
+US 6138207
+ Interpolation looping of audio samples in cache connected to
+ system bus with prioritization and modification of bus transfers
+ in accordance with loop ends and minimum block sizes
+ (Oct. 24, 2000)
+
+US 6151670
+ Method for conserving memory storage using a
+ pool of short term memory registers
+ (Nov. 21, 2000)
+
+US 6195715
+ Interrupt control for multiple programs communicating with
+ a common interrupt by associating programs to GP registers,
+ defining interrupt register, polling GP registers, and invoking
+ callback routine associated with defined interrupt register
+ (Feb. 27, 2001)
--- /dev/null
+===================================
+Serial UART 16450/16550 MIDI driver
+===================================
+
+The adaptor module parameter allows you to select either:
+
+* 0 - Roland Soundcanvas support (default)
+* 1 - Midiator MS-124T support (1)
+* 2 - Midiator MS-124W S/A mode (2)
+* 3 - MS-124W M/B mode support (3)
+* 4 - Generic device with multiple input support (4)
+
+For the Midiator MS-124W, you must set the physical M-S and A-B
+switches on the Midiator to match the driver mode you select.
+
+In Roland Soundcanvas mode, multiple ALSA raw MIDI substreams are supported
+(midiCnD0-midiCnD15). Whenever you write to a different substream, the driver
+sends the nonstandard MIDI command sequence F5 NN, where NN is the substream
+number plus 1. Roland modules use this command to switch between different
+"parts", so this feature lets you treat each part as a distinct raw MIDI
+substream. The driver provides no way to send F5 00 (no selection) or to not
+send the F5 NN command sequence at all; perhaps it ought to.
+
+Usage example for simple serial converter:
+::
+
+ /sbin/setserial /dev/ttyS0 uart none
+ /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 speed=115200
+
+Usage example for Roland SoundCanvas with 4 MIDI ports:
+::
+
+ /sbin/setserial /dev/ttyS0 uart none
+ /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 outs=4
+
+In MS-124T mode, one raw MIDI substream is supported (midiCnD0); the outs
+module parameter is automatically set to 1. The driver sends the same data to
+all four MIDI Out connectors. Set the A-B switch and the speed module
+parameter to match (A=19200, B=9600).
+
+Usage example for MS-124T, with A-B switch in A position:
+::
+
+ /sbin/setserial /dev/ttyS0 uart none
+ /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=1 \
+ speed=19200
+
+In MS-124W S/A mode, one raw MIDI substream is supported (midiCnD0);
+the outs module parameter is automatically set to 1. The driver sends
+the same data to all four MIDI Out connectors at full MIDI speed.
+
+Usage example for S/A mode:
+::
+
+ /sbin/setserial /dev/ttyS0 uart none
+ /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=2
+
+In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI substreams;
+the outs module parameter is automatically set to 16. The substream
+number gives a bitmask of which MIDI Out connectors the data should be
+sent to, with midiCnD1 sending to Out 1, midiCnD2 to Out 2, midiCnD4 to
+Out 3, and midiCnD8 to Out 4. Thus midiCnD15 sends the data to all 4 ports.
+As a special case, midiCnD0 also sends to all ports, since it is not useful
+to send the data to no ports. M/B mode has extra overhead to select the MIDI
+Out for each byte, so the aggregate data rate across all four MIDI Outs is
+at most one byte every 520 us, as compared with the full MIDI data rate of
+one byte every 320 us per port.
+
+Usage example for M/B mode:
+::
+
+ /sbin/setserial /dev/ttyS0 uart none
+ /sbin/modprobe snd-serial-u16550 port=0x3f8 irq=4 adaptor=3
+
+The MS-124W hardware's M/A mode is currently not supported. This mode allows
+the MIDI Outs to act independently at double the aggregate throughput of M/B,
+but does not allow sending the same byte simultaneously to multiple MIDI Outs.
+The M/A protocol requires the driver to twiddle the modem control lines under
+timing constraints, so it would be a bit more complicated to implement than
+the other modes.
+
+Midiator models other than MS-124W and MS-124T are currently not supported.
+Note that the suffix letter is significant; the MS-124 and MS-124B are not
+compatible, nor are the other known models MS-101, MS-101B, MS-103, and MS-114.
+I do have documentation (tim.mann@compaq.com) that partially covers these models,
+but no units to experiment with. The MS-124W support is tested with a real unit.
+The MS-124T support is untested, but should work.
+
+The Generic driver supports multiple input and output substreams over a single
+serial port. Similar to Roland Soundcanvas mode, F5 NN is used to select the
+appropriate input or output stream (depending on the data direction).
+Additionally, the CTS signal is used to regulate the data flow. The number of
+inputs is specified by the ins parameter.
--- /dev/null
+=============
+VIA82xx mixer
+=============
+
+On many VIA82xx boards, the ``Input Source Select`` mixer control does not work.
+Setting it to ``Input2`` on such boards will cause recording to hang, or fail
+with EIO (input/output error) via OSS emulation. This control should be left
+at ``Input1`` for such cards.
--- /dev/null
+============================
+ALSA PCM channel-mapping API
+============================
+
+Takashi Iwai <tiwai@suse.de>
+
+General
+=======
+
+The channel mapping API allows user to query the possible channel maps
+and the current channel map, also optionally to modify the channel map
+of the current stream.
+
+A channel map is an array of position for each PCM channel.
+Typically, a stereo PCM stream has a channel map of
+``{ front_left, front_right }``
+while a 4.0 surround PCM stream has a channel map of
+``{ front left, front right, rear left, rear right }.``
+
+The problem, so far, was that we had no standard channel map
+explicitly, and applications had no way to know which channel
+corresponds to which (speaker) position. Thus, applications applied
+wrong channels for 5.1 outputs, and you hear suddenly strange sound
+from rear. Or, some devices secretly assume that center/LFE is the
+third/fourth channels while others that C/LFE as 5th/6th channels.
+
+Also, some devices such as HDMI are configurable for different speaker
+positions even with the same number of total channels. However, there
+was no way to specify this because of lack of channel map
+specification. These are the main motivations for the new channel
+mapping API.
+
+
+Design
+======
+
+Actually, "the channel mapping API" doesn't introduce anything new in
+the kernel/user-space ABI perspective. It uses only the existing
+control element features.
+
+As a ground design, each PCM substream may contain a control element
+providing the channel mapping information and configuration. This
+element is specified by:
+
+* iface = SNDRV_CTL_ELEM_IFACE_PCM
+* name = "Playback Channel Map" or "Capture Channel Map"
+* device = the same device number for the assigned PCM substream
+* index = the same index number for the assigned PCM substream
+
+Note the name is different depending on the PCM substream direction.
+
+Each control element provides at least the TLV read operation and the
+read operation. Optionally, the write operation can be provided to
+allow user to change the channel map dynamically.
+
+TLV
+---
+
+The TLV operation gives the list of available channel
+maps. A list item of a channel map is usually a TLV of
+``type data-bytes ch0 ch1 ch2...``
+where type is the TLV type value, the second argument is the total
+bytes (not the numbers) of channel values, and the rest are the
+position value for each channel.
+
+As a TLV type, either ``SNDRV_CTL_TLVT_CHMAP_FIXED``,
+``SNDRV_CTL_TLV_CHMAP_VAR`` or ``SNDRV_CTL_TLVT_CHMAP_PAIRED`` can be used.
+The ``_FIXED`` type is for a channel map with the fixed channel position
+while the latter two are for flexible channel positions. ``_VAR`` type is
+for a channel map where all channels are freely swappable and ``_PAIRED``
+type is where pair-wise channels are swappable. For example, when you
+have {FL/FR/RL/RR} channel map, ``_PAIRED`` type would allow you to swap
+only {RL/RR/FL/FR} while ``_VAR`` type would allow even swapping FL and
+RR.
+
+These new TLV types are defined in ``sound/tlv.h``.
+
+The available channel position values are defined in ``sound/asound.h``,
+here is a cut:
+
+::
+
+ /* channel positions */
+ enum {
+ SNDRV_CHMAP_UNKNOWN = 0,
+ SNDRV_CHMAP_NA, /* N/A, silent */
+ SNDRV_CHMAP_MONO, /* mono stream */
+ /* this follows the alsa-lib mixer channel value + 3 */
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_RL, /* rear left */
+ SNDRV_CHMAP_RR, /* rear right */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_RC, /* rear center */
+ /* new definitions */
+ SNDRV_CHMAP_FLC, /* front left center */
+ SNDRV_CHMAP_FRC, /* front right center */
+ SNDRV_CHMAP_RLC, /* rear left center */
+ SNDRV_CHMAP_RRC, /* rear right center */
+ SNDRV_CHMAP_FLW, /* front left wide */
+ SNDRV_CHMAP_FRW, /* front right wide */
+ SNDRV_CHMAP_FLH, /* front left high */
+ SNDRV_CHMAP_FCH, /* front center high */
+ SNDRV_CHMAP_FRH, /* front right high */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_TFL, /* top front left */
+ SNDRV_CHMAP_TFR, /* top front right */
+ SNDRV_CHMAP_TFC, /* top front center */
+ SNDRV_CHMAP_TRL, /* top rear left */
+ SNDRV_CHMAP_TRR, /* top rear right */
+ SNDRV_CHMAP_TRC, /* top rear center */
+ SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC,
+ };
+
+When a PCM stream can provide more than one channel map, you can
+provide multiple channel maps in a TLV container type. The TLV data
+to be returned will contain such as:
+::
+
+ SNDRV_CTL_TLVT_CONTAINER 96
+ SNDRV_CTL_TLVT_CHMAP_FIXED 4 SNDRV_CHMAP_FC
+ SNDRV_CTL_TLVT_CHMAP_FIXED 8 SNDRV_CHMAP_FL SNDRV_CHMAP_FR
+ SNDRV_CTL_TLVT_CHMAP_FIXED 16 NDRV_CHMAP_FL SNDRV_CHMAP_FR \
+ SNDRV_CHMAP_RL SNDRV_CHMAP_RR
+
+The channel position is provided in LSB 16bits. The upper bits are
+used for bit flags.
+::
+
+ #define SNDRV_CHMAP_POSITION_MASK 0xffff
+ #define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
+ #define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
+
+``SNDRV_CHMAP_PHASE_INVERSE`` indicates the channel is phase inverted,
+(thus summing left and right channels would result in almost silence).
+Some digital mic devices have this.
+
+When ``SNDRV_CHMAP_DRIVER_SPEC`` is set, all the channel position values
+don't follow the standard definition above but driver-specific.
+
+Read Operation
+--------------
+
+The control read operation is for providing the current channel map of
+the given stream. The control element returns an integer array
+containing the position of each channel.
+
+When this is performed before the number of the channel is specified
+(i.e. hw_params is set), it should return all channels set to
+``UNKNOWN``.
+
+Write Operation
+---------------
+
+The control write operation is optional, and only for devices that can
+change the channel configuration on the fly, such as HDMI. User needs
+to pass an integer value containing the valid channel positions for
+all channels of the assigned PCM substream.
+
+This operation is allowed only at PCM PREPARED state. When called in
+other states, it shall return an error.
--- /dev/null
+=========================
+ALSA Compress-Offload API
+=========================
+
+Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com>
+
+Vinod Koul <vinod.koul@linux.intel.com>
+
+
+Overview
+========
+Since its early days, the ALSA API was defined with PCM support or
+constant bitrates payloads such as IEC61937 in mind. Arguments and
+returned values in frames are the norm, making it a challenge to
+extend the existing API to compressed data streams.
+
+In recent years, audio digital signal processors (DSP) were integrated
+in system-on-chip designs, and DSPs are also integrated in audio
+codecs. Processing compressed data on such DSPs results in a dramatic
+reduction of power consumption compared to host-based
+processing. Support for such hardware has not been very good in Linux,
+mostly because of a lack of a generic API available in the mainline
+kernel.
+
+Rather than requiring a compatibility break with an API change of the
+ALSA PCM interface, a new 'Compressed Data' API is introduced to
+provide a control and data-streaming interface for audio DSPs.
+
+The design of this API was inspired by the 2-year experience with the
+Intel Moorestown SOC, with many corrections required to upstream the
+API in the mainline kernel instead of the staging tree and make it
+usable by others.
+
+
+Requirements
+============
+The main requirements are:
+
+- separation between byte counts and time. Compressed formats may have
+ a header per file, per frame, or no header at all. The payload size
+ may vary from frame-to-frame. As a result, it is not possible to
+ estimate reliably the duration of audio buffers when handling
+ compressed data. Dedicated mechanisms are required to allow for
+ reliable audio-video synchronization, which requires precise
+ reporting of the number of samples rendered at any given time.
+
+- Handling of multiple formats. PCM data only requires a specification
+ of the sampling rate, number of channels and bits per sample. In
+ contrast, compressed data comes in a variety of formats. Audio DSPs
+ may also provide support for a limited number of audio encoders and
+ decoders embedded in firmware, or may support more choices through
+ dynamic download of libraries.
+
+- Focus on main formats. This API provides support for the most
+ popular formats used for audio and video capture and playback. It is
+ likely that as audio compression technology advances, new formats
+ will be added.
+
+- Handling of multiple configurations. Even for a given format like
+ AAC, some implementations may support AAC multichannel but HE-AAC
+ stereo. Likewise WMA10 level M3 may require too much memory and cpu
+ cycles. The new API needs to provide a generic way of listing these
+ formats.
+
+- Rendering/Grabbing only. This API does not provide any means of
+ hardware acceleration, where PCM samples are provided back to
+ user-space for additional processing. This API focuses instead on
+ streaming compressed data to a DSP, with the assumption that the
+ decoded samples are routed to a physical output or logical back-end.
+
+- Complexity hiding. Existing user-space multimedia frameworks all
+ have existing enums/structures for each compressed format. This new
+ API assumes the existence of a platform-specific compatibility layer
+ to expose, translate and make use of the capabilities of the audio
+ DSP, eg. Android HAL or PulseAudio sinks. By construction, regular
+ applications are not supposed to make use of this API.
+
+
+Design
+======
+The new API shares a number of concepts with the PCM API for flow
+control. Start, pause, resume, drain and stop commands have the same
+semantics no matter what the content is.
+
+The concept of memory ring buffer divided in a set of fragments is
+borrowed from the ALSA PCM API. However, only sizes in bytes can be
+specified.
+
+Seeks/trick modes are assumed to be handled by the host.
+
+The notion of rewinds/forwards is not supported. Data committed to the
+ring buffer cannot be invalidated, except when dropping all buffers.
+
+The Compressed Data API does not make any assumptions on how the data
+is transmitted to the audio DSP. DMA transfers from main memory to an
+embedded audio cluster or to a SPI interface for external DSPs are
+possible. As in the ALSA PCM case, a core set of routines is exposed;
+each driver implementer will have to write support for a set of
+mandatory routines and possibly make use of optional ones.
+
+The main additions are
+
+get_caps
+ This routine returns the list of audio formats supported. Querying the
+ codecs on a capture stream will return encoders, decoders will be
+ listed for playback streams.
+
+get_codec_caps
+ For each codec, this routine returns a list of
+ capabilities. The intent is to make sure all the capabilities
+ correspond to valid settings, and to minimize the risks of
+ configuration failures. For example, for a complex codec such as AAC,
+ the number of channels supported may depend on a specific profile. If
+ the capabilities were exposed with a single descriptor, it may happen
+ that a specific combination of profiles/channels/formats may not be
+ supported. Likewise, embedded DSPs have limited memory and cpu cycles,
+ it is likely that some implementations make the list of capabilities
+ dynamic and dependent on existing workloads. In addition to codec
+ settings, this routine returns the minimum buffer size handled by the
+ implementation. This information can be a function of the DMA buffer
+ sizes, the number of bytes required to synchronize, etc, and can be
+ used by userspace to define how much needs to be written in the ring
+ buffer before playback can start.
+
+set_params
+ This routine sets the configuration chosen for a specific codec. The
+ most important field in the parameters is the codec type; in most
+ cases decoders will ignore other fields, while encoders will strictly
+ comply to the settings
+
+get_params
+ This routines returns the actual settings used by the DSP. Changes to
+ the settings should remain the exception.
+
+get_timestamp
+ The timestamp becomes a multiple field structure. It lists the number
+ of bytes transferred, the number of samples processed and the number
+ of samples rendered/grabbed. All these values can be used to determine
+ the average bitrate, figure out if the ring buffer needs to be
+ refilled or the delay due to decoding/encoding/io on the DSP.
+
+Note that the list of codecs/profiles/modes was derived from the
+OpenMAX AL specification instead of reinventing the wheel.
+Modifications include:
+- Addition of FLAC and IEC formats
+- Merge of encoder/decoder capabilities
+- Profiles/modes listed as bitmasks to make descriptors more compact
+- Addition of set_params for decoders (missing in OpenMAX AL)
+- Addition of AMR/AMR-WB encoding modes (missing in OpenMAX AL)
+- Addition of format information for WMA
+- Addition of encoding options when required (derived from OpenMAX IL)
+- Addition of rateControlSupported (missing in OpenMAX AL)
+
+
+Gapless Playback
+================
+When playing thru an album, the decoders have the ability to skip the encoder
+delay and padding and directly move from one track content to another. The end
+user can perceive this as gapless playback as we don't have silence while
+switching from one track to another
+
+Also, there might be low-intensity noises due to encoding. Perfect gapless is
+difficult to reach with all types of compressed data, but works fine with most
+music content. The decoder needs to know the encoder delay and encoder padding.
+So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers
+and are not present by default in the bitstream, hence the need for a new
+interface to pass this information to the DSP. Also DSP and userspace needs to
+switch from one track to another and start using data for second track.
+
+The main additions are:
+
+set_metadata
+ This routine sets the encoder delay and encoder padding. This can be used by
+ decoder to strip the silence. This needs to be set before the data in the track
+ is written.
+
+set_next_track
+ This routine tells DSP that metadata and write operation sent after this would
+ correspond to subsequent track
+
+partial drain
+ This is called when end of file is reached. The userspace can inform DSP that
+ EOF is reached and now DSP can start skipping padding delay. Also next write
+ data would belong to next track
+
+Sequence flow for gapless would be:
+- Open
+- Get caps / codec caps
+- Set params
+- Set metadata of the first track
+- Fill data of the first track
+- Trigger start
+- User-space finished sending all,
+- Indicate next track data by sending set_next_track
+- Set metadata of the next track
+- then call partial_drain to flush most of buffer in DSP
+- Fill data of the next track
+- DSP switches to second track
+
+(note: order for partial_drain and write for next track can be reversed as well)
+
+
+Not supported
+=============
+- Support for VoIP/circuit-switched calls is not the target of this
+ API. Support for dynamic bit-rate changes would require a tight
+ coupling between the DSP and the host stack, limiting power savings.
+
+- Packet-loss concealment is not supported. This would require an
+ additional interface to let the decoder synthesize data when frames
+ are lost during transmission. This may be added in the future.
+
+- Volume control/routing is not handled by this API. Devices exposing a
+ compressed data interface will be considered as regular ALSA devices;
+ volume changes and routing information will be provided with regular
+ ALSA kcontrols.
+
+- Embedded audio effects. Such effects should be enabled in the same
+ manner, no matter if the input was PCM or compressed.
+
+- multichannel IEC encoding. Unclear if this is required.
+
+- Encoding/decoding acceleration is not supported as mentioned
+ above. It is possible to route the output of a decoder to a capture
+ stream, or even implement transcoding capabilities. This routing
+ would be enabled with ALSA kcontrols.
+
+- Audio policy/resource management. This API does not provide any
+ hooks to query the utilization of the audio DSP, nor any preemption
+ mechanisms.
+
+- No notion of underrun/overrun. Since the bytes written are compressed
+ in nature and data written/read doesn't translate directly to
+ rendered output in time, this does not deal with underrun/overrun and
+ maybe dealt in user-library
+
+
+Credits
+=======
+- Mark Brown and Liam Girdwood for discussions on the need for this API
+- Harsha Priya for her work on intel_sst compressed API
+- Rakesh Ughreja for valuable feedback
+- Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for
+ demonstrating and quantifying the benefits of audio offload on a
+ real platform.
--- /dev/null
+===========================
+Standard ALSA Control Names
+===========================
+
+This document describes standard names of mixer controls.
+
+Standard Syntax
+---------------
+Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION
+
+
+DIRECTION
+~~~~~~~~~
+================ ===============
+<nothing> both directions
+Playback one direction
+Capture one direction
+Bypass Playback one direction
+Bypass Capture one direction
+================ ===============
+
+FUNCTION
+~~~~~~~~
+======== =================================
+Switch on/off switch
+Volume amplifier
+Route route control, hardware specific
+======== =================================
+
+CHANNEL
+~~~~~~~
+============ ==================================================
+<nothing> channel independent, or applies to all channels
+Front front left/right channels
+Surround rear left/right in 4.0/5.1 surround
+CLFE C/LFE channels
+Center center cannel
+LFE LFE channel
+Side side left/right for 7.1 surround
+============ ==================================================
+
+LOCATION (Physical location of source)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+============ =====================
+Front front position
+Rear rear position
+Dock on docking station
+Internal internal
+============ =====================
+
+SOURCE
+~~~~~~
+=================== =================================================
+Master
+Master Mono
+Hardware Master
+Speaker internal speaker
+Bass Speaker internal LFE speaker
+Headphone
+Line Out
+Beep beep generator
+Phone
+Phone Input
+Phone Output
+Synth
+FM
+Mic
+Headset Mic mic part of combined headset jack - 4-pin
+ headphone + mic
+Headphone Mic mic part of either/or - 3-pin headphone or mic
+Line input only, use "Line Out" for output
+CD
+Video
+Zoom Video
+Aux
+PCM
+PCM Pan
+Loopback
+Analog Loopback D/A -> A/D loopback
+Digital Loopback playback -> capture loopback -
+ without analog path
+Mono
+Mono Output
+Multi
+ADC
+Wave
+Music
+I2S
+IEC958
+HDMI
+SPDIF output only
+SPDIF In
+Digital In
+HDMI/DP either HDMI or DisplayPort
+=================== =================================================
+
+Exceptions (deprecated)
+-----------------------
+
+===================================== =======================
+[Analogue|Digital] Capture Source
+[Analogue|Digital] Capture Switch aka input gain switch
+[Analogue|Digital] Capture Volume aka input gain volume
+[Analogue|Digital] Playback Switch aka output gain switch
+[Analogue|Digital] Playback Volume aka output gain volume
+Tone Control - Switch
+Tone Control - Bass
+Tone Control - Treble
+3D Control - Switch
+3D Control - Center
+3D Control - Depth
+3D Control - Wide
+3D Control - Space
+3D Control - Level
+Mic Boost [(?dB)]
+===================================== =======================
+
+PCM interface
+-------------
+
+=================== ========================================
+Sample Clock Source { "Word", "Internal", "AutoSync" }
+Clock Sync Status { "Lock", "Sync", "No Lock" }
+External Rate external capture rate
+Capture Rate capture rate taken from external source
+=================== ========================================
+
+IEC958 (S/PDIF) interface
+-------------------------
+
+============================================ ======================================
+IEC958 [...] [Playback|Capture] Switch turn on/off the IEC958 interface
+IEC958 [...] [Playback|Capture] Volume digital volume control
+IEC958 [...] [Playback|Capture] Default default or global value - read/write
+IEC958 [...] [Playback|Capture] Mask consumer and professional mask
+IEC958 [...] [Playback|Capture] Con Mask consumer mask
+IEC958 [...] [Playback|Capture] Pro Mask professional mask
+IEC958 [...] [Playback|Capture] PCM Stream the settings assigned to a PCM stream
+IEC958 Q-subcode [Playback|Capture] Default Q-subcode bits
+
+IEC958 Preamble [Playback|Capture] Default burst preamble words (4*16bits)
+============================================ ======================================
--- /dev/null
+Designs and Implementations
+===========================
+
+.. toctree::
+ :maxdepth: 2
+
+ control-names
+ channel-mapping-api
+ compress-offload
+ timestamping
+ jack-controls
+ procfile
+ powersave
+ oss-emulation
+ seq-oss
--- /dev/null
+==================
+ALSA Jack Controls
+==================
+
+Why we need Jack kcontrols
+==========================
+
+ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...)
+to user space. This means userspace applications like pulseaudio can
+switch off headphones and switch on speakers when no headphones are
+pluged in.
+
+The old ALSA jack code only created input devices for each registered
+jack. These jack input devices are not readable by userspace devices
+that run as non root.
+
+The new jack code creates embedded jack kcontrols for each jack that
+can be read by any process.
+
+This can be combined with UCM to allow userspace to route audio more
+intelligently based on jack insertion or removal events.
+
+Jack Kcontrol Internals
+=======================
+
+Each jack will have a kcontrol list, so that we can create a kcontrol
+and attach it to the jack, at jack creation stage. We can also add a
+kcontrol to an existing jack, at anytime when required.
+
+Those kcontrols will be freed automatically when the Jack is freed.
+
+How to use jack kcontrols
+=========================
+
+In order to keep compatibility, snd_jack_new() has been modified by
+adding two params:
+
+initial_kctl
+ if true, create a kcontrol and add it to the jack list.
+phantom_jack
+ Don't create a input device for phantom jacks.
+
+HDA jacks can set phantom_jack to true in order to create a phantom
+jack and set initial_kctl to true to create an initial kcontrol with
+the correct id.
+
+ASoC jacks should set initial_kctl as false. The pin name will be
+assigned as the jack kcontrol name.
--- /dev/null
+=============================
+Notes on Kernel OSS-Emulation
+=============================
+
+Jan. 22, 2004 Takashi Iwai <tiwai@suse.de>
+
+
+Modules
+=======
+
+ALSA provides a powerful OSS emulation on the kernel.
+The OSS emulation for PCM, mixer and sequencer devices is implemented
+as add-on kernel modules, snd-pcm-oss, snd-mixer-oss and snd-seq-oss.
+When you need to access the OSS PCM, mixer or sequencer devices, the
+corresponding module has to be loaded.
+
+These modules are loaded automatically when the corresponding service
+is called. The alias is defined ``sound-service-x-y``, where x and y are
+the card number and the minor unit number. Usually you don't have to
+define these aliases by yourself.
+
+Only necessary step for auto-loading of OSS modules is to define the
+card alias in ``/etc/modprobe.d/alsa.conf``, such as::
+
+ alias sound-slot-0 snd-emu10k1
+
+As the second card, define ``sound-slot-1`` as well.
+Note that you can't use the aliased name as the target name (i.e.
+``alias sound-slot-0 snd-card-0`` doesn't work any more like the old
+modutils).
+
+The currently available OSS configuration is shown in
+/proc/asound/oss/sndstat. This shows in the same syntax of
+/dev/sndstat, which is available on the commercial OSS driver.
+On ALSA, you can symlink /dev/sndstat to this proc file.
+
+Please note that the devices listed in this proc file appear only
+after the corresponding OSS-emulation module is loaded. Don't worry
+even if "NOT ENABLED IN CONFIG" is shown in it.
+
+
+Device Mapping
+==============
+
+ALSA supports the following OSS device files:
+::
+
+ PCM:
+ /dev/dspX
+ /dev/adspX
+
+ Mixer:
+ /dev/mixerX
+
+ MIDI:
+ /dev/midi0X
+ /dev/amidi0X
+
+ Sequencer:
+ /dev/sequencer
+ /dev/sequencer2 (aka /dev/music)
+
+where X is the card number from 0 to 7.
+
+(NOTE: Some distributions have the device files like /dev/midi0 and
+/dev/midi1. They are NOT for OSS but for tclmidi, which is
+a totally different thing.)
+
+Unlike the real OSS, ALSA cannot use the device files more than the
+assigned ones. For example, the first card cannot use /dev/dsp1 or
+/dev/dsp2, but only /dev/dsp0 and /dev/adsp0.
+
+As seen above, PCM and MIDI may have two devices. Usually, the first
+PCM device (``hw:0,0`` in ALSA) is mapped to /dev/dsp and the secondary
+device (``hw:0,1``) to /dev/adsp (if available). For MIDI, /dev/midi and
+/dev/amidi, respectively.
+
+You can change this device mapping via the module options of
+snd-pcm-oss and snd-rawmidi. In the case of PCM, the following
+options are available for snd-pcm-oss:
+
+dsp_map
+ PCM device number assigned to /dev/dspX
+ (default = 0)
+adsp_map
+ PCM device number assigned to /dev/adspX
+ (default = 1)
+
+For example, to map the third PCM device (``hw:0,2``) to /dev/adsp0,
+define like this:
+::
+
+ options snd-pcm-oss adsp_map=2
+
+The options take arrays. For configuring the second card, specify
+two entries separated by comma. For example, to map the third PCM
+device on the second card to /dev/adsp1, define like below:
+::
+
+ options snd-pcm-oss adsp_map=0,2
+
+To change the mapping of MIDI devices, the following options are
+available for snd-rawmidi:
+
+midi_map
+ MIDI device number assigned to /dev/midi0X
+ (default = 0)
+amidi_map
+ MIDI device number assigned to /dev/amidi0X
+ (default = 1)
+
+For example, to assign the third MIDI device on the first card to
+/dev/midi00, define as follows:
+::
+
+ options snd-rawmidi midi_map=2
+
+
+PCM Mode
+========
+
+As default, ALSA emulates the OSS PCM with so-called plugin layer,
+i.e. tries to convert the sample format, rate or channels
+automatically when the card doesn't support it natively.
+This will lead to some problems for some applications like quake or
+wine, especially if they use the card only in the MMAP mode.
+
+In such a case, you can change the behavior of PCM per application by
+writing a command to the proc file. There is a proc file for each PCM
+stream, ``/proc/asound/cardX/pcmY[cp]/oss``, where X is the card number
+(zero-based), Y the PCM device number (zero-based), and ``p`` is for
+playback and ``c`` for capture, respectively. Note that this proc file
+exists only after snd-pcm-oss module is loaded.
+
+The command sequence has the following syntax:
+::
+
+ app_name fragments fragment_size [options]
+
+``app_name`` is the name of application with (higher priority) or without
+path.
+``fragments`` specifies the number of fragments or zero if no specific
+number is given.
+``fragment_size`` is the size of fragment in bytes or zero if not given.
+``options`` is the optional parameters. The following options are
+available:
+
+disable
+ the application tries to open a pcm device for
+ this channel but does not want to use it.
+direct
+ don't use plugins
+block
+ force block open mode
+non-block
+ force non-block open mode
+partial-frag
+ write also partial fragments (affects playback only)
+no-silence
+ do not fill silence ahead to avoid clicks
+
+The ``disable`` option is useful when one stream direction (playback or
+capture) is not handled correctly by the application although the
+hardware itself does support both directions.
+The ``direct`` option is used, as mentioned above, to bypass the automatic
+conversion and useful for MMAP-applications.
+For example, to playback the first PCM device without plugins for
+quake, send a command via echo like the following:
+::
+
+ % echo "quake 0 0 direct" > /proc/asound/card0/pcm0p/oss
+
+While quake wants only playback, you may append the second command
+to notify driver that only this direction is about to be allocated:
+::
+
+ % echo "quake 0 0 disable" > /proc/asound/card0/pcm0c/oss
+
+The permission of proc files depend on the module options of snd.
+As default it's set as root, so you'll likely need to be superuser for
+sending the command above.
+
+The block and non-block options are used to change the behavior of
+opening the device file.
+
+As default, ALSA behaves as original OSS drivers, i.e. does not block
+the file when it's busy. The -EBUSY error is returned in this case.
+
+This blocking behavior can be changed globally via nonblock_open
+module option of snd-pcm-oss. For using the blocking mode as default
+for OSS devices, define like the following:
+::
+
+ options snd-pcm-oss nonblock_open=0
+
+The ``partial-frag`` and ``no-silence`` commands have been added recently.
+Both commands are for optimization use only. The former command
+specifies to invoke the write transfer only when the whole fragment is
+filled. The latter stops writing the silence data ahead
+automatically. Both are disabled as default.
+
+You can check the currently defined configuration by reading the proc
+file. The read image can be sent to the proc file again, hence you
+can save the current configuration
+::
+
+ % cat /proc/asound/card0/pcm0p/oss > /somewhere/oss-cfg
+
+and restore it like
+::
+
+ % cat /somewhere/oss-cfg > /proc/asound/card0/pcm0p/oss
+
+Also, for clearing all the current configuration, send ``erase`` command
+as below:
+::
+
+ % echo "erase" > /proc/asound/card0/pcm0p/oss
+
+
+Mixer Elements
+==============
+
+Since ALSA has completely different mixer interface, the emulation of
+OSS mixer is relatively complicated. ALSA builds up a mixer element
+from several different ALSA (mixer) controls based on the name
+string. For example, the volume element SOUND_MIXER_PCM is composed
+from "PCM Playback Volume" and "PCM Playback Switch" controls for the
+playback direction and from "PCM Capture Volume" and "PCM Capture
+Switch" for the capture directory (if exists). When the PCM volume of
+OSS is changed, all the volume and switch controls above are adjusted
+automatically.
+
+As default, ALSA uses the following control for OSS volumes:
+
+==================== ===================== =====
+OSS volume ALSA control Index
+==================== ===================== =====
+SOUND_MIXER_VOLUME Master 0
+SOUND_MIXER_BASS Tone Control - Bass 0
+SOUND_MIXER_TREBLE Tone Control - Treble 0
+SOUND_MIXER_SYNTH Synth 0
+SOUND_MIXER_PCM PCM 0
+SOUND_MIXER_SPEAKER PC Speaker 0
+SOUND_MIXER_LINE Line 0
+SOUND_MIXER_MIC Mic 0
+SOUND_MIXER_CD CD 0
+SOUND_MIXER_IMIX Monitor Mix 0
+SOUND_MIXER_ALTPCM PCM 1
+SOUND_MIXER_RECLEV (not assigned)
+SOUND_MIXER_IGAIN Capture 0
+SOUND_MIXER_OGAIN Playback 0
+SOUND_MIXER_LINE1 Aux 0
+SOUND_MIXER_LINE2 Aux 1
+SOUND_MIXER_LINE3 Aux 2
+SOUND_MIXER_DIGITAL1 Digital 0
+SOUND_MIXER_DIGITAL2 Digital 1
+SOUND_MIXER_DIGITAL3 Digital 2
+SOUND_MIXER_PHONEIN Phone 0
+SOUND_MIXER_PHONEOUT Phone 1
+SOUND_MIXER_VIDEO Video 0
+SOUND_MIXER_RADIO Radio 0
+SOUND_MIXER_MONITOR Monitor 0
+==================== ===================== =====
+
+The second column is the base-string of the corresponding ALSA
+control. In fact, the controls with ``XXX [Playback|Capture]
+[Volume|Switch]`` will be checked in addition.
+
+The current assignment of these mixer elements is listed in the proc
+file, /proc/asound/cardX/oss_mixer, which will be like the following
+::
+
+ VOLUME "Master" 0
+ BASS "" 0
+ TREBLE "" 0
+ SYNTH "" 0
+ PCM "PCM" 0
+ ...
+
+where the first column is the OSS volume element, the second column
+the base-string of the corresponding ALSA control, and the third the
+control index. When the string is empty, it means that the
+corresponding OSS control is not available.
+
+For changing the assignment, you can write the configuration to this
+proc file. For example, to map "Wave Playback" to the PCM volume,
+send the command like the following:
+::
+
+ % echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/oss_mixer
+
+The command is exactly as same as listed in the proc file. You can
+change one or more elements, one volume per line. In the last
+example, both "Wave Playback Volume" and "Wave Playback Switch" will
+be affected when PCM volume is changed.
+
+Like the case of PCM proc file, the permission of proc files depend on
+the module options of snd. you'll likely need to be superuser for
+sending the command above.
+
+As well as in the case of PCM proc file, you can save and restore the
+current mixer configuration by reading and writing the whole file
+image.
+
+
+Duplex Streams
+==============
+
+Note that when attempting to use a single device file for playback and
+capture, the OSS API provides no way to set the format, sample rate or
+number of channels different in each direction. Thus
+::
+
+ io_handle = open("device", O_RDWR)
+
+will only function correctly if the values are the same in each direction.
+
+To use different values in the two directions, use both
+::
+
+ input_handle = open("device", O_RDONLY)
+ output_handle = open("device", O_WRONLY)
+
+and set the values for the corresponding handle.
+
+
+Unsupported Features
+====================
+
+MMAP on ICE1712 driver
+----------------------
+ICE1712 supports only the unconventional format, interleaved
+10-channels 24bit (packed in 32bit) format. Therefore you cannot mmap
+the buffer as the conventional (mono or 2-channels, 8 or 16bit) format
+on OSS.
--- /dev/null
+==========================
+Notes on Power-Saving Mode
+==========================
+
+AC97 and HD-audio drivers have the automatic power-saving mode.
+This feature is enabled via Kconfig ``CONFIG_SND_AC97_POWER_SAVE``
+and ``CONFIG_SND_HDA_POWER_SAVE`` options, respectively.
+
+With the automatic power-saving, the driver turns off the codec power
+appropriately when no operation is required. When no applications use
+the device and/or no analog loopback is set, the power disablement is
+done fully or partially. It'll save a certain power consumption, thus
+good for laptops (even for desktops).
+
+The time-out for automatic power-off can be specified via ``power_save``
+module option of snd-ac97-codec and snd-hda-intel modules. Specify
+the time-out value in seconds. 0 means to disable the automatic
+power-saving. The default value of timeout is given via
+``CONFIG_SND_AC97_POWER_SAVE_DEFAULT`` and
+``CONFIG_SND_HDA_POWER_SAVE_DEFAULT`` Kconfig options. Setting this to 1
+(the minimum value) isn't recommended because many applications try to
+reopen the device frequently. 10 would be a good choice for normal
+operations.
+
+The ``power_save`` option is exported as writable. This means you can
+adjust the value via sysfs on the fly. For example, to turn on the
+automatic power-save mode with 10 seconds, write to
+``/sys/modules/snd_ac97_codec/parameters/power_save`` (usually as root):
+::
+
+ # echo 10 > /sys/modules/snd_ac97_codec/parameters/power_save
+
+
+Note that you might hear click noise/pop when changing the power
+state. Also, it often takes certain time to wake up from the
+power-down to the active state. These are often hardly to fix, so
+don't report extra bug reports unless you have a fix patch ;-)
+
+For HD-audio interface, there is another module option,
+power_save_controller. This enables/disables the power-save mode of
+the controller side. Setting this on may reduce a bit more power
+consumption, but might result in longer wake-up time and click noise.
+Try to turn it off when you experience such a thing too often.
--- /dev/null
+==========================
+Proc Files of ALSA Drivers
+==========================
+
+Takashi Iwai <tiwai@suse.de>
+
+General
+=======
+
+ALSA has its own proc tree, /proc/asound. Many useful information are
+found in this tree. When you encounter a problem and need debugging,
+check the files listed in the following sections.
+
+Each card has its subtree cardX, where X is from 0 to 7. The
+card-specific files are stored in the ``card*`` subdirectories.
+
+
+Global Information
+==================
+
+cards
+ Shows the list of currently configured ALSA drivers,
+ index, the id string, short and long descriptions.
+
+version
+ Shows the version string and compile date.
+
+modules
+ Lists the module of each card
+
+devices
+ Lists the ALSA native device mappings.
+
+meminfo
+ Shows the status of allocated pages via ALSA drivers.
+ Appears only when ``CONFIG_SND_DEBUG=y``.
+
+hwdep
+ Lists the currently available hwdep devices in format of
+ ``<card>-<device>: <name>``
+
+pcm
+ Lists the currently available PCM devices in format of
+ ``<card>-<device>: <id>: <name> : <sub-streams>``
+
+timer
+ Lists the currently available timer devices
+
+
+oss/devices
+ Lists the OSS device mappings.
+
+oss/sndstat
+ Provides the output compatible with /dev/sndstat.
+ You can symlink this to /dev/sndstat.
+
+
+Card Specific Files
+===================
+
+The card-specific files are found in ``/proc/asound/card*`` directories.
+Some drivers (e.g. cmipci) have their own proc entries for the
+register dump, etc (e.g. ``/proc/asound/card*/cmipci`` shows the register
+dump). These files would be really helpful for debugging.
+
+When PCM devices are available on this card, you can see directories
+like pcm0p or pcm1c. They hold the PCM information for each PCM
+stream. The number after ``pcm`` is the PCM device number from 0, and
+the last ``p`` or ``c`` means playback or capture direction. The files in
+this subtree is described later.
+
+The status of MIDI I/O is found in ``midi*`` files. It shows the device
+name and the received/transmitted bytes through the MIDI device.
+
+When the card is equipped with AC97 codecs, there are ``codec97#*``
+subdirectories (described later).
+
+When the OSS mixer emulation is enabled (and the module is loaded),
+oss_mixer file appears here, too. This shows the current mapping of
+OSS mixer elements to the ALSA control elements. You can change the
+mapping by writing to this device. Read OSS-Emulation.txt for
+details.
+
+
+PCM Proc Files
+==============
+
+``card*/pcm*/info``
+ The general information of this PCM device: card #, device #,
+ substreams, etc.
+
+``card*/pcm*/xrun_debug``
+ This file appears when ``CONFIG_SND_DEBUG=y`` and
+ ``CONFIG_PCM_XRUN_DEBUG=y``.
+ This shows the status of xrun (= buffer overrun/xrun) and
+ invalid PCM position debug/check of ALSA PCM middle layer.
+ It takes an integer value, can be changed by writing to this
+ file, such as::
+
+ # echo 5 > /proc/asound/card0/pcm0p/xrun_debug
+
+ The value consists of the following bit flags:
+
+ * bit 0 = Enable XRUN/jiffies debug messages
+ * bit 1 = Show stack trace at XRUN / jiffies check
+ * bit 2 = Enable additional jiffies check
+
+ When the bit 0 is set, the driver will show the messages to
+ kernel log when an xrun is detected. The debug message is
+ shown also when the invalid H/W pointer is detected at the
+ update of periods (usually called from the interrupt
+ handler).
+
+ When the bit 1 is set, the driver will show the stack trace
+ additionally. This may help the debugging.
+
+ Since 2.6.30, this option can enable the hwptr check using
+ jiffies. This detects spontaneous invalid pointer callback
+ values, but can be lead to too much corrections for a (mostly
+ buggy) hardware that doesn't give smooth pointer updates.
+ This feature is enabled via the bit 2.
+
+``card*/pcm*/sub*/info``
+ The general information of this PCM sub-stream.
+
+``card*/pcm*/sub*/status``
+ The current status of this PCM sub-stream, elapsed time,
+ H/W position, etc.
+
+``card*/pcm*/sub*/hw_params``
+ The hardware parameters set for this sub-stream.
+
+``card*/pcm*/sub*/sw_params``
+ The soft parameters set for this sub-stream.
+
+``card*/pcm*/sub*/prealloc``
+ The buffer pre-allocation information.
+
+``card*/pcm*/sub*/xrun_injection``
+ Triggers an XRUN to the running stream when any value is
+ written to this proc file. Used for fault injection.
+ This entry is write-only.
+
+AC97 Codec Information
+======================
+
+``card*/codec97#*/ac97#?-?``
+ Shows the general information of this AC97 codec chip, such as
+ name, capabilities, set up.
+
+``card*/codec97#0/ac97#?-?+regs``
+ Shows the AC97 register dump. Useful for debugging.
+
+ When CONFIG_SND_DEBUG is enabled, you can write to this file for
+ changing an AC97 register directly. Pass two hex numbers.
+ For example,
+
+::
+
+ # echo 02 9f1f > /proc/asound/card0/codec97#0/ac97#0-0+regs
+
+
+USB Audio Streams
+=================
+
+``card*/stream*``
+ Shows the assignment and the current status of each audio stream
+ of the given card. This information is very useful for debugging.
+
+
+HD-Audio Codecs
+===============
+
+``card*/codec#*``
+ Shows the general codec information and the attribute of each
+ widget node.
+
+``card*/eld#*``
+ Available for HDMI or DisplayPort interfaces.
+ Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
+ and describes its audio capabilities and configurations.
+
+ Some ELD fields may be modified by doing ``echo name hex_value > eld#*``.
+ Only do this if you are sure the HDMI sink provided value is wrong.
+ And if that makes your HDMI audio work, please report to us so that we
+ can fix it in future kernel releases.
+
+
+Sequencer Information
+=====================
+
+seq/drivers
+ Lists the currently available ALSA sequencer drivers.
+
+seq/clients
+ Shows the list of currently available sequencer clients and
+ ports. The connection status and the running status are shown
+ in this file, too.
+
+seq/queues
+ Lists the currently allocated/running sequencer queues.
+
+seq/timer
+ Lists the currently allocated/running sequencer timers.
+
+seq/oss
+ Lists the OSS-compatible sequencer stuffs.
+
+
+Help For Debugging?
+===================
+
+When the problem is related with PCM, first try to turn on xrun_debug
+mode. This will give you the kernel messages when and where xrun
+happened.
+
+If it's really a bug, report it with the following information:
+
+- the name of the driver/card, show in ``/proc/asound/cards``
+- the register dump, if available (e.g. ``card*/cmipci``)
+
+when it's a PCM problem,
+
+- set-up of PCM, shown in hw_parms, sw_params, and status in the PCM
+ sub-stream directory
+
+when it's a mixer problem,
+
+- AC97 proc files, ``codec97#*/*`` files
+
+for USB audio/midi,
+
+- output of ``lsusb -v``
+- ``stream*`` files in card directory
+
+
+The ALSA bug-tracking system is found at:
+https://bugtrack.alsa-project.org/alsa-bug/
--- /dev/null
+===============================
+OSS Sequencer Emulation on ALSA
+===============================
+
+Copyright (c) 1998,1999 by Takashi Iwai
+
+ver.0.1.8; Nov. 16, 1999
+
+Description
+===========
+
+This directory contains the OSS sequencer emulation driver on ALSA. Note
+that this program is still in the development state.
+
+What this does - it provides the emulation of the OSS sequencer, access
+via ``/dev/sequencer`` and ``/dev/music`` devices.
+The most of applications using OSS can run if the appropriate ALSA
+sequencer is prepared.
+
+The following features are emulated by this driver:
+
+* Normal sequencer and MIDI events:
+
+ They are converted to the ALSA sequencer events, and sent to the
+ corresponding port.
+
+* Timer events:
+
+ The timer is not selectable by ioctl. The control rate is fixed to
+ 100 regardless of HZ. That is, even on Alpha system, a tick is always
+ 1/100 second. The base rate and tempo can be changed in ``/dev/music``.
+
+* Patch loading:
+
+ It purely depends on the synth drivers whether it's supported since
+ the patch loading is realized by callback to the synth driver.
+
+* I/O controls:
+
+ Most of controls are accepted. Some controls
+ are dependent on the synth driver, as well as even on original OSS.
+
+Furthermore, you can find the following advanced features:
+
+* Better queue mechanism:
+
+ The events are queued before processing them.
+
+* Multiple applications:
+
+ You can run two or more applications simultaneously (even for OSS
+ sequencer)!
+ However, each MIDI device is exclusive - that is, if a MIDI device
+ is opened once by some application, other applications can't use
+ it. No such a restriction in synth devices.
+
+* Real-time event processing:
+
+ The events can be processed in real time without using out of bound
+ ioctl. To switch to real-time mode, send ABSTIME 0 event. The followed
+ events will be processed in real-time without queued. To switch off the
+ real-time mode, send RELTIME 0 event.
+
+* ``/proc`` interface:
+
+ The status of applications and devices can be shown via
+ ``/proc/asound/seq/oss`` at any time. In the later version,
+ configuration will be changed via ``/proc`` interface, too.
+
+
+Installation
+============
+
+Run configure script with both sequencer support (``--with-sequencer=yes``)
+and OSS emulation (``--with-oss=yes``) options. A module ``snd-seq-oss.o``
+will be created. If the synth module of your sound card supports for OSS
+emulation (so far, only Emu8000 driver), this module will be loaded
+automatically.
+Otherwise, you need to load this module manually.
+
+At beginning, this module probes all the MIDI ports which have been
+already connected to the sequencer. Once after that, the creation and deletion
+of ports are watched by announcement mechanism of ALSA sequencer.
+
+The available synth and MIDI devices can be found in proc interface.
+Run ``cat /proc/asound/seq/oss``, and check the devices. For example,
+if you use an AWE64 card, you'll see like the following:
+::
+
+ OSS sequencer emulation version 0.1.8
+ ALSA client number 63
+ ALSA receiver port 0
+
+ Number of applications: 0
+
+ Number of synth devices: 1
+ synth 0: [EMU8000]
+ type 0x1 : subtype 0x20 : voices 32
+ capabilties : ioctl enabled / load_patch enabled
+
+ Number of MIDI devices: 3
+ midi 0: [Emu8000 Port-0] ALSA port 65:0
+ capability write / opened none
+
+ midi 1: [Emu8000 Port-1] ALSA port 65:1
+ capability write / opened none
+
+ midi 2: [0: MPU-401 (UART)] ALSA port 64:0
+ capability read/write / opened none
+
+Note that the device number may be different from the information of
+``/proc/asound/oss-devices`` or ones of the original OSS driver.
+Use the device number listed in ``/proc/asound/seq/oss``
+to play via OSS sequencer emulation.
+
+Using Synthesizer Devices
+=========================
+
+Run your favorite program. I've tested playmidi-2.4, awemidi-0.4.3, gmod-3.1
+and xmp-1.1.5. You can load samples via ``/dev/sequencer`` like sfxload,
+too.
+
+If the lowlevel driver supports multiple access to synth devices (like
+Emu8000 driver), two or more applications are allowed to run at the same
+time.
+
+Using MIDI Devices
+==================
+
+So far, only MIDI output was tested. MIDI input was not checked at all,
+but hopefully it will work. Use the device number listed in
+``/proc/asound/seq/oss``.
+Be aware that these numbers are mostly different from the list in
+``/proc/asound/oss-devices``.
+
+Module Options
+==============
+
+The following module options are available:
+
+maxqlen
+ specifies the maximum read/write queue length. This queue is private
+ for OSS sequencer, so that it is independent from the queue length of ALSA
+ sequencer. Default value is 1024.
+
+seq_oss_debug
+ specifies the debug level and accepts zero (= no debug message) or
+ positive integer. Default value is 0.
+
+Queue Mechanism
+===============
+
+OSS sequencer emulation uses an ALSA priority queue. The
+events from ``/dev/sequencer`` are processed and put onto the queue
+specified by module option.
+
+All the events from ``/dev/sequencer`` are parsed at beginning.
+The timing events are also parsed at this moment, so that the events may
+be processed in real-time. Sending an event ABSTIME 0 switches the operation
+mode to real-time mode, and sending an event RELTIME 0 switches it off.
+In the real-time mode, all events are dispatched immediately.
+
+The queued events are dispatched to the corresponding ALSA sequencer
+ports after scheduled time by ALSA sequencer dispatcher.
+
+If the write-queue is full, the application sleeps until a certain amount
+(as default one half) becomes empty in blocking mode. The synchronization
+to write timing was implemented, too.
+
+The input from MIDI devices or echo-back events are stored on read FIFO
+queue. If application reads ``/dev/sequencer`` in blocking mode, the
+process will be awaked.
+
+Interface to Synthesizer Device
+===============================
+
+Registration
+------------
+
+To register an OSS synthesizer device, use snd_seq_oss_synth_register()
+function:
+::
+
+ int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
+ snd_seq_oss_callback_t *oper, void *private_data)
+
+The arguments ``name``, ``type``, ``subtype`` and ``nvoices``
+are used for making the appropriate synth_info structure for ioctl. The
+return value is an index number of this device. This index must be remembered
+for unregister. If registration is failed, -errno will be returned.
+
+To release this device, call snd_seq_oss_synth_unregister() function:
+::
+
+ int snd_seq_oss_synth_unregister(int index)
+
+where the ``index`` is the index number returned by register function.
+
+Callbacks
+---------
+
+OSS synthesizer devices have capability for sample downloading and ioctls
+like sample reset. In OSS emulation, these special features are realized
+by using callbacks. The registration argument oper is used to specify these
+callbacks. The following callback functions must be defined:
+::
+
+ snd_seq_oss_callback_t:
+ int (*open)(snd_seq_oss_arg_t *p, void *closure);
+ int (*close)(snd_seq_oss_arg_t *p);
+ int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+ int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+ int (*reset)(snd_seq_oss_arg_t *p);
+
+Except for ``open`` and ``close`` callbacks, they are allowed to be NULL.
+
+Each callback function takes the argument type ``snd_seq_oss_arg_t`` as the
+first argument.
+::
+
+ struct snd_seq_oss_arg_t {
+ int app_index;
+ int file_mode;
+ int seq_mode;
+ snd_seq_addr_t addr;
+ void *private_data;
+ int event_passing;
+ };
+
+The first three fields, ``app_index``, ``file_mode`` and ``seq_mode``
+are initialized by OSS sequencer. The ``app_index`` is the application
+index which is unique to each application opening OSS sequencer. The
+``file_mode`` is bit-flags indicating the file operation mode. See
+``seq_oss.h`` for its meaning. The ``seq_mode`` is sequencer operation
+mode. In the current version, only ``SND_OSSSEQ_MODE_SYNTH`` is used.
+
+The next two fields, ``addr`` and ``private_data``, must be
+filled by the synth driver at open callback. The ``addr`` contains
+the address of ALSA sequencer port which is assigned to this device. If
+the driver allocates memory for ``private_data``, it must be released
+in close callback by itself.
+
+The last field, ``event_passing``, indicates how to translate note-on
+/ off events. In ``PROCESS_EVENTS`` mode, the note 255 is regarded
+as velocity change, and key pressure event is passed to the port. In
+``PASS_EVENTS`` mode, all note on/off events are passed to the port
+without modified. ``PROCESS_KEYPRESS`` mode checks the note above 128
+and regards it as key pressure event (mainly for Emu8000 driver).
+
+Open Callback
+-------------
+
+The ``open`` is called at each time this device is opened by an application
+using OSS sequencer. This must not be NULL. Typically, the open callback
+does the following procedure:
+
+#. Allocate private data record.
+#. Create an ALSA sequencer port.
+#. Set the new port address on ``arg->addr``.
+#. Set the private data record pointer on ``arg->private_data``.
+
+Note that the type bit-flags in port_info of this synth port must NOT contain
+``TYPE_MIDI_GENERIC``
+bit. Instead, ``TYPE_SPECIFIC`` should be used. Also, ``CAP_SUBSCRIPTION``
+bit should NOT be included, too. This is necessary to tell it from other
+normal MIDI devices. If the open procedure succeeded, return zero. Otherwise,
+return -errno.
+
+Ioctl Callback
+--------------
+
+The ``ioctl`` callback is called when the sequencer receives device-specific
+ioctls. The following two ioctls should be processed by this callback:
+
+IOCTL_SEQ_RESET_SAMPLES
+ reset all samples on memory -- return 0
+
+IOCTL_SYNTH_MEMAVL
+ return the available memory size
+
+FM_4OP_ENABLE
+ can be ignored usually
+
+The other ioctls are processed inside the sequencer without passing to
+the lowlevel driver.
+
+Load_Patch Callback
+-------------------
+
+The ``load_patch`` callback is used for sample-downloading. This callback
+must read the data on user-space and transfer to each device. Return 0
+if succeeded, and -errno if failed. The format argument is the patch key
+in patch_info record. The buf is user-space pointer where patch_info record
+is stored. The offs can be ignored. The count is total data size of this
+sample data.
+
+Close Callback
+--------------
+
+The ``close`` callback is called when this device is closed by the
+application. If any private data was allocated in open callback, it must
+be released in the close callback. The deletion of ALSA port should be
+done here, too. This callback must not be NULL.
+
+Reset Callback
+--------------
+
+The ``reset`` callback is called when sequencer device is reset or
+closed by applications. The callback should turn off the sounds on the
+relevant port immediately, and initialize the status of the port. If this
+callback is undefined, OSS seq sends a ``HEARTBEAT`` event to the
+port.
+
+Events
+======
+
+Most of the events are processed by sequencer and translated to the adequate
+ALSA sequencer events, so that each synth device can receive by input_event
+callback of ALSA sequencer port. The following ALSA events should be
+implemented by the driver:
+
+============= ===================
+ALSA event Original OSS events
+============= ===================
+NOTEON SEQ_NOTEON, MIDI_NOTEON
+NOTE SEQ_NOTEOFF, MIDI_NOTEOFF
+KEYPRESS MIDI_KEY_PRESSURE
+CHANPRESS SEQ_AFTERTOUCH, MIDI_CHN_PRESSURE
+PGMCHANGE SEQ_PGMCHANGE, MIDI_PGM_CHANGE
+PITCHBEND SEQ_CONTROLLER(CTRL_PITCH_BENDER),
+ MIDI_PITCH_BEND
+CONTROLLER MIDI_CTL_CHANGE,
+ SEQ_BALANCE (with CTL_PAN)
+CONTROL14 SEQ_CONTROLLER
+REGPARAM SEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)
+SYSEX SEQ_SYSEX
+============= ===================
+
+The most of these behavior can be realized by MIDI emulation driver
+included in the Emu8000 lowlevel driver. In the future release, this module
+will be independent.
+
+Some OSS events (``SEQ_PRIVATE`` and ``SEQ_VOLUME`` events) are passed as event
+type SND_SEQ_OSS_PRIVATE. The OSS sequencer passes these event 8 byte
+packets without any modification. The lowlevel driver should process these
+events appropriately.
+
+Interface to MIDI Device
+========================
+
+Since the OSS emulation probes the creation and deletion of ALSA MIDI
+sequencer ports automatically by receiving announcement from ALSA
+sequencer, the MIDI devices don't need to be registered explicitly
+like synth devices.
+However, the MIDI port_info registered to ALSA sequencer must include
+a group name ``SND_SEQ_GROUP_DEVICE`` and a capability-bit
+``CAP_READ`` or ``CAP_WRITE``. Also, subscription capabilities,
+``CAP_SUBS_READ`` or ``CAP_SUBS_WRITE``, must be defined, too. If
+these conditions are not satisfied, the port is not registered as OSS
+sequencer MIDI device.
+
+The events via MIDI devices are parsed in OSS sequencer and converted
+to the corresponding ALSA sequencer events. The input from MIDI sequencer
+is also converted to MIDI byte events by OSS sequencer. This works just
+a reverse way of seq_midi module.
+
+Known Problems / TODO's
+=======================
+
+* Patch loading via ALSA instrument layer is not implemented yet.
+
--- /dev/null
+=====================
+ALSA PCM Timestamping
+=====================
+
+The ALSA API can provide two different system timestamps:
+
+- Trigger_tstamp is the system time snapshot taken when the .trigger
+ callback is invoked. This snapshot is taken by the ALSA core in the
+ general case, but specific hardware may have synchronization
+ capabilities or conversely may only be able to provide a correct
+ estimate with a delay. In the latter two cases, the low-level driver
+ is responsible for updating the trigger_tstamp at the most appropriate
+ and precise moment. Applications should not rely solely on the first
+ trigger_tstamp but update their internal calculations if the driver
+ provides a refined estimate with a delay.
+
+- tstamp is the current system timestamp updated during the last
+ event or application query.
+ The difference (tstamp - trigger_tstamp) defines the elapsed time.
+
+The ALSA API provides two basic pieces of information, avail
+and delay, which combined with the trigger and current system
+timestamps allow for applications to keep track of the 'fullness' of
+the ring buffer and the amount of queued samples.
+
+The use of these different pointers and time information depends on
+the application needs:
+
+- ``avail`` reports how much can be written in the ring buffer
+- ``delay`` reports the time it will take to hear a new sample after all
+ queued samples have been played out.
+
+When timestamps are enabled, the avail/delay information is reported
+along with a snapshot of system time. Applications can select from
+``CLOCK_REALTIME`` (NTP corrections including going backwards),
+``CLOCK_MONOTONIC`` (NTP corrections but never going backwards),
+``CLOCK_MONOTIC_RAW`` (without NTP corrections) and change the mode
+dynamically with sw_params
+
+
+The ALSA API also provide an audio_tstamp which reflects the passage
+of time as measured by different components of audio hardware. In
+ascii-art, this could be represented as follows (for the playback
+case):
+::
+
+ --------------------------------------------------------------> time
+ ^ ^ ^ ^ ^
+ | | | | |
+ analog link dma app FullBuffer
+ time time time time time
+ | | | | |
+ |< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
+ |<----------------- delay---------------------->| |
+ |<----ring buffer length---->|
+
+
+The analog time is taken at the last stage of the playback, as close
+as possible to the actual transducer
+
+The link time is taken at the output of the SoC/chipset as the samples
+are pushed on a link. The link time can be directly measured if
+supported in hardware by sample counters or wallclocks (e.g. with
+HDAudio 24MHz or PTP clock for networked solutions) or indirectly
+estimated (e.g. with the frame counter in USB).
+
+The DMA time is measured using counters - typically the least reliable
+of all measurements due to the bursty nature of DMA transfers.
+
+The app time corresponds to the time tracked by an application after
+writing in the ring buffer.
+
+The application can query the hardware capabilities, define which
+audio time it wants reported by selecting the relevant settings in
+audio_tstamp_config fields, thus get an estimate of the timestamp
+accuracy. It can also request the delay-to-analog be included in the
+measurement. Direct access to the link time is very interesting on
+platforms that provide an embedded DSP; measuring directly the link
+time with dedicated hardware, possibly synchronized with system time,
+removes the need to keep track of internal DSP processing times and
+latency.
+
+In case the application requests an audio tstamp that is not supported
+in hardware/low-level driver, the type is overridden as DEFAULT and the
+timestamp will report the DMA time based on the hw_pointer value.
+
+For backwards compatibility with previous implementations that did not
+provide timestamp selection, with a zero-valued COMPAT timestamp type
+the results will default to the HDAudio wall clock for playback
+streams and to the DMA time (hw_ptr) in all other cases.
+
+The audio timestamp accuracy can be returned to user-space, so that
+appropriate decisions are made:
+
+- for dma time (default), the granularity of the transfers can be
+ inferred from the steps between updates and in turn provide
+ information on how much the application pointer can be rewound
+ safely.
+
+- the link time can be used to track long-term drifts between audio
+ and system time using the (tstamp-trigger_tstamp)/audio_tstamp
+ ratio, the precision helps define how much smoothing/low-pass
+ filtering is required. The link time can be either reset on startup
+ or reported as is (the latter being useful to compare progress of
+ different streams - but may require the wallclock to be always
+ running and not wrap-around during idle periods). If supported in
+ hardware, the absolute link time could also be used to define a
+ precise start time (patches WIP)
+
+- including the delay in the audio timestamp may
+ counter-intuitively not increase the precision of timestamps, e.g. if a
+ codec includes variable-latency DSP processing or a chain of
+ hardware components the delay is typically not known with precision.
+
+The accuracy is reported in nanosecond units (using an unsigned 32-bit
+word), which gives a max precision of 4.29s, more than enough for
+audio applications...
+
+Due to the varied nature of timestamping needs, even for a single
+application, the audio_tstamp_config can be changed dynamically. In
+the ``STATUS`` ioctl, the parameters are read-only and do not allow for
+any application selection. To work around this limitation without
+impacting legacy applications, a new ``STATUS_EXT`` ioctl is introduced
+with read/write parameters. ALSA-lib will be modified to make use of
+``STATUS_EXT`` and effectively deprecate ``STATUS``.
+
+The ALSA API only allows for a single audio timestamp to be reported
+at a time. This is a conscious design decision, reading the audio
+timestamps from hardware registers or from IPC takes time, the more
+timestamps are read the more imprecise the combined measurements
+are. To avoid any interpretation issues, a single (system, audio)
+timestamp is reported. Applications that need different timestamps
+will be required to issue multiple queries and perform an
+interpolation of the results
+
+In some hardware-specific configuration, the system timestamp is
+latched by a low-level audio subsystem, and the information provided
+back to the driver. Due to potential delays in the communication with
+the hardware, there is a risk of misalignment with the avail and delay
+information. To make sure applications are not confused, a
+driver_timestamp field is added in the snd_pcm_status structure; this
+timestamp shows when the information is put together by the driver
+before returning from the ``STATUS`` and ``STATUS_EXT`` ioctl. in most cases
+this driver_timestamp will be identical to the regular system tstamp.
+
+Examples of typestamping with HDaudio:
+
+1. DMA timestamp, no compensation for DMA+analog delay
+::
+
+ $ ./audio_time -p --ts_type=1
+ playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
+ playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
+ playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
+ playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
+ playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
+ playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
+
+2. DMA timestamp, compensation for DMA+analog delay
+::
+
+ $ ./audio_time -p --ts_type=1 -d
+ playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
+ playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
+ playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
+ playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
+ playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
+
+3. link timestamp, compensation for DMA+analog delay
+::
+
+ $ ./audio_time -p --ts_type=2 -d
+ playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
+ playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
+ playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
+ playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
+ playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
+ playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
+
+Example 1 shows that the timestamp at the DMA level is close to 1ms
+ahead of the actual playback time (as a side time this sort of
+measurement can help define rewind safeguards). Compensating for the
+DMA-link delay in example 2 helps remove the hardware buffering but
+the information is still very jittery, with up to one sample of
+error. In example 3 where the timestamps are measured with the link
+wallclock, the timestamps show a monotonic behavior and a lower
+dispersion.
+
+Example 3 and 4 are with USB audio class. Example 3 shows a high
+offset between audio time and system time due to buffering. Example 4
+shows how compensating for the delay exposes a 1ms accuracy (due to
+the use of the frame counter by the driver)
+
+Example 3: DMA timestamp, no compensation for delay, delta of ~5ms
+::
+
+ $ ./audio_time -p -Dhw:1 -t1
+ playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
+ playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
+ playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
+ playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
+ playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
+ playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
+ playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
+
+Example 4: DMA timestamp, compensation for delay, delay of ~1ms
+::
+
+ $ ./audio_time -p -Dhw:1 -t1 -d
+ playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
+ playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
+ playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
+ playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
+ playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
+ playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847
--- /dev/null
+======================================
+HD-Audio Codec-Specific Mixer Controls
+======================================
+
+
+This file explains the codec-specific mixer controls.
+
+Realtek codecs
+--------------
+
+Channel Mode
+ This is an enum control to change the surround-channel setup,
+ appears only when the surround channels are available.
+ It gives the number of channels to be used, "2ch", "4ch", "6ch",
+ and "8ch". According to the configuration, this also controls the
+ jack-retasking of multi-I/O jacks.
+
+Auto-Mute Mode
+ This is an enum control to change the auto-mute behavior of the
+ headphone and line-out jacks. If built-in speakers and headphone
+ and/or line-out jacks are available on a machine, this controls
+ appears.
+ When there are only either headphones or line-out jacks, it gives
+ "Disabled" and "Enabled" state. When enabled, the speaker is muted
+ automatically when a jack is plugged.
+
+ When both headphone and line-out jacks are present, it gives
+ "Disabled", "Speaker Only" and "Line-Out+Speaker". When
+ speaker-only is chosen, plugging into a headphone or a line-out jack
+ mutes the speakers, but not line-outs. When line-out+speaker is
+ selected, plugging to a headphone jack mutes both speakers and
+ line-outs.
+
+
+IDT/Sigmatel codecs
+-------------------
+
+Analog Loopback
+ This control enables/disables the analog-loopback circuit. This
+ appears only when "loopback" is set to true in a codec hint
+ (see HD-Audio.txt). Note that on some codecs the analog-loopback
+ and the normal PCM playback are exclusive, i.e. when this is on, you
+ won't hear any PCM stream.
+
+Swap Center/LFE
+ Swaps the center and LFE channel order. Normally, the left
+ corresponds to the center and the right to the LFE. When this is
+ ON, the left to the LFE and the right to the center.
+
+Headphone as Line Out
+ When this control is ON, treat the headphone jacks as line-out
+ jacks. That is, the headphone won't auto-mute the other line-outs,
+ and no HP-amp is set to the pins.
+
+Mic Jack Mode, Line Jack Mode, etc
+ These enum controls the direction and the bias of the input jack
+ pins. Depending on the jack type, it can set as "Mic In" and "Line
+ In", for determining the input bias, or it can be set to "Line Out"
+ when the pin is a multi-I/O jack for surround channels.
+
+
+VIA codecs
+----------
+
+Smart 5.1
+ An enum control to re-task the multi-I/O jacks for surround outputs.
+ When it's ON, the corresponding input jacks (usually a line-in and a
+ mic-in) are switched as the surround and the CLFE output jacks.
+
+Independent HP
+ When this enum control is enabled, the headphone output is routed
+ from an individual stream (the third PCM such as hw:0,2) instead of
+ the primary stream. In the case the headphone DAC is shared with a
+ side or a CLFE-channel DAC, the DAC is switched to the headphone
+ automatically.
+
+Loopback Mixing
+ An enum control to determine whether the analog-loopback route is
+ enabled or not. When it's enabled, the analog-loopback is mixed to
+ the front-channel. Also, the same route is used for the headphone
+ and speaker outputs. As a side-effect, when this mode is set, the
+ individual volume controls will be no longer available for
+ headphones and speakers because there is only one DAC connected to a
+ mixer widget.
+
+Dynamic Power-Control
+ This control determines whether the dynamic power-control per jack
+ detection is enabled or not. When enabled, the widgets power state
+ (D0/D3) are changed dynamically depending on the jack plugging
+ state for saving power consumptions. However, if your system
+ doesn't provide a proper jack-detection, this won't work; in such a
+ case, turn this control OFF.
+
+Jack Detect
+ This control is provided only for VT1708 codec which gives no proper
+ unsolicited event per jack plug. When this is on, the driver polls
+ the jack detection so that the headphone auto-mute can work, while
+ turning this off would reduce the power consumption.
+
+
+Conexant codecs
+---------------
+
+Auto-Mute Mode
+ See Reatek codecs.
+
+
+Analog codecs
+--------------
+
+Channel Mode
+ This is an enum control to change the surround-channel setup,
+ appears only when the surround channels are available.
+ It gives the number of channels to be used, "2ch", "4ch" and "6ch".
+ According to the configuration, this also controls the
+ jack-retasking of multi-I/O jacks.
+
+Independent HP
+ When this enum control is enabled, the headphone output is routed
+ from an individual stream (the third PCM such as hw:0,2) instead of
+ the primary stream.
--- /dev/null
+=======================
+HD-Audio DP-MST Support
+=======================
+
+To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
+and dynamic pcm assignment.
+
+Virtual pin is an extension of per_pin. The most difference of DP MST
+from legacy is that DP MST introduces device entry. Each pin can contain
+several device entries. Each device entry behaves as a pin.
+
+As each pin may contain several device entries and each codec may contain
+several pins, if we use one pcm per per_pin, there will be many PCMs.
+The new solution is to create a few PCMs and to dynamically bind pcm to
+per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
+the new solution.
+
+PCM
+===
+To be added
+
+
+Jack
+====
+
+Presume:
+ - MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
+ - NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
+
+So there are the following scenarios:
+ a. MST (&& dyn_pcm_assign && acomp)
+ b. NON-MST && dyn_pcm_assign && acomp
+ c. NON-MST && !dyn_pcm_assign && !acomp
+
+Below discussion will ignore MST and NON-MST difference as it doesn't
+impact on jack handling too much.
+
+Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
+a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
+
+For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
+
+For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
+when monitor is hotplugged.
+
+
+Build Jack
+----------
+
+- dyn_pcm_assign
+
+ Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
+
+- !dyn_pcm_assign
+
+ Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
+
+
+Unsolicited Event Enabling
+--------------------------
+Enable unsolicited event if !acomp.
+
+
+Monitor Hotplug Event Handling
+------------------------------
+- acomp
+
+ pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
+ sync_eld_via_acomp().
+
+ Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
+ both dyn_pcm_assign and !dyn_pcm_assign
+
+- !acomp
+
+ hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
+ hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
+
+ Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
+ Use hda_jack mechanism to handle jack events.
+
+
+Others to be added later
+========================
--- /dev/null
+HD-Audio
+========
+
+.. toctree::
+ :maxdepth: 2
+
+ notes
+ models
+ controls
+ dp-mst
--- /dev/null
+==============================
+HD-Audio Codec-Specific Models
+==============================
+
+ALC880
+======
+3stack
+ 3-jack in back and a headphone out
+3stack-digout
+ 3-jack in back, a HP out and a SPDIF out
+5stack
+ 5-jack in back, 2-jack in front
+5stack-digout
+ 5-jack in back, 2-jack in front, a SPDIF out
+6stack
+ 6-jack in back, 2-jack in front
+6stack-digout
+ 6-jack with a SPDIF out
+
+ALC260
+======
+gpio1
+ Enable GPIO1
+coef
+ Enable EAPD via COEF table
+fujitsu
+ Quirk for FSC S7020
+fujitsu-jwse
+ Quirk for FSC S7020 with jack modes and HP mic support
+
+ALC262
+======
+inv-dmic
+ Inverted internal mic workaround
+
+ALC267/268
+==========
+inv-dmic
+ Inverted internal mic workaround
+hp-eapd
+ Disable HP EAPD on NID 0x15
+
+ALC22x/23x/25x/269/27x/28x/29x (and vendor-specific ALC3xxx models)
+===================================================================
+laptop-amic
+ Laptops with analog-mic input
+laptop-dmic
+ Laptops with digital-mic input
+alc269-dmic
+ Enable ALC269(VA) digital mic workaround
+alc271-dmic
+ Enable ALC271X digital mic workaround
+inv-dmic
+ Inverted internal mic workaround
+headset-mic
+ Indicates a combined headset (headphone+mic) jack
+headset-mode
+ More comprehensive headset support for ALC269 & co
+headset-mode-no-hp-mic
+ Headset mode support without headphone mic
+lenovo-dock
+ Enables docking station I/O for some Lenovos
+hp-gpio-led
+ GPIO LED support on HP laptops
+dell-headset-multi
+ Headset jack, which can also be used as mic-in
+dell-headset-dock
+ Headset jack (without mic-in), and also dock I/O
+alc283-dac-wcaps
+ Fixups for Chromebook with ALC283
+alc283-sense-combo
+ Combo jack sensing on ALC283
+tpt440-dock
+ Pin configs for Lenovo Thinkpad Dock support
+
+ALC66x/67x/892
+==============
+mario
+ Chromebook mario model fixup
+asus-mode1
+ ASUS
+asus-mode2
+ ASUS
+asus-mode3
+ ASUS
+asus-mode4
+ ASUS
+asus-mode5
+ ASUS
+asus-mode6
+ ASUS
+asus-mode7
+ ASUS
+asus-mode8
+ ASUS
+inv-dmic
+ Inverted internal mic workaround
+dell-headset-multi
+ Headset jack, which can also be used as mic-in
+
+ALC680
+======
+N/A
+
+ALC88x/898/1150
+======================
+acer-aspire-4930g
+ Acer Aspire 4930G/5930G/6530G/6930G/7730G
+acer-aspire-8930g
+ Acer Aspire 8330G/6935G
+acer-aspire
+ Acer Aspire others
+inv-dmic
+ Inverted internal mic workaround
+no-primary-hp
+ VAIO Z/VGC-LN51JGB workaround (for fixed speaker DAC)
+
+ALC861/660
+==========
+N/A
+
+ALC861VD/660VD
+==============
+N/A
+
+CMI9880
+=======
+minimal
+ 3-jack in back
+min_fp
+ 3-jack in back, 2-jack in front
+full
+ 6-jack in back, 2-jack in front
+full_dig
+ 6-jack in back, 2-jack in front, SPDIF I/O
+allout
+ 5-jack in back, 2-jack in front, SPDIF out
+auto
+ auto-config reading BIOS (default)
+
+AD1882 / AD1882A
+================
+3stack
+ 3-stack mode
+3stack-automute
+ 3-stack with automute front HP (default)
+6stack
+ 6-stack mode
+
+AD1884A / AD1883 / AD1984A / AD1984B
+====================================
+desktop 3-stack desktop (default)
+laptop laptop with HP jack sensing
+mobile mobile devices with HP jack sensing
+thinkpad Lenovo Thinkpad X300
+touchsmart HP Touchsmart
+
+AD1884
+======
+N/A
+
+AD1981
+======
+basic 3-jack (default)
+hp HP nx6320
+thinkpad Lenovo Thinkpad T60/X60/Z60
+toshiba Toshiba U205
+
+AD1983
+======
+N/A
+
+AD1984
+======
+basic default configuration
+thinkpad Lenovo Thinkpad T61/X61
+dell_desktop Dell T3400
+
+AD1986A
+=======
+3stack
+ 3-stack, shared surrounds
+laptop
+ 2-channel only (FSC V2060, Samsung M50)
+laptop-imic
+ 2-channel with built-in mic
+eapd
+ Turn on EAPD constantly
+
+AD1988/AD1988B/AD1989A/AD1989B
+==============================
+6stack
+ 6-jack
+6stack-dig
+ ditto with SPDIF
+3stack
+ 3-jack
+3stack-dig
+ ditto with SPDIF
+laptop
+ 3-jack with hp-jack automute
+laptop-dig
+ ditto with SPDIF
+auto
+ auto-config reading BIOS (default)
+
+Conexant 5045
+=============
+laptop-hpsense
+ Laptop with HP sense (old model laptop)
+laptop-micsense
+ Laptop with Mic sense (old model fujitsu)
+laptop-hpmicsense
+ Laptop with HP and Mic senses
+benq
+ Benq R55E
+laptop-hp530
+ HP 530 laptop
+test
+ for testing/debugging purpose, almost all controls can be
+ adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y
+
+Conexant 5047
+=============
+laptop
+ Basic Laptop config
+laptop-hp
+ Laptop config for some HP models (subdevice 30A5)
+laptop-eapd
+ Laptop config with EAPD support
+test
+ for testing/debugging purpose, almost all controls can be
+ adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y
+
+Conexant 5051
+=============
+laptop
+ Basic Laptop config (default)
+hp
+ HP Spartan laptop
+hp-dv6736
+ HP dv6736
+hp-f700
+ HP Compaq Presario F700
+ideapad
+ Lenovo IdeaPad laptop
+toshiba
+ Toshiba Satellite M300
+
+Conexant 5066
+=============
+laptop
+ Basic Laptop config (default)
+hp-laptop
+ HP laptops, e g G60
+asus
+ Asus K52JU, Lenovo G560
+dell-laptop
+ Dell laptops
+dell-vostro
+ Dell Vostro
+olpc-xo-1_5
+ OLPC XO 1.5
+ideapad
+ Lenovo IdeaPad U150
+thinkpad
+ Lenovo Thinkpad
+
+STAC9200
+========
+ref
+ Reference board
+oqo
+ OQO Model 2
+dell-d21
+ Dell (unknown)
+dell-d22
+ Dell (unknown)
+dell-d23
+ Dell (unknown)
+dell-m21
+ Dell Inspiron 630m, Dell Inspiron 640m
+dell-m22
+ Dell Latitude D620, Dell Latitude D820
+dell-m23
+ Dell XPS M1710, Dell Precision M90
+dell-m24
+ Dell Latitude 120L
+dell-m25
+ Dell Inspiron E1505n
+dell-m26
+ Dell Inspiron 1501
+dell-m27
+ Dell Inspiron E1705/9400
+gateway-m4
+ Gateway laptops with EAPD control
+gateway-m4-2
+ Gateway laptops with EAPD control
+panasonic
+ Panasonic CF-74
+auto
+ BIOS setup (default)
+
+STAC9205/9254
+=============
+ref
+ Reference board
+dell-m42
+ Dell (unknown)
+dell-m43
+ Dell Precision
+dell-m44
+ Dell Inspiron
+eapd
+ Keep EAPD on (e.g. Gateway T1616)
+auto
+ BIOS setup (default)
+
+STAC9220/9221
+=============
+ref
+ Reference board
+3stack
+ D945 3stack
+5stack
+ D945 5stack + SPDIF
+intel-mac-v1
+ Intel Mac Type 1
+intel-mac-v2
+ Intel Mac Type 2
+intel-mac-v3
+ Intel Mac Type 3
+intel-mac-v4
+ Intel Mac Type 4
+intel-mac-v5
+ Intel Mac Type 5
+intel-mac-auto
+ Intel Mac (detect type according to subsystem id)
+macmini
+ Intel Mac Mini (equivalent with type 3)
+macbook
+ Intel Mac Book (eq. type 5)
+macbook-pro-v1
+ Intel Mac Book Pro 1st generation (eq. type 3)
+macbook-pro
+ Intel Mac Book Pro 2nd generation (eq. type 3)
+imac-intel
+ Intel iMac (eq. type 2)
+imac-intel-20
+ Intel iMac (newer version) (eq. type 3)
+ecs202
+ ECS/PC chips
+dell-d81
+ Dell (unknown)
+dell-d82
+ Dell (unknown)
+dell-m81
+ Dell (unknown)
+dell-m82
+ Dell XPS M1210
+auto
+ BIOS setup (default)
+
+STAC9202/9250/9251
+==================
+ref
+ Reference board, base config
+m1
+ Some Gateway MX series laptops (NX560XL)
+m1-2
+ Some Gateway MX series laptops (MX6453)
+m2
+ Some Gateway MX series laptops (M255)
+m2-2
+ Some Gateway MX series laptops
+m3
+ Some Gateway MX series laptops
+m5
+ Some Gateway MX series laptops (MP6954)
+m6
+ Some Gateway NX series laptops
+auto
+ BIOS setup (default)
+
+STAC9227/9228/9229/927x
+=======================
+ref
+ Reference board
+ref-no-jd
+ Reference board without HP/Mic jack detection
+3stack
+ D965 3stack
+5stack
+ D965 5stack + SPDIF
+5stack-no-fp
+ D965 5stack without front panel
+dell-3stack
+ Dell Dimension E520
+dell-bios
+ Fixes with Dell BIOS setup
+dell-bios-amic
+ Fixes with Dell BIOS setup including analog mic
+volknob
+ Fixes with volume-knob widget 0x24
+auto
+ BIOS setup (default)
+
+STAC92HD71B*
+============
+ref
+ Reference board
+dell-m4-1
+ Dell desktops
+dell-m4-2
+ Dell desktops
+dell-m4-3
+ Dell desktops
+hp-m4
+ HP mini 1000
+hp-dv5
+ HP dv series
+hp-hdx
+ HP HDX series
+hp-dv4-1222nr
+ HP dv4-1222nr (with LED support)
+auto
+ BIOS setup (default)
+
+STAC92HD73*
+===========
+ref
+ Reference board
+no-jd
+ BIOS setup but without jack-detection
+intel
+ Intel DG45* mobos
+dell-m6-amic
+ Dell desktops/laptops with analog mics
+dell-m6-dmic
+ Dell desktops/laptops with digital mics
+dell-m6
+ Dell desktops/laptops with both type of mics
+dell-eq
+ Dell desktops/laptops
+alienware
+ Alienware M17x
+auto
+ BIOS setup (default)
+
+STAC92HD83*
+===========
+ref
+ Reference board
+mic-ref
+ Reference board with power management for ports
+dell-s14
+ Dell laptop
+dell-vostro-3500
+ Dell Vostro 3500 laptop
+hp-dv7-4000
+ HP dv-7 4000
+hp_cNB11_intquad
+ HP CNB models with 4 speakers
+hp-zephyr
+ HP Zephyr
+hp-led
+ HP with broken BIOS for mute LED
+hp-inv-led
+ HP with broken BIOS for inverted mute LED
+hp-mic-led
+ HP with mic-mute LED
+headset-jack
+ Dell Latitude with a 4-pin headset jack
+hp-envy-bass
+ Pin fixup for HP Envy bass speaker (NID 0x0f)
+hp-envy-ts-bass
+ Pin fixup for HP Envy TS bass speaker (NID 0x10)
+hp-bnb13-eq
+ Hardware equalizer setup for HP laptops
+auto
+ BIOS setup (default)
+
+STAC92HD95
+==========
+hp-led
+ LED support for HP laptops
+hp-bass
+ Bass HPF setup for HP Spectre 13
+
+STAC9872
+========
+vaio
+ VAIO laptop without SPDIF
+auto
+ BIOS setup (default)
+
+Cirrus Logic CS4206/4207
+========================
+mbp55
+ MacBook Pro 5,5
+imac27
+ IMac 27 Inch
+auto
+ BIOS setup (default)
+
+Cirrus Logic CS4208
+===================
+mba6
+ MacBook Air 6,1 and 6,2
+gpio0
+ Enable GPIO 0 amp
+auto
+ BIOS setup (default)
+
+VIA VT17xx/VT18xx/VT20xx
+========================
+auto
+ BIOS setup (default)
--- /dev/null
+=============================
+More Notes on HD-Audio Driver
+=============================
+
+Takashi Iwai <tiwai@suse.de>
+
+
+General
+=======
+
+HD-audio is the new standard on-board audio component on modern PCs
+after AC97. Although Linux has been supporting HD-audio since long
+time ago, there are often problems with new machines. A part of the
+problem is broken BIOS, and the rest is the driver implementation.
+This document explains the brief trouble-shooting and debugging
+methods for the HD-audio hardware.
+
+The HD-audio component consists of two parts: the controller chip and
+the codec chips on the HD-audio bus. Linux provides a single driver
+for all controllers, snd-hda-intel. Although the driver name contains
+a word of a well-known hardware vendor, it's not specific to it but for
+all controller chips by other companies. Since the HD-audio
+controllers are supposed to be compatible, the single snd-hda-driver
+should work in most cases. But, not surprisingly, there are known
+bugs and issues specific to each controller type. The snd-hda-intel
+driver has a bunch of workarounds for these as described below.
+
+A controller may have multiple codecs. Usually you have one audio
+codec and optionally one modem codec. In theory, there might be
+multiple audio codecs, e.g. for analog and digital outputs, and the
+driver might not work properly because of conflict of mixer elements.
+This should be fixed in future if such hardware really exists.
+
+The snd-hda-intel driver has several different codec parsers depending
+on the codec. It has a generic parser as a fallback, but this
+functionality is fairly limited until now. Instead of the generic
+parser, usually the codec-specific parser (coded in patch_*.c) is used
+for the codec-specific implementations. The details about the
+codec-specific problems are explained in the later sections.
+
+If you are interested in the deep debugging of HD-audio, read the
+HD-audio specification at first. The specification is found on
+Intel's web page, for example:
+
+* http://www.intel.com/standards/hdaudio/
+
+
+HD-Audio Controller
+===================
+
+DMA-Position Problem
+--------------------
+The most common problem of the controller is the inaccurate DMA
+pointer reporting. The DMA pointer for playback and capture can be
+read in two ways, either via a LPIB register or via a position-buffer
+map. As default the driver tries to read from the io-mapped
+position-buffer, and falls back to LPIB if the position-buffer appears
+dead. However, this detection isn't perfect on some devices. In such
+a case, you can change the default method via ``position_fix`` option.
+
+``position_fix=1`` means to use LPIB method explicitly.
+``position_fix=2`` means to use the position-buffer.
+``position_fix=3`` means to use a combination of both methods, needed
+for some VIA controllers. The capture stream position is corrected
+by comparing both LPIB and position-buffer values.
+``position_fix=4`` is another combination available for all controllers,
+and uses LPIB for the playback and the position-buffer for the capture
+streams.
+0 is the default value for all other
+controllers, the automatic check and fallback to LPIB as described in
+the above. If you get a problem of repeated sounds, this option might
+help.
+
+In addition to that, every controller is known to be broken regarding
+the wake-up timing. It wakes up a few samples before actually
+processing the data on the buffer. This caused a lot of problems, for
+example, with ALSA dmix or JACK. Since 2.6.27 kernel, the driver puts
+an artificial delay to the wake up timing. This delay is controlled
+via ``bdl_pos_adj`` option.
+
+When ``bdl_pos_adj`` is a negative value (as default), it's assigned to
+an appropriate value depending on the controller chip. For Intel
+chips, it'd be 1 while it'd be 32 for others. Usually this works.
+Only in case it doesn't work and you get warning messages, you should
+change this parameter to other values.
+
+
+Codec-Probing Problem
+---------------------
+A less often but a more severe problem is the codec probing. When
+BIOS reports the available codec slots wrongly, the driver gets
+confused and tries to access the non-existing codec slot. This often
+results in the total screw-up, and destructs the further communication
+with the codec chips. The symptom appears usually as error messages
+like:
+::
+
+ hda_intel: azx_get_response timeout, switching to polling mode:
+ last cmd=0x12345678
+ hda_intel: azx_get_response timeout, switching to single_cmd mode:
+ last cmd=0x12345678
+
+The first line is a warning, and this is usually relatively harmless.
+It means that the codec response isn't notified via an IRQ. The
+driver uses explicit polling method to read the response. It gives
+very slight CPU overhead, but you'd unlikely notice it.
+
+The second line is, however, a fatal error. If this happens, usually
+it means that something is really wrong. Most likely you are
+accessing a non-existing codec slot.
+
+Thus, if the second error message appears, try to narrow the probed
+codec slots via ``probe_mask`` option. It's a bitmask, and each bit
+corresponds to the codec slot. For example, to probe only the first
+slot, pass ``probe_mask=1``. For the first and the third slots, pass
+``probe_mask=5`` (where 5 = 1 | 4), and so on.
+
+Since 2.6.29 kernel, the driver has a more robust probing method, so
+this error might happen rarely, though.
+
+On a machine with a broken BIOS, sometimes you need to force the
+driver to probe the codec slots the hardware doesn't report for use.
+In such a case, turn the bit 8 (0x100) of ``probe_mask`` option on.
+Then the rest 8 bits are passed as the codec slots to probe
+unconditionally. For example, ``probe_mask=0x103`` will force to probe
+the codec slots 0 and 1 no matter what the hardware reports.
+
+
+Interrupt Handling
+------------------
+HD-audio driver uses MSI as default (if available) since 2.6.33
+kernel as MSI works better on some machines, and in general, it's
+better for performance. However, Nvidia controllers showed bad
+regressions with MSI (especially in a combination with AMD chipset),
+thus we disabled MSI for them.
+
+There seem also still other devices that don't work with MSI. If you
+see a regression wrt the sound quality (stuttering, etc) or a lock-up
+in the recent kernel, try to pass ``enable_msi=0`` option to disable
+MSI. If it works, you can add the known bad device to the blacklist
+defined in hda_intel.c. In such a case, please report and give the
+patch back to the upstream developer.
+
+
+HD-Audio Codec
+==============
+
+Model Option
+------------
+The most common problem regarding the HD-audio driver is the
+unsupported codec features or the mismatched device configuration.
+Most of codec-specific code has several preset models, either to
+override the BIOS setup or to provide more comprehensive features.
+
+The driver checks PCI SSID and looks through the static configuration
+table until any matching entry is found. If you have a new machine,
+you may see a message like below:
+::
+
+ hda_codec: ALC880: BIOS auto-probing.
+
+Meanwhile, in the earlier versions, you would see a message like:
+::
+
+ hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
+
+Even if you see such a message, DON'T PANIC. Take a deep breath and
+keep your towel. First of all, it's an informational message, no
+warning, no error. This means that the PCI SSID of your device isn't
+listed in the known preset model (white-)list. But, this doesn't mean
+that the driver is broken. Many codec-drivers provide the automatic
+configuration mechanism based on the BIOS setup.
+
+The HD-audio codec has usually "pin" widgets, and BIOS sets the default
+configuration of each pin, which indicates the location, the
+connection type, the jack color, etc. The HD-audio driver can guess
+the right connection judging from these default configuration values.
+However -- some codec-support codes, such as patch_analog.c, don't
+support the automatic probing (yet as of 2.6.28). And, BIOS is often,
+yes, pretty often broken. It sets up wrong values and screws up the
+driver.
+
+The preset model (or recently called as "fix-up") is provided
+basically to overcome such a situation. When the matching preset
+model is found in the white-list, the driver assumes the static
+configuration of that preset with the correct pin setup, etc.
+Thus, if you have a newer machine with a slightly different PCI SSID
+(or codec SSID) from the existing one, you may have a good chance to
+re-use the same model. You can pass the ``model`` option to specify the
+preset model instead of PCI (and codec-) SSID look-up.
+
+What ``model`` option values are available depends on the codec chip.
+Check your codec chip from the codec proc file (see "Codec Proc-File"
+section below). It will show the vendor/product name of your codec
+chip. Then, see Documentation/sound/HD-Audio-Models.rst file,
+the section of HD-audio driver. You can find a list of codecs
+and ``model`` options belonging to each codec. For example, for Realtek
+ALC262 codec chip, pass ``model=ultra`` for devices that are compatible
+with Samsung Q1 Ultra.
+
+Thus, the first thing you can do for any brand-new, unsupported and
+non-working HD-audio hardware is to check HD-audio codec and several
+different ``model`` option values. If you have any luck, some of them
+might suit with your device well.
+
+There are a few special model option values:
+
+* when 'nofixup' is passed, the device-specific fixups in the codec
+ parser are skipped.
+* when ``generic`` is passed, the codec-specific parser is skipped and
+ only the generic parser is used.
+
+
+Speaker and Headphone Output
+----------------------------
+One of the most frequent (and obvious) bugs with HD-audio is the
+silent output from either or both of a built-in speaker and a
+headphone jack. In general, you should try a headphone output at
+first. A speaker output often requires more additional controls like
+the external amplifier bits. Thus a headphone output has a slightly
+better chance.
+
+Before making a bug report, double-check whether the mixer is set up
+correctly. The recent version of snd-hda-intel driver provides mostly
+"Master" volume control as well as "Front" volume (where Front
+indicates the front-channels). In addition, there can be individual
+"Headphone" and "Speaker" controls.
+
+Ditto for the speaker output. There can be "External Amplifier"
+switch on some codecs. Turn on this if present.
+
+Another related problem is the automatic mute of speaker output by
+headphone plugging. This feature is implemented in most cases, but
+not on every preset model or codec-support code.
+
+In anyway, try a different model option if you have such a problem.
+Some other models may match better and give you more matching
+functionality. If none of the available models works, send a bug
+report. See the bug report section for details.
+
+If you are masochistic enough to debug the driver problem, note the
+following:
+
+* The speaker (and the headphone, too) output often requires the
+ external amplifier. This can be set usually via EAPD verb or a
+ certain GPIO. If the codec pin supports EAPD, you have a better
+ chance via SET_EAPD_BTL verb (0x70c). On others, GPIO pin (mostly
+ it's either GPIO0 or GPIO1) may turn on/off EAPD.
+* Some Realtek codecs require special vendor-specific coefficients to
+ turn on the amplifier. See patch_realtek.c.
+* IDT codecs may have extra power-enable/disable controls on each
+ analog pin. See patch_sigmatel.c.
+* Very rare but some devices don't accept the pin-detection verb until
+ triggered. Issuing GET_PIN_SENSE verb (0xf09) may result in the
+ codec-communication stall. Some examples are found in
+ patch_realtek.c.
+
+
+Capture Problems
+----------------
+The capture problems are often because of missing setups of mixers.
+Thus, before submitting a bug report, make sure that you set up the
+mixer correctly. For example, both "Capture Volume" and "Capture
+Switch" have to be set properly in addition to the right "Capture
+Source" or "Input Source" selection. Some devices have "Mic Boost"
+volume or switch.
+
+When the PCM device is opened via "default" PCM (without pulse-audio
+plugin), you'll likely have "Digital Capture Volume" control as well.
+This is provided for the extra gain/attenuation of the signal in
+software, especially for the inputs without the hardware volume
+control such as digital microphones. Unless really needed, this
+should be set to exactly 50%, corresponding to 0dB -- neither extra
+gain nor attenuation. When you use "hw" PCM, i.e., a raw access PCM,
+this control will have no influence, though.
+
+It's known that some codecs / devices have fairly bad analog circuits,
+and the recorded sound contains a certain DC-offset. This is no bug
+of the driver.
+
+Most of modern laptops have no analog CD-input connection. Thus, the
+recording from CD input won't work in many cases although the driver
+provides it as the capture source. Use CDDA instead.
+
+The automatic switching of the built-in and external mic per plugging
+is implemented on some codec models but not on every model. Partly
+because of my laziness but mostly lack of testers. Feel free to
+submit the improvement patch to the author.
+
+
+Direct Debugging
+----------------
+If no model option gives you a better result, and you are a tough guy
+to fight against evil, try debugging via hitting the raw HD-audio
+codec verbs to the device. Some tools are available: hda-emu and
+hda-analyzer. The detailed description is found in the sections
+below. You'd need to enable hwdep for using these tools. See "Kernel
+Configuration" section.
+
+
+Other Issues
+============
+
+Kernel Configuration
+--------------------
+In general, I recommend you to enable the sound debug option,
+``CONFIG_SND_DEBUG=y``, no matter whether you are debugging or not.
+This enables snd_printd() macro and others, and you'll get additional
+kernel messages at probing.
+
+In addition, you can enable ``CONFIG_SND_DEBUG_VERBOSE=y``. But this
+will give you far more messages. Thus turn this on only when you are
+sure to want it.
+
+Don't forget to turn on the appropriate ``CONFIG_SND_HDA_CODEC_*``
+options. Note that each of them corresponds to the codec chip, not
+the controller chip. Thus, even if lspci shows the Nvidia controller,
+you may need to choose the option for other vendors. If you are
+unsure, just select all yes.
+
+``CONFIG_SND_HDA_HWDEP`` is a useful option for debugging the driver.
+When this is enabled, the driver creates hardware-dependent devices
+(one per each codec), and you have a raw access to the device via
+these device files. For example, ``hwC0D2`` will be created for the
+codec slot #2 of the first card (#0). For debug-tools such as
+hda-verb and hda-analyzer, the hwdep device has to be enabled.
+Thus, it'd be better to turn this on always.
+
+``CONFIG_SND_HDA_RECONFIG`` is a new option, and this depends on the
+hwdep option above. When enabled, you'll have some sysfs files under
+the corresponding hwdep directory. See "HD-audio reconfiguration"
+section below.
+
+``CONFIG_SND_HDA_POWER_SAVE`` option enables the power-saving feature.
+See "Power-saving" section below.
+
+
+Codec Proc-File
+---------------
+The codec proc-file is a treasure-chest for debugging HD-audio.
+It shows most of useful information of each codec widget.
+
+The proc file is located in /proc/asound/card*/codec#*, one file per
+each codec slot. You can know the codec vendor, product id and
+names, the type of each widget, capabilities and so on.
+This file, however, doesn't show the jack sensing state, so far. This
+is because the jack-sensing might be depending on the trigger state.
+
+This file will be picked up by the debug tools, and also it can be fed
+to the emulator as the primary codec information. See the debug tools
+section below.
+
+This proc file can be also used to check whether the generic parser is
+used. When the generic parser is used, the vendor/product ID name
+will appear as "Realtek ID 0262", instead of "Realtek ALC262".
+
+
+HD-Audio Reconfiguration
+------------------------
+This is an experimental feature to allow you re-configure the HD-audio
+codec dynamically without reloading the driver. The following sysfs
+files are available under each codec-hwdep device directory (e.g.
+/sys/class/sound/hwC0D0):
+
+vendor_id
+ Shows the 32bit codec vendor-id hex number. You can change the
+ vendor-id value by writing to this file.
+subsystem_id
+ Shows the 32bit codec subsystem-id hex number. You can change the
+ subsystem-id value by writing to this file.
+revision_id
+ Shows the 32bit codec revision-id hex number. You can change the
+ revision-id value by writing to this file.
+afg
+ Shows the AFG ID. This is read-only.
+mfg
+ Shows the MFG ID. This is read-only.
+name
+ Shows the codec name string. Can be changed by writing to this
+ file.
+modelname
+ Shows the currently set ``model`` option. Can be changed by writing
+ to this file.
+init_verbs
+ The extra verbs to execute at initialization. You can add a verb by
+ writing to this file. Pass three numbers: nid, verb and parameter
+ (separated with a space).
+hints
+ Shows / stores hint strings for codec parsers for any use.
+ Its format is ``key = value``. For example, passing ``jack_detect = no``
+ will disable the jack detection of the machine completely.
+init_pin_configs
+ Shows the initial pin default config values set by BIOS.
+driver_pin_configs
+ Shows the pin default values set by the codec parser explicitly.
+ This doesn't show all pin values but only the changed values by
+ the parser. That is, if the parser doesn't change the pin default
+ config values by itself, this will contain nothing.
+user_pin_configs
+ Shows the pin default config values to override the BIOS setup.
+ Writing this (with two numbers, NID and value) appends the new
+ value. The given will be used instead of the initial BIOS value at
+ the next reconfiguration time. Note that this config will override
+ even the driver pin configs, too.
+reconfig
+ Triggers the codec re-configuration. When any value is written to
+ this file, the driver re-initialize and parses the codec tree
+ again. All the changes done by the sysfs entries above are taken
+ into account.
+clear
+ Resets the codec, removes the mixer elements and PCM stuff of the
+ specified codec, and clear all init verbs and hints.
+
+For example, when you want to change the pin default configuration
+value of the pin widget 0x14 to 0x9993013f, and let the driver
+re-configure based on that state, run like below:
+::
+
+ # echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
+ # echo 1 > /sys/class/sound/hwC0D0/reconfig
+
+
+Hint Strings
+------------
+The codec parser have several switches and adjustment knobs for
+matching better with the actual codec or device behavior. Many of
+them can be adjusted dynamically via "hints" strings as mentioned in
+the section above. For example, by passing ``jack_detect = no`` string
+via sysfs or a patch file, you can disable the jack detection, thus
+the codec parser will skip the features like auto-mute or mic
+auto-switch. As a boolean value, either ``yes``, ``no``, ``true``, ``false``,
+``1`` or ``0`` can be passed.
+
+The generic parser supports the following hints:
+
+jack_detect (bool)
+ specify whether the jack detection is available at all on this
+ machine; default true
+inv_jack_detect (bool)
+ indicates that the jack detection logic is inverted
+trigger_sense (bool)
+ indicates that the jack detection needs the explicit call of
+ AC_VERB_SET_PIN_SENSE verb
+inv_eapd (bool)
+ indicates that the EAPD is implemented in the inverted logic
+pcm_format_first (bool)
+ sets the PCM format before the stream tag and channel ID
+sticky_stream (bool)
+ keep the PCM format, stream tag and ID as long as possible;
+ default true
+spdif_status_reset (bool)
+ reset the SPDIF status bits at each time the SPDIF stream is set
+ up
+pin_amp_workaround (bool)
+ the output pin may have multiple amp values
+single_adc_amp (bool)
+ ADCs can have only single input amps
+auto_mute (bool)
+ enable/disable the headphone auto-mute feature; default true
+auto_mic (bool)
+ enable/disable the mic auto-switch feature; default true
+line_in_auto_switch (bool)
+ enable/disable the line-in auto-switch feature; default false
+need_dac_fix (bool)
+ limits the DACs depending on the channel count
+primary_hp (bool)
+ probe headphone jacks as the primary outputs; default true
+multi_io (bool)
+ try probing multi-I/O config (e.g. shared line-in/surround,
+ mic/clfe jacks)
+multi_cap_vol (bool)
+ provide multiple capture volumes
+inv_dmic_split (bool)
+ provide split internal mic volume/switch for phase-inverted
+ digital mics
+indep_hp (bool)
+ provide the independent headphone PCM stream and the corresponding
+ mixer control, if available
+add_stereo_mix_input (bool)
+ add the stereo mix (analog-loopback mix) to the input mux if
+ available
+add_jack_modes (bool)
+ add "xxx Jack Mode" enum controls to each I/O jack for allowing to
+ change the headphone amp and mic bias VREF capabilities
+power_save_node (bool)
+ advanced power management for each widget, controlling the power
+ sate (D0/D3) of each widget node depending on the actual pin and
+ stream states
+power_down_unused (bool)
+ power down the unused widgets, a subset of power_save_node, and
+ will be dropped in future
+add_hp_mic (bool)
+ add the headphone to capture source if possible
+hp_mic_detect (bool)
+ enable/disable the hp/mic shared input for a single built-in mic
+ case; default true
+mixer_nid (int)
+ specifies the widget NID of the analog-loopback mixer
+
+
+Early Patching
+--------------
+When ``CONFIG_SND_HDA_PATCH_LOADER=y`` is set, you can pass a "patch"
+as a firmware file for modifying the HD-audio setup before
+initializing the codec. This can work basically like the
+reconfiguration via sysfs in the above, but it does it before the
+first codec configuration.
+
+A patch file is a plain text file which looks like below:
+
+::
+
+ [codec]
+ 0x12345678 0xabcd1234 2
+
+ [model]
+ auto
+
+ [pincfg]
+ 0x12 0x411111f0
+
+ [verb]
+ 0x20 0x500 0x03
+ 0x20 0x400 0xff
+
+ [hint]
+ jack_detect = no
+
+
+The file needs to have a line ``[codec]``. The next line should contain
+three numbers indicating the codec vendor-id (0x12345678 in the
+example), the codec subsystem-id (0xabcd1234) and the address (2) of
+the codec. The rest patch entries are applied to this specified codec
+until another codec entry is given. Passing 0 or a negative number to
+the first or the second value will make the check of the corresponding
+field be skipped. It'll be useful for really broken devices that don't
+initialize SSID properly.
+
+The ``[model]`` line allows to change the model name of the each codec.
+In the example above, it will be changed to model=auto.
+Note that this overrides the module option.
+
+After the ``[pincfg]`` line, the contents are parsed as the initial
+default pin-configurations just like ``user_pin_configs`` sysfs above.
+The values can be shown in user_pin_configs sysfs file, too.
+
+Similarly, the lines after ``[verb]`` are parsed as ``init_verbs``
+sysfs entries, and the lines after ``[hint]`` are parsed as ``hints``
+sysfs entries, respectively.
+
+Another example to override the codec vendor id from 0x12345678 to
+0xdeadbeef is like below:
+::
+
+ [codec]
+ 0x12345678 0xabcd1234 2
+
+ [vendor_id]
+ 0xdeadbeef
+
+
+In the similar way, you can override the codec subsystem_id via
+``[subsystem_id]``, the revision id via ``[revision_id]`` line.
+Also, the codec chip name can be rewritten via ``[chip_name]`` line.
+::
+
+ [codec]
+ 0x12345678 0xabcd1234 2
+
+ [subsystem_id]
+ 0xffff1111
+
+ [revision_id]
+ 0x10
+
+ [chip_name]
+ My-own NEWS-0002
+
+
+The hd-audio driver reads the file via request_firmware(). Thus,
+a patch file has to be located on the appropriate firmware path,
+typically, /lib/firmware. For example, when you pass the option
+``patch=hda-init.fw``, the file /lib/firmware/hda-init.fw must be
+present.
+
+The patch module option is specific to each card instance, and you
+need to give one file name for each instance, separated by commas.
+For example, if you have two cards, one for an on-board analog and one
+for an HDMI video board, you may pass patch option like below:
+::
+
+ options snd-hda-intel patch=on-board-patch,hdmi-patch
+
+
+Power-Saving
+------------
+The power-saving is a kind of auto-suspend of the device. When the
+device is inactive for a certain time, the device is automatically
+turned off to save the power. The time to go down is specified via
+``power_save`` module option, and this option can be changed dynamically
+via sysfs.
+
+The power-saving won't work when the analog loopback is enabled on
+some codecs. Make sure that you mute all unneeded signal routes when
+you want the power-saving.
+
+The power-saving feature might cause audible click noises at each
+power-down/up depending on the device. Some of them might be
+solvable, but some are hard, I'm afraid. Some distros such as
+openSUSE enables the power-saving feature automatically when the power
+cable is unplugged. Thus, if you hear noises, suspect first the
+power-saving. See /sys/module/snd_hda_intel/parameters/power_save to
+check the current value. If it's non-zero, the feature is turned on.
+
+The recent kernel supports the runtime PM for the HD-audio controller
+chip, too. It means that the HD-audio controller is also powered up /
+down dynamically. The feature is enabled only for certain controller
+chips like Intel LynxPoint. You can enable/disable this feature
+forcibly by setting ``power_save_controller`` option, which is also
+available at /sys/module/snd_hda_intel/parameters directory.
+
+
+Tracepoints
+-----------
+The hd-audio driver gives a few basic tracepoints.
+``hda:hda_send_cmd`` traces each CORB write while ``hda:hda_get_response``
+traces the response from RIRB (only when read from the codec driver).
+``hda:hda_bus_reset`` traces the bus-reset due to fatal error, etc,
+``hda:hda_unsol_event`` traces the unsolicited events, and
+``hda:hda_power_down`` and ``hda:hda_power_up`` trace the power down/up
+via power-saving behavior.
+
+Enabling all tracepoints can be done like
+::
+
+ # echo 1 > /sys/kernel/debug/tracing/events/hda/enable
+
+then after some commands, you can traces from
+/sys/kernel/debug/tracing/trace file. For example, when you want to
+trace what codec command is sent, enable the tracepoint like:
+::
+
+ # cat /sys/kernel/debug/tracing/trace
+ # tracer: nop
+ #
+ # TASK-PID CPU# TIMESTAMP FUNCTION
+ # | | | | |
+ <...>-7807 [002] 105147.774889: hda_send_cmd: [0:0] val=e3a019
+ <...>-7807 [002] 105147.774893: hda_send_cmd: [0:0] val=e39019
+ <...>-7807 [002] 105147.999542: hda_send_cmd: [0:0] val=e3a01a
+ <...>-7807 [002] 105147.999543: hda_send_cmd: [0:0] val=e3901a
+ <...>-26764 [001] 349222.837143: hda_send_cmd: [0:0] val=e3a019
+ <...>-26764 [001] 349222.837148: hda_send_cmd: [0:0] val=e39019
+ <...>-26764 [001] 349223.058539: hda_send_cmd: [0:0] val=e3a01a
+ <...>-26764 [001] 349223.058541: hda_send_cmd: [0:0] val=e3901a
+
+Here ``[0:0]`` indicates the card number and the codec address, and
+``val`` shows the value sent to the codec, respectively. The value is
+a packed value, and you can decode it via hda-decode-verb program
+included in hda-emu package below. For example, the value e3a019 is
+to set the left output-amp value to 25.
+::
+
+ % hda-decode-verb 0xe3a019
+ raw value = 0x00e3a019
+ cid = 0, nid = 0x0e, verb = 0x3a0, parm = 0x19
+ raw value: verb = 0x3a0, parm = 0x19
+ verbname = set_amp_gain_mute
+ amp raw val = 0xa019
+ output, left, idx=0, mute=0, val=25
+
+
+Development Tree
+----------------
+The latest development codes for HD-audio are found on sound git tree:
+
+* git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+
+The master branch or for-next branches can be used as the main
+development branches in general while the development for the current
+and next kernels are found in for-linus and for-next branches,
+respectively.
+
+
+Sending a Bug Report
+--------------------
+If any model or module options don't work for your device, it's time
+to send a bug report to the developers. Give the following in your
+bug report:
+
+* Hardware vendor, product and model names
+* Kernel version (and ALSA-driver version if you built externally)
+* ``alsa-info.sh`` output; run with ``--no-upload`` option. See the
+ section below about alsa-info
+
+If it's a regression, at best, send alsa-info outputs of both working
+and non-working kernels. This is really helpful because we can
+compare the codec registers directly.
+
+Send a bug report either the followings:
+
+kernel-bugzilla
+ https://bugzilla.kernel.org/
+alsa-devel ML
+ alsa-devel@alsa-project.org
+
+
+Debug Tools
+===========
+
+This section describes some tools available for debugging HD-audio
+problems.
+
+alsa-info
+---------
+The script ``alsa-info.sh`` is a very useful tool to gather the audio
+device information. It's included in alsa-utils package. The latest
+version can be found on git repository:
+
+* git://git.alsa-project.org/alsa-utils.git
+
+The script can be fetched directly from the following URL, too:
+
+* http://www.alsa-project.org/alsa-info.sh
+
+Run this script as root, and it will gather the important information
+such as the module lists, module parameters, proc file contents
+including the codec proc files, mixer outputs and the control
+elements. As default, it will store the information onto a web server
+on alsa-project.org. But, if you send a bug report, it'd be better to
+run with ``--no-upload`` option, and attach the generated file.
+
+There are some other useful options. See ``--help`` option output for
+details.
+
+When a probe error occurs or when the driver obviously assigns a
+mismatched model, it'd be helpful to load the driver with
+``probe_only=1`` option (at best after the cold reboot) and run
+alsa-info at this state. With this option, the driver won't configure
+the mixer and PCM but just tries to probe the codec slot. After
+probing, the proc file is available, so you can get the raw codec
+information before modified by the driver. Of course, the driver
+isn't usable with ``probe_only=1``. But you can continue the
+configuration via hwdep sysfs file if hda-reconfig option is enabled.
+Using ``probe_only`` mask 2 skips the reset of HDA codecs (use
+``probe_only=3`` as module option). The hwdep interface can be used
+to determine the BIOS codec initialization.
+
+
+hda-verb
+--------
+hda-verb is a tiny program that allows you to access the HD-audio
+codec directly. You can execute a raw HD-audio codec verb with this.
+This program accesses the hwdep device, thus you need to enable the
+kernel config ``CONFIG_SND_HDA_HWDEP=y`` beforehand.
+
+The hda-verb program takes four arguments: the hwdep device file, the
+widget NID, the verb and the parameter. When you access to the codec
+on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
+argument, typically. (However, the real path name depends on the
+system.)
+
+The second parameter is the widget number-id to access. The third
+parameter can be either a hex/digit number or a string corresponding
+to a verb. Similarly, the last parameter is the value to write, or
+can be a string for the parameter type.
+
+::
+
+ % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
+ nid = 0x12, verb = 0x701, param = 0x2
+ value = 0x0
+
+ % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
+ nid = 0x0, verb = 0xf00, param = 0x0
+ value = 0x10ec0262
+
+ % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
+ nid = 0x2, verb = 0x300, param = 0xb080
+ value = 0x0
+
+
+Although you can issue any verbs with this program, the driver state
+won't be always updated. For example, the volume values are usually
+cached in the driver, and thus changing the widget amp value directly
+via hda-verb won't change the mixer value.
+
+The hda-verb program is included now in alsa-tools:
+
+* git://git.alsa-project.org/alsa-tools.git
+
+Also, the old stand-alone package is found in the ftp directory:
+
+* ftp://ftp.suse.com/pub/people/tiwai/misc/
+
+Also a git repository is available:
+
+* git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
+
+See README file in the tarball for more details about hda-verb
+program.
+
+
+hda-analyzer
+------------
+hda-analyzer provides a graphical interface to access the raw HD-audio
+control, based on pyGTK2 binding. It's a more powerful version of
+hda-verb. The program gives you an easy-to-use GUI stuff for showing
+the widget information and adjusting the amp values, as well as the
+proc-compatible output.
+
+The hda-analyzer:
+
+* http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
+
+is a part of alsa.git repository in alsa-project.org:
+
+* git://git.alsa-project.org/alsa.git
+
+Codecgraph
+----------
+Codecgraph is a utility program to generate a graph and visualizes the
+codec-node connection of a codec chip. It's especially useful when
+you analyze or debug a codec without a proper datasheet. The program
+parses the given codec proc file and converts to SVG via graphiz
+program.
+
+The tarball and GIT trees are found in the web page at:
+
+* http://helllabs.org/codecgraph/
+
+
+hda-emu
+-------
+hda-emu is an HD-audio emulator. The main purpose of this program is
+to debug an HD-audio codec without the real hardware. Thus, it
+doesn't emulate the behavior with the real audio I/O, but it just
+dumps the codec register changes and the ALSA-driver internal changes
+at probing and operating the HD-audio driver.
+
+The program requires a codec proc-file to simulate. Get a proc file
+for the target codec beforehand, or pick up an example codec from the
+codec proc collections in the tarball. Then, run the program with the
+proc file, and the hda-emu program will start parsing the codec file
+and simulates the HD-audio driver:
+
+::
+
+ % hda-emu codecs/stac9200-dell-d820-laptop
+ # Parsing..
+ hda_codec: Unknown model for STAC9200, using BIOS defaults
+ hda_codec: pin nid 08 bios pin config 40c003fa
+ ....
+
+
+The program gives you only a very dumb command-line interface. You
+can get a proc-file dump at the current state, get a list of control
+(mixer) elements, set/get the control element value, simulate the PCM
+operation, the jack plugging simulation, etc.
+
+The program is found in the git repository below:
+
+* git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
+
+See README file in the repository for more details about hda-emu
+program.
+
+
+hda-jack-retask
+---------------
+hda-jack-retask is a user-friendly GUI program to manipulate the
+HD-audio pin control for jack retasking. If you have a problem about
+the jack assignment, try this program and check whether you can get
+useful results. Once when you figure out the proper pin assignment,
+it can be fixed either in the driver code statically or via passing a
+firmware patch file (see "Early Patching" section).
+
+The program is included in alsa-tools now:
+
+* git://git.alsa-project.org/alsa-tools.git
--- /dev/null
+===================================
+Linux Sound Subsystem Documentation
+===================================
+
+.. toctree::
+ :maxdepth: 2
+
+ kernel-api/index
+ designs/index
+ soc/index
+ alsa-configuration
+ hd-audio/index
+ cards/index
+
+.. only:: subproject
+
+ Indices
+ =======
+
+ * :ref:`genindex`
--- /dev/null
+===================
+The ALSA Driver API
+===================
+
+Management of Cards and Devices
+===============================
+
+Card Management
+---------------
+.. kernel-doc:: sound/core/init.c
+
+Device Components
+-----------------
+.. kernel-doc:: sound/core/device.c
+
+Module requests and Device File Entries
+---------------------------------------
+.. kernel-doc:: sound/core/sound.c
+
+Memory Management Helpers
+-------------------------
+.. kernel-doc:: sound/core/memory.c
+.. kernel-doc:: sound/core/memalloc.c
+
+
+PCM API
+=======
+
+PCM Core
+--------
+.. kernel-doc:: sound/core/pcm.c
+.. kernel-doc:: sound/core/pcm_lib.c
+.. kernel-doc:: sound/core/pcm_native.c
+.. kernel-doc:: include/sound/pcm.h
+
+PCM Format Helpers
+------------------
+.. kernel-doc:: sound/core/pcm_misc.c
+
+PCM Memory Management
+---------------------
+.. kernel-doc:: sound/core/pcm_memory.c
+
+PCM DMA Engine API
+------------------
+.. kernel-doc:: sound/core/pcm_dmaengine.c
+.. kernel-doc:: include/sound/dmaengine_pcm.h
+
+Control/Mixer API
+=================
+
+General Control Interface
+-------------------------
+.. kernel-doc:: sound/core/control.c
+
+AC97 Codec API
+--------------
+.. kernel-doc:: sound/pci/ac97/ac97_codec.c
+.. kernel-doc:: sound/pci/ac97/ac97_pcm.c
+
+Virtual Master Control API
+--------------------------
+.. kernel-doc:: sound/core/vmaster.c
+.. kernel-doc:: include/sound/control.h
+
+MIDI API
+========
+
+Raw MIDI API
+------------
+.. kernel-doc:: sound/core/rawmidi.c
+
+MPU401-UART API
+---------------
+.. kernel-doc:: sound/drivers/mpu401/mpu401_uart.c
+
+Proc Info API
+=============
+
+Proc Info Interface
+-------------------
+.. kernel-doc:: sound/core/info.c
+
+Compress Offload
+================
+
+Compress Offload API
+--------------------
+.. kernel-doc:: sound/core/compress_offload.c
+.. kernel-doc:: include/uapi/sound/compress_offload.h
+.. kernel-doc:: include/uapi/sound/compress_params.h
+.. kernel-doc:: include/sound/compress_driver.h
+
+ASoC
+====
+
+ASoC Core API
+-------------
+.. kernel-doc:: include/sound/soc.h
+.. kernel-doc:: sound/soc/soc-core.c
+.. kernel-doc:: sound/soc/soc-devres.c
+.. kernel-doc:: sound/soc/soc-io.c
+.. kernel-doc:: sound/soc/soc-pcm.c
+.. kernel-doc:: sound/soc/soc-ops.c
+.. kernel-doc:: sound/soc/soc-compress.c
+
+ASoC DAPM API
+-------------
+.. kernel-doc:: sound/soc/soc-dapm.c
+
+ASoC DMA Engine API
+-------------------
+.. kernel-doc:: sound/soc/soc-generic-dmaengine-pcm.c
+
+Miscellaneous Functions
+=======================
+
+Hardware-Dependent Devices API
+------------------------------
+.. kernel-doc:: sound/core/hwdep.c
+
+Jack Abstraction Layer API
+--------------------------
+.. kernel-doc:: include/sound/jack.h
+.. kernel-doc:: sound/core/jack.c
+.. kernel-doc:: sound/soc/soc-jack.c
+
+ISA DMA Helpers
+---------------
+.. kernel-doc:: sound/core/isadma.c
+
+Other Helper Macros
+-------------------
+.. kernel-doc:: include/sound/core.h
--- /dev/null
+ALSA Kernel API Documentation
+=============================
+
+.. toctree::
+ :maxdepth: 2
+
+ alsa-driver-api
+ writing-an-alsa-driver
--- /dev/null
+======================
+Writing an ALSA Driver
+======================
+
+:Author: Takashi Iwai <tiwai@suse.de>
+:Date: Oct 15, 2007
+:Edition: 0.3.7
+
+Preface
+=======
+
+This document describes how to write an `ALSA (Advanced Linux Sound
+Architecture) <http://www.alsa-project.org/>`__ driver. The document
+focuses mainly on PCI soundcards. 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.
+
+This document targets people who already have enough C language skills
+and have basic linux kernel programming knowledge. This document doesn't
+explain the general topic of linux kernel coding and doesn't cover
+low-level driver implementation details. It only describes the standard
+way to write a PCI sound driver on ALSA.
+
+If you are already familiar with the older ALSA ver.0.5.x API, you can
+check the drivers such as ``sound/pci/es1938.c`` or
+``sound/pci/maestro3.c`` which have also almost the same code-base in
+the ALSA 0.5.x tree, so you can compare the differences.
+
+This document is still a draft version. Any feedback and corrections,
+please!!
+
+File Tree Structure
+===================
+
+General
+-------
+
+The ALSA drivers are provided in two ways.
+
+One is the trees provided as a tarball or via cvs from the ALSA's ftp
+site, and another is the 2.6 (or later) Linux kernel tree. To
+synchronize both, the ALSA driver tree is split into two different
+trees: alsa-kernel and alsa-driver. The former contains purely the
+source code for the Linux 2.6 (or later) tree. This tree is designed
+only for compilation on 2.6 or later environment. The latter,
+alsa-driver, contains many subtle files for compiling ALSA drivers
+outside of the Linux kernel tree, 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 (and eventually to the 2.6 kernel
+tree) when they are finished and confirmed to work fine.
+
+The file tree structure of ALSA driver is depicted below. Both
+alsa-kernel and alsa-driver have almost the same file structure, except
+for “core” directory. It's named as “acore” in alsa-driver tree.
+
+::
+
+ 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
+
+
+core directory
+--------------
+
+This directory contains the middle layer which 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.
+
+core/oss
+~~~~~~~~
+
+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 `below <#core-seq-oss>`__).
+
+core/ioctl32
+~~~~~~~~~~~~
+
+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.
+
+core/seq
+~~~~~~~~
+
+This directory 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
+``CONFIG_SND_SEQUENCER`` is set in the kernel config.
+
+core/seq/oss
+~~~~~~~~~~~~
+
+This contains the OSS sequencer emulation codes.
+
+core/seq/instr
+~~~~~~~~~~~~~~
+
+This directory contains the modules for the sequencer instrument layer.
+
+include directory
+-----------------
+
+This is the place for the public header files of ALSA drivers, which are
+to be exported to 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
+reasons :)
+
+drivers directory
+-----------------
+
+This directory contains code shared among different drivers on different
+architectures. They are hence supposed not to be architecture-specific.
+For example, the dummy pcm driver and the serial MIDI driver are found
+in this directory. In the sub-directories, there is code for components
+which are independent from bus and cpu architectures.
+
+drivers/mpu401
+~~~~~~~~~~~~~~
+
+The MPU401 and MPU401-UART modules are stored here.
+
+drivers/opl3 and opl4
+~~~~~~~~~~~~~~~~~~~~~
+
+The OPL3 and OPL4 FM-synth stuff is found here.
+
+i2c directory
+-------------
+
+This contains the ALSA i2c components.
+
+Although there is a standard i2c layer on Linux, ALSA has its own i2c
+code for some cards, because the soundcard needs only a simple operation
+and the standard i2c API is too complicated for such a purpose.
+
+i2c/l3
+~~~~~~
+
+This is a sub-directory for ARM L3 i2c.
+
+synth directory
+---------------
+
+This contains the synth middle-level modules.
+
+So far, there is only Emu8000/Emu10k1 synth driver under the
+``synth/emux`` sub-directory.
+
+pci directory
+-------------
+
+This directory and its sub-directories hold the top-level card modules
+for PCI soundcards and the code specific to the PCI BUS.
+
+The drivers compiled from a single file are stored directly in the pci
+directory, while the drivers with several source files are stored on
+their own sub-directory (e.g. emu10k1, ice1712).
+
+isa directory
+-------------
+
+This directory and its sub-directories hold the top-level card modules
+for ISA soundcards.
+
+arm, ppc, and sparc directories
+-------------------------------
+
+They are used for top-level card modules which are specific to one of
+these architectures.
+
+usb directory
+-------------
+
+This directory contains the USB-audio driver. In the latest version, the
+USB MIDI driver is integrated in the usb-audio driver.
+
+pcmcia directory
+----------------
+
+The PCMCIA, especially PCCard drivers will go here. CardBus drivers will
+be in the pci directory, because their API is identical to that of
+standard PCI cards.
+
+oss directory
+-------------
+
+The OSS/Lite source files are stored here in Linux 2.6 (or later) tree.
+In the ALSA driver tarball, this directory is empty, of course :)
+
+Basic Flow for PCI Drivers
+==========================
+
+Outline
+-------
+
+The minimum flow for PCI soundcards is as follows:
+
+- define the PCI ID table (see the section `PCI Entries`_).
+
+- create ``probe`` callback.
+
+- create ``remove`` callback.
+
+- create a :c:type:`struct pci_driver <pci_driver>` structure
+ containing the three pointers above.
+
+- create an ``init`` function just calling the
+ :c:func:`pci_register_driver()` to register the pci_driver
+ table defined above.
+
+- create an ``exit`` function to call the
+ :c:func:`pci_unregister_driver()` function.
+
+Full Code Example
+-----------------
+
+The code example is shown below. Some parts are kept unimplemented at
+this moment but will be filled in the next sections. The numbers in the
+comment lines of the :c:func:`snd_mychip_probe()` function refer
+to details explained in the following section.
+
+::
+
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+
+ /* module parameters (see "Module Parameters") */
+ /* SNDRV_CARDS: maximum number of cards supported by this module */
+ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+ /* definition of the chip-specific record */
+ struct mychip {
+ struct snd_card *card;
+ /* the rest of the implementation will be in section
+ * "PCI Resource Management"
+ */
+ };
+
+ /* chip-specific destructor
+ * (see "PCI Resource Management")
+ */
+ static int snd_mychip_free(struct mychip *chip)
+ {
+ .... /* will be implemented later... */
+ }
+
+ /* component-destructor
+ * (see "Management of Cards and Components")
+ */
+ static int snd_mychip_dev_free(struct snd_device *device)
+ {
+ return snd_mychip_free(device->device_data);
+ }
+
+ /* chip-specific constructor
+ * (see "Management of Cards and Components")
+ */
+ static int snd_mychip_create(struct snd_card *card,
+ struct pci_dev *pci,
+ struct mychip **rchip)
+ {
+ struct mychip *chip;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_mychip_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* check PCI availability here
+ * (see "PCI Resource Management")
+ */
+ ....
+
+ /* allocate a chip-specific data with zero filled */
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->card = card;
+
+ /* rest of initialization here; will be implemented
+ * later, see "PCI Resource Management"
+ */
+ ....
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_mychip_free(chip);
+ return err;
+ }
+
+ *rchip = chip;
+ return 0;
+ }
+
+ /* constructor -- see "Driver Constructor" sub-section */
+ static int snd_mychip_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+ {
+ static int dev;
+ struct snd_card *card;
+ struct mychip *chip;
+ int err;
+
+ /* (1) */
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* (2) */
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
+ if (err < 0)
+ return err;
+
+ /* (3) */
+ err = snd_mychip_create(card, pci, &chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* (4) */
+ 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);
+
+ /* (5) */
+ .... /* implemented later */
+
+ /* (6) */
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* (7) */
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+ }
+
+ /* destructor -- see the "Destructor" sub-section */
+ static void snd_mychip_remove(struct pci_dev *pci)
+ {
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+ }
+
+
+
+Driver Constructor
+------------------
+
+The real constructor of PCI drivers is the ``probe`` callback. The
+``probe`` callback and other component-constructors which are called
+from the ``probe`` callback cannot be used with the ``__init`` prefix
+because any PCI device could be a hotplug device.
+
+In the ``probe`` callback, the following scheme is often used.
+
+1) Check and increment the device index.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static int dev;
+ ....
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+
+where ``enable[dev]`` is the module option.
+
+Each time the ``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 (`step 7
+<#set-the-pci-driver-data-and-return-zero>`__).
+
+2) Create a card instance
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ struct snd_card *card;
+ int err;
+ ....
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
+
+
+The details will be explained in the section `Management of Cards and
+Components`_.
+
+3) Create a main component
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this part, the PCI resources are allocated.
+
+::
+
+ struct mychip *chip;
+ ....
+ err = snd_mychip_create(card, pci, &chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+The details will be explained in the section `PCI Resource
+Management`_.
+
+4) Set the driver ID and name strings.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ 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);
+
+The driver field holds the minimal ID string of the chip. This is used
+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.
+
+The shortname field is a string shown as more verbose name. The longname
+field contains the information shown in ``/proc/asound/cards``.
+
+5) Create other components, such as mixer, MIDI, etc.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here you define the basic components such as `PCM <#PCM-Interface>`__,
+mixer (e.g. `AC97 <#API-for-AC97-Codec>`__), MIDI (e.g.
+`MPU-401 <#MIDI-MPU401-UART-Interface>`__), and other interfaces.
+Also, if you want a `proc file <#Proc-Interface>`__, define it here,
+too.
+
+6) Register the card instance.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+Will be explained in the section `Management of Cards and
+Components`_, too.
+
+7) Set the PCI driver data and return zero.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+
+In the above, the card record is stored. This pointer is used in the
+remove callback and power-management callbacks, too.
+
+Destructor
+----------
+
+The destructor, remove callback, simply releases the card instance. Then
+the ALSA middle layer will release all the attached components
+automatically.
+
+It would be typically like the following:
+
+::
+
+ static void snd_mychip_remove(struct pci_dev *pci)
+ {
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+ }
+
+
+The above code assumes that the card pointer is set to the PCI driver
+data.
+
+Header Files
+------------
+
+For the above example, at least the following include files are
+necessary.
+
+::
+
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+
+where the last one is necessary only when module options are defined
+in the source file. If the code is split into several files, the files
+without module options don't need them.
+
+In addition to these headers, you'll need ``<linux/interrupt.h>`` for
+interrupt handling, and ``<asm/io.h>`` for I/O access. If you use the
+:c:func:`mdelay()` or :c:func:`udelay()` functions, you'll need
+to include ``<linux/delay.h>`` too.
+
+The ALSA interfaces like the PCM and control APIs are defined in other
+``<sound/xxx.h>`` header files. They have to be included after
+``<sound/core.h>``.
+
+Management of Cards and Components
+==================================
+
+Card Instance
+-------------
+
+For each soundcard, a “card” record must be allocated.
+
+A card record is the headquarters of the soundcard. It manages the whole
+list of 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 correct release of
+resources at destruction.
+
+As mentioned above, to create a card instance, call
+:c:func:`snd_card_new()`.
+
+::
+
+ struct snd_card *card;
+ int err;
+ err = snd_card_new(&pci->dev, index, id, module, extra_size, &card);
+
+
+The function takes six arguments: the parent device pointer, the
+card-index number, the id string, the module pointer (usually
+``THIS_MODULE``), the size of extra-data space, and the pointer to
+return the card instance. The extra_size argument is used to allocate
+card->private_data for the chip-specific data. Note that these data are
+allocated by :c:func:`snd_card_new()`.
+
+The first argument, the pointer of struct :c:type:`struct device
+<device>`, specifies the parent device. For PCI devices, typically
+``&pci->`` is passed there.
+
+Components
+----------
+
+After the card is created, you can attach the components (devices) to
+the card instance. In an ALSA driver, a component is represented as a
+:c:type:`struct snd_device <snd_device>` object. A component
+can be a PCM instance, a control interface, a raw MIDI interface, etc.
+Each such instance has one component entry.
+
+A component can be created via :c:func:`snd_device_new()`
+function.
+
+::
+
+ snd_device_new(card, SNDRV_DEV_XXX, chip, &ops);
+
+This takes the card pointer, the device-level (``SNDRV_DEV_XXX``), the
+data pointer, and the callback pointers (``&ops``). The device-level
+defines the type of components and the order of registration and
+de-registration. For most components, the device-level is already
+defined. For a user-defined component, you can use
+``SNDRV_DEV_LOWLEVEL``.
+
+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 (``chip`` in the above example) is used as the
+identifier for the instance.
+
+Each pre-defined ALSA component such as ac97 and pcm calls
+:c:func:`snd_device_new()` 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.
+
+If you wish to create your own component, you need to set the destructor
+function to the dev_free callback in the ``ops``, so that it can be
+released automatically via :c:func:`snd_card_free()`. The next
+example will show an implementation of chip-specific data.
+
+Chip-Specific Data
+------------------
+
+Chip-specific information, e.g. the I/O port address, its resource
+pointer, or the irq number, is stored in the chip-specific record.
+
+::
+
+ struct mychip {
+ ....
+ };
+
+
+In general, there are two ways of allocating the chip record.
+
+1. Allocating via :c:func:`snd_card_new()`.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As mentioned above, you can pass the extra-data-length to the 5th
+argument of :c:func:`snd_card_new()`, i.e.
+
+::
+
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct mychip), &card);
+
+:c:type:`struct mychip <mychip>` is the type of the chip record.
+
+In return, the allocated record can be accessed as
+
+::
+
+ struct mychip *chip = card->private_data;
+
+With this method, you don't have to allocate twice. The record is
+released together with the card instance.
+
+2. Allocating an extra device.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After allocating a card instance via :c:func:`snd_card_new()`
+(with ``0`` on the 4th arg), call :c:func:`kzalloc()`.
+
+::
+
+ struct snd_card *card;
+ struct mychip *chip;
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
+ .....
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+
+The chip record should have the field to hold the card pointer at least,
+
+::
+
+ struct mychip {
+ struct snd_card *card;
+ ....
+ };
+
+
+Then, set the card pointer in the returned chip instance.
+
+::
+
+ chip->card = card;
+
+Next, initialize the fields, and register this chip record as a
+low-level device with a specified ``ops``,
+
+::
+
+ static struct snd_device_ops ops = {
+ .dev_free = snd_mychip_dev_free,
+ };
+ ....
+ snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+
+:c:func:`snd_mychip_dev_free()` is the device-destructor
+function, which will call the real destructor.
+
+::
+
+ static int snd_mychip_dev_free(struct snd_device *device)
+ {
+ return snd_mychip_free(device->device_data);
+ }
+
+where :c:func:`snd_mychip_free()` is the real destructor.
+
+Registration and Release
+------------------------
+
+After all components are assigned, register the card instance by calling
+:c:func:`snd_card_register()`. Access to the device files is
+enabled at this point. That is, before
+:c:func:`snd_card_register()` is called, the components are safely
+inaccessible from external side. If this call fails, exit the probe
+function after releasing the card via :c:func:`snd_card_free()`.
+
+For releasing the card instance, you can call simply
+:c:func:`snd_card_free()`. As mentioned earlier, all components
+are released automatically by this call.
+
+For a device which allows hotplugging, you can use
+:c:func:`snd_card_free_when_closed()`. This one will postpone
+the destruction until all devices are closed.
+
+PCI Resource Management
+=======================
+
+Full Code Example
+-----------------
+
+In this section, we'll complete the chip-specific constructor,
+destructor and PCI entries. Example code is shown first, below.
+
+::
+
+ struct mychip {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ unsigned long port;
+ int irq;
+ };
+
+ static int snd_mychip_free(struct mychip *chip)
+ {
+ /* disable hardware here if any */
+ .... /* (not implemented in this document) */
+
+ /* release the irq */
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+ /* release the I/O ports & memory */
+ pci_release_regions(chip->pci);
+ /* disable the PCI entry */
+ pci_disable_device(chip->pci);
+ /* release the data */
+ kfree(chip);
+ return 0;
+ }
+
+ /* chip-specific constructor */
+ static int snd_mychip_create(struct snd_card *card,
+ struct pci_dev *pci,
+ struct mychip **rchip)
+ {
+ struct mychip *chip;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_mychip_dev_free,
+ };
+
+ *rchip = NULL;
+
+ /* initialize the PCI entry */
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+ /* check PCI availability (28bit DMA) */
+ if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
+ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ printk(KERN_ERR "error to set 28bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ /* initialize the stuff */
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ /* (1) PCI resource allocation */
+ err = pci_request_regions(pci, "My Chip");
+ if (err < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->port = pci_resource_start(pci, 0);
+ if (request_irq(pci->irq, snd_mychip_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
+ snd_mychip_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ /* (2) initialization of the chip hardware */
+ .... /* (not implemented in this document) */
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_mychip_free(chip);
+ return err;
+ }
+
+ *rchip = chip;
+ return 0;
+ }
+
+ /* PCI IDs */
+ static struct pci_device_id snd_mychip_ids[] = {
+ { 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 = KBUILD_MODNAME,
+ .id_table = snd_mychip_ids,
+ .probe = snd_mychip_probe,
+ .remove = snd_mychip_remove,
+ };
+
+ /* module initialization */
+ static int __init alsa_card_mychip_init(void)
+ {
+ return pci_register_driver(&driver);
+ }
+
+ /* module clean up */
+ 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 */
+
+Some Hafta's
+------------
+
+The allocation of PCI resources is done in the ``probe`` function, and
+usually an extra :c:func:`xxx_create()` function is written for this
+purpose.
+
+In the case of PCI devices, you first have to call the
+:c:func:`pci_enable_device()` 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
+:c:func:`pci_set_master()` function, too.
+
+Suppose the 28bit mask, and the code to be added would be like:
+
+::
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+ if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
+ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+ printk(KERN_ERR "error to set 28bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+
+Resource Allocation
+-------------------
+
+The allocation of I/O ports and irqs is 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 to allocate (pseudo-)DMA for PCI
+like in ALSA 0.5.x.
+
+Now assume that the PCI device has an I/O port with 8 bytes and an
+interrupt. Then :c:type:`struct mychip <mychip>` will have the
+following fields:
+
+::
+
+ struct mychip {
+ struct snd_card *card;
+
+ unsigned long port;
+ int irq;
+ };
+
+
+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
+:c:func:`kzalloc()` automatically, so you don't have to take care of
+resetting them.
+
+The allocation of an I/O port is done like this:
+
+::
+
+ err = pci_request_regions(pci, "My Chip");
+ if (err < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+ chip->port = pci_resource_start(pci, 0);
+
+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
+:c:func:`kmalloc()` by :c:func:`request_region()`. The pointer
+must be released via :c:func:`kfree()`, but there is a problem with
+this. This issue will be explained later.
+
+The allocation of an interrupt source is done like this:
+
+::
+
+ if (request_irq(pci->irq, snd_mychip_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
+ snd_mychip_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+where :c:func:`snd_mychip_interrupt()` is the interrupt handler
+defined `later <#pcm-interface-interrupt-handler>`__. Note that
+``chip->irq`` should be defined only when :c:func:`request_irq()`
+succeeded.
+
+On the PCI bus, interrupts can be shared. Thus, ``IRQF_SHARED`` is used
+as the interrupt flag of :c:func:`request_irq()`.
+
+The last argument of :c:func:`request_irq()` 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.
+
+I won't give details about the interrupt handler at this point, but at
+least its appearance can be explained now. The interrupt handler looks
+usually like the following:
+
+::
+
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
+ {
+ struct mychip *chip = dev_id;
+ ....
+ return IRQ_HANDLED;
+ }
+
+
+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 code is not written here.
+
+To release the resources, the “check-and-release” method is a safer way.
+For the interrupt, do like this:
+
+::
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+
+Since the irq number can start from 0, you should initialize
+``chip->irq`` with a negative value (e.g. -1), so that you can check
+the validity of the irq number as above.
+
+When you requested I/O ports or memory regions via
+:c:func:`pci_request_region()` or
+:c:func:`pci_request_regions()` like in this example, release the
+resource(s) using the corresponding function,
+:c:func:`pci_release_region()` or
+:c:func:`pci_release_regions()`.
+
+::
+
+ pci_release_regions(chip->pci);
+
+When you requested manually via :c:func:`request_region()` or
+:c:func:`request_mem_region()`, you can release it via
+:c:func:`release_resource()`. Suppose that you keep the resource
+pointer returned from :c:func:`request_region()` in
+chip->res_port, the release procedure looks like:
+
+::
+
+ release_and_free_resource(chip->res_port);
+
+Don't forget to call :c:func:`pci_disable_device()` before the
+end.
+
+And finally, release the chip-specific record.
+
+::
+
+ kfree(chip);
+
+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 hardware disabling if the hardware was not
+initialized yet.
+
+When the chip-data is assigned to the card using
+:c:func:`snd_device_new()` with ``SNDRV_DEV_LOWLELVEL`` , its
+destructor is called at the last. That is, it is assured that all other
+components like PCMs and controls have already been released. You don't
+have to stop PCMs, etc. explicitly, but just call low-level hardware
+stopping.
+
+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:
+
+::
+
+ struct mychip {
+ ....
+ unsigned long iobase_phys;
+ void __iomem *iobase_virt;
+ };
+
+and the allocation would be like below:
+
+::
+
+ if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ kfree(chip);
+ return err;
+ }
+ chip->iobase_phys = pci_resource_start(pci, 0);
+ chip->iobase_virt = ioremap_nocache(chip->iobase_phys,
+ pci_resource_len(pci, 0));
+
+and the corresponding destructor would be:
+
+::
+
+ static int snd_mychip_free(struct mychip *chip)
+ {
+ ....
+ if (chip->iobase_virt)
+ iounmap(chip->iobase_virt);
+ ....
+ pci_release_regions(chip->pci);
+ ....
+ }
+
+PCI Entries
+-----------
+
+So far, so good. Let's finish the missing PCI stuff. At first, we need a
+:c:type:`struct pci_device_id <pci_device_id>` table for
+this chipset. It's a table of PCI vendor/device ID number, and some
+masks.
+
+For example,
+
+::
+
+ static struct pci_device_id snd_mychip_ids[] = {
+ { 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);
+
+The first and second fields of the :c:type:`struct pci_device_id
+<pci_device_id>` structure are the vendor and device IDs. If you
+have no reason to filter the matching devices, you can leave the
+remaining fields as above. The last field of the :c:type:`struct
+pci_device_id <pci_device_id>` struct contains private data
+for this entry. You can specify any value here, for example, to define
+specific operations for supported device IDs. Such an example is found
+in the intel8x0 driver.
+
+The last entry of this list is the terminator. You must specify this
+all-zero entry.
+
+Then, prepare the :c:type:`struct pci_driver <pci_driver>`
+record:
+
+::
+
+ static struct pci_driver driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_mychip_ids,
+ .probe = snd_mychip_probe,
+ .remove = snd_mychip_remove,
+ };
+
+The ``probe`` and ``remove`` functions have already been defined in
+the previous sections. The ``name`` field is the name string of this
+device. Note that you must not use a slash “/” in this string.
+
+And at last, the module entries:
+
+::
+
+ static int __init alsa_card_mychip_init(void)
+ {
+ return pci_register_driver(&driver);
+ }
+
+ static void __exit alsa_card_mychip_exit(void)
+ {
+ pci_unregister_driver(&driver);
+ }
+
+ module_init(alsa_card_mychip_init)
+ module_exit(alsa_card_mychip_exit)
+
+Note that these module entries are tagged with ``__init`` and ``__exit``
+prefixes.
+
+Oh, one thing was forgotten. If you have no exported symbols, you need
+to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels).
+
+::
+
+ EXPORT_NO_SYMBOLS;
+
+That's all!
+
+PCM Interface
+=============
+
+General
+-------
+
+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.
+
+For accessing to the PCM layer, you need to include ``<sound/pcm.h>``
+first. In addition, ``<sound/pcm_params.h>`` might be needed if you
+access to some functions related with hw_param.
+
+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 numbers.
+Once when 64bit device number is used, we'll have more pcm instances
+available.
+
+A pcm instance consists of pcm playback and capture streams, and each
+pcm stream consists of one or more pcm substreams. Some soundcards
+support multiple playback functions. 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 successful open
+will either block or error with ``EAGAIN`` according to the file open
+mode. But you don't have to care about such details in your driver. The
+PCM middle layer will take care of such work.
+
+Full Code Example
+-----------------
+
+The example code below does not include any hardware access routines but
+shows only the skeleton, how to build up the PCM interfaces.
+
+::
+
+ #include <sound/pcm.h>
+ ....
+
+ /* hardware definition */
+ static struct snd_pcm_hardware 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,
+ };
+
+ /* hardware definition */
+ static struct snd_pcm_hardware snd_mychip_capture_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_playback_open(struct snd_pcm_substream *substream)
+ {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_mychip_playback_hw;
+ /* more hardware-initialization will be done here */
+ ....
+ return 0;
+ }
+
+ /* close callback */
+ static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
+ {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ /* the hardware-specific codes will be here */
+ ....
+ return 0;
+
+ }
+
+ /* open callback */
+ static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
+ {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_mychip_capture_hw;
+ /* more hardware-initialization will be done here */
+ ....
+ return 0;
+ }
+
+ /* close callback */
+ static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
+ {
+ struct mychip *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(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+ {
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ }
+
+ /* hw_free callback */
+ static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
+ {
+ return snd_pcm_lib_free_pages(substream);
+ }
+
+ /* prepare callback */
+ static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
+ {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *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_addr,
+ chip->buffer_size,
+ chip->period_size);
+ return 0;
+ }
+
+ /* trigger callback */
+ static int snd_mychip_pcm_trigger(struct snd_pcm_substream *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(struct snd_pcm_substream *substream)
+ {
+ struct mychip *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 struct snd_pcm_ops 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,
+ };
+
+ /* operators */
+ static struct snd_pcm_ops snd_mychip_capture_ops = {
+ .open = snd_mychip_capture_open,
+ .close = snd_mychip_capture_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 snd_mychip_new_pcm(struct mychip *chip)
+ {
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+ if (err < 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 */
+ /* NOTE: this may fail */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, 64*1024);
+ return 0;
+ }
+
+
+PCM Constructor
+---------------
+
+A pcm instance is allocated by the :c:func:`snd_pcm_new()`
+function. It would be better to create a constructor for pcm, namely,
+
+::
+
+ static int snd_mychip_new_pcm(struct mychip *chip)
+ {
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, "My Chip");
+ chip->pcm = pcm;
+ ....
+ return 0;
+ }
+
+The :c:func:`snd_pcm_new()` function takes four arguments. The
+first argument is the card pointer to which this pcm is assigned, and
+the second is the ID string.
+
+The third argument (``index``, 0 in the above) is the index of this new
+pcm. It begins from zero. If you create more than one pcm instances,
+specify the different numbers in this argument. For example, ``index =
+1`` for the second PCM device.
+
+The fourth and fifth arguments are the number of substreams for playback
+and capture, respectively. Here 1 is used for both arguments. When no
+playback or capture substreams are available, pass 0 to the
+corresponding argument.
+
+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 :c:type:`struct snd_pcm_substream
+<snd_pcm_substream>` data passed to each callback as follows:
+
+::
+
+ struct snd_pcm_substream *substream;
+ int index = substream->number;
+
+
+After the pcm is created, you need to set operators for each pcm stream.
+
+::
+
+ 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);
+
+The operators are defined typically like this:
+
+::
+
+ static struct snd_pcm_ops 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,
+ };
+
+All the callbacks are described in the Operators_ subsection.
+
+After setting the operators, you probably will want to pre-allocate the
+buffer. For the pre-allocation, simply call the following:
+
+::
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, 64*1024);
+
+It will allocate a buffer up to 64kB as default. Buffer management
+details will be described in the later section `Buffer and Memory
+Management`_.
+
+Additionally, you can set some extra information for this pcm in
+``pcm->info_flags``. The available values are defined as
+``SNDRV_PCM_INFO_XXX`` in ``<sound/asound.h>``, which is used for the
+hardware definition (described later). When your soundchip supports only
+half-duplex, specify like this:
+
+::
+
+ pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+
+
+... And the Destructor?
+-----------------------
+
+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 the destructor explicitly.
+
+The destructor would be necessary if you created special records
+internally and needed to release them. In such a case, set the
+destructor function to ``pcm->private_free``:
+
+::
+
+ static void mychip_pcm_free(struct snd_pcm *pcm)
+ {
+ struct mychip *chip = snd_pcm_chip(pcm);
+ /* free your own data */
+ kfree(chip->my_private_pcm_data);
+ /* do what you like else */
+ ....
+ }
+
+ static int snd_mychip_new_pcm(struct mychip *chip)
+ {
+ struct snd_pcm *pcm;
+ ....
+ /* allocate your own data */
+ chip->my_private_pcm_data = kmalloc(...);
+ /* set the destructor */
+ pcm->private_data = chip;
+ pcm->private_free = mychip_pcm_free;
+ ....
+ }
+
+
+
+Runtime Pointer - The Chest of PCM Information
+----------------------------------------------
+
+When the PCM substream is opened, a PCM runtime instance is allocated
+and assigned to the substream. This pointer is accessible via
+``substream->runtime``. This runtime pointer holds most information you
+need to control the PCM: the copy of hw_params and sw_params
+configurations, the buffer pointers, mmap records, spinlocks, etc.
+
+The definition of runtime instance is found in ``<sound/pcm.h>``. Here
+are the contents of this file:
+
+::
+
+ struct _snd_pcm_runtime {
+ /* -- Status -- */
+ struct snd_pcm_substream *trigger_master;
+ snd_timestamp_t trigger_tstamp; /* trigger timestamp */
+ int overrange;
+ snd_pcm_uframes_t avail_max;
+ snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
+ snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/
+
+ /* -- HW params -- */
+ snd_pcm_access_t access; /* access mode */
+ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */
+ snd_pcm_subformat_t subformat; /* subformat */
+ unsigned int rate; /* rate in Hz */
+ unsigned int channels; /* channels */
+ snd_pcm_uframes_t period_size; /* period size */
+ unsigned int periods; /* periods */
+ snd_pcm_uframes_t buffer_size; /* buffer size */
+ unsigned int tick_time; /* tick time */
+ snd_pcm_uframes_t min_align; /* Min alignment for the format */
+ size_t byte_align;
+ unsigned int frame_bits;
+ unsigned int sample_bits;
+ unsigned int info;
+ unsigned int rate_num;
+ unsigned int rate_den;
+
+ /* -- SW params -- */
+ struct timespec tstamp_mode; /* mmap timestamp is updated */
+ unsigned int period_step;
+ unsigned int sleep_min; /* min ticks to sleep */
+ snd_pcm_uframes_t start_threshold;
+ snd_pcm_uframes_t stop_threshold;
+ snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
+ noise is nearest than this */
+ snd_pcm_uframes_t silence_size; /* Silence filling size */
+ snd_pcm_uframes_t boundary; /* pointers wrap point */
+
+ snd_pcm_uframes_t silenced_start;
+ snd_pcm_uframes_t silenced_size;
+
+ snd_pcm_sync_id_t sync; /* hardware synchronization ID */
+
+ /* -- mmap -- */
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ atomic_t mmap_count;
+
+ /* -- locking / scheduling -- */
+ spinlock_t lock;
+ wait_queue_head_t sleep;
+ struct timer_list tick_timer;
+ struct fasync_struct *fasync;
+
+ /* -- private section -- */
+ void *private_data;
+ void (*private_free)(struct snd_pcm_runtime *runtime);
+
+ /* -- hardware description -- */
+ struct snd_pcm_hardware hw;
+ struct snd_pcm_hw_constraints hw_constraints;
+
+ /* -- timer -- */
+ unsigned int timer_resolution; /* timer resolution */
+
+ /* -- DMA -- */
+ unsigned char *dma_area; /* DMA area */
+ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */
+ size_t dma_bytes; /* size of DMA area */
+
+ struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
+
+ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+ /* -- OSS things -- */
+ struct snd_pcm_oss_runtime oss;
+ #endif
+ };
+
+
+For the operators (callbacks) of each sound driver, most of these
+records are supposed to be read-only. Only the PCM middle-layer changes
+/ updates them. The exceptions are the hardware description (hw) DMA
+buffer information and the private data. Besides, if you use the
+standard buffer allocation method via
+:c:func:`snd_pcm_lib_malloc_pages()`, you don't need to set the
+DMA buffer information by yourself.
+
+In the sections below, important records are explained.
+
+Hardware Description
+~~~~~~~~~~~~~~~~~~~~
+
+The hardware descriptor (:c:type:`struct snd_pcm_hardware
+<snd_pcm_hardware>`) contains the definitions of the fundamental
+hardware configuration. Above all, you'll need to define this in the
+`PCM open callback`_. Note that the runtime instance holds the copy of
+the descriptor, not the pointer to the existing descriptor. That is,
+in the open callback, you can modify the copied descriptor
+(``runtime->hw``) as you need. For example, if the maximum number of
+channels is 1 only on some chip models, you can still use the same
+hardware descriptor and change the channels_max later:
+
+::
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ...
+ runtime->hw = snd_mychip_playback_hw; /* common definition */
+ if (chip->model == VERY_OLD_ONE)
+ runtime->hw.channels_max = 1;
+
+Typically, you'll have a hardware descriptor as below:
+
+::
+
+ static struct snd_pcm_hardware 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,
+ };
+
+- The ``info`` field contains the type and capabilities of this
+ pcm. The bit flags are defined in ``<sound/asound.h>`` as
+ ``SNDRV_PCM_INFO_XXX``. Here, at least, you have to specify whether
+ the mmap is supported and which interleaved format is
+ supported. When the hardware supports mmap, add the
+ ``SNDRV_PCM_INFO_MMAP`` flag here. When the hardware supports the
+ interleaved or the non-interleaved formats,
+ ``SNDRV_PCM_INFO_INTERLEAVED`` or ``SNDRV_PCM_INFO_NONINTERLEAVED``
+ flag must be set, respectively. If both are supported, you can set
+ both, too.
+
+ In the above example, ``MMAP_VALID`` and ``BLOCK_TRANSFER`` are
+ specified for the OSS mmap mode. Usually both are set. Of course,
+ ``MMAP_VALID`` is set only if the mmap is really supported.
+
+ The other possible flags are ``SNDRV_PCM_INFO_PAUSE`` and
+ ``SNDRV_PCM_INFO_RESUME``. The ``PAUSE`` bit means that the pcm
+ supports the “pause” operation, while the ``RESUME`` bit means that
+ the pcm supports the full “suspend/resume” operation. If the
+ ``PAUSE`` flag is set, the ``trigger`` callback below must handle
+ the corresponding (pause push/release) commands. The suspend/resume
+ trigger commands can be defined even without the ``RESUME``
+ flag. See `Power Management`_ section for details.
+
+ When the PCM substreams can be synchronized (typically,
+ synchronized start/stop of a playback and a capture streams), you
+ can give ``SNDRV_PCM_INFO_SYNC_START``, too. In this case, you'll
+ need to check the linked-list of PCM substreams in the trigger
+ callback. This will be described in the later section.
+
+- ``formats`` field contains the bit-flags of supported formats
+ (``SNDRV_PCM_FMTBIT_XXX``). 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.
+
+- ``rates`` field contains the bit-flags of supported rates
+ (``SNDRV_PCM_RATE_XXX``). When the chip supports continuous rates,
+ pass ``CONTINUOUS`` bit additionally. The pre-defined rate bits are
+ provided only for typical rates. If your chip supports
+ unconventional rates, you need to add the ``KNOT`` bit and set up
+ the hardware constraint manually (explained later).
+
+- ``rate_min`` and ``rate_max`` define the minimum and maximum sample
+ rate. This should correspond somehow to ``rates`` bits.
+
+- ``channel_min`` and ``channel_max`` define, as you might already
+ expected, the minimum and maximum number of channels.
+
+- ``buffer_bytes_max`` defines the maximum buffer size in
+ bytes. There is no ``buffer_bytes_min`` field, since it can be
+ calculated from the minimum period size and the minimum number of
+ periods. Meanwhile, ``period_bytes_min`` and define the minimum and
+ maximum size of the period in bytes. ``periods_max`` and
+ ``periods_min`` define the maximum and minimum number of periods in
+ the buffer.
+
+ The “period” is a term that corresponds to a fragment in the OSS
+ world. The period defines the size at which a PCM interrupt is
+ generated. This size strongly depends on the hardware. Generally,
+ the smaller period size will give you more interrupts, that is,
+ more controls. In the case of capture, this size defines the input
+ latency. On the other hand, the whole buffer size defines the
+ output latency for the playback direction.
+
+- There is also a field ``fifo_size``. This specifies the size of the
+ hardware FIFO, but currently it is neither used in the driver nor
+ in the alsa-lib. So, you can ignore this field.
+
+PCM Configurations
+~~~~~~~~~~~~~~~~~~
+
+Ok, let's go back again to the PCM runtime records. The most
+frequently referred records in the runtime instance are the PCM
+configurations. The PCM configurations are stored in the runtime
+instance after the application sends ``hw_params`` data via
+alsa-lib. There are many fields copied from hw_params and sw_params
+structs. For example, ``format`` holds the format type chosen by the
+application. This field contains the enum value
+``SNDRV_PCM_FORMAT_XXX``.
+
+One thing to be noted is that the configured buffer and period sizes
+are stored in “frames” in the runtime. In the ALSA world, ``1 frame =
+channels \* samples-size``. For conversion between frames and bytes,
+you can use the :c:func:`frames_to_bytes()` and
+:c:func:`bytes_to_frames()` helper functions.
+
+::
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+Also, many software parameters (sw_params) are stored in frames, too.
+Please check the type of the field. ``snd_pcm_uframes_t`` is for the
+frames as unsigned integer while ``snd_pcm_sframes_t`` is for the
+frames as signed integer.
+
+DMA Buffer Information
+~~~~~~~~~~~~~~~~~~~~~~
+
+The DMA buffer is defined by the following four fields, ``dma_area``,
+``dma_addr``, ``dma_bytes`` and ``dma_private``. The ``dma_area``
+holds the buffer pointer (the logical address). You can call
+:c:func:`memcpy()` from/to this pointer. Meanwhile, ``dma_addr`` holds
+the physical address of the buffer. This field is specified only when
+the buffer is a linear buffer. ``dma_bytes`` holds the size of buffer
+in bytes. ``dma_private`` is used for the ALSA DMA allocator.
+
+If you use a standard ALSA function,
+:c:func:`snd_pcm_lib_malloc_pages()`, for allocating the buffer,
+these fields are set by the ALSA middle layer, and you should *not*
+change them by yourself. You can read them but not write them. On the
+other hand, if you want to allocate the buffer by yourself, you'll
+need to manage it in hw_params callback. At least, ``dma_bytes`` is
+mandatory. ``dma_area`` is necessary when the buffer is mmapped. If
+your driver doesn't support mmap, this field is not
+necessary. ``dma_addr`` is also optional. You can use dma_private as
+you like, too.
+
+Running Status
+~~~~~~~~~~~~~~
+
+The running status can be referred via ``runtime->status``. This is
+the pointer to the :c:type:`struct snd_pcm_mmap_status
+<snd_pcm_mmap_status>` record. For example, you can get the current
+DMA hardware pointer via ``runtime->status->hw_ptr``.
+
+The DMA application pointer can be referred via ``runtime->control``,
+which points to the :c:type:`struct snd_pcm_mmap_control
+<snd_pcm_mmap_control>` record. However, accessing directly to
+this value is not recommended.
+
+Private Data
+~~~~~~~~~~~~
+
+You can allocate a record for the substream and store it in
+``runtime->private_data``. Usually, this is done in the `PCM open
+callback`_. Don't mix this with ``pcm->private_data``. The
+``pcm->private_data`` usually points to the chip instance assigned
+statically at the creation of PCM, while the ``runtime->private_data``
+points to a dynamic data structure created at the PCM open
+callback.
+
+::
+
+ static int snd_xxx_open(struct snd_pcm_substream *substream)
+ {
+ struct my_pcm_data *data;
+ ....
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ substream->runtime->private_data = data;
+ ....
+ }
+
+
+The allocated object must be released in the `close callback`_.
+
+Operators
+---------
+
+OK, now let me give details about each pcm callback (``ops``). In
+general, every callback must return 0 if successful, or a negative
+error number such as ``-EINVAL``. To choose an appropriate error
+number, it is advised to check what value other parts of the kernel
+return when the same kind of request fails.
+
+The callback function takes at least the argument with :c:type:`struct
+snd_pcm_substream <snd_pcm_substream>` pointer. To retrieve the chip
+record from the given substream instance, you can use the following
+macro.
+
+::
+
+ int xxx() {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ ....
+ }
+
+The macro reads ``substream->private_data``, which is a copy of
+``pcm->private_data``. You can override the former if you need to
+assign different data records per PCM substream. For example, the
+cmi8330 driver assigns different ``private_data`` for playback and
+capture directions, because it uses two different codecs (SB- and
+AD-compatible) for different directions.
+
+PCM open callback
+~~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_open(struct snd_pcm_substream *substream);
+
+This is called when a pcm substream is opened.
+
+At least, here you have to initialize the ``runtime->hw``
+record. Typically, this is done by like this:
+
+::
+
+ static int snd_xxx_open(struct snd_pcm_substream *substream)
+ {
+ struct mychip *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_mychip_playback_hw;
+ return 0;
+ }
+
+where ``snd_mychip_playback_hw`` is the pre-defined hardware
+description.
+
+You can allocate a private data in this callback, as described in
+`Private Data`_ section.
+
+If the hardware configuration needs more constraints, set the hardware
+constraints here, too. See Constraints_ for more details.
+
+close callback
+~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_close(struct snd_pcm_substream *substream);
+
+
+Obviously, this is called when a pcm substream is closed.
+
+Any private instance for a pcm substream allocated in the ``open``
+callback will be released here.
+
+::
+
+ static int snd_xxx_close(struct snd_pcm_substream *substream)
+ {
+ ....
+ kfree(substream->runtime->private_data);
+ ....
+ }
+
+ioctl callback
+~~~~~~~~~~~~~~
+
+This is used for any special call to pcm ioctls. But usually you can
+pass a generic ioctl callback, :c:func:`snd_pcm_lib_ioctl()`.
+
+hw_params callback
+~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params);
+
+This is called when the hardware parameter (``hw_params``) 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.
+
+Many hardware setups should be done in this callback, including the
+allocation of buffers.
+
+Parameters to be initialized are retrieved by
+:c:func:`params_xxx()` macros. To allocate buffer, you can call a
+helper function,
+
+::
+
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+
+:c:func:`snd_pcm_lib_malloc_pages()` is available only when the
+DMA buffers have been pre-allocated. See the section `Buffer Types`_
+for more details.
+
+Note that this and ``prepare`` callbacks may be called multiple times
+per initialization. For example, the OSS emulation may call these
+callbacks at each change via its ioctl.
+
+Thus, you need to be careful not to allocate the same buffers many
+times, which will lead to memory leaks! Calling the helper function
+above many times is OK. It will release the previous buffer
+automatically when it was already allocated.
+
+Another note is that this callback is non-atomic (schedulable) as
+default, i.e. when no ``nonatomic`` flag set. This is important,
+because the ``trigger`` callback is atomic (non-schedulable). That is,
+mutexes or any schedule-related functions are not available in
+``trigger`` callback. Please see the subsection Atomicity_ for
+details.
+
+hw_free callback
+~~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_hw_free(struct snd_pcm_substream *substream);
+
+This is called to release the resources allocated via
+``hw_params``. For example, releasing the buffer via
+:c:func:`snd_pcm_lib_malloc_pages()` is done by calling the
+following:
+
+::
+
+ snd_pcm_lib_free_pages(substream);
+
+This function is always called before the close callback is called.
+Also, the callback may be called multiple times, too. Keep track
+whether the resource was already released.
+
+prepare callback
+~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_prepare(struct snd_pcm_substream *substream);
+
+This callback is called when the pcm is “prepared”. You can set the
+format type, sample rate, etc. here. The difference from ``hw_params``
+is that the ``prepare`` callback will be called each time
+:c:func:`snd_pcm_prepare()` is called, i.e. when recovering after
+underruns, etc.
+
+Note that this callback is now non-atomic. You can use
+schedule-related functions safely in this callback.
+
+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.
+
+Be careful that this callback will be called many times at each setup,
+too.
+
+trigger callback
+~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_trigger(struct snd_pcm_substream *substream, int cmd);
+
+This is called when the pcm is started, stopped or paused.
+
+Which action is specified in the second argument,
+``SNDRV_PCM_TRIGGER_XXX`` in ``<sound/pcm.h>``. At least, the ``START``
+and ``STOP`` commands must be defined in this callback.
+
+::
+
+ 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;
+ }
+
+When the pcm supports the pause operation (given in the info field of
+the hardware table), the ``PAUSE_PUSH`` and ``PAUSE_RELEASE`` commands
+must be handled here, too. The former is the command to pause the pcm,
+and the latter to restart the pcm again.
+
+When the pcm supports the suspend/resume operation, regardless of full
+or partial suspend/resume support, the ``SUSPEND`` and ``RESUME``
+commands must be handled, too. These commands are issued when the
+power-management status is changed. Obviously, the ``SUSPEND`` and
+``RESUME`` commands suspend and resume the pcm substream, and usually,
+they are identical to the ``STOP`` and ``START`` commands, respectively.
+See the `Power Management`_ section for details.
+
+As mentioned, this callback is atomic as default unless ``nonatomic``
+flag set, and you cannot call functions which may sleep. The
+``trigger`` callback should be as minimal as possible, just really
+triggering the DMA. The other stuff should be initialized
+``hw_params`` and ``prepare`` callbacks properly beforehand.
+
+pointer callback
+~~~~~~~~~~~~~~~~
+
+::
+
+ static snd_pcm_uframes_t snd_xxx_pointer(struct snd_pcm_substream *substream)
+
+This callback is called when the PCM middle layer inquires the current
+hardware position on the buffer. The position must be returned in
+frames, ranging from 0 to ``buffer_size - 1``.
+
+This is called usually from the buffer-update routine in the pcm
+middle layer, which is invoked when :c:func:`snd_pcm_period_elapsed()`
+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.
+
+This callback is also atomic as default.
+
+copy and silence callbacks
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These callbacks are not mandatory, and can be omitted in most cases.
+These callbacks are used when the hardware buffer cannot be in 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.
+
+If these two callbacks are defined, copy and set-silence operations
+are done by them. The detailed will be described in the later section
+`Buffer and Memory Management`_.
+
+ack callback
+~~~~~~~~~~~~
+
+This callback is also not mandatory. This callback is called when the
+``appl_ptr`` is updated in read or write operations. Some drivers like
+emu10k1-fx and cs46xx need to track the current ``appl_ptr`` for the
+internal buffer, and this callback is useful only for such a purpose.
+
+This callback is atomic as default.
+
+page callback
+~~~~~~~~~~~~~
+
+This callback is optional too. This callback is used mainly for
+non-contiguous buffers. The mmap calls this callback to get the page
+address. Some examples will be explained in the later section `Buffer
+and Memory Management`_, too.
+
+PCM Interrupt Handler
+---------------------
+
+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 the
+:c:func:`snd_pcm_period_elapsed()` function.
+
+There are several types of sound chips to generate the interrupts.
+
+Interrupts at the period (fragment) boundary
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the most frequently found type: the hardware generates an
+interrupt at each period boundary. In this case, you can call
+:c:func:`snd_pcm_period_elapsed()` at each interrupt.
+
+:c:func:`snd_pcm_period_elapsed()` 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).
+
+If you acquire 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 :c:func:`snd_pcm_period_elapsed()`, because
+:c:func:`snd_pcm_period_elapsed()` calls other pcm callbacks
+inside.
+
+Typical code would be like:
+
+::
+
+
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
+ {
+ struct mychip *chip = dev_id;
+ 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);
+ /* acknowledge the interrupt if necessary */
+ }
+ ....
+ spin_unlock(&chip->lock);
+ return IRQ_HANDLED;
+ }
+
+
+
+High frequency timer interrupts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This happens when the hardware doesn't generate interrupts at the period
+boundary but issues timer interrupts at a fixed timer rate (e.g. es1968
+or ymfpci drivers). In this case, you need to check the current hardware
+position and accumulate the processed sample length at each interrupt.
+When the accumulated size exceeds the period size, call
+:c:func:`snd_pcm_period_elapsed()` and reset the accumulator.
+
+Typical code would be like the following.
+
+::
+
+
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
+ {
+ struct mychip *chip = dev_id;
+ 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);
+ }
+ /* acknowledge the interrupt if necessary */
+ }
+ ....
+ spin_unlock(&chip->lock);
+ return IRQ_HANDLED;
+ }
+
+
+
+On calling :c:func:`snd_pcm_period_elapsed()`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In both cases, even if more than one period are elapsed, you don't have
+to call :c:func:`snd_pcm_period_elapsed()` many times. Call only
+once. And the pcm layer will check the current hardware pointer and
+update to the latest status.
+
+Atomicity
+---------
+
+One of the most important (and thus difficult to debug) problems in
+kernel programming are race conditions. In the Linux kernel, they are
+usually avoided via spin-locks, mutexes or semaphores. In general, if a
+race condition can happen in an interrupt handler, it has to be managed
+atomically, and you have to use a spinlock to protect the critical
+session. If the critical section is not in interrupt handler code and if
+taking a relatively long time to execute is acceptable, you should use
+mutexes or semaphores instead.
+
+As already seen, some pcm callbacks are atomic and some are not. For
+example, the ``hw_params`` callback is non-atomic, while ``trigger``
+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 choose a locking scheme in the callbacks.
+
+In the atomic callbacks, you cannot use functions which may call
+:c:func:`schedule()` or go to :c:func:`sleep()`. Semaphores and
+mutexes can sleep, and hence they cannot be used inside the atomic
+callbacks (e.g. ``trigger`` callback). To implement some delay in such a
+callback, please use :c:func:`udelay()` or :c:func:`mdelay()`.
+
+All three atomic callbacks (trigger, pointer, and ack) are called with
+local interrupts disabled.
+
+The recent changes in PCM core code, however, allow all PCM operations
+to be non-atomic. This assumes that the all caller sides are in
+non-atomic contexts. For example, the function
+:c:func:`snd_pcm_period_elapsed()` is called typically from the
+interrupt handler. But, if you set up the driver to use a threaded
+interrupt handler, this call can be in non-atomic context, too. In such
+a case, you can set ``nonatomic`` filed of :c:type:`struct snd_pcm
+<snd_pcm>` object after creating it. When this flag is set, mutex
+and rwsem are used internally in the PCM core instead of spin and
+rwlocks, so that you can call all PCM functions safely in a non-atomic
+context.
+
+Constraints
+-----------
+
+If your chip supports unconventional sample rates, or only the limited
+samples, you need to set a constraint for the condition.
+
+For example, in order to restrict the sample rates in the some supported
+values, use :c:func:`snd_pcm_hw_constraint_list()`. You need to
+call this function in the open callback.
+
+::
+
+ static unsigned int rates[] =
+ {4000, 10000, 22050, 44100};
+ static struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+
+ static int snd_mychip_pcm_open(struct snd_pcm_substream *substream)
+ {
+ int err;
+ ....
+ err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ if (err < 0)
+ return err;
+ ....
+ }
+
+
+
+There are many different constraints. Look at ``sound/pcm.h`` for a
+complete list. You can even define your own constraint rules. For
+example, let's suppose my_chip can manage a substream of 1 channel if
+and only if the format is ``S16_LE``, otherwise it supports any format
+specified in the :c:type:`struct snd_pcm_hardware
+<snd_pcm_hardware>` structure (or in any other
+constraint_list). You can build a rule like this:
+
+::
+
+ static int hw_rule_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+ {
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_interval ch;
+
+ snd_interval_any(&ch);
+ if (f->bits[0] == SNDRV_PCM_FMTBIT_S16_LE) {
+ ch.min = ch.max = 1;
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
+ }
+ return 0;
+ }
+
+
+Then you need to call this function to add your rule:
+
+::
+
+ snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+
+The rule function is called when an application sets the PCM format, and
+it refines the number of channels accordingly. But an application may
+set the number of channels before setting the format. Thus you also need
+to define the inverse rule:
+
+::
+
+ static int hw_rule_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+ {
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask fmt;
+
+ snd_mask_any(&fmt); /* Init the struct */
+ if (c->min < 2) {
+ fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE;
+ return snd_mask_refine(f, &fmt);
+ }
+ return 0;
+ }
+
+
+... and in the open callback:
+
+::
+
+ snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+I won't give more details here, rather I would like to say, “Luke, use
+the source.”
+
+Control Interface
+=================
+
+General
+-------
+
+The control interface is used widely for many switches, sliders, etc.
+which are accessed from user-space. Its most important use is the mixer
+interface. In other words, since ALSA 0.9.x, all the mixer stuff is
+implemented on the control kernel API.
+
+ALSA has a well-defined AC97 control module. If your chip supports only
+the AC97 and nothing else, you can skip this section.
+
+The control API is defined in ``<sound/control.h>``. Include this file
+if you want to add your own controls.
+
+Definition of Controls
+----------------------
+
+To create a new control, you need to define the following three
+callbacks: ``info``, ``get`` and ``put``. Then, define a
+:c:type:`struct snd_kcontrol_new <snd_kcontrol_new>` record, such as:
+
+::
+
+
+ static struct snd_kcontrol_new my_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0xffff,
+ .info = my_control_info,
+ .get = my_control_get,
+ .put = my_control_put
+ };
+
+
+The ``iface`` field specifies the control type,
+``SNDRV_CTL_ELEM_IFACE_XXX``, which is usually ``MIXER``. Use ``CARD``
+for global controls that are not logically part of the mixer. If the
+control is closely associated with some specific device on the sound
+card, use ``HWDEP``, ``PCM``, ``RAWMIDI``, ``TIMER``, or ``SEQUENCER``,
+and specify the device number with the ``device`` and ``subdevice``
+fields.
+
+The ``name`` is the name identifier string. Since 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 `Control Names`_ subsection.
+
+The ``index`` 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.
+
+The ``access`` field contains the access type of this control. Give
+the combination of bit masks, ``SNDRV_CTL_ELEM_ACCESS_XXX``,
+there. The details will be explained in the `Access Flags`_
+subsection.
+
+The ``private_value`` field contains an arbitrary long integer value
+for this record. When using the generic ``info``, ``get`` and ``put``
+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.
+
+The ``tlv`` field can be used to provide metadata about the control;
+see the `Metadata`_ subsection.
+
+The other three are `Control Callbacks`_.
+
+Control Names
+-------------
+
+There are some standards to define the control names. A control is
+usually defined from the three parts as “SOURCE DIRECTION FUNCTION”.
+
+The first, ``SOURCE``, specifies the source of the control, and is a
+string such as “Master”, “PCM”, “CD” and “Line”. There are many
+pre-defined sources.
+
+The second, ``DIRECTION``, is one of the following strings according to
+the direction of the control: “Playback”, “Capture”, “Bypass Playback”
+and “Bypass Capture”. Or, it can be omitted, meaning both playback and
+capture directions.
+
+The third, ``FUNCTION``, is one of the following strings according to
+the function of the control: “Switch”, “Volume” and “Route”.
+
+The example of control names are, thus, “Master Capture Switch” or “PCM
+Playback Volume”.
+
+There are some exceptions:
+
+Global capture and playback
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+“Capture Source”, “Capture Switch” and “Capture Volume” are used for the
+global capture (input) source, switch and volume. Similarly, “Playback
+Switch” and “Playback Volume” are used for the global output gain switch
+and volume.
+
+Tone-controls
+~~~~~~~~~~~~~
+
+tone-control switch and volumes are specified like “Tone Control - XXX”,
+e.g. “Tone Control - Switch”, “Tone Control - Bass”, “Tone Control -
+Center”.
+
+3D controls
+~~~~~~~~~~~
+
+3D-control switches and volumes are specified like “3D Control - XXX”,
+e.g. “3D Control - Switch”, “3D Control - Center”, “3D Control - Space”.
+
+Mic boost
+~~~~~~~~~
+
+Mic-boost switch is set as “Mic Boost” or “Mic Boost (6dB)”.
+
+More precise information can be found in
+``Documentation/sound/alsa/ControlNames.txt``.
+
+Access Flags
+------------
+
+The access flag is the bitmask which specifies the access type of the
+given control. The default access type is
+``SNDRV_CTL_ELEM_ACCESS_READWRITE``, which means both read and write are
+allowed to this control. When the access flag is omitted (i.e. = 0), it
+is considered as ``READWRITE`` access as default.
+
+When the control is read-only, pass ``SNDRV_CTL_ELEM_ACCESS_READ``
+instead. In this case, you don't have to define the ``put`` callback.
+Similarly, when the control is write-only (although it's a rare case),
+you can use the ``WRITE`` flag instead, and you don't need the ``get``
+callback.
+
+If the control value changes frequently (e.g. the VU meter),
+``VOLATILE`` flag should be given. This means that the control may be
+changed without `Change notification`_. Applications should poll such
+a control constantly.
+
+When the control is inactive, set the ``INACTIVE`` flag, too. There are
+``LOCK`` and ``OWNER`` flags to change the write permissions.
+
+Control Callbacks
+-----------------
+
+info callback
+~~~~~~~~~~~~~
+
+The ``info`` callback is used to get detailed information on this
+control. This must store the values of the given :c:type:`struct
+snd_ctl_elem_info <snd_ctl_elem_info>` object. For example,
+for a boolean control with a single element:
+
+::
+
+
+ static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+ {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+ }
+
+
+
+The ``type`` field specifies the type of the control. There are
+``BOOLEAN``, ``INTEGER``, ``ENUMERATED``, ``BYTES``, ``IEC958`` and
+``INTEGER64``. The ``count`` field specifies the number of elements in
+this control. For example, a stereo volume would have count = 2. The
+``value`` field is a union, and the values stored are depending on the
+type. The boolean and integer types are identical.
+
+The enumerated type is a bit different from others. You'll need to set
+the string for the currently given item index.
+
+::
+
+ static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+ {
+ static char *texts[4] = {
+ "First", "Second", "Third", "Fourth"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ 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]);
+ return 0;
+ }
+
+The above callback can be simplified with a helper function,
+:c:func:`snd_ctl_enum_info()`. The final code looks like below.
+(You can pass ``ARRAY_SIZE(texts)`` instead of 4 in the third argument;
+it's a matter of taste.)
+
+::
+
+ static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+ {
+ static char *texts[4] = {
+ "First", "Second", "Third", "Fourth"
+ };
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
+ }
+
+
+Some common info callbacks are available for your convenience:
+:c:func:`snd_ctl_boolean_mono_info()` and
+:c:func:`snd_ctl_boolean_stereo_info()`. Obviously, the former
+is an info callback for a mono channel boolean item, just like
+:c:func:`snd_myctl_mono_info()` above, and the latter is for a
+stereo channel boolean item.
+
+get callback
+~~~~~~~~~~~~
+
+This callback is used to read the current value of the control and to
+return to user-space.
+
+For example,
+
+::
+
+
+ static int snd_myctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+ {
+ struct mychip *chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = get_some_value(chip);
+ return 0;
+ }
+
+
+
+The ``value`` field depends on the type of control as well as on the
+info callback. For example, the sb driver uses this field to store the
+register offset, the bit-shift and the bit-mask. The ``private_value``
+field is set as follows:
+
+::
+
+ .private_value = reg | (shift << 16) | (mask << 24)
+
+and is retrieved in callbacks like
+
+::
+
+ static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+ {
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 16) & 0xff;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ ....
+ }
+
+In the ``get`` callback, you have to fill all the elements if the
+control has more than one elements, i.e. ``count > 1``. In the example
+above, we filled only one element (``value.integer.value[0]``) since
+it's assumed as ``count = 1``.
+
+put callback
+~~~~~~~~~~~~
+
+This callback is used to write a value from user-space.
+
+For example,
+
+::
+
+
+ static int snd_myctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+ {
+ struct mychip *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;
+ }
+
+
+
+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.
+
+As in the ``get`` callback, when the control has more than one
+elements, all elements must be evaluated in this callback, too.
+
+Callbacks are not atomic
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+All these three callbacks are basically not atomic.
+
+Control Constructor
+-------------------
+
+When everything is ready, finally we can create a new control. To create
+a control, there are two functions to be called,
+:c:func:`snd_ctl_new1()` and :c:func:`snd_ctl_add()`.
+
+In the simplest way, you can do like this:
+
+::
+
+ err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
+ if (err < 0)
+ return err;
+
+where ``my_control`` is the :c:type:`struct snd_kcontrol_new
+<snd_kcontrol_new>` object defined above, and chip is the object
+pointer to be passed to kcontrol->private_data which can be referred
+to in callbacks.
+
+:c:func:`snd_ctl_new1()` allocates a new :c:type:`struct
+snd_kcontrol <snd_kcontrol>` instance, and
+:c:func:`snd_ctl_add()` assigns the given control component to the
+card.
+
+Change Notification
+-------------------
+
+If you need to change and update a control in the interrupt routine, you
+can call :c:func:`snd_ctl_notify()`. For example,
+
+::
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer);
+
+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 :c:type:`struct
+snd_ctl_elem_id <snd_ctl_elem_id>` to be notified. You can
+find some examples in ``es1938.c`` or ``es1968.c`` for hardware volume
+interrupts.
+
+Metadata
+--------
+
+To provide information about the dB values of a mixer control, use on of
+the ``DECLARE_TLV_xxx`` macros from ``<sound/tlv.h>`` to define a
+variable containing this information, set the ``tlv.p`` field to point to
+this variable, and include the ``SNDRV_CTL_ELEM_ACCESS_TLV_READ`` flag
+in the ``access`` field; like this:
+
+::
+
+ static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);
+
+ static struct snd_kcontrol_new my_control = {
+ ...
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ ...
+ .tlv.p = db_scale_my_control,
+ };
+
+
+The :c:func:`DECLARE_TLV_DB_SCALE()` macro defines information
+about a mixer control where each step in the control's value changes the
+dB value by a constant dB amount. The first parameter is the name of the
+variable to be defined. The second parameter is the minimum value, in
+units of 0.01 dB. The third parameter is the step size, in units of 0.01
+dB. Set the fourth parameter to 1 if the minimum value actually mutes
+the control.
+
+The :c:func:`DECLARE_TLV_DB_LINEAR()` macro defines information
+about a mixer control where the control's value affects the output
+linearly. The first parameter is the name of the variable to be defined.
+The second parameter is the minimum value, in units of 0.01 dB. The
+third parameter is the maximum value, in units of 0.01 dB. If the
+minimum value mutes the control, set the second parameter to
+``TLV_DB_GAIN_MUTE``.
+
+API for AC97 Codec
+==================
+
+General
+-------
+
+The ALSA AC97 codec layer is a well-defined one, and you don't have to
+write much code to control it. Only low-level control routines are
+necessary. The AC97 codec API is defined in ``<sound/ac97_codec.h>``.
+
+Full Code Example
+-----------------
+
+::
+
+ struct mychip {
+ ....
+ struct snd_ac97 *ac97;
+ ....
+ };
+
+ static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+ {
+ struct mychip *chip = ac97->private_data;
+ ....
+ /* read a register value here from the codec */
+ return the_register_value;
+ }
+
+ static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+ {
+ struct mychip *chip = ac97->private_data;
+ ....
+ /* write the given register value to the codec */
+ }
+
+ static int snd_mychip_ac97(struct mychip *chip)
+ {
+ struct snd_ac97_bus *bus;
+ struct snd_ac97_template ac97;
+ int err;
+ static struct snd_ac97_bus_ops ops = {
+ .write = snd_mychip_ac97_write,
+ .read = snd_mychip_ac97_read,
+ };
+
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (err < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ return snd_ac97_mixer(bus, &ac97, &chip->ac97);
+ }
+
+
+AC97 Constructor
+----------------
+
+To create an ac97 instance, first call :c:func:`snd_ac97_bus()`
+with an ``ac97_bus_ops_t`` record with callback functions.
+
+::
+
+ struct snd_ac97_bus *bus;
+ static struct snd_ac97_bus_ops ops = {
+ .write = snd_mychip_ac97_write,
+ .read = snd_mychip_ac97_read,
+ };
+
+ snd_ac97_bus(card, 0, &ops, NULL, &pbus);
+
+The bus record is shared among all belonging ac97 instances.
+
+And then call :c:func:`snd_ac97_mixer()` with an :c:type:`struct
+snd_ac97_template <snd_ac97_template>` record together with
+the bus pointer created above.
+
+::
+
+ struct snd_ac97_template ac97;
+ int err;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = chip;
+ snd_ac97_mixer(bus, &ac97, &chip->ac97);
+
+where chip->ac97 is a pointer to a newly created ``ac97_t``
+instance. In this case, the chip pointer is set as the private data,
+so that the read/write callback functions can refer to this chip
+instance. This instance is not necessarily stored in the chip
+record. If 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.
+
+AC97 Callbacks
+--------------
+
+The standard callbacks are ``read`` and ``write``. Obviously they
+correspond to the functions for read and write accesses to the
+hardware low-level codes.
+
+The ``read`` callback returns the register value specified in the
+argument.
+
+::
+
+ static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+ {
+ struct mychip *chip = ac97->private_data;
+ ....
+ return the_register_value;
+ }
+
+Here, the chip can be cast from ``ac97->private_data``.
+
+Meanwhile, the ``write`` callback is used to set the register
+value
+
+::
+
+ static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+
+
+These callbacks are non-atomic like the control API callbacks.
+
+There are also other callbacks: ``reset``, ``wait`` and ``init``.
+
+The ``reset`` callback is used to reset the codec. If the chip
+requires a special kind of reset, you can define this callback.
+
+The ``wait`` callback is used to add some waiting time in the standard
+initialization of the codec. If the chip requires the extra waiting
+time, define this callback.
+
+The ``init`` callback is used for additional initialization of the
+codec.
+
+Updating Registers in The Driver
+--------------------------------
+
+If you need to access to the codec from the driver, you can call the
+following functions: :c:func:`snd_ac97_write()`,
+:c:func:`snd_ac97_read()`, :c:func:`snd_ac97_update()` and
+:c:func:`snd_ac97_update_bits()`.
+
+Both :c:func:`snd_ac97_write()` and
+:c:func:`snd_ac97_update()` functions are used to set a value to
+the given register (``AC97_XXX``). The difference between them is that
+:c:func:`snd_ac97_update()` doesn't write a value if the given
+value has been already set, while :c:func:`snd_ac97_write()`
+always rewrites the value.
+
+::
+
+ snd_ac97_write(ac97, AC97_MASTER, 0x8080);
+ snd_ac97_update(ac97, AC97_MASTER, 0x8080);
+
+:c:func:`snd_ac97_read()` is used to read the value of the given
+register. For example,
+
+::
+
+ value = snd_ac97_read(ac97, AC97_MASTER);
+
+:c:func:`snd_ac97_update_bits()` is used to update some bits in
+the given register.
+
+::
+
+ snd_ac97_update_bits(ac97, reg, mask, value);
+
+Also, there is a function to change the sample rate (of a given register
+such as ``AC97_PCM_FRONT_DAC_RATE``) when VRA or DRA is supported by the
+codec: :c:func:`snd_ac97_set_rate()`.
+
+::
+
+ snd_ac97_set_rate(ac97, AC97_PCM_FRONT_DAC_RATE, 44100);
+
+
+The following registers are available to set the rate:
+``AC97_PCM_MIC_ADC_RATE``, ``AC97_PCM_FRONT_DAC_RATE``,
+``AC97_PCM_LR_ADC_RATE``, ``AC97_SPDIF``. When ``AC97_SPDIF`` is
+specified, the register is not really changed but the corresponding
+IEC958 status bits will be updated.
+
+Clock Adjustment
+----------------
+
+In some chips, the clock of the codec isn't 48000 but using a PCI clock
+(to save a quartz!). In this case, change the field ``bus->clock`` to
+the corresponding value. For example, intel8x0 and es1968 drivers have
+their own function to read from the clock.
+
+Proc Files
+----------
+
+The ALSA AC97 interface will create a proc file such as
+``/proc/asound/card0/codec97#0/ac97#0-0`` and ``ac97#0-0+regs``. You
+can refer to these files to see the current status and registers of
+the codec.
+
+Multiple Codecs
+---------------
+
+When there are several codecs on the same card, you need to call
+:c:func:`snd_ac97_mixer()` multiple times with ``ac97.num=1`` or
+greater. The ``num`` field specifies the codec number.
+
+If you set up multiple codecs, you either need to write different
+callbacks for each codec or check ``ac97->num`` in the callback
+routines.
+
+MIDI (MPU401-UART) Interface
+============================
+
+General
+-------
+
+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
+``<sound/mpu401.h>``.
+
+Some soundchips have a similar but slightly different implementation of
+mpu401 stuff. For example, emu10k1 has its own mpu401 routines.
+
+MIDI Constructor
+----------------
+
+To create a rawmidi object, call :c:func:`snd_mpu401_uart_new()`.
+
+::
+
+ struct snd_rawmidi *rmidi;
+ snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, info_flags,
+ irq, &rmidi);
+
+
+The first argument is the card pointer, and the second is the index of
+this component. You can create up to 8 rawmidi devices.
+
+The third argument is the type of the hardware, ``MPU401_HW_XXX``. If
+it's not a special one, you can use ``MPU401_HW_MPU401``.
+
+The 4th argument is the I/O port address. Many backward-compatible
+MPU401 have 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.
+
+The 5th argument is a bitflag for additional information. When the I/O
+port address above is 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 a bit flag ``MPU401_INFO_INTEGRATED``, and the
+mpu401-uart layer will allocate the I/O ports by itself.
+
+When the controller supports only the input or output MIDI stream, pass
+the ``MPU401_INFO_INPUT`` or ``MPU401_INFO_OUTPUT`` bitflag,
+respectively. Then the rawmidi instance is created as a single stream.
+
+``MPU401_INFO_MMIO`` bitflag is used to change the access method to MMIO
+(via readb and writeb) instead of iob and outb. In this case, you have
+to pass the iomapped address to :c:func:`snd_mpu401_uart_new()`.
+
+When ``MPU401_INFO_TX_IRQ`` is set, the output stream isn't checked in
+the default interrupt handler. The driver needs to call
+:c:func:`snd_mpu401_uart_interrupt_tx()` by itself to start
+processing the output stream in the irq handler.
+
+If the MPU-401 interface shares its interrupt with the other logical
+devices on the card, set ``MPU401_INFO_IRQ_HOOK`` (see
+`below <#MIDI-Interrupt-Handler>`__).
+
+Usually, the port address corresponds to the command port and port + 1
+corresponds to the data port. If not, you may change the ``cport``
+field of :c:type:`struct snd_mpu401 <snd_mpu401>` manually afterward.
+However, :c:type:`struct snd_mpu401 <snd_mpu401>` pointer is
+not returned explicitly by :c:func:`snd_mpu401_uart_new()`. You
+need to cast ``rmidi->private_data`` to :c:type:`struct snd_mpu401
+<snd_mpu401>` explicitly,
+
+::
+
+ struct snd_mpu401 *mpu;
+ mpu = rmidi->private_data;
+
+and reset the ``cport`` as you like:
+
+::
+
+ mpu->cport = my_own_control_port;
+
+The 6th argument specifies the ISA irq number that will be allocated. If
+no interrupt is to be allocated (because your code is already allocating
+a shared interrupt, or because the device does not use interrupts), pass
+-1 instead. For a MPU-401 device without an interrupt, a polling timer
+will be used instead.
+
+MIDI Interrupt Handler
+----------------------
+
+When the interrupt is allocated in
+:c:func:`snd_mpu401_uart_new()`, an exclusive ISA interrupt
+handler is automatically used, hence you don't have anything else to do
+than creating the mpu401 stuff. Otherwise, you have to set
+``MPU401_INFO_IRQ_HOOK``, and call
+:c:func:`snd_mpu401_uart_interrupt()` explicitly from your own
+interrupt handler when it has determined that a UART interrupt has
+occurred.
+
+In this case, you need to pass the private_data of the returned rawmidi
+object from :c:func:`snd_mpu401_uart_new()` as the second
+argument of :c:func:`snd_mpu401_uart_interrupt()`.
+
+::
+
+ snd_mpu401_uart_interrupt(irq, rmidi->private_data, regs);
+
+
+RawMIDI Interface
+=================
+
+Overview
+--------
+
+The raw MIDI interface is used for hardware MIDI ports that can be
+accessed as a byte stream. It is not used for synthesizer chips that do
+not directly understand MIDI.
+
+ALSA handles file and buffer management. All you have to do is to write
+some code to move data between the buffer and the hardware.
+
+The rawmidi API is defined in ``<sound/rawmidi.h>``.
+
+RawMIDI Constructor
+-------------------
+
+To create a rawmidi device, call the :c:func:`snd_rawmidi_new()`
+function:
+
+::
+
+ struct snd_rawmidi *rmidi;
+ err = snd_rawmidi_new(chip->card, "MyMIDI", 0, outs, ins, &rmidi);
+ if (err < 0)
+ return err;
+ rmidi->private_data = chip;
+ strcpy(rmidi->name, "My MIDI");
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+The first argument is the card pointer, the second argument is the ID
+string.
+
+The third argument is the index of this component. You can create up to
+8 rawmidi devices.
+
+The fourth and fifth arguments are the number of output and input
+substreams, respectively, of this device (a substream is the equivalent
+of a MIDI port).
+
+Set the ``info_flags`` field to specify the capabilities of the
+device. Set ``SNDRV_RAWMIDI_INFO_OUTPUT`` if there is at least one
+output port, ``SNDRV_RAWMIDI_INFO_INPUT`` if there is at least one
+input port, and ``SNDRV_RAWMIDI_INFO_DUPLEX`` if the device can handle
+output and input at the same time.
+
+After the rawmidi device is created, you need to set the operators
+(callbacks) for each substream. There are helper functions to set the
+operators for all the substreams of a device:
+
+::
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mymidi_output_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mymidi_input_ops);
+
+The operators are usually defined like this:
+
+::
+
+ static struct snd_rawmidi_ops snd_mymidi_output_ops = {
+ .open = snd_mymidi_output_open,
+ .close = snd_mymidi_output_close,
+ .trigger = snd_mymidi_output_trigger,
+ };
+
+These callbacks are explained in the `RawMIDI Callbacks`_ section.
+
+If there are more than one substream, you should give a unique name to
+each of them:
+
+::
+
+ struct snd_rawmidi_substream *substream;
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+ list {
+ sprintf(substream->name, "My MIDI Port %d", substream->number + 1);
+ }
+ /* same for SNDRV_RAWMIDI_STREAM_INPUT */
+
+RawMIDI Callbacks
+-----------------
+
+In all the callbacks, the private data that you've set for the rawmidi
+device can be accessed as ``substream->rmidi->private_data``.
+
+If there is more than one port, your callbacks can determine the port
+index from the struct snd_rawmidi_substream data passed to each
+callback:
+
+::
+
+ struct snd_rawmidi_substream *substream;
+ int index = substream->number;
+
+RawMIDI open callback
+~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_open(struct snd_rawmidi_substream *substream);
+
+
+This is called when a substream is opened. You can initialize the
+hardware here, but you shouldn't start transmitting/receiving data yet.
+
+RawMIDI close callback
+~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static int snd_xxx_close(struct snd_rawmidi_substream *substream);
+
+Guess what.
+
+The ``open`` and ``close`` callbacks of a rawmidi device are
+serialized with a mutex, and can sleep.
+
+Rawmidi trigger callback for output substreams
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static void snd_xxx_output_trigger(struct snd_rawmidi_substream *substream, int up);
+
+
+This is called with a nonzero ``up`` parameter when there is some data
+in the substream buffer that must be transmitted.
+
+To read data from the buffer, call
+:c:func:`snd_rawmidi_transmit_peek()`. It will return the number
+of bytes that have been read; this will be less than the number of bytes
+requested when there are no more data in the buffer. After the data have
+been transmitted successfully, call
+:c:func:`snd_rawmidi_transmit_ack()` to remove the data from the
+substream buffer:
+
+::
+
+ unsigned char data;
+ while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
+ if (snd_mychip_try_to_transmit(data))
+ snd_rawmidi_transmit_ack(substream, 1);
+ else
+ break; /* hardware FIFO full */
+ }
+
+If you know beforehand that the hardware will accept data, you can use
+the :c:func:`snd_rawmidi_transmit()` function which reads some
+data and removes them from the buffer at once:
+
+::
+
+ while (snd_mychip_transmit_possible()) {
+ unsigned char data;
+ if (snd_rawmidi_transmit(substream, &data, 1) != 1)
+ break; /* no more data */
+ snd_mychip_transmit(data);
+ }
+
+If you know beforehand how many bytes you can accept, you can use a
+buffer size greater than one with the
+:c:func:`snd_rawmidi_transmit\*()` functions.
+
+The ``trigger`` callback must not sleep. If the hardware FIFO is full
+before the substream buffer has been emptied, you have to continue
+transmitting data later, either in an interrupt handler, or with a
+timer if the hardware doesn't have a MIDI transmit interrupt.
+
+The ``trigger`` callback is called with a zero ``up`` parameter when
+the transmission of data should be aborted.
+
+RawMIDI trigger callback for input substreams
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ static void snd_xxx_input_trigger(struct snd_rawmidi_substream *substream, int up);
+
+
+This is called with a nonzero ``up`` parameter to enable receiving data,
+or with a zero ``up`` parameter do disable receiving data.
+
+The ``trigger`` callback must not sleep; the actual reading of data
+from the device is usually done in an interrupt handler.
+
+When data reception is enabled, your interrupt handler should call
+:c:func:`snd_rawmidi_receive()` for all received data:
+
+::
+
+ void snd_mychip_midi_interrupt(...)
+ {
+ while (mychip_midi_available()) {
+ unsigned char data;
+ data = mychip_midi_read();
+ snd_rawmidi_receive(substream, &data, 1);
+ }
+ }
+
+
+drain callback
+~~~~~~~~~~~~~~
+
+::
+
+ static void snd_xxx_drain(struct snd_rawmidi_substream *substream);
+
+
+This is only used with output substreams. This function should wait
+until all data read from the substream buffer have been transmitted.
+This ensures that the device can be closed and the driver unloaded
+without losing data.
+
+This callback is optional. If you do not set ``drain`` in the struct
+snd_rawmidi_ops structure, ALSA will simply wait for 50 milliseconds
+instead.
+
+Miscellaneous Devices
+=====================
+
+FM OPL3
+-------
+
+The FM OPL3 is still used in many chips (mainly for backward
+compatibility). ALSA has a nice OPL3 FM control layer, too. The OPL3 API
+is defined in ``<sound/opl3.h>``.
+
+FM registers can be directly accessed through the direct-FM API, defined
+in ``<sound/asound_fm.h>``. In ALSA native mode, FM registers are
+accessed through the Hardware-Dependent Device direct-FM extension API,
+whereas in OSS compatible mode, FM registers can be accessed with the
+OSS direct-FM compatible API in ``/dev/dmfmX`` device.
+
+To create the OPL3 component, you have two functions to call. The first
+one is a constructor for the ``opl3_t`` instance.
+
+::
+
+ struct snd_opl3 *opl3;
+ snd_opl3_create(card, lport, rport, OPL3_HW_OPL3_XXX,
+ integrated, &opl3);
+
+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.
+
+The fourth argument is the hardware type.
+
+When the left and right ports have been already allocated by the card
+driver, pass non-zero to the fifth argument (``integrated``). Otherwise,
+the opl3 module will allocate the specified ports by itself.
+
+When the accessing the hardware requires special method instead of the
+standard I/O access, you can create opl3 instance separately with
+:c:func:`snd_opl3_new()`.
+
+::
+
+ struct snd_opl3 *opl3;
+ snd_opl3_new(card, OPL3_HW_OPL3_XXX, &opl3);
+
+Then set ``command``, ``private_data`` and ``private_free`` for the
+private access function, the private data and the destructor. The
+``l_port`` and ``r_port`` are not necessarily set. Only the command
+must be set properly. You can retrieve the data from the
+``opl3->private_data`` field.
+
+After creating the opl3 instance via :c:func:`snd_opl3_new()`,
+call :c:func:`snd_opl3_init()` to initialize the chip to the
+proper state. Note that :c:func:`snd_opl3_create()` always calls
+it internally.
+
+If the opl3 instance is created successfully, then create a hwdep device
+for this opl3.
+
+::
+
+ struct snd_hwdep *opl3hwdep;
+ snd_opl3_hwdep_new(opl3, 0, 1, &opl3hwdep);
+
+The first argument is the ``opl3_t`` instance you created, and the
+second is the index number, usually 0.
+
+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).
+
+Hardware-Dependent Devices
+--------------------------
+
+Some chips need user-space access 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
+``<sound/hwdep.h>``. You can find examples in opl3 driver or
+``isa/sb/sb16_csp.c``.
+
+The creation of the ``hwdep`` instance is done via
+:c:func:`snd_hwdep_new()`.
+
+::
+
+ struct snd_hwdep *hw;
+ snd_hwdep_new(card, "My HWDEP", 0, &hw);
+
+where the third argument is the index number.
+
+You can then pass any pointer value to the ``private_data``. If you
+assign a private data, you should define the destructor, too. The
+destructor function is set in the ``private_free`` field.
+
+::
+
+ struct mydata *p = kmalloc(sizeof(*p), GFP_KERNEL);
+ hw->private_data = p;
+ hw->private_free = mydata_free;
+
+and the implementation of the destructor would be:
+
+::
+
+ static void mydata_free(struct snd_hwdep *hw)
+ {
+ struct mydata *p = hw->private_data;
+ kfree(p);
+ }
+
+The arbitrary file operations can be defined for this instance. The file
+operators are defined in the ``ops`` table. For example, assume that
+this chip needs an ioctl.
+
+::
+
+ hw->ops.open = mydata_open;
+ hw->ops.ioctl = mydata_ioctl;
+ hw->ops.release = mydata_release;
+
+And implement the callback functions as you like.
+
+IEC958 (S/PDIF)
+---------------
+
+Usually the controls for IEC958 devices are implemented via the control
+interface. There is a macro to compose a name string for IEC958
+controls, :c:func:`SNDRV_CTL_NAME_IEC958()` defined in
+``<include/asound.h>``.
+
+There are some standard controls for IEC958 status bits. These controls
+use the type ``SNDRV_CTL_ELEM_TYPE_IEC958``, and the size of element is
+fixed as 4 bytes array (value.iec958.status[x]). For the ``info``
+callback, you don't specify the value field for this type (the count
+field must be set, though).
+
+“IEC958 Playback Con Mask” is used to return the bit-mask for the IEC958
+status bits of consumer mode. Similarly, “IEC958 Playback Pro Mask”
+returns the bitmask for professional mode. They are read-only controls,
+and are defined as MIXER controls (iface =
+``SNDRV_CTL_ELEM_IFACE_MIXER``).
+
+Meanwhile, “IEC958 Playback Default” control is defined for getting and
+setting the current default IEC958 bits. Note that this one is usually
+defined as a PCM control (iface = ``SNDRV_CTL_ELEM_IFACE_PCM``),
+although in some places it's defined as a MIXER control.
+
+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 “IEC958 xxx”, preferably using the
+:c:func:`SNDRV_CTL_NAME_IEC958()` macro.
+
+You can find several cases, for example, ``pci/emu10k1``,
+``pci/ice1712``, or ``pci/cmipci.c``.
+
+Buffer and Memory Management
+============================
+
+Buffer Types
+------------
+
+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
+:c:func:`snd_malloc_xxx_pages()` function, where xxx is the bus
+type.
+
+The allocation of pages with fallback is
+:c:func:`snd_malloc_xxx_pages_fallback()`. This function tries
+to allocate the specified pages but if the pages are not available, it
+tries to reduce the page sizes until enough space is found.
+
+The release the pages, call :c:func:`snd_free_xxx_pages()`
+function.
+
+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 “pre-allocation”. As already written, you can call the
+following function at pcm instance construction time (in the case of PCI
+bus).
+
+::
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(pci), size, max);
+
+where ``size`` is the byte size to be pre-allocated and the ``max`` is
+the maximum size to be changed via the ``prealloc`` proc file. The
+allocator will try to get an area as large as possible within the
+given size.
+
+The second argument (type) and the third argument (device pointer) are
+dependent on the bus. In the case of the ISA bus, pass
+:c:func:`snd_dma_isa_data()` as the third argument with
+``SNDRV_DMA_TYPE_DEV`` type. For the continuous buffer unrelated to the
+bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type and the
+``snd_dma_continuous_data(GFP_KERNEL)`` device pointer, where
+``GFP_KERNEL`` is the kernel allocation flag to use. For the PCI
+scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with
+``snd_dma_pci_data(pci)`` (see the `Non-Contiguous Buffers`_
+section).
+
+Once the buffer is pre-allocated, you can use the allocator in the
+``hw_params`` callback:
+
+::
+
+ snd_pcm_lib_malloc_pages(substream, size);
+
+Note that you have to pre-allocate to use this function.
+
+External Hardware Buffers
+-------------------------
+
+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).
+
+The first case works fine if the external hardware buffer is large
+enough. This method doesn't need any extra buffers and thus is more
+effective. You need to define the ``copy`` and ``silence`` 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.
+
+The second case allows for mmap on the buffer, although you have to
+handle an interrupt or a tasklet to transfer the data from the
+intermediate buffer to the hardware buffer. You can find an example in
+the vxpocket driver.
+
+Another case is when 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 the Intel one. In non-mmap mode, the data
+cannot be transferred as in the normal way. Thus you need to define the
+``copy`` and ``silence`` callbacks as well, as in the cases above. The
+examples are found in ``rme32.c`` and ``rme96.c``.
+
+The implementation of the ``copy`` and ``silence`` callbacks depends
+upon whether the hardware supports interleaved or non-interleaved
+samples. The ``copy`` callback is defined like below, a bit
+differently depending whether the direction is playback or capture:
+
+::
+
+ static int playback_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count);
+ static int capture_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count);
+
+In the case of interleaved samples, the second argument (``channel``) is
+not used. The third argument (``pos``) points the current position
+offset in frames.
+
+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.
+
+The last argument is the number of frames to be copied.
+
+What you have to do in this callback is again different between playback
+and capture directions. In the playback case, you copy the given amount
+of data (``count``) at the specified pointer (``src``) to the specified
+offset (``pos``) on the hardware buffer. When coded like memcpy-like
+way, the copy would be like:
+
+::
+
+ my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src,
+ frames_to_bytes(runtime, count));
+
+For the capture direction, you copy the given amount of data (``count``)
+at the specified offset (``pos``) on the hardware buffer to the
+specified pointer (``dst``).
+
+::
+
+ my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos),
+ frames_to_bytes(runtime, count));
+
+Note that both the position and the amount of data are given in frames.
+
+In the case of non-interleaved samples, the implementation will be a bit
+more complicated.
+
+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 ``isa/gus/gus_pcm.c`` as an example.
+
+The ``silence`` callback is also implemented in a similar way
+
+::
+
+ static int silence(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
+
+The meanings of arguments are the same as in the ``copy`` callback,
+although there is no ``src/dst`` argument. In the case of interleaved
+samples, the channel argument has no meaning, as well as on ``copy``
+callback.
+
+The role of ``silence`` callback is to set the given amount
+(``count``) of silence data at the specified offset (``pos``) 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:
+
+::
+
+ my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0,
+ frames_to_bytes(runtime, count));
+
+In the case of non-interleaved samples, again, the implementation
+becomes a bit more complicated. See, for example, ``isa/gus/gus_pcm.c``.
+
+Non-Contiguous Buffers
+----------------------
+
+If your hardware supports the page table as in emu10k1 or the buffer
+descriptors as in via82xx, you can use the scatter-gather (SG) DMA. ALSA
+provides an interface for handling SG-buffers. The API is provided in
+``<sound/pcm.h>``.
+
+For creating the SG-buffer handler, call
+:c:func:`snd_pcm_lib_preallocate_pages()` or
+:c:func:`snd_pcm_lib_preallocate_pages_for_all()` with
+``SNDRV_DMA_TYPE_DEV_SG`` in the PCM constructor like other PCI
+pre-allocator. You need to pass ``snd_dma_pci_data(pci)``, where pci is
+the :c:type:`struct pci_dev <pci_dev>` pointer of the chip as
+well. The ``struct snd_sg_buf`` instance is created as
+``substream->dma_private``. You can cast the pointer like:
+
+::
+
+ struct snd_sg_buf *sgbuf = (struct snd_sg_buf *)substream->dma_private;
+
+Then call :c:func:`snd_pcm_lib_malloc_pages()` in the ``hw_params``
+callback as well as in the case of normal PCI buffer. The SG-buffer
+handler will allocate the non-contiguous kernel pages of the given size
+and map them onto the virtually contiguous memory. The virtual pointer
+is addressed in runtime->dma_area. The physical address
+(``runtime->dma_addr``) is set to zero, because the buffer is
+physically non-contiguous. The physical address table is set up in
+``sgbuf->table``. You can get the physical address at a certain offset
+via :c:func:`snd_pcm_sgbuf_get_addr()`.
+
+When a SG-handler is used, you need to set
+:c:func:`snd_pcm_sgbuf_ops_page()` as the ``page`` callback. (See
+`page callback`_ section.)
+
+To release the data, call :c:func:`snd_pcm_lib_free_pages()` in
+the ``hw_free`` callback as usual.
+
+Vmalloc'ed Buffers
+------------------
+
+It's possible to use a buffer allocated via :c:func:`vmalloc()`, for
+example, for an intermediate buffer. Since the allocated pages are not
+contiguous, you need to set the ``page`` callback to obtain the physical
+address at every offset.
+
+The implementation of ``page`` callback would be like this:
+
+::
+
+ #include <linux/vmalloc.h>
+
+ /* get the physical page pointer on the given offset */
+ static struct page *mychip_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+ {
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+ }
+
+Proc Interface
+==============
+
+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 ``<sound/info.h>``.
+
+To create a proc file, call :c:func:`snd_card_proc_new()`.
+
+::
+
+ struct snd_info_entry *entry;
+ int err = snd_card_proc_new(card, "my-file", &entry);
+
+where the second argument specifies the name of the proc file to be
+created. The above example will create a file ``my-file`` under the
+card directory, e.g. ``/proc/asound/card0/my-file``.
+
+Like other components, the proc entry created via
+:c:func:`snd_card_proc_new()` will be registered and released
+automatically in the card registration and release functions.
+
+When the creation is successful, the function stores a new instance in
+the pointer given in the third argument. It is initialized as a text
+proc file for read only. To use this proc file as a read-only text file
+as it is, set the read callback with a private data via
+:c:func:`snd_info_set_text_ops()`.
+
+::
+
+ snd_info_set_text_ops(entry, chip, my_proc_read);
+
+where the second argument (``chip``) is the private data to be used in
+the callbacks. The third parameter specifies the read buffer size and
+the fourth (``my_proc_read``) is the callback function, which is
+defined like
+
+::
+
+ static void my_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer);
+
+In the read callback, use :c:func:`snd_iprintf()` for output
+strings, which works just like normal :c:func:`printf()`. For
+example,
+
+::
+
+ static void my_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+ {
+ struct my_chip *chip = entry->private_data;
+
+ snd_iprintf(buffer, "This is my chip!\n");
+ snd_iprintf(buffer, "Port = %ld\n", chip->port);
+ }
+
+The file permissions can be changed afterwards. As default, it's set as
+read only for all users. If you want to add write permission for the
+user (root as default), do as follows:
+
+::
+
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+
+and set the write buffer size and the callback
+
+::
+
+ entry->c.text.write = my_proc_write;
+
+For the write callback, you can use :c:func:`snd_info_get_line()`
+to get a text line, and :c:func:`snd_info_get_str()` to retrieve
+a string from the line. Some examples are found in
+``core/oss/mixer_oss.c``, core/oss/and ``pcm_oss.c``.
+
+For a raw-data proc-file, set the attributes as follows:
+
+::
+
+ 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;
+
+For the raw data, ``size`` field must be set properly. This specifies
+the maximum size of the proc file access.
+
+The read/write callbacks of raw mode are more direct than the text mode.
+You need to use a low-level I/O functions such as
+:c:func:`copy_from/to_user()` to transfer the data.
+
+::
+
+ static ssize_t my_file_io_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char *buf,
+ size_t count,
+ loff_t pos)
+ {
+ if (copy_to_user(buf, local_data + pos, count))
+ return -EFAULT;
+ return count;
+ }
+
+If the size of the info entry has been set up properly, ``count`` and
+``pos`` are guaranteed to fit within 0 and the given size. You don't
+have to check the range in the callbacks unless any other condition is
+required.
+
+Power Management
+================
+
+If the chip is supposed to work with suspend/resume functions, you need
+to add power-management code to the driver. The additional code for
+power-management should be ifdef-ed with ``CONFIG_PM``.
+
+If the driver *fully* supports suspend/resume that is, the device can be
+properly resumed to its state when suspend was called, you can set the
+``SNDRV_PCM_INFO_RESUME`` flag in the pcm info field. Usually, this is
+possible when the registers of the chip can be safely saved and restored
+to RAM. If this is set, the trigger callback is called with
+``SNDRV_PCM_TRIGGER_RESUME`` after the resume callback completes.
+
+Even if the driver doesn't support PM fully but partial suspend/resume
+is still possible, it's still worthy to implement suspend/resume
+callbacks. In such a case, applications would reset the status by
+calling :c:func:`snd_pcm_prepare()` and restart the stream
+appropriately. Hence, you can define suspend/resume callbacks below but
+don't set ``SNDRV_PCM_INFO_RESUME`` info flag to the PCM.
+
+Note that the trigger with SUSPEND can always be called when
+:c:func:`snd_pcm_suspend_all()` is called, regardless of the
+``SNDRV_PCM_INFO_RESUME`` flag. The ``RESUME`` flag affects only the
+behavior of :c:func:`snd_pcm_resume()`. (Thus, in theory,
+``SNDRV_PCM_TRIGGER_RESUME`` isn't needed to be handled in the trigger
+callback when no ``SNDRV_PCM_INFO_RESUME`` flag is set. But, it's better
+to keep it for compatibility reasons.)
+
+In the earlier version of ALSA drivers, a common power-management layer
+was provided, but it has been removed. The driver needs to define the
+suspend/resume hooks according to the bus the device is connected to. In
+the case of PCI drivers, the callbacks look like below:
+
+::
+
+ #ifdef CONFIG_PM
+ static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
+ {
+ .... /* do things for suspend */
+ return 0;
+ }
+ static int snd_my_resume(struct pci_dev *pci)
+ {
+ .... /* do things for suspend */
+ return 0;
+ }
+ #endif
+
+The scheme of the real suspend job is as follows.
+
+1. Retrieve the card and the chip data.
+
+2. Call :c:func:`snd_power_change_state()` with
+ ``SNDRV_CTL_POWER_D3hot`` to change the power status.
+
+3. Call :c:func:`snd_pcm_suspend_all()` to suspend the running
+ PCM streams.
+
+4. If AC97 codecs are used, call :c:func:`snd_ac97_suspend()` for
+ each codec.
+
+5. Save the register values if necessary.
+
+6. Stop the hardware if necessary.
+
+7. Disable the PCI device by calling
+ :c:func:`pci_disable_device()`. Then, call
+ :c:func:`pci_save_state()` at last.
+
+A typical code would be like:
+
+::
+
+ static int mychip_suspend(struct pci_dev *pci, pm_message_t state)
+ {
+ /* (1) */
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct mychip *chip = card->private_data;
+ /* (2) */
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ /* (3) */
+ snd_pcm_suspend_all(chip->pcm);
+ /* (4) */
+ snd_ac97_suspend(chip->ac97);
+ /* (5) */
+ snd_mychip_save_registers(chip);
+ /* (6) */
+ snd_mychip_stop_hardware(chip);
+ /* (7) */
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ return 0;
+ }
+
+
+The scheme of the real resume job is as follows.
+
+1. Retrieve the card and the chip data.
+
+2. Set up PCI. First, call :c:func:`pci_restore_state()`. Then
+ enable the pci device again by calling
+ :c:func:`pci_enable_device()`. Call
+ :c:func:`pci_set_master()` if necessary, too.
+
+3. Re-initialize the chip.
+
+4. Restore the saved registers if necessary.
+
+5. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`.
+
+6. Restart the hardware (if any).
+
+7. Call :c:func:`snd_power_change_state()` with
+ ``SNDRV_CTL_POWER_D0`` to notify the processes.
+
+A typical code would be like:
+
+::
+
+ static int mychip_resume(struct pci_dev *pci)
+ {
+ /* (1) */
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct mychip *chip = card->private_data;
+ /* (2) */
+ pci_restore_state(pci);
+ pci_enable_device(pci);
+ pci_set_master(pci);
+ /* (3) */
+ snd_mychip_reinit_chip(chip);
+ /* (4) */
+ snd_mychip_restore_registers(chip);
+ /* (5) */
+ snd_ac97_resume(chip->ac97);
+ /* (6) */
+ snd_mychip_restart_chip(chip);
+ /* (7) */
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+ }
+
+As shown in the above, it's better to save registers after suspending
+the PCM operations via :c:func:`snd_pcm_suspend_all()` or
+:c:func:`snd_pcm_suspend()`. It means that the PCM streams are
+already stopped when the register snapshot is taken. But, remember that
+you don't have to restart the PCM stream in the resume callback. It'll
+be restarted via trigger call with ``SNDRV_PCM_TRIGGER_RESUME`` when
+necessary.
+
+OK, we have all callbacks now. Let's set them up. In the initialization
+of the card, make sure that you can get the chip data from the card
+instance, typically via ``private_data`` field, in case you created the
+chip data individually.
+
+::
+
+ static int snd_mychip_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+ {
+ ....
+ struct snd_card *card;
+ struct mychip *chip;
+ int err;
+ ....
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
+ ....
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ ....
+ card->private_data = chip;
+ ....
+ }
+
+When you created the chip data with :c:func:`snd_card_new()`, it's
+anyway accessible via ``private_data`` field.
+
+::
+
+ static int snd_mychip_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+ {
+ ....
+ struct snd_card *card;
+ struct mychip *chip;
+ int err;
+ ....
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct mychip), &card);
+ ....
+ chip = card->private_data;
+ ....
+ }
+
+If you need a space to save the registers, allocate the buffer for it
+here, too, since it would be fatal if you cannot allocate a memory in
+the suspend phase. The allocated buffer should be released in the
+corresponding destructor.
+
+And next, set suspend/resume callbacks to the pci_driver.
+
+::
+
+ static struct pci_driver driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_my_ids,
+ .probe = snd_my_probe,
+ .remove = snd_my_remove,
+ #ifdef CONFIG_PM
+ .suspend = snd_my_suspend,
+ .resume = snd_my_resume,
+ #endif
+ };
+
+Module Parameters
+=================
+
+There are standard module options for ALSA. At least, each module should
+have the ``index``, ``id`` and ``enable`` options.
+
+If the module supports multiple cards (usually up to 8 = ``SNDRV_CARDS``
+cards), they should be arrays. The default initial values are defined
+already as constants for easier programming:
+
+::
+
+ 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;
+
+If the module supports only a single card, they could be single
+variables, instead. ``enable`` option is not always necessary in this
+case, but it would be better to have a dummy option for compatibility.
+
+The module parameters must be declared with the standard
+``module_param()()``, ``module_param_array()()`` and
+:c:func:`MODULE_PARM_DESC()` macros.
+
+The typical coding would be like below:
+
+::
+
+ #define CARD_NAME "My Chip"
+
+ module_param_array(index, int, NULL, 0444);
+ MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+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 “tainted”.
+
+::
+
+ MODULE_DESCRIPTION("My Chip");
+ MODULE_LICENSE("GPL");
+ MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}");
+
+
+How To Put Your Driver Into ALSA Tree
+=====================================
+
+General
+-------
+
+So far, you've learned how to write the driver codes. And you might have
+a question now: how to put my own driver into the ALSA driver tree? Here
+(finally :) the standard procedure is described briefly.
+
+Suppose that you create a new PCI driver for the card “xyz”. The card
+module name would be snd-xyz. The new driver is usually put into the
+alsa-driver tree, ``alsa-driver/pci`` directory in the case of PCI
+cards. Then the driver is evaluated, audited and tested by developers
+and users. After a certain time, the driver will go to the alsa-kernel
+tree (to the corresponding directory, such as ``alsa-kernel/pci``) and
+eventually will be integrated into the Linux 2.6 tree (the directory
+would be ``linux/sound/pci``).
+
+In the following sections, the driver code is supposed to be put into
+alsa-driver tree. The two cases are covered: a driver consisting of a
+single source file and one consisting of several source files.
+
+Driver with A Single Source File
+--------------------------------
+
+1. Modify alsa-driver/pci/Makefile
+
+ Suppose you have a file xyz.c. Add the following two lines
+
+::
+
+ snd-xyz-objs := xyz.o
+ obj-$(CONFIG_SND_XYZ) += snd-xyz.o
+
+2. Create the Kconfig entry
+
+ Add the new entry of Kconfig for your xyz driver. config SND_XYZ
+ tristate "Foobar XYZ" depends on SND select SND_PCM help Say Y here
+ to include support for Foobar XYZ soundcard. To compile this driver
+ as a module, choose M here: the module will be called snd-xyz. the
+ line, select SND_PCM, specifies that the driver xyz supports PCM. In
+ addition to SND_PCM, the following components are supported for
+ select command: SND_RAWMIDI, SND_TIMER, SND_HWDEP,
+ SND_MPU401_UART, SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB,
+ SND_AC97_CODEC. Add the select command for each supported
+ component.
+
+ Note that some selections imply the lowlevel selections. For example,
+ PCM includes TIMER, MPU401_UART includes RAWMIDI, AC97_CODEC
+ includes PCM, and OPL3_LIB includes HWDEP. You don't need to give
+ the lowlevel selections again.
+
+ For the details of Kconfig script, refer to the kbuild documentation.
+
+3. Run cvscompile script to re-generate the configure script and build
+ the whole stuff again.
+
+Drivers with Several Source Files
+---------------------------------
+
+Suppose that the driver snd-xyz have several source files. They are
+located in the new subdirectory, pci/xyz.
+
+1. Add a new directory (``xyz``) in ``alsa-driver/pci/Makefile`` as
+ below
+
+::
+
+ obj-$(CONFIG_SND) += xyz/
+
+
+2. Under the directory ``xyz``, create a Makefile
+
+::
+
+ ifndef SND_TOPDIR
+ SND_TOPDIR=../..
+ endif
+
+ include $(SND_TOPDIR)/toplevel.config
+ include $(SND_TOPDIR)/Makefile.conf
+
+ snd-xyz-objs := xyz.o abc.o def.o
+
+ obj-$(CONFIG_SND_XYZ) += snd-xyz.o
+
+ include $(SND_TOPDIR)/Rules.make
+
+3. Create the Kconfig entry
+
+ This procedure is as same as in the last section.
+
+4. Run cvscompile script to re-generate the configure script and build
+ the whole stuff again.
+
+Useful Functions
+================
+
+:c:func:`snd_printk()` and friends
+---------------------------------------
+
+ALSA provides a verbose version of the :c:func:`printk()` function.
+If a kernel config ``CONFIG_SND_VERBOSE_PRINTK`` is set, this function
+prints the given message together with the file name and the line of the
+caller. The ``KERN_XXX`` prefix is processed as well as the original
+:c:func:`printk()` does, so it's recommended to add this prefix,
+e.g. snd_printk(KERN_ERR "Oh my, sorry, it's extremely bad!\\n");
+
+There are also :c:func:`printk()`'s for debugging.
+:c:func:`snd_printd()` can be used for general debugging purposes.
+If ``CONFIG_SND_DEBUG`` is set, this function is compiled, and works
+just like :c:func:`snd_printk()`. If the ALSA is compiled without
+the debugging flag, it's ignored.
+
+:c:func:`snd_printdd()` is compiled in only when
+``CONFIG_SND_DEBUG_VERBOSE`` is set. Please note that
+``CONFIG_SND_DEBUG_VERBOSE`` is not set as default even if you configure
+the alsa-driver with ``--with-debug=full`` option. You need to give
+explicitly ``--with-debug=detect`` option instead.
+
+:c:func:`snd_BUG()`
+------------------------
+
+It shows the ``BUG?`` message and stack trace as well as
+:c:func:`snd_BUG_ON()` at the point. It's useful to show that a
+fatal error happens there.
+
+When no debug flag is set, this macro is ignored.
+
+:c:func:`snd_BUG_ON()`
+----------------------------
+
+:c:func:`snd_BUG_ON()` macro is similar with
+:c:func:`WARN_ON()` macro. For example, snd_BUG_ON(!pointer); or
+it can be used as the condition, if (snd_BUG_ON(non_zero_is_bug))
+return -EINVAL;
+
+The macro takes an conditional expression to evaluate. When
+``CONFIG_SND_DEBUG``, is set, if the expression is non-zero, it shows
+the warning message such as ``BUG? (xxx)`` normally followed by stack
+trace. In both cases it returns the evaluated value.
+
+Acknowledgments
+===============
+
+I would like to thank Phil Kerr for his help for improvement and
+corrections of this document.
+
+Kevin Conder reformatted the original plain-text to the DocBook format.
+
+Giuliano Pochini corrected typos and contributed the example codes in
+the hardware constraints section.
--- /dev/null
+==============
+Audio Clocking
+==============
+
+This text describes the audio clocking terms in ASoC and digital audio in
+general. Note: Audio clocking can be complex!
+
+
+Master Clock
+------------
+
+Every audio subsystem is driven by a master clock (sometimes referred to as MCLK
+or SYSCLK). This audio master clock can be derived from a number of sources
+(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
+audio playback and capture sample rates.
+
+Some master clocks (e.g. PLLs and CPU based clocks) are configurable in that
+their speed can be altered by software (depending on the system use and to save
+power). Other master clocks are fixed at a set frequency (i.e. crystals).
+
+
+DAI Clocks
+----------
+The Digital Audio Interface is usually driven by a Bit Clock (often referred to
+as BCLK). This clock is used to drive the digital audio data across the link
+between the codec and CPU.
+
+The DAI also has a frame clock to signal the start of each audio frame. This
+clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
+runs at exactly the sample rate (LRC = Rate).
+
+Bit Clock can be generated as follows:-
+
+- BCLK = MCLK / x, or
+- BCLK = LRC * x, or
+- BCLK = LRC * Channels * Word Size
+
+This relationship depends on the codec or SoC CPU in particular. In general
+it is best to configure BCLK to the lowest possible speed (depending on your
+rate, number of channels and word size) to save on power.
+
+It is also desirable to use the codec (if possible) to drive (or master) the
+audio clocks as it usually gives more accurate sample rates than the CPU.
+
+
+
--- /dev/null
+==============================================
+Creating codec to codec dai link for ALSA dapm
+==============================================
+
+Mostly the flow of audio is always from CPU to codec so your system
+will look as below:
+::
+
+ --------- ---------
+ | | dai | |
+ CPU -------> codec
+ | | | |
+ --------- ---------
+
+In case your system looks as below:
+::
+
+ ---------
+ | |
+ codec-2
+ | |
+ ---------
+ |
+ dai-2
+ |
+ ---------- ---------
+ | | dai-1 | |
+ CPU -------> codec-1
+ | | | |
+ ---------- ---------
+ |
+ dai-3
+ |
+ ---------
+ | |
+ codec-3
+ | |
+ ---------
+
+Suppose codec-2 is a bluetooth chip and codec-3 is connected to
+a speaker and you have a below scenario:
+codec-2 will receive the audio data and the user wants to play that
+audio through codec-3 without involving the CPU.This
+aforementioned case is the ideal case when codec to codec
+connection should be used.
+
+Your dai_link should appear as below in your machine
+file:
+::
+
+ /*
+ * this pcm stream only supports 24 bit, 2 channel and
+ * 48k sampling rate.
+ */
+ static const struct snd_soc_pcm_stream dsp_codec_params = {
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ };
+
+ {
+ .name = "CPU-DSP",
+ .stream_name = "CPU-DSP",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_name = "codec-2,
+ .codec_dai_name = "codec-2-dai_name",
+ .platform_name = "samsung-i2s.0",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &dsp_codec_params,
+ },
+ {
+ .name = "DSP-CODEC",
+ .stream_name = "DSP-CODEC",
+ .cpu_dai_name = "wm0010-sdi2",
+ .codec_name = "codec-3,
+ .codec_dai_name = "codec-3-dai_name",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &dsp_codec_params,
+ },
+
+Above code snippet is motivated from sound/soc/samsung/speyside.c.
+
+Note the "params" callback which lets the dapm know that this
+dai_link is a codec to codec connection.
+
+In dapm core a route is created between cpu_dai playback widget
+and codec_dai capture widget for playback path and vice-versa is
+true for capture path. In order for this aforementioned route to get
+triggered, DAPM needs to find a valid endpoint which could be either
+a sink or source widget corresponding to playback and capture path
+respectively.
+
+In order to trigger this dai_link widget, a thin codec driver for
+the speaker amp can be created as demonstrated in wm8727.c file, it
+sets appropriate constraints for the device even if it needs no control.
+
+Make sure to name your corresponding cpu and codec playback and capture
+dai names ending with "Playback" and "Capture" respectively as dapm core
+will link and power those dais based on the name.
+
+Note that in current device tree there is no way to mark a dai_link
+as codec to codec. However, it may change in future.
--- /dev/null
+=======================
+ASoC Codec Class Driver
+=======================
+
+The codec class driver is generic and hardware independent code that configures
+the codec, FM, MODEM, BT or external DSP to provide audio capture and playback.
+It should contain no code that is specific to the target platform or machine.
+All platform and machine specific code should be added to the platform and
+machine drivers respectively.
+
+Each codec class driver *must* provide the following features:-
+
+1. Codec DAI and PCM configuration
+2. Codec control IO - using RegMap API
+3. Mixers and audio controls
+4. Codec audio operations
+5. DAPM description.
+6. DAPM event handler.
+
+Optionally, codec drivers can also provide:-
+
+7. DAC Digital mute control.
+
+Its probably best to use this guide in conjunction with the existing codec
+driver code in sound/soc/codecs/
+
+ASoC Codec driver breakdown
+===========================
+
+Codec DAI and PCM configuration
+-------------------------------
+Each codec driver must have a struct snd_soc_dai_driver to define its DAI and
+PCM capabilities and operations. This struct is exported so that it can be
+registered with the core by your machine driver.
+
+e.g.
+::
+
+ static struct snd_soc_dai_ops wm8731_dai_ops = {
+ .prepare = wm8731_pcm_prepare,
+ .hw_params = wm8731_hw_params,
+ .shutdown = wm8731_shutdown,
+ .digital_mute = wm8731_mute,
+ .set_sysclk = wm8731_set_dai_sysclk,
+ .set_fmt = wm8731_set_dai_fmt,
+ };
+
+ struct snd_soc_dai_driver wm8731_dai = {
+ .name = "wm8731-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .ops = &wm8731_dai_ops,
+ .symmetric_rates = 1,
+ };
+
+
+Codec control IO
+----------------
+The codec can usually be controlled via an I2C or SPI style interface
+(AC97 combines control with data in the DAI). The codec driver should use the
+Regmap API for all codec IO. Please see include/linux/regmap.h and existing
+codec drivers for example regmap usage.
+
+
+Mixers and audio controls
+-------------------------
+All the codec mixers and audio controls can be defined using the convenience
+macros defined in soc.h.
+::
+
+ #define SOC_SINGLE(xname, reg, shift, mask, invert)
+
+Defines a single control as follows:-
+::
+
+ xname = Control name e.g. "Playback Volume"
+ reg = codec register
+ shift = control bit(s) offset in register
+ mask = control bit size(s) e.g. mask of 7 = 3 bits
+ invert = the control is inverted
+
+Other macros include:-
+::
+
+ #define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
+
+A stereo control
+::
+
+ #define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
+
+A stereo control spanning 2 registers
+::
+
+ #define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
+
+Defines an single enumerated control as follows:-
+::
+
+ xreg = register
+ xshift = control bit(s) offset in register
+ xmask = control bit(s) size
+ xtexts = pointer to array of strings that describe each setting
+
+ #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
+
+Defines a stereo enumerated control
+
+
+Codec Audio Operations
+----------------------
+The codec driver also supports the following ALSA PCM operations:-
+::
+
+ /* SoC audio ops */
+ struct snd_soc_ops {
+ int (*startup)(struct snd_pcm_substream *);
+ void (*shutdown)(struct snd_pcm_substream *);
+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
+ int (*hw_free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+ };
+
+Please refer to the ALSA driver PCM documentation for details.
+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/
+
+
+DAPM description
+----------------
+The Dynamic Audio Power Management description describes the codec power
+components and their relationships and registers to the ASoC core.
+Please read dapm.txt for details of building the description.
+
+Please also see the examples in other codec drivers.
+
+
+DAPM event handler
+------------------
+This function is a callback that handles codec domain PM calls and system
+domain PM calls (e.g. suspend and resume). It is used to put the codec
+to sleep when not in use.
+
+Power states:-
+::
+
+ SNDRV_CTL_POWER_D0: /* full On */
+ /* vref/mid, clk and osc on, active */
+
+ SNDRV_CTL_POWER_D1: /* partial On */
+ SNDRV_CTL_POWER_D2: /* partial On */
+
+ SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ /* everything off except vref/vmid, inactive */
+
+ SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
+
+
+Codec DAC digital mute control
+------------------------------
+Most codecs have a digital mute before the DACs that can be used to
+minimise any system noise. The mute stops any digital data from
+entering the DAC.
+
+A callback can be created that is called by the core for each codec DAI
+when the mute is applied or freed.
+
+i.e.
+::
+
+ static int wm8974_mute(struct snd_soc_dai *dai, int mute)
+ {
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
+
+ if (mute)
+ snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
+ else
+ snd_soc_write(codec, WM8974_DAC, mute_reg);
+ return 0;
+ }
--- /dev/null
+==================================
+ASoC Digital Audio Interface (DAI)
+==================================
+
+ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
+SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.
+
+
+AC97
+====
+
+AC97 is a five wire interface commonly found on many PC sound cards. It is
+now also popular in many portable devices. This DAI has a reset line and time
+multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
+The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
+frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
+frame is 21uS long and is divided into 13 time slots.
+
+The AC97 specification can be found at :
+http://www.intel.com/p/en_US/business/design
+
+
+I2S
+===
+
+I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
+Rx lines are used for audio transmission, whilst the bit clock (BCLK) and
+left/right clock (LRC) synchronise the link. I2S is flexible in that either the
+controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
+usually varies depending on the sample rate and the master system clock
+(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
+ADC and DAC LRCLKs, this allows for simultaneous capture and playback at
+different sample rates.
+
+I2S has several different operating modes:-
+
+I2S
+ MSB is transmitted on the falling edge of the first BCLK after LRC
+ transition.
+
+Left Justified
+ MSB is transmitted on transition of LRC.
+
+Right Justified
+ MSB is transmitted sample size BCLKs before LRC transition.
+
+PCM
+===
+
+PCM is another 4 wire interface, very similar to I2S, which can support a more
+flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
+to synchronise the link whilst the Tx and Rx lines are used to transmit and
+receive the audio data. Bit clock usually varies depending on sample rate
+whilst sync runs at the sample rate. PCM also supports Time Division
+Multiplexing (TDM) in that several devices can use the bus simultaneously (this
+is sometimes referred to as network mode).
+
+Common PCM operating modes:-
+
+Mode A
+ MSB is transmitted on falling edge of first BCLK after FRAME/SYNC.
+
+Mode B
+ MSB is transmitted on rising edge of FRAME/SYNC.
--- /dev/null
+===================================================
+Dynamic Audio Power Management for Portable Devices
+===================================================
+
+Description
+===========
+
+Dynamic Audio Power Management (DAPM) is designed to allow portable
+Linux devices to use the minimum amount of power within the audio
+subsystem at all times. It is independent of other kernel PM and as
+such, can easily co-exist with the other PM systems.
+
+DAPM is also completely transparent to all user space applications as
+all power switching is done within the ASoC core. No code changes or
+recompiling are required for user space applications. DAPM makes power
+switching decisions based upon any audio stream (capture/playback)
+activity and audio mixer settings within the device.
+
+DAPM spans the whole machine. It covers power control within the entire
+audio subsystem, this includes internal codec power blocks and machine
+level power systems.
+
+There are 4 power domains within DAPM
+
+Codec bias domain
+ VREF, VMID (core codec and audio power)
+
+ Usually controlled at codec probe/remove and suspend/resume, although
+ can be set at stream time if power is not needed for sidetone, etc.
+
+Platform/Machine domain
+ physically connected inputs and outputs
+
+ Is platform/machine and user action specific, is configured by the
+ machine driver and responds to asynchronous events e.g when HP
+ are inserted
+
+Path domain
+ audio subsystem signal paths
+
+ Automatically set when mixer and mux settings are changed by the user.
+ e.g. alsamixer, amixer.
+
+Stream domain
+ DACs and ADCs.
+
+ Enabled and disabled when stream playback/capture is started and
+ stopped respectively. e.g. aplay, arecord.
+
+All DAPM power switching decisions are made automatically by consulting an audio
+routing map of the whole machine. This map is specific to each machine and
+consists of the interconnections between every audio component (including
+internal codec components). All audio components that effect power are called
+widgets hereafter.
+
+
+DAPM Widgets
+============
+
+Audio DAPM widgets fall into a number of types:-
+
+Mixer
+ Mixes several analog signals into a single analog signal.
+Mux
+ An analog switch that outputs only one of many inputs.
+PGA
+ A programmable gain amplifier or attenuation widget.
+ADC
+ Analog to Digital Converter
+DAC
+ Digital to Analog Converter
+Switch
+ An analog switch
+Input
+ A codec input pin
+Output
+ A codec output pin
+Headphone
+ Headphone (and optional Jack)
+Mic
+ Mic (and optional Jack)
+Line
+ Line Input/Output (and optional Jack)
+Speaker
+ Speaker
+Supply
+ Power or clock supply widget used by other widgets.
+Regulator
+ External regulator that supplies power to audio components.
+Clock
+ External clock that supplies clock to audio components.
+AIF IN
+ Audio Interface Input (with TDM slot mask).
+AIF OUT
+ Audio Interface Output (with TDM slot mask).
+Siggen
+ Signal Generator.
+DAI IN
+ Digital Audio Interface Input.
+DAI OUT
+ Digital Audio Interface Output.
+DAI Link
+ DAI Link between two DAI structures
+Pre
+ Special PRE widget (exec before all others)
+Post
+ Special POST widget (exec after all others)
+
+(Widgets are defined in include/sound/soc-dapm.h)
+
+Widgets can be added to the sound card by any of the component driver types.
+There are convenience macros defined in soc-dapm.h that can be used to quickly
+build a list of widgets of the codecs and machines DAPM widgets.
+
+Most widgets have a name, register, shift and invert. Some widgets have extra
+parameters for stream name and kcontrols.
+
+
+Stream Domain Widgets
+---------------------
+
+Stream Widgets relate to the stream power domain and only consist of ADCs
+(analog to digital converters), DACs (digital to analog converters),
+AIF IN and AIF OUT.
+
+Stream widgets have the following format:-
+::
+
+ SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
+ SND_SOC_DAPM_AIF_IN(name, stream, slot, reg, shift, invert)
+
+NOTE: the stream name must match the corresponding stream name in your codec
+snd_soc_codec_dai.
+
+e.g. stream widgets for HiFi playback and capture
+::
+
+ SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
+ SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
+
+e.g. stream widgets for AIF
+::
+
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+
+Path Domain Widgets
+-------------------
+
+Path domain widgets have a ability to control or affect the audio signal or
+audio paths within the audio subsystem. They have the following form:-
+::
+
+ SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
+
+Any widget kcontrols can be set using the controls and num_controls members.
+
+e.g. Mixer widget (the kcontrols are declared first)
+::
+
+ /* Output Mixer */
+ static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
+ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+ };
+
+ SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
+ ARRAY_SIZE(wm8731_output_mixer_controls)),
+
+If you don't want the mixer elements prefixed with the name of the mixer widget,
+you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
+as for SND_SOC_DAPM_MIXER.
+
+
+Machine domain Widgets
+----------------------
+
+Machine widgets are different from codec widgets in that they don't have a
+codec register bit associated with them. A machine widget is assigned to each
+machine audio component (non codec or DSP) that can be independently
+powered. e.g.
+
+* Speaker Amp
+* Microphone Bias
+* Jack connectors
+
+A machine widget can have an optional call back.
+
+e.g. Jack connector widget for an external Mic that enables Mic Bias
+when the Mic is inserted:-::
+
+ static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
+ {
+ gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+ }
+
+ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
+
+
+Codec (BIAS) Domain
+-------------------
+
+The codec bias power domain has no widgets and is handled by the codecs DAPM
+event handler. This handler is called when the codec powerstate is changed wrt
+to any stream event or by kernel PM events.
+
+
+Virtual Widgets
+---------------
+
+Sometimes widgets exist in the codec or machine audio map that don't have any
+corresponding soft power control. In this case it is necessary to create
+a virtual widget - a widget with no control bits e.g.
+::
+
+ SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
+
+This can be used to merge to signal paths together in software.
+
+After all the widgets have been defined, they can then be added to the DAPM
+subsystem individually with a call to snd_soc_dapm_new_control().
+
+
+Codec/DSP Widget Interconnections
+=================================
+
+Widgets are connected to each other within the codec, platform and machine by
+audio paths (called interconnections). Each interconnection must be defined in
+order to create a map of all audio paths between widgets.
+
+This is easiest with a diagram of the codec or DSP (and schematic of the machine
+audio system), as it requires joining widgets together via their audio signal
+paths.
+
+e.g., from the WM8731 output mixer (wm8731.c)
+
+The WM8731 output mixer has 3 inputs (sources)
+
+1. Line Bypass Input
+2. DAC (HiFi playback)
+3. Mic Sidetone Input
+
+Each input in this example has a kcontrol associated with it (defined in example
+above) and is connected to the output mixer via its kcontrol name. We can now
+connect the destination widget (wrt audio signal) with its source widgets.
+::
+
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+So we have :-
+
+* Destination Widget <=== Path Name <=== Source Widget, or
+* Sink, Path, Source, or
+* ``Output Mixer`` is connected to the ``DAC`` via the ``HiFi Playback Switch``.
+
+When there is no path name connecting widgets (e.g. a direct connection) we
+pass NULL for the path name.
+
+Interconnections are created with a call to:-
+::
+
+ snd_soc_dapm_connect_input(codec, sink, path, source);
+
+Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and
+interconnections have been registered with the core. This causes the core to
+scan the codec and machine so that the internal DAPM state matches the
+physical state of the machine.
+
+
+Machine Widget Interconnections
+-------------------------------
+Machine widget interconnections are created in the same way as codec ones and
+directly connect the codec pins to machine level widgets.
+
+e.g. connects the speaker out codec pins to the internal speaker.
+::
+
+ /* ext speaker connected to codec pins LOUT2, ROUT2 */
+ {"Ext Spk", NULL , "ROUT2"},
+ {"Ext Spk", NULL , "LOUT2"},
+
+This allows the DAPM to power on and off pins that are connected (and in use)
+and pins that are NC respectively.
+
+
+Endpoint Widgets
+================
+An endpoint is a start or end point (widget) of an audio signal within the
+machine and includes the codec. e.g.
+
+* Headphone Jack
+* Internal Speaker
+* Internal Mic
+* Mic Jack
+* Codec Pins
+
+Endpoints are added to the DAPM graph so that their usage can be determined in
+order to save power. e.g. NC codecs pins will be switched OFF, unconnected
+jacks can also be switched OFF.
+
+
+DAPM Widget Events
+==================
+
+Some widgets can register their interest with the DAPM core in PM events.
+e.g. A Speaker with an amplifier registers a widget so the amplifier can be
+powered only when the spk is in use.
+::
+
+ /* turn speaker amplifier on/off depending on use */
+ static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
+ {
+ gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+ }
+
+ /* corgi machine dapm widgets */
+ static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
+ SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
+
+Please see soc-dapm.h for all other widgets that support events.
+
+
+Event types
+-----------
+
+The following event types are supported by event widgets.
+::
+
+ /* dapm event types */
+ #define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
+ #define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
+ #define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
+ #define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
+ #define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
+ #define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
--- /dev/null
+===========
+Dynamic PCM
+===========
+
+Description
+===========
+
+Dynamic PCM allows an ALSA PCM device to digitally route its PCM audio to
+various digital endpoints during the PCM stream runtime. e.g. PCM0 can route
+digital audio to I2S DAI0, I2S DAI1 or PDM DAI2. This is useful for on SoC DSP
+drivers that expose several ALSA PCMs and can route to multiple DAIs.
+
+The DPCM runtime routing is determined by the ALSA mixer settings in the same
+way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
+graph representing the DSP internal audio paths and uses the mixer settings to
+determine the patch used by each ALSA PCM.
+
+DPCM re-uses all the existing component codec, platform and DAI drivers without
+any modifications.
+
+
+Phone Audio System with SoC based DSP
+-------------------------------------
+
+Consider the following phone audio subsystem. This will be used in this
+document for all examples :-
+::
+
+ | Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
+
+ *************
+ PCM0 <------------> * * <----DAI0-----> Codec Headset
+ * *
+ PCM1 <------------> * * <----DAI1-----> Codec Speakers
+ * DSP *
+ PCM2 <------------> * * <----DAI2-----> MODEM
+ * *
+ PCM3 <------------> * * <----DAI3-----> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+This diagram shows a simple smart phone audio subsystem. It supports Bluetooth,
+FM digital radio, Speakers, Headset Jack, digital microphones and cellular
+modem. This sound card exposes 4 DSP front end (FE) ALSA PCM devices and
+supports 6 back end (BE) DAIs. Each FE PCM can digitally route audio data to any
+of the BE DAIs. The FE PCM devices can also route audio to more than 1 BE DAI.
+
+
+
+Example - DPCM Switching playback from DAI0 to DAI1
+---------------------------------------------------
+
+Audio is being played to the Headset. After a while the user removes the headset
+and audio continues playing on the speakers.
+
+Playback on PCM0 to Headset would look like :-
+::
+
+ *************
+ PCM0 <============> * * <====DAI0=====> Codec Headset
+ * *
+ PCM1 <------------> * * <----DAI1-----> Codec Speakers
+ * DSP *
+ PCM2 <------------> * * <----DAI2-----> MODEM
+ * *
+ PCM3 <------------> * * <----DAI3-----> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+The headset is removed from the jack by user so the speakers must now be used :-
+::
+
+ *************
+ PCM0 <============> * * <----DAI0-----> Codec Headset
+ * *
+ PCM1 <------------> * * <====DAI1=====> Codec Speakers
+ * DSP *
+ PCM2 <------------> * * <----DAI2-----> MODEM
+ * *
+ PCM3 <------------> * * <----DAI3-----> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+The audio driver processes this as follows :-
+
+1. Machine driver receives Jack removal event.
+
+2. Machine driver OR audio HAL disables the Headset path.
+
+3. DPCM runs the PCM trigger(stop), hw_free(), shutdown() operations on DAI0
+ for headset since the path is now disabled.
+
+4. Machine driver or audio HAL enables the speaker path.
+
+5. DPCM runs the PCM ops for startup(), hw_params(), prepapre() and
+ trigger(start) for DAI1 Speakers since the path is enabled.
+
+In this example, the machine driver or userspace audio HAL can alter the routing
+and then DPCM will take care of managing the DAI PCM operations to either bring
+the link up or down. Audio playback does not stop during this transition.
+
+
+
+DPCM machine driver
+===================
+
+The DPCM enabled ASoC machine driver is similar to normal machine drivers
+except that we also have to :-
+
+1. Define the FE and BE DAI links.
+
+2. Define any FE/BE PCM operations.
+
+3. Define widget graph connections.
+
+
+FE and BE DAI links
+-------------------
+::
+
+ | Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
+
+ *************
+ PCM0 <------------> * * <----DAI0-----> Codec Headset
+ * *
+ PCM1 <------------> * * <----DAI1-----> Codec Speakers
+ * DSP *
+ PCM2 <------------> * * <----DAI2-----> MODEM
+ * *
+ PCM3 <------------> * * <----DAI3-----> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+For the example above we have to define 4 FE DAI links and 6 BE DAI links. The
+FE DAI links are defined as follows :-
+::
+
+ static struct snd_soc_dai_link machine_dais[] = {
+ {
+ .name = "PCM0 System",
+ .stream_name = "System Playback",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "dsp-audio",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ .....< other FE and BE DAI links here >
+ };
+
+This FE DAI link is pretty similar to a regular DAI link except that we also
+set the DAI link to a DPCM FE with the ``dynamic = 1``. The supported FE stream
+directions should also be set with the ``dpcm_playback`` and ``dpcm_capture``
+flags. There is also an option to specify the ordering of the trigger call for
+each FE. This allows the ASoC core to trigger the DSP before or after the other
+components (as some DSPs have strong requirements for the ordering DAI/DSP
+start and stop sequences).
+
+The FE DAI above sets the codec and code DAIs to dummy devices since the BE is
+dynamic and will change depending on runtime config.
+
+The BE DAIs are configured as follows :-
+::
+
+ static struct snd_soc_dai_link machine_dais[] = {
+ .....< FE DAI links here >
+ {
+ .name = "Codec Headset",
+ .cpu_dai_name = "ssp-dai.0",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_name = "rt5640.0-001c",
+ .codec_dai_name = "rt5640-aif1",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = hswult_ssp0_fixup,
+ .ops = &haswell_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ .....< other BE DAI links here >
+ };
+
+This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
+the ``no_pcm`` flag to mark it has a BE and sets flags for supported stream
+directions using ``dpcm_playback`` and ``dpcm_capture`` above.
+
+The BE has also flags set for ignoring suspend and PM down time. This allows
+the BE to work in a hostless mode where the host CPU is not transferring data
+like a BT phone call :-
+::
+
+ *************
+ PCM0 <------------> * * <----DAI0-----> Codec Headset
+ * *
+ PCM1 <------------> * * <----DAI1-----> Codec Speakers
+ * DSP *
+ PCM2 <------------> * * <====DAI2=====> MODEM
+ * *
+ PCM3 <------------> * * <====DAI3=====> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+This allows the host CPU to sleep whilst the DSP, MODEM DAI and the BT DAI are
+still in operation.
+
+A BE DAI link can also set the codec to a dummy device if the code is a device
+that is managed externally.
+
+Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
+DSP firmware.
+
+
+FE/BE PCM operations
+--------------------
+
+The BE above also exports some PCM operations and a ``fixup`` callback. The fixup
+callback is used by the machine driver to (re)configure the DAI based upon the
+FE hw params. i.e. the DSP may perform SRC or ASRC from the FE to BE.
+
+e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo for
+DAI0. This means all FE hw_params have to be fixed in the machine driver for
+DAI0 so that the DAI is running at desired configuration regardless of the FE
+configuration.
+::
+
+ static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+ {
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will covert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set DAI0 to 16 bit */
+ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+ }
+
+The other PCM operation are the same as for regular DAI links. Use as necessary.
+
+
+Widget graph connections
+------------------------
+
+The BE DAI links will normally be connected to the graph at initialisation time
+by the ASoC DAPM core. However, if the BE codec or BE DAI is a dummy then this
+has to be set explicitly in the driver :-
+::
+
+ /* BE for codec Headset - DAI0 is dummy and managed by DSP FW */
+ {"DAI0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
+
+
+Writing a DPCM DSP driver
+=========================
+
+The DPCM DSP driver looks much like a standard platform class ASoC driver
+combined with elements from a codec class driver. A DSP platform driver must
+implement :-
+
+1. Front End PCM DAIs - i.e. struct snd_soc_dai_driver.
+
+2. DAPM graph showing DSP audio routing from FE DAIs to BEs.
+
+3. DAPM widgets from DSP graph.
+
+4. Mixers for gains, routing, etc.
+
+5. DMA configuration.
+
+6. BE AIF widgets.
+
+Items 6 is important for routing the audio outside of the DSP. AIF need to be
+defined for each BE and each stream direction. e.g for BE DAI0 above we would
+have :-
+::
+
+ SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+The BE AIF are used to connect the DSP graph to the graphs for the other
+component drivers (e.g. codec graph).
+
+
+Hostless PCM streams
+====================
+
+A hostless PCM stream is a stream that is not routed through the host CPU. An
+example of this would be a phone call from handset to modem.
+::
+
+ *************
+ PCM0 <------------> * * <----DAI0-----> Codec Headset
+ * *
+ PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic
+ * DSP *
+ PCM2 <------------> * * <====DAI2=====> MODEM
+ * *
+ PCM3 <------------> * * <----DAI3-----> BT
+ * *
+ * * <----DAI4-----> DMIC
+ * *
+ * * <----DAI5-----> FM
+ *************
+
+In this case the PCM data is routed via the DSP. The host CPU in this use case
+is only used for control and can sleep during the runtime of the stream.
+
+The host can control the hostless link either by :-
+
+ 1. Configuring the link as a CODEC <-> CODEC style link. In this case the link
+ is enabled or disabled by the state of the DAPM graph. This usually means
+ there is a mixer control that can be used to connect or disconnect the path
+ between both DAIs.
+
+ 2. Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM
+ graph. Control is then carried out by the FE as regular PCM operations.
+ This method gives more control over the DAI links, but requires much more
+ userspace code to control the link. Its recommended to use CODEC<->CODEC
+ unless your HW needs more fine grained sequencing of the PCM ops.
+
+
+CODEC <-> CODEC link
+--------------------
+
+This DAI link is enabled when DAPM detects a valid path within the DAPM graph.
+The machine driver sets some additional parameters to the DAI link i.e.
+::
+
+ static const struct snd_soc_pcm_stream dai_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 2,
+ .channels_max = 2,
+ };
+
+ static struct snd_soc_dai_link dais[] = {
+ < ... more DAI links above ... >
+ {
+ .name = "MODEM",
+ .stream_name = "MODEM",
+ .cpu_dai_name = "dai2",
+ .codec_dai_name = "modem-aif1",
+ .codec_name = "modem",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .params = &dai_params,
+ }
+ < ... more DAI links here ... >
+
+These parameters are used to configure the DAI hw_params() when DAPM detects a
+valid path and then calls the PCM operations to start the link. DAPM will also
+call the appropriate PCM operations to disable the DAI when the path is no
+longer valid.
+
+
+Hostless FE
+-----------
+
+The DAI link(s) are enabled by a FE that does not read or write any PCM data.
+This means creating a new FE that is connected with a virtual path to both
+DAI links. The DAI links will be started when the FE PCM is started and stopped
+when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
+this configuration.
+
+
--- /dev/null
+==============
+ALSA SoC Layer
+==============
+
+The documentation is spilt into the following sections:-
+
+.. toctree::
+ :maxdepth: 2
+
+ overview
+ codec
+ dai
+ dapm
+ platform
+ machine
+ pops-clicks
+ clocking
+ jack
+ dpcm
+ codec-to-codec
--- /dev/null
+===================
+ASoC jack detection
+===================
+
+ALSA has a standard API for representing physical jacks to user space,
+the kernel side of which can be seen in include/sound/jack.h. ASoC
+provides a version of this API adding two additional features:
+
+ - It allows more than one jack detection method to work together on one
+ user visible jack. In embedded systems it is common for multiple
+ to be present on a single jack but handled by separate bits of
+ hardware.
+
+ - Integration with DAPM, allowing DAPM endpoints to be updated
+ automatically based on the detected jack status (eg, turning off the
+ headphone outputs if no headphones are present).
+
+This is done by splitting the jacks up into three things working
+together: the jack itself represented by a struct snd_soc_jack, sets of
+snd_soc_jack_pins representing DAPM endpoints to update and blocks of
+code providing jack reporting mechanisms.
+
+For example, a system may have a stereo headset jack with two reporting
+mechanisms, one for the headphone and one for the microphone. Some
+systems won't be able to use their speaker output while a headphone is
+connected and so will want to make sure to update both speaker and
+headphone when the headphone jack status changes.
+
+The jack - struct snd_soc_jack
+==============================
+
+This represents a physical jack on the system and is what is visible to
+user space. The jack itself is completely passive, it is set up by the
+machine driver and updated by jack detection methods.
+
+Jacks are created by the machine driver calling snd_soc_jack_new().
+
+snd_soc_jack_pin
+================
+
+These represent a DAPM pin to update depending on some of the status
+bits supported by the jack. Each snd_soc_jack has zero or more of these
+which are updated automatically. They are created by the machine driver
+and associated with the jack using snd_soc_jack_add_pins(). The status
+of the endpoint may configured to be the opposite of the jack status if
+required (eg, enabling a built in microphone if a microphone is not
+connected via a jack).
+
+Jack detection methods
+======================
+
+Actual jack detection is done by code which is able to monitor some
+input to the system and update a jack by calling snd_soc_jack_report(),
+specifying a subset of bits to update. The jack detection code should
+be set up by the machine driver, taking configuration for the jack to
+update and the set of things to report when the jack is connected.
+
+Often this is done based on the status of a GPIO - a handler for this is
+provided by the snd_soc_jack_add_gpio() function. Other methods are
+also available, for example integrated into CODECs. One example of
+CODEC integrated jack detection can be see in the WM8350 driver.
+
+Each jack may have multiple reporting mechanisms, though it will need at
+least one to be useful.
+
+Machine drivers
+===============
+
+These are all hooked together by the machine driver depending on the
+system hardware. The machine driver will set up the snd_soc_jack and
+the list of pins to update then set up one or more jack detection
+mechanisms to update that jack based on their current status.
--- /dev/null
+===================
+ASoC Machine Driver
+===================
+
+The ASoC machine (or board) driver is the code that glues together all the
+component drivers (e.g. codecs, platforms and DAIs). It also describes the
+relationships between each component which include audio paths, GPIOs,
+interrupts, clocking, jacks and voltage regulators.
+
+The machine driver can contain codec and platform specific code. It registers
+the audio subsystem with the kernel as a platform device and is represented by
+the following struct:-
+::
+
+ /* SoC machine */
+ struct snd_soc_card {
+ char *name;
+
+ ...
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+
+ /* the pre and post PM functions are used to do any PM work before and
+ * after the codec and DAIs do any PM work. */
+ int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
+ int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
+ int (*resume_pre)(struct platform_device *pdev);
+ int (*resume_post)(struct platform_device *pdev);
+
+ ...
+
+ /* CPU <--> Codec DAI links */
+ struct snd_soc_dai_link *dai_link;
+ int num_links;
+
+ ...
+ };
+
+probe()/remove()
+----------------
+probe/remove are optional. Do any machine specific probe here.
+
+
+suspend()/resume()
+------------------
+The machine driver has pre and post versions of suspend and resume to take care
+of any machine audio tasks that have to be done before or after the codec, DAIs
+and DMA is suspended and resumed. Optional.
+
+
+Machine DAI Configuration
+-------------------------
+The machine DAI configuration glues all the codec and CPU DAIs together. It can
+also be used to set up the DAI system clock and for any machine related DAI
+initialisation e.g. the machine audio map can be connected to the codec audio
+map, unconnected codec pins can be set as such.
+
+struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
+::
+
+ /* corgi digital audio interface glue - connects codec <--> CPU */
+ static struct snd_soc_dai_link corgi_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai_name = "pxa-is2-dai",
+ .codec_dai_name = "wm8731-hifi",
+ .platform_name = "pxa-pcm-audio",
+ .codec_name = "wm8713-codec.0-001a",
+ .init = corgi_wm8731_init,
+ .ops = &corgi_ops,
+ };
+
+struct snd_soc_card then sets up the machine with its DAIs. e.g.
+::
+
+ /* corgi audio machine driver */
+ static struct snd_soc_card snd_soc_corgi = {
+ .name = "Corgi",
+ .dai_link = &corgi_dai,
+ .num_links = 1,
+ };
+
+
+Machine Power Map
+-----------------
+
+The machine driver can optionally extend the codec power map and to become an
+audio power map of the audio subsystem. This allows for automatic power up/down
+of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack
+sockets in the machine init function.
+
+
+Machine Controls
+----------------
+
+Machine specific audio mixer controls can be added in the DAI init function.
--- /dev/null
+=======================
+ALSA SoC Layer Overview
+=======================
+
+The overall project goal of the ALSA System on Chip (ASoC) layer is to
+provide better ALSA support for embedded system-on-chip processors (e.g.
+pxa2xx, au1x00, iMX, etc) and portable audio codecs. Prior to the ASoC
+subsystem there was some support in the kernel for SoC audio, however it
+had some limitations:-
+
+ * Codec drivers were often tightly coupled to the underlying SoC
+ CPU. This is not ideal and leads to code duplication - for example,
+ Linux had different wm8731 drivers for 4 different SoC platforms.
+
+ * There was no standard method to signal user initiated audio events (e.g.
+ Headphone/Mic insertion, Headphone/Mic detection after an insertion
+ event). These are quite common events on portable devices and often require
+ machine specific code to re-route audio, enable amps, etc., after such an
+ event.
+
+ * Drivers tended to power up the entire codec when playing (or
+ recording) audio. This is fine for a PC, but tends to waste a lot of
+ power on portable devices. There was also no support for saving
+ power via changing codec oversampling rates, bias currents, etc.
+
+
+ASoC Design
+===========
+
+The ASoC layer is designed to address these issues and provide the following
+features :-
+
+ * Codec independence. Allows reuse of codec drivers on other platforms
+ and machines.
+
+ * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC
+ interface and codec registers its audio interface capabilities with the
+ core and are subsequently matched and configured when the application
+ hardware parameters are known.
+
+ * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
+ its minimum power state at all times. This includes powering up/down
+ internal power blocks depending on the internal codec audio routing and any
+ active streams.
+
+ * Pop and click reduction. Pops and clicks can be reduced by powering the
+ codec up/down in the correct sequence (including using digital mute). ASoC
+ signals the codec when to change power states.
+
+ * Machine specific controls: Allow machines to add controls to the sound card
+ (e.g. volume control for speaker amplifier).
+
+To achieve all this, ASoC basically splits an embedded audio system into
+multiple re-usable component drivers :-
+
+ * Codec class drivers: The codec class driver is platform independent and
+ contains audio controls, audio interface capabilities, codec DAPM
+ definition and codec IO functions. This class extends to BT, FM and MODEM
+ ICs if required. Codec class drivers should be generic code that can run
+ on any architecture and machine.
+
+ * Platform class drivers: The platform class driver includes the audio DMA
+ engine driver, digital audio interface (DAI) drivers (e.g. I2S, AC97, PCM)
+ and any audio DSP drivers for that platform.
+
+ * Machine class driver: The machine driver class acts as the glue that
+ describes and binds the other component drivers together to form an ALSA
+ "sound card device". It handles any machine specific controls and
+ machine level audio events (e.g. turning on an amp at start of playback).
--- /dev/null
+====================
+ASoC Platform Driver
+====================
+
+An ASoC platform driver class can be divided into audio DMA drivers, SoC DAI
+drivers and DSP drivers. The platform drivers only target the SoC CPU and must
+have no board specific code.
+
+Audio DMA
+=========
+
+The platform DMA driver optionally supports the following ALSA operations:-
+::
+
+ /* SoC audio ops */
+ struct snd_soc_ops {
+ int (*startup)(struct snd_pcm_substream *);
+ void (*shutdown)(struct snd_pcm_substream *);
+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
+ int (*hw_free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+ int (*trigger)(struct snd_pcm_substream *, int);
+ };
+
+The platform driver exports its DMA functionality via struct
+snd_soc_platform_driver:-
+::
+
+ struct snd_soc_platform_driver {
+ char *name;
+
+ int (*probe)(struct platform_device *pdev);
+ int (*remove)(struct platform_device *pdev);
+ int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
+ int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
+
+ /* pcm creation and destruction */
+ int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *);
+ void (*pcm_free)(struct snd_pcm *);
+
+ /*
+ * For platform caused delay reporting.
+ * Optional.
+ */
+ snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
+ struct snd_soc_dai *);
+
+ /* platform stream ops */
+ struct snd_pcm_ops *pcm_ops;
+ };
+
+Please refer to the ALSA driver documentation for details of audio DMA.
+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/
+
+An example DMA driver is soc/pxa/pxa2xx-pcm.c
+
+
+SoC DAI Drivers
+===============
+
+Each SoC DAI driver must provide the following features:-
+
+1. Digital audio interface (DAI) description
+2. Digital audio interface configuration
+3. PCM's description
+4. SYSCLK configuration
+5. Suspend and resume (optional)
+
+Please see codec.txt for a description of items 1 - 4.
+
+
+SoC DSP Drivers
+===============
+
+Each SoC DSP driver usually supplies the following features :-
+
+1. DAPM graph
+2. Mixer controls
+3. DMA IO to/from DSP buffers (if applicable)
+4. Definition of DSP front end (FE) PCM devices.
+
+Please see DPCM.txt for a description of item 4.
--- /dev/null
+=====================
+Audio Pops and Clicks
+=====================
+
+Pops and clicks are unwanted audio artifacts caused by the powering up and down
+of components within the audio subsystem. This is noticeable on PCs when an
+audio module is either loaded or unloaded (at module load time the sound card is
+powered up and causes a popping noise on the speakers).
+
+Pops and clicks can be more frequent on portable systems with DAPM. This is
+because the components within the subsystem are being dynamically powered
+depending on the audio usage and this can subsequently cause a small pop or
+click every time a component power state is changed.
+
+
+Minimising Playback Pops and Clicks
+===================================
+
+Playback pops in portable audio subsystems cannot be completely eliminated
+currently, however future audio codec hardware will have better pop and click
+suppression. Pops can be reduced within playback by powering the audio
+components in a specific order. This order is different for startup and
+shutdown and follows some basic rules:-
+::
+
+ Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
+
+ Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC
+
+This assumes that the codec PCM output path from the DAC is via a mixer and then
+a PGA (programmable gain amplifier) before being output to the speakers.
+
+
+Minimising Capture Pops and Clicks
+==================================
+
+Capture artifacts are somewhat easier to get rid as we can delay activating the
+ADC until all the pops have occurred. This follows similar power rules to
+playback in that components are powered in a sequence depending upon stream
+startup or shutdown.
+::
+
+ Startup Order - Input PGA --> Mixers --> ADC
+
+ Shutdown Order - ADC --> Mixers --> Input PGA
+
+
+Zipper Noise
+============
+An unwanted zipper noise can occur within the audio playback or capture stream
+when a volume control is changed near its maximum gain value. The zipper noise
+is heard when the gain increase or decrease changes the mean audio signal
+amplitude too quickly. It can be minimised by enabling the zero cross setting
+for each volume control. The ZC forces the gain change to occur when the signal
+crosses the zero amplitude line.
1. Acquisition Orders
---------------------
-(to be written)
+The acquisition orders for mutexes are as follows:
+
+- kvm->lock is taken outside vcpu->mutex
+
+- kvm->lock is taken outside kvm->slots_lock and kvm->irq_lock
+
+- kvm->slots_lock is taken outside kvm->irq_lock, though acquiring
+ them together is quite rare.
+
+For spinlocks, kvm_lock is taken outside kvm->mmu_lock. Everything
+else is a leaf: no other lock is taken inside the critical sections.
2: Exception
------------
F: drivers/net/ethernet/broadcom/genet/
BROADCOM BNX2 GIGABIT ETHERNET DRIVER
-M: Sony Chacko <sony.chacko@qlogic.com>
-M: Dept-HSGLinuxNICDev@qlogic.com
+M: Rasesh Mody <rasesh.mody@cavium.com>
+M: Harish Patil <harish.patil@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/broadcom/bnx2.*
F: drivers/net/ethernet/broadcom/bnx2_*
BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER
-M: Ariel Elior <ariel.elior@qlogic.com>
+M: Yuval Mintz <Yuval.Mintz@cavium.com>
+M: Ariel Elior <ariel.elior@cavium.com>
+M: everest-linux-l2@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/broadcom/bnx2x/
F: drivers/scsi/bfa/
BROCADE BNA 10 GIGABIT ETHERNET DRIVER
-M: Rasesh Mody <rasesh.mody@qlogic.com>
+M: Rasesh Mody <rasesh.mody@cavium.com>
+M: Sudarsana Kalluru <sudarsana.kalluru@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/brocade/bna/
MEMORY TECHNOLOGY DEVICES (MTD)
M: David Woodhouse <dwmw2@infradead.org>
M: Brian Norris <computersforpeace@gmail.com>
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Marek Vasut <marek.vasut@gmail.com>
+M: Richard Weinberger <richard@nod.at>
+M: Cyrille Pitchen <cyrille.pitchen@atmel.com>
L: linux-mtd@lists.infradead.org
W: http://www.linux-mtd.infradead.org/
Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
F: drivers/net/wireless/
NETXEN (1/10) GbE SUPPORT
-M: Manish Chopra <manish.chopra@qlogic.com>
-M: Sony Chacko <sony.chacko@qlogic.com>
-M: Rajesh Borundia <rajesh.borundia@qlogic.com>
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Rahul Verma <rahul.verma@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
-W: http://www.qlogic.com
S: Supported
F: drivers/net/ethernet/qlogic/netxen/
F: drivers/scsi/qla4xxx/
QLOGIC QLA3XXX NETWORK DRIVER
-M: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
-M: Ron Mercer <ron.mercer@qlogic.com>
-M: linux-driver@qlogic.com
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: Documentation/networking/LICENSE.qla3xxx
F: drivers/net/ethernet/qlogic/qla3xxx.*
QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER
-M: Dept-GELinuxNICDev@qlogic.com
+M: Harish Patil <harish.patil@cavium.com>
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/qlogic/qlcnic/
QLOGIC QLGE 10Gb ETHERNET DRIVER
-M: Harish Patil <harish.patil@qlogic.com>
-M: Sudarsana Kalluru <sudarsana.kalluru@qlogic.com>
-M: Dept-GELinuxNICDev@qlogic.com
-M: linux-driver@qlogic.com
+M: Harish Patil <harish.patil@cavium.com>
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/qlogic/qlge/
QLOGIC QL4xxx ETHERNET DRIVER
-M: Yuval Mintz <Yuval.Mintz@qlogic.com>
-M: Ariel Elior <Ariel.Elior@qlogic.com>
-M: everest-linux-l2@qlogic.com
+M: Yuval Mintz <Yuval.Mintz@cavium.com>
+M: Ariel Elior <Ariel.Elior@cavium.com>
+M: everest-linux-l2@cavium.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/qlogic/qed/
S: Maintained
F: drivers/clk/spear/
+SPI NOR SUBSYSTEM
+M: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-mtd@lists.infradead.org
+W: http://www.linux-mtd.infradead.org/
+Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
+T: git git://github.com/spi-nor/linux.git
+S: Maintained
+F: drivers/mtd/spi-nor/
+F: include/linux/mtd/spi-nor.h
+
SPI SUBSYSTEM
M: Mark Brown <broonie@kernel.org>
L: linux-spi@vger.kernel.org
VIRTIO CORE, NET AND BLOCK DRIVERS
M: "Michael S. Tsirkin" <mst@redhat.com>
+M: Jason Wang <jasowang@redhat.com>
L: virtualization@lists.linux-foundation.org
S: Maintained
F: Documentation/devicetree/bindings/virtio/
VIRTIO HOST (VHOST)
M: "Michael S. Tsirkin" <mst@redhat.com>
+M: Jason Wang <jasowang@redhat.com>
L: kvm@vger.kernel.org
L: virtualization@lists.linux-foundation.org
L: netdev@vger.kernel.org
VERSION = 4
PATCHLEVEL = 9
SUBLEVEL = 0
-EXTRAVERSION = -rc3
+EXTRAVERSION = -rc4
NAME = Psychotic Stoned Sheep
# *DOCUMENTATION*
* This may need to be greater than __NR_last_syscall+1 in order to
* account for the padding in the syscall table
*/
-#define __NR_syscalls (396)
+#define __NR_syscalls (400)
#define __ARCH_WANT_STAT64
#define __ARCH_WANT_SYS_GETHOSTNAME
#define __NR_copy_file_range (__NR_SYSCALL_BASE+391)
#define __NR_preadv2 (__NR_SYSCALL_BASE+392)
#define __NR_pwritev2 (__NR_SYSCALL_BASE+393)
+#define __NR_pkey_mprotect (__NR_SYSCALL_BASE+394)
+#define __NR_pkey_alloc (__NR_SYSCALL_BASE+395)
+#define __NR_pkey_free (__NR_SYSCALL_BASE+396)
/*
* The following SWIs are ARM private.
CALL(sys_copy_file_range)
CALL(sys_preadv2)
CALL(sys_pwritev2)
+ CALL(sys_pkey_mprotect)
+/* 395 */ CALL(sys_pkey_alloc)
+ CALL(sys_pkey_free)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
* : r4 = aborted context pc
* : r5 = aborted context psr
*
- * Returns : r4-r5, r10-r11, r13 preserved
+ * Returns : r4-r5, r9-r11, r13 preserved
*
* Purpose : obtain information about current aborted instruction.
* Note: we read user space. This means we might cause a data
/* c */ b do_DataAbort @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m
/* d */ b do_DataAbort @ ldc rd, [rn, #m]
/* e */ b .data_unknown
-/* f */
+/* f */ b .data_unknown
+
+.data_unknown_r9:
+ ldr r9, [sp], #4
.data_unknown: @ Part of jumptable
mov r0, r4
mov r1, r8
.data_arm_ldmstm:
tst r8, #1 << 21 @ check writeback bit
beq do_DataAbort @ no writeback -> no fixup
+ str r9, [sp, #-4]!
mov r7, #0x11
orr r7, r7, #0x1100
and r6, r8, r7
subne r7, r7, r6, lsl #2 @ Undo increment
addeq r7, r7, r6, lsl #2 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
+ ldr r9, [sp], #4
b do_DataAbort
.data_arm_lateldrhpre:
tst r8, #1 << 21 @ Check writeback bit
beq do_DataAbort @ No writeback -> no fixup
.data_arm_lateldrhpost:
+ str r9, [sp, #-4]!
and r9, r8, #0x00f @ get Rm / low nibble of immediate value
tst r8, #1 << 22 @ if (immediate offset)
andne r6, r8, #0xf00 @ { immediate high nibble
subne r7, r7, r6 @ Undo incrmenet
addeq r7, r7, r6 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
+ ldr r9, [sp], #4
b do_DataAbort
.data_arm_lateldrpreconst:
.data_arm_lateldrpostconst:
movs r6, r8, lsl #20 @ Get offset
beq do_DataAbort @ zero -> no fixup
+ str r9, [sp, #-4]!
and r9, r8, #15 << 16 @ Extract 'n' from instruction
ldr r7, [r2, r9, lsr #14] @ Get register 'Rn'
tst r8, #1 << 23 @ Check U bit
subne r7, r7, r6, lsr #20 @ Undo increment
addeq r7, r7, r6, lsr #20 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
+ ldr r9, [sp], #4
b do_DataAbort
.data_arm_lateldrprereg:
.data_arm_lateldrpostreg:
and r7, r8, #15 @ Extract 'm' from instruction
ldr r6, [r2, r7, lsl #2] @ Get register 'Rm'
+ str r9, [sp, #-4]!
mov r9, r8, lsr #7 @ get shift count
ands r9, r9, #31
and r7, r8, #0x70 @ get shift type
b .data_arm_apply_r6_and_rn
b .data_arm_apply_r6_and_rn @ 1: LSL #0
nop
- b .data_unknown @ 2: MUL?
+ b .data_unknown_r9 @ 2: MUL?
nop
- b .data_unknown @ 3: MUL?
+ b .data_unknown_r9 @ 3: MUL?
nop
mov r6, r6, lsr r9 @ 4: LSR #!0
b .data_arm_apply_r6_and_rn
mov r6, r6, lsr #32 @ 5: LSR #32
b .data_arm_apply_r6_and_rn
- b .data_unknown @ 6: MUL?
+ b .data_unknown_r9 @ 6: MUL?
nop
- b .data_unknown @ 7: MUL?
+ b .data_unknown_r9 @ 7: MUL?
nop
mov r6, r6, asr r9 @ 8: ASR #!0
b .data_arm_apply_r6_and_rn
mov r6, r6, asr #32 @ 9: ASR #32
b .data_arm_apply_r6_and_rn
- b .data_unknown @ A: MUL?
+ b .data_unknown_r9 @ A: MUL?
nop
- b .data_unknown @ B: MUL?
+ b .data_unknown_r9 @ B: MUL?
nop
mov r6, r6, ror r9 @ C: ROR #!0
b .data_arm_apply_r6_and_rn
mov r6, r6, rrx @ D: RRX
b .data_arm_apply_r6_and_rn
- b .data_unknown @ E: MUL?
+ b .data_unknown_r9 @ E: MUL?
nop
- b .data_unknown @ F: MUL?
+ b .data_unknown_r9 @ F: MUL?
.data_thumb_abort:
ldrh r8, [r4] @ read instruction
.data_thumb_pushpop:
tst r8, #1 << 10
beq .data_unknown
+ str r9, [sp, #-4]!
and r6, r8, #0x55 @ hweight8(r8) + R bit
and r9, r8, #0xaa
add r6, r6, r9, lsr #1
addeq r7, r7, r6, lsl #2 @ increment SP if PUSH
subne r7, r7, r6, lsl #2 @ decrement SP if POP
str r7, [r2, #13 << 2]
+ ldr r9, [sp], #4
b do_DataAbort
.data_thumb_ldmstm:
+ str r9, [sp, #-4]!
and r6, r8, #0x55 @ hweight8(r8)
and r9, r8, #0xaa
add r6, r6, r9, lsr #1
and r6, r6, #15 @ number of regs to transfer
sub r7, r7, r6, lsl #2 @ always decrement
str r7, [r2, r9, lsr #6]
+ ldr r9, [sp], #4
b do_DataAbort
bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) \
VMLINUX_ENTRY_ADDRESS=$(entry-y) \
- PLATFORM=$(platform-y)
+ PLATFORM="$(platform-y)"
ifdef CONFIG_32BIT
bootvars-y += ADDR_BITS=32
endif
fpga_regs: system-controller@1f000000 {
compatible = "mti,malta-fpga", "syscon", "simple-mfd";
reg = <0x1f000000 0x1000>;
+ native-endian;
reboot {
compatible = "syscon-reboot";
regmap = <&fpga_regs>;
offset = <0x500>;
- mask = <0x4d>;
+ mask = <0x42>;
};
};
static __initdata const void *mach_match_data;
void __init prom_init(void)
+{
+ plat_get_fdt();
+ BUG_ON(!fdt);
+}
+
+void __init *plat_get_fdt(void)
{
const struct mips_machine *check_mach;
const struct of_device_id *match;
+ if (fdt)
+ /* Already set up */
+ return (void *)fdt;
+
if ((fw_arg0 == -2) && !fdt_check_header((void *)fw_arg1)) {
/*
* We booted using the UHI boot protocol, so we have been
/* Retrieve the machine's FDT */
fdt = mach->fdt;
}
-
- BUG_ON(!fdt);
-}
-
-void __init *plat_get_fdt(void)
-{
return (void *)fdt;
}
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
struct mips_fpu_struct *ctx, int has_fpu,
void *__user *fault_addr);
+void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
+ struct task_struct *tsk);
int process_fpemu_return(int sig, void __user *fault_addr,
unsigned long fcr31);
int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN);
}
+/*
+ * Mask the FCSR Cause bits according to the Enable bits, observing
+ * that Unimplemented is always enabled.
+ */
+static inline unsigned long mask_fcr31_x(unsigned long fcr31)
+{
+ return fcr31 & (FPU_CSR_UNI_X |
+ ((fcr31 & FPU_CSR_ALL_E) <<
+ (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E))));
+}
+
#endif /* _ASM_FPU_EMULATOR_H */
/* Host KSEG0 address of the EI/DI offset */
void *kseg0_commpage;
- u32 io_gpr; /* GPR used as IO source/target */
+ /* Resume PC after MMIO completion */
+ unsigned long io_pc;
+ /* GPR used as IO source/target */
+ u32 io_gpr;
struct hrtimer comparecount_timer;
/* Count timer control KVM register */
/* Bitmask of pending exceptions to be cleared */
unsigned long pending_exceptions_clr;
- u32 pending_load_cause;
-
/* Save/Restore the entryhi register when are are preempted/scheduled back in */
unsigned long preempt_entryhi;
} \
} while (0)
+/*
+ * Check FCSR for any unmasked exceptions pending set with `ptrace',
+ * clear them and send a signal.
+ */
+#define __sanitize_fcr31(next) \
+do { \
+ unsigned long fcr31 = mask_fcr31_x(next->thread.fpu.fcr31); \
+ void __user *pc; \
+ \
+ if (unlikely(fcr31)) { \
+ pc = (void __user *)task_pt_regs(next)->cp0_epc; \
+ next->thread.fpu.fcr31 &= ~fcr31; \
+ force_fcr31_sig(fcr31, pc, next); \
+ } \
+} while (0)
+
/*
* For newly created kernel threads switch_to() will return to
* ret_from_kernel_thread, newly created user threads to ret_from_fork.
do { \
__mips_mt_fpaff_switch_to(prev); \
lose_fpu_inatomic(1, prev); \
+ if (tsk_used_math(next)) \
+ __sanitize_fcr31(next); \
if (cpu_has_dsp) { \
__save_dsp(prev); \
__restore_dsp(next); \
static DEFINE_PER_CPU_ALIGNED(unsigned long, cpc_core_lock_flags);
+phys_addr_t __weak mips_cpc_default_phys_base(void)
+{
+ return 0;
+}
+
/**
* mips_cpc_phys_base - retrieve the physical base address of the CPC
*
if (cpc_base & CM_GCR_CPC_BASE_CPCEN_MSK)
return cpc_base & CM_GCR_CPC_BASE_CPCBASE_MSK;
- /* Otherwise, give it the default address & enable it */
+ /* Otherwise, use the default address */
cpc_base = mips_cpc_default_phys_base();
+ if (!cpc_base)
+ return cpc_base;
+
+ /* Enable the CPC, mapped at the default address */
write_gcr_cpc_base(cpc_base | CM_GCR_CPC_BASE_CPCEN_MSK);
return cpc_base;
}
* mipsr2_decoder: Decode and emulate a MIPS R2 instruction
* @regs: Process register set
* @inst: Instruction to decode and emulate
- * @fcr31: Floating Point Control and Status Register returned
+ * @fcr31: Floating Point Control and Status Register Cause bits returned
*/
int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31)
{
err = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0,
&fault_addr);
- *fcr31 = current->thread.fpu.fcr31;
/*
- * We can't allow the emulated instruction to leave any of
- * the cause bits set in $fcr31.
+ * We can't allow the emulated instruction to leave any
+ * enabled Cause bits set in $fcr31.
*/
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+ *fcr31 = res = mask_fcr31_x(current->thread.fpu.fcr31);
+ current->thread.fpu.fcr31 &= ~res;
/*
* this is a tricky issue - lose_fpu() uses LL/SC atomics
}
/*
- * Poke at FCSR according to its mask. Don't set the cause bits as
- * this is currently not handled correctly in FP context restoration
- * and will cause an oops if a corresponding enable bit is set.
+ * Poke at FCSR according to its mask. Set the Cause bits even
+ * if a corresponding Enable bit is set. This will be noticed at
+ * the time the thread is switched to and SIGFPE thrown accordingly.
*/
static void ptrace_setfcr31(struct task_struct *child, u32 value)
{
u32 fcr31;
u32 mask;
- value &= ~FPU_CSR_ALL_X;
fcr31 = child->thread.fpu.fcr31;
mask = boot_cpu_data.fpu_msk31;
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
break;
#endif
case FPC_CSR:
+ init_fp_ctx(child);
ptrace_setfcr31(child, data);
break;
case DSP_BASE ... DSP_BASE + 5: {
#include <asm/regdef.h>
#define EX(a,b) \
+9: a,##b; \
+ .section __ex_table,"a"; \
+ PTR 9b,fault; \
+ .previous
+
+#define EX2(a,b) \
9: a,##b; \
.section __ex_table,"a"; \
PTR 9b,bad_stack; \
+ PTR 9b+4,bad_stack; \
.previous
.set noreorder
.set mips1
- /* Save floating point context */
+
+/**
+ * _save_fp_context() - save FP context from the FPU
+ * @a0 - pointer to fpregs field of sigcontext
+ * @a1 - pointer to fpc_csr field of sigcontext
+ *
+ * Save FP context, including the 32 FP data registers and the FP
+ * control & status register, from the FPU to signal context.
+ */
LEAF(_save_fp_context)
.set push
SET_HARDFLOAT
li v0, 0 # assume success
- cfc1 t1,fcr31
- EX(swc1 $f0,(SC_FPREGS+0)(a0))
- EX(swc1 $f1,(SC_FPREGS+8)(a0))
- EX(swc1 $f2,(SC_FPREGS+16)(a0))
- EX(swc1 $f3,(SC_FPREGS+24)(a0))
- EX(swc1 $f4,(SC_FPREGS+32)(a0))
- EX(swc1 $f5,(SC_FPREGS+40)(a0))
- EX(swc1 $f6,(SC_FPREGS+48)(a0))
- EX(swc1 $f7,(SC_FPREGS+56)(a0))
- EX(swc1 $f8,(SC_FPREGS+64)(a0))
- EX(swc1 $f9,(SC_FPREGS+72)(a0))
- EX(swc1 $f10,(SC_FPREGS+80)(a0))
- EX(swc1 $f11,(SC_FPREGS+88)(a0))
- EX(swc1 $f12,(SC_FPREGS+96)(a0))
- EX(swc1 $f13,(SC_FPREGS+104)(a0))
- EX(swc1 $f14,(SC_FPREGS+112)(a0))
- EX(swc1 $f15,(SC_FPREGS+120)(a0))
- EX(swc1 $f16,(SC_FPREGS+128)(a0))
- EX(swc1 $f17,(SC_FPREGS+136)(a0))
- EX(swc1 $f18,(SC_FPREGS+144)(a0))
- EX(swc1 $f19,(SC_FPREGS+152)(a0))
- EX(swc1 $f20,(SC_FPREGS+160)(a0))
- EX(swc1 $f21,(SC_FPREGS+168)(a0))
- EX(swc1 $f22,(SC_FPREGS+176)(a0))
- EX(swc1 $f23,(SC_FPREGS+184)(a0))
- EX(swc1 $f24,(SC_FPREGS+192)(a0))
- EX(swc1 $f25,(SC_FPREGS+200)(a0))
- EX(swc1 $f26,(SC_FPREGS+208)(a0))
- EX(swc1 $f27,(SC_FPREGS+216)(a0))
- EX(swc1 $f28,(SC_FPREGS+224)(a0))
- EX(swc1 $f29,(SC_FPREGS+232)(a0))
- EX(swc1 $f30,(SC_FPREGS+240)(a0))
- EX(swc1 $f31,(SC_FPREGS+248)(a0))
- EX(sw t1,(SC_FPC_CSR)(a0))
- cfc1 t0,$0 # implementation/version
+ cfc1 t1, fcr31
+ EX2(s.d $f0, 0(a0))
+ EX2(s.d $f2, 16(a0))
+ EX2(s.d $f4, 32(a0))
+ EX2(s.d $f6, 48(a0))
+ EX2(s.d $f8, 64(a0))
+ EX2(s.d $f10, 80(a0))
+ EX2(s.d $f12, 96(a0))
+ EX2(s.d $f14, 112(a0))
+ EX2(s.d $f16, 128(a0))
+ EX2(s.d $f18, 144(a0))
+ EX2(s.d $f20, 160(a0))
+ EX2(s.d $f22, 176(a0))
+ EX2(s.d $f24, 192(a0))
+ EX2(s.d $f26, 208(a0))
+ EX2(s.d $f28, 224(a0))
+ EX2(s.d $f30, 240(a0))
jr ra
+ EX(sw t1, (a1))
.set pop
- .set nomacro
- EX(sw t0,(SC_FPC_EIR)(a0))
- .set macro
END(_save_fp_context)
-/*
- * Restore FPU state:
- * - fp gp registers
- * - cp1 status/control register
+/**
+ * _restore_fp_context() - restore FP context to the FPU
+ * @a0 - pointer to fpregs field of sigcontext
+ * @a1 - pointer to fpc_csr field of sigcontext
*
- * We base the decision which registers to restore from the signal stack
- * frame on the current content of c0_status, not on the content of the
- * stack frame which might have been changed by the user.
+ * Restore FP context, including the 32 FP data registers and the FP
+ * control & status register, from signal context to the FPU.
*/
LEAF(_restore_fp_context)
.set push
SET_HARDFLOAT
li v0, 0 # assume success
- EX(lw t0,(SC_FPC_CSR)(a0))
- EX(lwc1 $f0,(SC_FPREGS+0)(a0))
- EX(lwc1 $f1,(SC_FPREGS+8)(a0))
- EX(lwc1 $f2,(SC_FPREGS+16)(a0))
- EX(lwc1 $f3,(SC_FPREGS+24)(a0))
- EX(lwc1 $f4,(SC_FPREGS+32)(a0))
- EX(lwc1 $f5,(SC_FPREGS+40)(a0))
- EX(lwc1 $f6,(SC_FPREGS+48)(a0))
- EX(lwc1 $f7,(SC_FPREGS+56)(a0))
- EX(lwc1 $f8,(SC_FPREGS+64)(a0))
- EX(lwc1 $f9,(SC_FPREGS+72)(a0))
- EX(lwc1 $f10,(SC_FPREGS+80)(a0))
- EX(lwc1 $f11,(SC_FPREGS+88)(a0))
- EX(lwc1 $f12,(SC_FPREGS+96)(a0))
- EX(lwc1 $f13,(SC_FPREGS+104)(a0))
- EX(lwc1 $f14,(SC_FPREGS+112)(a0))
- EX(lwc1 $f15,(SC_FPREGS+120)(a0))
- EX(lwc1 $f16,(SC_FPREGS+128)(a0))
- EX(lwc1 $f17,(SC_FPREGS+136)(a0))
- EX(lwc1 $f18,(SC_FPREGS+144)(a0))
- EX(lwc1 $f19,(SC_FPREGS+152)(a0))
- EX(lwc1 $f20,(SC_FPREGS+160)(a0))
- EX(lwc1 $f21,(SC_FPREGS+168)(a0))
- EX(lwc1 $f22,(SC_FPREGS+176)(a0))
- EX(lwc1 $f23,(SC_FPREGS+184)(a0))
- EX(lwc1 $f24,(SC_FPREGS+192)(a0))
- EX(lwc1 $f25,(SC_FPREGS+200)(a0))
- EX(lwc1 $f26,(SC_FPREGS+208)(a0))
- EX(lwc1 $f27,(SC_FPREGS+216)(a0))
- EX(lwc1 $f28,(SC_FPREGS+224)(a0))
- EX(lwc1 $f29,(SC_FPREGS+232)(a0))
- EX(lwc1 $f30,(SC_FPREGS+240)(a0))
- EX(lwc1 $f31,(SC_FPREGS+248)(a0))
+ EX(lw t0, (a1))
+ EX2(l.d $f0, 0(a0))
+ EX2(l.d $f2, 16(a0))
+ EX2(l.d $f4, 32(a0))
+ EX2(l.d $f6, 48(a0))
+ EX2(l.d $f8, 64(a0))
+ EX2(l.d $f10, 80(a0))
+ EX2(l.d $f12, 96(a0))
+ EX2(l.d $f14, 112(a0))
+ EX2(l.d $f16, 128(a0))
+ EX2(l.d $f18, 144(a0))
+ EX2(l.d $f20, 160(a0))
+ EX2(l.d $f22, 176(a0))
+ EX2(l.d $f24, 192(a0))
+ EX2(l.d $f26, 208(a0))
+ EX2(l.d $f28, 224(a0))
+ EX2(l.d $f30, 240(a0))
jr ra
- ctc1 t0,fcr31
+ ctc1 t0, fcr31
.set pop
END(_restore_fp_context)
.set reorder
.set push
SET_HARDFLOAT
- /* Save floating point context */
+/**
+ * _save_fp_context() - save FP context from the FPU
+ * @a0 - pointer to fpregs field of sigcontext
+ * @a1 - pointer to fpc_csr field of sigcontext
+ *
+ * Save FP context, including the 32 FP data registers and the FP
+ * control & status register, from the FPU to signal context.
+ */
LEAF(_save_fp_context)
mfc0 t0,CP0_STATUS
sll t0,t0,2
cfc1 t1,fcr31
/* Store the 16 double precision registers */
- sdc1 $f0,(SC_FPREGS+0)(a0)
- sdc1 $f2,(SC_FPREGS+16)(a0)
- sdc1 $f4,(SC_FPREGS+32)(a0)
- sdc1 $f6,(SC_FPREGS+48)(a0)
- sdc1 $f8,(SC_FPREGS+64)(a0)
- sdc1 $f10,(SC_FPREGS+80)(a0)
- sdc1 $f12,(SC_FPREGS+96)(a0)
- sdc1 $f14,(SC_FPREGS+112)(a0)
- sdc1 $f16,(SC_FPREGS+128)(a0)
- sdc1 $f18,(SC_FPREGS+144)(a0)
- sdc1 $f20,(SC_FPREGS+160)(a0)
- sdc1 $f22,(SC_FPREGS+176)(a0)
- sdc1 $f24,(SC_FPREGS+192)(a0)
- sdc1 $f26,(SC_FPREGS+208)(a0)
- sdc1 $f28,(SC_FPREGS+224)(a0)
- sdc1 $f30,(SC_FPREGS+240)(a0)
+ sdc1 $f0,0(a0)
+ sdc1 $f2,16(a0)
+ sdc1 $f4,32(a0)
+ sdc1 $f6,48(a0)
+ sdc1 $f8,64(a0)
+ sdc1 $f10,80(a0)
+ sdc1 $f12,96(a0)
+ sdc1 $f14,112(a0)
+ sdc1 $f16,128(a0)
+ sdc1 $f18,144(a0)
+ sdc1 $f20,160(a0)
+ sdc1 $f22,176(a0)
+ sdc1 $f24,192(a0)
+ sdc1 $f26,208(a0)
+ sdc1 $f28,224(a0)
+ sdc1 $f30,240(a0)
jr ra
- sw t0,SC_FPC_CSR(a0)
+ sw t0,(a1)
1: jr ra
nop
END(_save_fp_context)
-/* Restore FPU state:
- * - fp gp registers
- * - cp1 status/control register
+/**
+ * _restore_fp_context() - restore FP context to the FPU
+ * @a0 - pointer to fpregs field of sigcontext
+ * @a1 - pointer to fpc_csr field of sigcontext
*
- * We base the decision which registers to restore from the signal stack
- * frame on the current content of c0_status, not on the content of the
- * stack frame which might have been changed by the user.
+ * Restore FP context, including the 32 FP data registers and the FP
+ * control & status register, from signal context to the FPU.
*/
LEAF(_restore_fp_context)
mfc0 t0,CP0_STATUS
sll t0,t0,2
bgez t0,1f
- lw t0,SC_FPC_CSR(a0)
+ lw t0,(a1)
/* Restore the 16 double precision registers */
- ldc1 $f0,(SC_FPREGS+0)(a0)
- ldc1 $f2,(SC_FPREGS+16)(a0)
- ldc1 $f4,(SC_FPREGS+32)(a0)
- ldc1 $f6,(SC_FPREGS+48)(a0)
- ldc1 $f8,(SC_FPREGS+64)(a0)
- ldc1 $f10,(SC_FPREGS+80)(a0)
- ldc1 $f12,(SC_FPREGS+96)(a0)
- ldc1 $f14,(SC_FPREGS+112)(a0)
- ldc1 $f16,(SC_FPREGS+128)(a0)
- ldc1 $f18,(SC_FPREGS+144)(a0)
- ldc1 $f20,(SC_FPREGS+160)(a0)
- ldc1 $f22,(SC_FPREGS+176)(a0)
- ldc1 $f24,(SC_FPREGS+192)(a0)
- ldc1 $f26,(SC_FPREGS+208)(a0)
- ldc1 $f28,(SC_FPREGS+224)(a0)
- ldc1 $f30,(SC_FPREGS+240)(a0)
+ ldc1 $f0,0(a0)
+ ldc1 $f2,16(a0)
+ ldc1 $f4,32(a0)
+ ldc1 $f6,48(a0)
+ ldc1 $f8,64(a0)
+ ldc1 $f10,80(a0)
+ ldc1 $f12,96(a0)
+ ldc1 $f14,112(a0)
+ ldc1 $f16,128(a0)
+ ldc1 $f18,144(a0)
+ ldc1 $f20,160(a0)
+ ldc1 $f22,176(a0)
+ ldc1 $f24,192(a0)
+ ldc1 $f26,208(a0)
+ ldc1 $f28,224(a0)
+ ldc1 $f30,240(a0)
jr ra
ctc1 t0,fcr31
1: jr ra
#if defined(CONFIG_USE_OF)
/* Get any additional entropy passed in device tree */
- {
+ if (initial_boot_params) {
int node, len;
u64 *prop;
end = PFN_DOWN(boot_mem_map.map[i].addr
+ boot_mem_map.map[i].size);
+#ifndef CONFIG_HIGHMEM
+ /*
+ * Skip highmem here so we get an accurate max_low_pfn if low
+ * memory stops short of high memory.
+ * If the region overlaps HIGHMEM_START, end is clipped so
+ * max_pfn excludes the highmem portion.
+ */
+ if (start >= PFN_DOWN(HIGHMEM_START))
+ continue;
+ if (end > PFN_DOWN(HIGHMEM_START))
+ end = PFN_DOWN(HIGHMEM_START);
+#endif
+
if (end > max_low_pfn)
max_low_pfn = end;
if (start < min_low_pfn)
print_ip_sym(pc);
pc = unwind_stack(task, &sp, pc, &ra);
} while (pc);
- printk("\n");
+ pr_cont("\n");
}
/*
printk("Stack :");
i = 0;
while ((unsigned long) sp & (PAGE_SIZE - 1)) {
- if (i && ((i % (64 / field)) == 0))
- printk("\n ");
+ if (i && ((i % (64 / field)) == 0)) {
+ pr_cont("\n");
+ printk(" ");
+ }
if (i > 39) {
- printk(" ...");
+ pr_cont(" ...");
break;
}
if (__get_user(stackdata, sp++)) {
- printk(" (Bad stack address)");
+ pr_cont(" (Bad stack address)");
break;
}
- printk(" %0*lx", field, stackdata);
+ pr_cont(" %0*lx", field, stackdata);
i++;
}
- printk("\n");
+ pr_cont("\n");
show_backtrace(task, regs);
}
long i;
unsigned short __user *pc16 = NULL;
- printk("\nCode:");
+ printk("Code:");
if ((unsigned long)pc & 1)
pc16 = (unsigned short __user *)((unsigned long)pc & ~1);
for(i = -3 ; i < 6 ; i++) {
unsigned int insn;
if (pc16 ? __get_user(insn, pc16 + i) : __get_user(insn, pc + i)) {
- printk(" (Bad address in epc)\n");
+ pr_cont(" (Bad address in epc)\n");
break;
}
- printk("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>'));
+ pr_cont("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>'));
}
+ pr_cont("\n");
}
static void __show_regs(const struct pt_regs *regs)
if ((i % 4) == 0)
printk("$%2d :", i);
if (i == 0)
- printk(" %0*lx", field, 0UL);
+ pr_cont(" %0*lx", field, 0UL);
else if (i == 26 || i == 27)
- printk(" %*s", field, "");
+ pr_cont(" %*s", field, "");
else
- printk(" %0*lx", field, regs->regs[i]);
+ pr_cont(" %0*lx", field, regs->regs[i]);
i++;
if ((i % 4) == 0)
- printk("\n");
+ pr_cont("\n");
}
#ifdef CONFIG_CPU_HAS_SMARTMIPS
if (cpu_has_3kex) {
if (regs->cp0_status & ST0_KUO)
- printk("KUo ");
+ pr_cont("KUo ");
if (regs->cp0_status & ST0_IEO)
- printk("IEo ");
+ pr_cont("IEo ");
if (regs->cp0_status & ST0_KUP)
- printk("KUp ");
+ pr_cont("KUp ");
if (regs->cp0_status & ST0_IEP)
- printk("IEp ");
+ pr_cont("IEp ");
if (regs->cp0_status & ST0_KUC)
- printk("KUc ");
+ pr_cont("KUc ");
if (regs->cp0_status & ST0_IEC)
- printk("IEc ");
+ pr_cont("IEc ");
} else if (cpu_has_4kex) {
if (regs->cp0_status & ST0_KX)
- printk("KX ");
+ pr_cont("KX ");
if (regs->cp0_status & ST0_SX)
- printk("SX ");
+ pr_cont("SX ");
if (regs->cp0_status & ST0_UX)
- printk("UX ");
+ pr_cont("UX ");
switch (regs->cp0_status & ST0_KSU) {
case KSU_USER:
- printk("USER ");
+ pr_cont("USER ");
break;
case KSU_SUPERVISOR:
- printk("SUPERVISOR ");
+ pr_cont("SUPERVISOR ");
break;
case KSU_KERNEL:
- printk("KERNEL ");
+ pr_cont("KERNEL ");
break;
default:
- printk("BAD_MODE ");
+ pr_cont("BAD_MODE ");
break;
}
if (regs->cp0_status & ST0_ERL)
- printk("ERL ");
+ pr_cont("ERL ");
if (regs->cp0_status & ST0_EXL)
- printk("EXL ");
+ pr_cont("EXL ");
if (regs->cp0_status & ST0_IE)
- printk("IE ");
+ pr_cont("IE ");
}
- printk("\n");
+ pr_cont("\n");
exccode = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE;
printk("Cause : %08x (ExcCode %02x)\n", cause, exccode);
exception_exit(prev_state);
}
+/*
+ * Send SIGFPE according to FCSR Cause bits, which must have already
+ * been masked against Enable bits. This is impotant as Inexact can
+ * happen together with Overflow or Underflow, and `ptrace' can set
+ * any bits.
+ */
+void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
+ struct task_struct *tsk)
+{
+ struct siginfo si = { .si_addr = fault_addr, .si_signo = SIGFPE };
+
+ if (fcr31 & FPU_CSR_INV_X)
+ si.si_code = FPE_FLTINV;
+ else if (fcr31 & FPU_CSR_DIV_X)
+ si.si_code = FPE_FLTDIV;
+ else if (fcr31 & FPU_CSR_OVF_X)
+ si.si_code = FPE_FLTOVF;
+ else if (fcr31 & FPU_CSR_UDF_X)
+ si.si_code = FPE_FLTUND;
+ else if (fcr31 & FPU_CSR_INE_X)
+ si.si_code = FPE_FLTRES;
+ else
+ si.si_code = __SI_FAULT;
+ force_sig_info(SIGFPE, &si, tsk);
+}
+
int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
{
struct siginfo si = { 0 };
return 0;
case SIGFPE:
- si.si_addr = fault_addr;
- si.si_signo = sig;
- /*
- * Inexact can happen together with Overflow or Underflow.
- * Respect the mask to deliver the correct exception.
- */
- fcr31 &= (fcr31 & FPU_CSR_ALL_E) <<
- (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E));
- if (fcr31 & FPU_CSR_INV_X)
- si.si_code = FPE_FLTINV;
- else if (fcr31 & FPU_CSR_DIV_X)
- si.si_code = FPE_FLTDIV;
- else if (fcr31 & FPU_CSR_OVF_X)
- si.si_code = FPE_FLTOVF;
- else if (fcr31 & FPU_CSR_UDF_X)
- si.si_code = FPE_FLTUND;
- else if (fcr31 & FPU_CSR_INE_X)
- si.si_code = FPE_FLTRES;
- else
- si.si_code = __SI_FAULT;
- force_sig_info(sig, &si, current);
+ force_fcr31_sig(fcr31, fault_addr, current);
return 1;
case SIGBUS:
/* Run the emulator */
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
&fault_addr);
- fcr31 = current->thread.fpu.fcr31;
/*
- * We can't allow the emulated instruction to leave any of
- * the cause bits set in $fcr31.
+ * We can't allow the emulated instruction to leave any
+ * enabled Cause bits set in $fcr31.
*/
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
+ current->thread.fpu.fcr31 &= ~fcr31;
/* Restore the hardware register state */
own_fpu(1);
goto out;
/* Clear FCSR.Cause before enabling interrupts */
- write_32bit_cp1_register(CP1_STATUS, fcr31 & ~FPU_CSR_ALL_X);
+ write_32bit_cp1_register(CP1_STATUS, fcr31 & ~mask_fcr31_x(fcr31));
local_irq_enable();
die_if_kernel("FP exception in kernel code", regs);
/* Run the emulator */
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
&fault_addr);
- fcr31 = current->thread.fpu.fcr31;
/*
- * We can't allow the emulated instruction to leave any of
- * the cause bits set in $fcr31.
+ * We can't allow the emulated instruction to leave any
+ * enabled Cause bits set in $fcr31.
*/
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
+ current->thread.fpu.fcr31 &= ~fcr31;
/* Restore the hardware register state */
own_fpu(1); /* Using the FPU again. */
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0,
&fault_addr);
- fcr31 = current->thread.fpu.fcr31;
/*
* We can't allow the emulated instruction to leave
- * any of the cause bits set in $fcr31.
+ * any enabled Cause bits set in $fcr31.
*/
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
+ current->thread.fpu.fcr31 &= ~fcr31;
/* Send a signal if required. */
if (!process_fpemu_return(sig, fault_addr, fcr31) && !err)
struct mips_coproc *cop0 = vcpu->arch.cop0;
enum emulation_result er = EMULATE_DONE;
- if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
+ if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
+ kvm_clear_c0_guest_status(cop0, ST0_ERL);
+ vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
+ } else if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
kvm_debug("[%#lx] ERET to %#lx\n", vcpu->arch.pc,
kvm_read_c0_guest_epc(cop0));
kvm_clear_c0_guest_status(cop0, ST0_EXL);
vcpu->arch.pc = kvm_read_c0_guest_epc(cop0);
- } else if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
- kvm_clear_c0_guest_status(cop0, ST0_ERL);
- vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
} else {
kvm_err("[%#lx] ERET when MIPS_SR_EXL|MIPS_SR_ERL == 0\n",
vcpu->arch.pc);
struct kvm_vcpu *vcpu)
{
enum emulation_result er = EMULATE_DO_MMIO;
+ unsigned long curr_pc;
u32 op, rt;
u32 bytes;
rt = inst.i_format.rt;
op = inst.i_format.opcode;
- vcpu->arch.pending_load_cause = cause;
+ /*
+ * Find the resume PC now while we have safe and easy access to the
+ * prior branch instruction, and save it for
+ * kvm_mips_complete_mmio_load() to restore later.
+ */
+ curr_pc = vcpu->arch.pc;
+ er = update_pc(vcpu, cause);
+ if (er == EMULATE_FAIL)
+ return er;
+ vcpu->arch.io_pc = vcpu->arch.pc;
+ vcpu->arch.pc = curr_pc;
+
vcpu->arch.io_gpr = rt;
switch (op) {
goto done;
}
- er = update_pc(vcpu, vcpu->arch.pending_load_cause);
- if (er == EMULATE_FAIL)
- return er;
+ /* Restore saved resume PC */
+ vcpu->arch.pc = vcpu->arch.io_pc;
switch (run->mmio.len) {
case 4:
break;
}
- if (vcpu->arch.pending_load_cause & CAUSEF_BD)
- kvm_debug("[%#lx] Completing %d byte BD Load to gpr %d (0x%08lx) type %d\n",
- vcpu->arch.pc, run->mmio.len, vcpu->arch.io_gpr, *gpr,
- vcpu->mmio_needed);
-
done:
return er;
}
static void kvm_mips_check_asids(struct kvm_vcpu *vcpu)
{
struct mips_coproc *cop0 = vcpu->arch.cop0;
- int cpu = smp_processor_id();
+ int i, cpu = smp_processor_id();
unsigned int gasid;
/*
vcpu);
vcpu->arch.guest_user_asid[cpu] =
vcpu->arch.guest_user_mm.context.asid[cpu];
+ for_each_possible_cpu(i)
+ if (i != cpu)
+ vcpu->arch.guest_user_asid[cpu] = 0;
vcpu->arch.last_user_gasid = gasid;
}
}
if ((vcpu->arch.guest_user_asid[cpu] ^ asid_cache(cpu)) &
asid_version_mask(cpu)) {
- u32 gasid = kvm_read_c0_guest_entryhi(vcpu->arch.cop0) &
- KVM_ENTRYHI_ASID;
-
kvm_get_new_mmu_context(&vcpu->arch.guest_user_mm, cpu, vcpu);
vcpu->arch.guest_user_asid[cpu] =
vcpu->arch.guest_user_mm.context.asid[cpu];
- vcpu->arch.last_user_gasid = gasid;
newasid++;
kvm_debug("[%d]: cpu_context: %#lx\n", cpu,
c0 = (entrylo0 & ENTRYLO_C) >> ENTRYLO_C_SHIFT;
c1 = (entrylo1 & ENTRYLO_C) >> ENTRYLO_C_SHIFT;
- printk("va=%0*lx asid=%0*lx",
- vwidth, (entryhi & ~0x1fffUL),
- asidwidth, entryhi & asidmask);
+ pr_cont("va=%0*lx asid=%0*lx",
+ vwidth, (entryhi & ~0x1fffUL),
+ asidwidth, entryhi & asidmask);
if (cpu_has_guestid)
- printk(" gid=%02lx",
- (guestctl1 & MIPS_GCTL1_RID)
+ pr_cont(" gid=%02lx",
+ (guestctl1 & MIPS_GCTL1_RID)
>> MIPS_GCTL1_RID_SHIFT);
/* RI/XI are in awkward places, so mask them off separately */
pa = entrylo0 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI);
if (xpa)
pa |= (unsigned long long)readx_c0_entrylo0() << 30;
pa = (pa << 6) & PAGE_MASK;
- printk("\n\t[");
+ pr_cont("\n\t[");
if (cpu_has_rixi)
- printk("ri=%d xi=%d ",
- (entrylo0 & MIPS_ENTRYLO_RI) ? 1 : 0,
- (entrylo0 & MIPS_ENTRYLO_XI) ? 1 : 0);
- printk("pa=%0*llx c=%d d=%d v=%d g=%d] [",
- pwidth, pa, c0,
- (entrylo0 & ENTRYLO_D) ? 1 : 0,
- (entrylo0 & ENTRYLO_V) ? 1 : 0,
- (entrylo0 & ENTRYLO_G) ? 1 : 0);
+ pr_cont("ri=%d xi=%d ",
+ (entrylo0 & MIPS_ENTRYLO_RI) ? 1 : 0,
+ (entrylo0 & MIPS_ENTRYLO_XI) ? 1 : 0);
+ pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d] [",
+ pwidth, pa, c0,
+ (entrylo0 & ENTRYLO_D) ? 1 : 0,
+ (entrylo0 & ENTRYLO_V) ? 1 : 0,
+ (entrylo0 & ENTRYLO_G) ? 1 : 0);
/* RI/XI are in awkward places, so mask them off separately */
pa = entrylo1 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI);
if (xpa)
pa |= (unsigned long long)readx_c0_entrylo1() << 30;
pa = (pa << 6) & PAGE_MASK;
if (cpu_has_rixi)
- printk("ri=%d xi=%d ",
- (entrylo1 & MIPS_ENTRYLO_RI) ? 1 : 0,
- (entrylo1 & MIPS_ENTRYLO_XI) ? 1 : 0);
- printk("pa=%0*llx c=%d d=%d v=%d g=%d]\n",
- pwidth, pa, c1,
- (entrylo1 & ENTRYLO_D) ? 1 : 0,
- (entrylo1 & ENTRYLO_V) ? 1 : 0,
- (entrylo1 & ENTRYLO_G) ? 1 : 0);
+ pr_cont("ri=%d xi=%d ",
+ (entrylo1 & MIPS_ENTRYLO_RI) ? 1 : 0,
+ (entrylo1 & MIPS_ENTRYLO_XI) ? 1 : 0);
+ pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d]\n",
+ pwidth, pa, c1,
+ (entrylo1 & ENTRYLO_D) ? 1 : 0,
+ (entrylo1 & ENTRYLO_V) ? 1 : 0,
+ (entrylo1 & ENTRYLO_G) ? 1 : 0);
}
printk("\n");
*/
printk("Index: %2d ", i);
- printk("va=%08lx asid=%08lx"
- " [pa=%06lx n=%d d=%d v=%d g=%d]",
- entryhi & PAGE_MASK,
- entryhi & asid_mask,
- entrylo0 & PAGE_MASK,
- (entrylo0 & R3K_ENTRYLO_N) ? 1 : 0,
- (entrylo0 & R3K_ENTRYLO_D) ? 1 : 0,
- (entrylo0 & R3K_ENTRYLO_V) ? 1 : 0,
- (entrylo0 & R3K_ENTRYLO_G) ? 1 : 0);
+ pr_cont("va=%08lx asid=%08lx"
+ " [pa=%06lx n=%d d=%d v=%d g=%d]",
+ entryhi & PAGE_MASK,
+ entryhi & asid_mask,
+ entrylo0 & PAGE_MASK,
+ (entrylo0 & R3K_ENTRYLO_N) ? 1 : 0,
+ (entrylo0 & R3K_ENTRYLO_D) ? 1 : 0,
+ (entrylo0 & R3K_ENTRYLO_V) ? 1 : 0,
+ (entrylo0 & R3K_ENTRYLO_G) ? 1 : 0);
}
}
printk("\n");
#define __IGNORE_select /* newselect */
#define __IGNORE_fadvise64 /* fadvise64_64 */
-
+#define __IGNORE_pkey_mprotect
+#define __IGNORE_pkey_alloc
+#define __IGNORE_pkey_free
#define LINUX_GATEWAY_ADDR 0x100
if (dev->num_addrs) {
int k;
- printk(", additional addresses: ");
+ pr_cont(", additional addresses: ");
for (k = 0; k < dev->num_addrs; k++)
- printk("0x%lx ", dev->addr[k]);
+ pr_cont("0x%lx ", dev->addr[k]);
}
- printk("\n");
+ pr_cont("\n");
}
/**
.endr
/* This address must remain fixed at 0x100 for glibc's syscalls to work */
- .align 256
+ .align LINUX_GATEWAY_ADDR
linux_gateway_entry:
gate .+8, %r0 /* become privileged */
mtsp %r0,%sr4 /* get kernel space into sr4 */
mtsp %r0,%sr5 /* get kernel space into sr5 */
mtsp %r0,%sr6 /* get kernel space into sr6 */
- mfsp %sr7,%r1 /* save user sr7 */
- mtsp %r1,%sr3 /* and store it in sr3 */
#ifdef CONFIG_64BIT
/* for now we can *always* set the W bit on entry to the syscall
depdi 0, 31, 32, %r21
1:
#endif
+
+ /* We use a rsm/ssm pair to prevent sr3 from being clobbered
+ * by external interrupts.
+ */
+ mfsp %sr7,%r1 /* save user sr7 */
+ rsm PSW_SM_I, %r0 /* disable interrupts */
+ mtsp %r1,%sr3 /* and store it in sr3 */
+
mfctl %cr30,%r1
xor %r1,%r30,%r30 /* ye olde xor trick */
xor %r1,%r30,%r1
*/
mtsp %r0,%sr7 /* get kernel space into sr7 */
+ ssm PSW_SM_I, %r0 /* enable interrupts */
STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */
mfctl %cr30,%r1 /* get task ptr in %r1 */
LDREG TI_TASK(%r1),%r1
comiclr,>> __NR_lws_entries, %r20, %r0
b,n lws_exit_nosys
- /* WARNING: Trashing sr2 and sr3 */
- mfsp %sr7,%r1 /* get userspace into sr3 */
- mtsp %r1,%sr3
- mtsp %r0,%sr2 /* get kernel space into sr2 */
-
/* Load table start */
ldil L%lws_table, %r1
ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */
stw %r1, 4(%sr2,%r20)
#endif
/* The load and store could fail */
-1: ldw,ma 0(%sr3,%r26), %r28
+1: ldw,ma 0(%r26), %r28
sub,<> %r28, %r25, %r0
-2: stw,ma %r24, 0(%sr3,%r26)
+2: stw,ma %r24, 0(%r26)
/* Free lock */
stw,ma %r20, 0(%sr2,%r20)
#if ENABLE_LWS_DEBUG
nop
/* 8bit load */
-4: ldb 0(%sr3,%r25), %r25
+4: ldb 0(%r25), %r25
b cas2_lock_start
-5: ldb 0(%sr3,%r24), %r24
+5: ldb 0(%r24), %r24
nop
nop
nop
nop
/* 16bit load */
-6: ldh 0(%sr3,%r25), %r25
+6: ldh 0(%r25), %r25
b cas2_lock_start
-7: ldh 0(%sr3,%r24), %r24
+7: ldh 0(%r24), %r24
nop
nop
nop
nop
/* 32bit load */
-8: ldw 0(%sr3,%r25), %r25
+8: ldw 0(%r25), %r25
b cas2_lock_start
-9: ldw 0(%sr3,%r24), %r24
+9: ldw 0(%r24), %r24
nop
nop
nop
/* 64bit load */
#ifdef CONFIG_64BIT
-10: ldd 0(%sr3,%r25), %r25
-11: ldd 0(%sr3,%r24), %r24
+10: ldd 0(%r25), %r25
+11: ldd 0(%r24), %r24
#else
/* Load new value into r22/r23 - high/low */
-10: ldw 0(%sr3,%r25), %r22
-11: ldw 4(%sr3,%r25), %r23
+10: ldw 0(%r25), %r22
+11: ldw 4(%r25), %r23
/* Load new value into fr4 for atomic store later */
-12: flddx 0(%sr3,%r24), %fr4
+12: flddx 0(%r24), %fr4
#endif
cas2_lock_start:
ldo 1(%r0),%r28
/* 8bit CAS */
-13: ldb,ma 0(%sr3,%r26), %r29
+13: ldb,ma 0(%r26), %r29
sub,= %r29, %r25, %r0
b,n cas2_end
-14: stb,ma %r24, 0(%sr3,%r26)
+14: stb,ma %r24, 0(%r26)
b cas2_end
copy %r0, %r28
nop
nop
/* 16bit CAS */
-15: ldh,ma 0(%sr3,%r26), %r29
+15: ldh,ma 0(%r26), %r29
sub,= %r29, %r25, %r0
b,n cas2_end
-16: sth,ma %r24, 0(%sr3,%r26)
+16: sth,ma %r24, 0(%r26)
b cas2_end
copy %r0, %r28
nop
nop
/* 32bit CAS */
-17: ldw,ma 0(%sr3,%r26), %r29
+17: ldw,ma 0(%r26), %r29
sub,= %r29, %r25, %r0
b,n cas2_end
-18: stw,ma %r24, 0(%sr3,%r26)
+18: stw,ma %r24, 0(%r26)
b cas2_end
copy %r0, %r28
nop
/* 64bit CAS */
#ifdef CONFIG_64BIT
-19: ldd,ma 0(%sr3,%r26), %r29
+19: ldd,ma 0(%r26), %r29
sub,*= %r29, %r25, %r0
b,n cas2_end
-20: std,ma %r24, 0(%sr3,%r26)
+20: std,ma %r24, 0(%r26)
copy %r0, %r28
#else
/* Compare first word */
-19: ldw,ma 0(%sr3,%r26), %r29
+19: ldw,ma 0(%r26), %r29
sub,= %r29, %r22, %r0
b,n cas2_end
/* Compare second word */
-20: ldw,ma 4(%sr3,%r26), %r29
+20: ldw,ma 4(%r26), %r29
sub,= %r29, %r23, %r0
b,n cas2_end
/* Perform the store */
-21: fstdx %fr4, 0(%sr3,%r26)
+21: fstdx %fr4, 0(%r26)
copy %r0, %r28
#endif
return (__force __sum16)(~((__force u32)sum + tmp) >> 16);
}
-static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
#ifdef __powerpc64__
unsigned long s = (__force u32)sum;
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented
*/
-static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
if (r < 0)
goto out;
- diag224_buf = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!diag224_buf || diag224(diag224_buf))
goto out;
sctns->par.infpval1 |= PAR_WGHT_VLD;
out:
- kfree(diag224_buf);
+ free_page((unsigned long)diag224_buf);
vfree(diag204_buf);
}
unsigned int icache_line_size;
unsigned int ecache_size;
unsigned int ecache_line_size;
- unsigned short sock_id;
+ unsigned short sock_id; /* physical package */
unsigned short core_id;
- int proc_id;
+ unsigned short max_cache_id; /* groupings of highest shared cache */
+ unsigned short proc_id; /* strand (aka HW thread) id */
} cpuinfo_sparc;
DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data);
*(volatile __u32 *)&lp->lock = ~0U;
}
-static void inline arch_write_unlock(arch_rwlock_t *lock)
+static inline void arch_write_unlock(arch_rwlock_t *lock)
{
__asm__ __volatile__(
" st %%g0, [%0]"
/* Multi-reader locks, these are much saner than the 32-bit Sparc ones... */
-static void inline arch_read_lock(arch_rwlock_t *lock)
+static inline void arch_read_lock(arch_rwlock_t *lock)
{
unsigned long tmp1, tmp2;
: "memory");
}
-static int inline arch_read_trylock(arch_rwlock_t *lock)
+static inline int arch_read_trylock(arch_rwlock_t *lock)
{
int tmp1, tmp2;
return tmp1;
}
-static void inline arch_read_unlock(arch_rwlock_t *lock)
+static inline void arch_read_unlock(arch_rwlock_t *lock)
{
unsigned long tmp1, tmp2;
: "memory");
}
-static void inline arch_write_lock(arch_rwlock_t *lock)
+static inline void arch_write_lock(arch_rwlock_t *lock)
{
unsigned long mask, tmp1, tmp2;
: "memory");
}
-static void inline arch_write_unlock(arch_rwlock_t *lock)
+static inline void arch_write_unlock(arch_rwlock_t *lock)
{
__asm__ __volatile__(
" stw %%g0, [%0]"
: "memory");
}
-static int inline arch_write_trylock(arch_rwlock_t *lock)
+static inline int arch_write_trylock(arch_rwlock_t *lock)
{
unsigned long mask, tmp1, tmp2, result;
#define topology_physical_package_id(cpu) (cpu_data(cpu).proc_id)
#define topology_core_id(cpu) (cpu_data(cpu).core_id)
#define topology_core_cpumask(cpu) (&cpu_core_sib_map[cpu])
+#define topology_core_cache_cpumask(cpu) (&cpu_core_sib_cache_map[cpu])
#define topology_sibling_cpumask(cpu) (&per_cpu(cpu_sibling_map, cpu))
#endif /* CONFIG_SMP */
extern cpumask_t cpu_core_map[NR_CPUS];
extern cpumask_t cpu_core_sib_map[NR_CPUS];
+extern cpumask_t cpu_core_sib_cache_map[NR_CPUS];
+
+/**
+ * Return cores that shares the last level cache.
+ */
static inline const struct cpumask *cpu_coregroup_mask(int cpu)
{
- return &cpu_core_map[cpu];
+ return &cpu_core_sib_cache_map[cpu];
}
#endif /* _ASM_SPARC64_TOPOLOGY_H */
return 1;
}
-void __ret_efault(void);
void __retl_efault(void);
/* Uh, these should become the main single-value transfer routines..
unsigned long __must_check ___copy_from_user(void *to,
const void __user *from,
unsigned long size);
-unsigned long copy_from_user_fixup(void *to, const void __user *from,
- unsigned long size);
static inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long size)
{
- unsigned long ret;
-
check_object_size(to, size, false);
- ret = ___copy_from_user(to, from, size);
- if (unlikely(ret))
- ret = copy_from_user_fixup(to, from, size);
-
- return ret;
+ return ___copy_from_user(to, from, size);
}
#define __copy_from_user copy_from_user
unsigned long __must_check ___copy_to_user(void __user *to,
const void *from,
unsigned long size);
-unsigned long copy_to_user_fixup(void __user *to, const void *from,
- unsigned long size);
static inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long size)
{
- unsigned long ret;
-
check_object_size(from, size, true);
- ret = ___copy_to_user(to, from, size);
- if (unlikely(ret))
- ret = copy_to_user_fixup(to, from, size);
- return ret;
+ return ___copy_to_user(to, from, size);
}
#define __copy_to_user copy_to_user
unsigned long __must_check ___copy_in_user(void __user *to,
const void __user *from,
unsigned long size);
-unsigned long copy_in_user_fixup(void __user *to, void __user *from,
- unsigned long size);
static inline unsigned long __must_check
copy_in_user(void __user *to, void __user *from, unsigned long size)
{
- unsigned long ret = ___copy_in_user(to, from, size);
-
- if (unlikely(ret))
- ret = copy_in_user_fixup(to, from, size);
- return ret;
+ return ___copy_in_user(to, from, size);
}
#define __copy_in_user copy_in_user
EXPORT_SYMBOL(tlb_type)
.section ".fixup",#alloc,#execinstr
- .globl __ret_efault, __retl_efault, __ret_one, __retl_one
-ENTRY(__ret_efault)
- ret
- restore %g0, -EFAULT, %o0
-ENDPROC(__ret_efault)
-EXPORT_SYMBOL(__ret_efault)
-
ENTRY(__retl_efault)
retl
mov -EFAULT, %o0
ENDPROC(__retl_efault)
-ENTRY(__retl_one)
- retl
- mov 1, %o0
-ENDPROC(__retl_one)
-
-ENTRY(__retl_one_fp)
- VISExitHalf
- retl
- mov 1, %o0
-ENDPROC(__retl_one_fp)
-
-ENTRY(__ret_one_asi)
- wr %g0, ASI_AIUS, %asi
- ret
- restore %g0, 1, %o0
-ENDPROC(__ret_one_asi)
-
-ENTRY(__retl_one_asi)
- wr %g0, ASI_AIUS, %asi
- retl
- mov 1, %o0
-ENDPROC(__retl_one_asi)
-
-ENTRY(__retl_one_asi_fp)
- wr %g0, ASI_AIUS, %asi
- VISExitHalf
- retl
- mov 1, %o0
-ENDPROC(__retl_one_asi_fp)
-
ENTRY(__retl_o1)
retl
mov %o1, %o0
void arch_jump_label_transform(struct jump_entry *entry,
enum jump_label_type type)
{
- u32 val;
u32 *insn = (u32 *) (unsigned long) entry->code;
+ u32 val;
if (type == JUMP_LABEL_JMP) {
s32 off = (s32)entry->target - (s32)entry->code;
+ bool use_v9_branch = false;
+
+ BUG_ON(off & 3);
#ifdef CONFIG_SPARC64
- /* ba,pt %xcc, . + (off << 2) */
- val = 0x10680000 | ((u32) off >> 2);
-#else
- /* ba . + (off << 2) */
- val = 0x10800000 | ((u32) off >> 2);
+ if (off <= 0xfffff && off >= -0x100000)
+ use_v9_branch = true;
#endif
+ if (use_v9_branch) {
+ /* WDISP19 - target is . + immed << 2 */
+ /* ba,pt %xcc, . + off */
+ val = 0x10680000 | (((u32) off >> 2) & 0x7ffff);
+ } else {
+ /* WDISP22 - target is . + immed << 2 */
+ BUG_ON(off > 0x7fffff);
+ BUG_ON(off < -0x800000);
+ /* ba . + off */
+ val = 0x10800000 | (((u32) off >> 2) & 0x3fffff);
+ }
} else {
val = 0x01000000;
}
cpu_data(*id).core_id = core_id;
}
-static void __mark_sock_id(struct mdesc_handle *hp, u64 node,
- int sock_id)
+static void __mark_max_cache_id(struct mdesc_handle *hp, u64 node,
+ int max_cache_id)
{
const u64 *id = mdesc_get_property(hp, node, "id", NULL);
- if (*id < num_possible_cpus())
- cpu_data(*id).sock_id = sock_id;
+ if (*id < num_possible_cpus()) {
+ cpu_data(*id).max_cache_id = max_cache_id;
+
+ /**
+ * On systems without explicit socket descriptions socket
+ * is max_cache_id
+ */
+ cpu_data(*id).sock_id = max_cache_id;
+ }
}
static void mark_core_ids(struct mdesc_handle *hp, u64 mp,
find_back_node_value(hp, mp, "cpu", __mark_core_id, core_id, 10);
}
-static void mark_sock_ids(struct mdesc_handle *hp, u64 mp,
- int sock_id)
+static void mark_max_cache_ids(struct mdesc_handle *hp, u64 mp,
+ int max_cache_id)
{
- find_back_node_value(hp, mp, "cpu", __mark_sock_id, sock_id, 10);
+ find_back_node_value(hp, mp, "cpu", __mark_max_cache_id,
+ max_cache_id, 10);
}
static void set_core_ids(struct mdesc_handle *hp)
}
}
-static int set_sock_ids_by_cache(struct mdesc_handle *hp, int level)
+static int set_max_cache_ids_by_cache(struct mdesc_handle *hp, int level)
{
u64 mp;
int idx = 1;
int fnd = 0;
- /* Identify unique sockets by looking for cpus backpointed to by
- * shared level n caches.
+ /**
+ * Identify unique highest level of shared cache by looking for cpus
+ * backpointed to by shared level N caches.
*/
mdesc_for_each_node_by_name(hp, mp, "cache") {
const u64 *cur_lvl;
cur_lvl = mdesc_get_property(hp, mp, "level", NULL);
if (*cur_lvl != level)
continue;
-
- mark_sock_ids(hp, mp, idx);
+ mark_max_cache_ids(hp, mp, idx);
idx++;
fnd = 1;
}
{
u64 mp;
- /* If machine description exposes sockets data use it.
- * Otherwise fallback to use shared L3 or L2 caches.
+ /**
+ * Find the highest level of shared cache which pre-T7 is also
+ * the socket.
*/
+ if (!set_max_cache_ids_by_cache(hp, 3))
+ set_max_cache_ids_by_cache(hp, 2);
+
+ /* If machine description exposes sockets data use it.*/
mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "sockets");
if (mp != MDESC_NODE_NULL)
- return set_sock_ids_by_socket(hp, mp);
-
- if (!set_sock_ids_by_cache(hp, 3))
- set_sock_ids_by_cache(hp, 2);
+ set_sock_ids_by_socket(hp, mp);
}
static void mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id)
cpumask_t cpu_core_sib_map[NR_CPUS] __read_mostly = {
[0 ... NR_CPUS-1] = CPU_MASK_NONE };
+cpumask_t cpu_core_sib_cache_map[NR_CPUS] __read_mostly = {
+ [0 ... NR_CPUS - 1] = CPU_MASK_NONE };
+
EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);
EXPORT_SYMBOL(cpu_core_map);
EXPORT_SYMBOL(cpu_core_sib_map);
+EXPORT_SYMBOL(cpu_core_sib_cache_map);
static cpumask_t smp_commenced_mask;
unsigned int j;
for_each_present_cpu(j) {
+ if (cpu_data(i).max_cache_id ==
+ cpu_data(j).max_cache_id)
+ cpumask_set_cpu(j, &cpu_core_sib_cache_map[i]);
+
if (cpu_data(i).sock_id == cpu_data(j).sock_id)
cpumask_set_cpu(j, &cpu_core_sib_map[i]);
}
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#define GLOBAL_SPARE %g7
#else
#define GLOBAL_SPARE %g5
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST(x,y) x
#endif
#ifndef LOAD
.register %g3,#scratch
.text
+
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+ENTRY(GEN_retl_o4_1)
+ add %o4, %o2, %o4
+ retl
+ add %o4, 1, %o0
+ENDPROC(GEN_retl_o4_1)
+ENTRY(GEN_retl_g1_8)
+ add %g1, %o2, %g1
+ retl
+ add %g1, 8, %o0
+ENDPROC(GEN_retl_g1_8)
+ENTRY(GEN_retl_o2_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(GEN_retl_o2_4)
+ENTRY(GEN_retl_o2_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(GEN_retl_o2_1)
+#endif
+
.align 64
.globl FUNC_NAME
sub %g0, %o4, %o4
sub %o2, %o4, %o2
1: subcc %o4, 1, %o4
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %o1, %g1),GEN_retl_o4_1)
+ EX_ST(STORE(stb, %g1, %o0),GEN_retl_o4_1)
add %o1, 1, %o1
bne,pt %XCC, 1b
add %o0, 1, %o0
andn %o2, 0x7, %g1
sub %o2, %g1, %o2
1: subcc %g1, 0x8, %g1
- EX_LD(LOAD(ldx, %o1, %g2))
- EX_ST(STORE(stx, %g2, %o0))
+ EX_LD(LOAD(ldx, %o1, %g2),GEN_retl_g1_8)
+ EX_ST(STORE(stx, %g2, %o0),GEN_retl_g1_8)
add %o1, 0x8, %o1
bne,pt %XCC, 1b
add %o0, 0x8, %o0
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %g1),GEN_retl_o2_4)
+ EX_ST(STORE(stw, %g1, %o1 + %o3),GEN_retl_o2_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %g1),GEN_retl_o2_1)
+ EX_ST(STORE(stb, %g1, %o1 + %o3),GEN_retl_o2_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o
lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o
-lib-$(CONFIG_SPARC64) += copy_in_user.o user_fixup.o memmove.o
+lib-$(CONFIG_SPARC64) += copy_in_user.o memmove.o
lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
obj-$(CONFIG_SPARC64) += iomap.o
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
fsrc2 %x6, %f12; \
fsrc2 %x7, %f14;
#define FREG_LOAD_1(base, x0) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0))
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1)
#define FREG_LOAD_2(base, x0, x1) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1);
#define FREG_LOAD_3(base, x0, x1, x2) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1);
#define FREG_LOAD_4(base, x0, x1, x2, x3) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1);
#define FREG_LOAD_5(base, x0, x1, x2, x3, x4) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1);
#define FREG_LOAD_6(base, x0, x1, x2, x3, x4, x5) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD_FP(LOAD(ldd, base + 0x28, %x5));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5), NG2_retl_o2_plus_g1);
#define FREG_LOAD_7(base, x0, x1, x2, x3, x4, x5, x6) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD_FP(LOAD(ldd, base + 0x28, %x5)); \
- EX_LD_FP(LOAD(ldd, base + 0x30, %x6));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x30, %x6), NG2_retl_o2_plus_g1);
.register %g2,#scratch
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_fp:
+ VISExitHalf
+__restore_asi:
+ retl
+ wr %g0, ASI_AIUS, %asi
+ENTRY(NG2_retl_o2)
+ ba,pt %xcc, __restore_asi
+ mov %o2, %o0
+ENDPROC(NG2_retl_o2)
+ENTRY(NG2_retl_o2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %o2, 1, %o0
+ENDPROC(NG2_retl_o2_plus_1)
+ENTRY(NG2_retl_o2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %o2, 4, %o0
+ENDPROC(NG2_retl_o2_plus_4)
+ENTRY(NG2_retl_o2_plus_8)
+ ba,pt %xcc, __restore_asi
+ add %o2, 8, %o0
+ENDPROC(NG2_retl_o2_plus_8)
+ENTRY(NG2_retl_o2_plus_o4_plus_1)
+ add %o4, 1, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_1)
+ENTRY(NG2_retl_o2_plus_o4_plus_8)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_8)
+ENTRY(NG2_retl_o2_plus_o4_plus_16)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_16)
+ENTRY(NG2_retl_o2_plus_g1_fp)
+ ba,pt %xcc, __restore_fp
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_fp)
+ENTRY(NG2_retl_o2_plus_g1_plus_64_fp)
+ add %g1, 64, %g1
+ ba,pt %xcc, __restore_fp
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_plus_64_fp)
+ENTRY(NG2_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_plus_1)
+ENTRY(NG2_retl_o2_and_7_plus_o4)
+ and %o2, 7, %o2
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_and_7_plus_o4)
+ENTRY(NG2_retl_o2_and_7_plus_o4_plus_8)
+ and %o2, 7, %o2
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_and_7_plus_o4_plus_8)
+#endif
+
.align 64
.globl FUNC_NAME
sub %g0, %o4, %o4 ! bytes to align dst
sub %o2, %o4, %o2
1: subcc %o4, 1, %o4
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %o1, %g1), NG2_retl_o2_plus_o4_plus_1)
+ EX_ST(STORE(stb, %g1, %o0), NG2_retl_o2_plus_o4_plus_1)
add %o1, 1, %o1
bne,pt %XCC, 1b
add %o0, 1, %o0
nop
/* fall through for 0 < low bits < 8 */
110: sub %o4, 64, %g2
- EX_LD_FP(LOAD_BLK(%g2, %f0))
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+ EX_LD_FP(LOAD_BLK(%g2, %f0), NG2_retl_o2_plus_g1)
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f14, f16)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_8(f16, f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
120: sub %o4, 56, %g2
FREG_LOAD_7(%g2, f0, f2, f4, f6, f8, f10, f12)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f16, f18)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_7(f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
130: sub %o4, 48, %g2
FREG_LOAD_6(%g2, f0, f2, f4, f6, f8, f10)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f16, f18, f20)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_6(f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
140: sub %o4, 40, %g2
FREG_LOAD_5(%g2, f0, f2, f4, f6, f8)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f16, f18, f20, f22)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_5(f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
150: sub %o4, 32, %g2
FREG_LOAD_4(%g2, f0, f2, f4, f6)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f16, f18, f20, f22, f24)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_4(f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
160: sub %o4, 24, %g2
FREG_LOAD_3(%g2, f0, f2, f4)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f16, f18, f20, f22, f24, f26)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_3(f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
170: sub %o4, 16, %g2
FREG_LOAD_2(%g2, f0, f2)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f16, f18, f20, f22, f24, f26, f28)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_2(f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
180: sub %o4, 8, %g2
FREG_LOAD_1(%g2, f0)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f16, f18, f20, f22, f24, f26, f28, f30)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_1(f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
nop
190:
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
subcc %g1, 64, %g1
- EX_LD_FP(LOAD_BLK(%o4, %f0))
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f0), NG2_retl_o2_plus_g1_plus_64)
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1_plus_64)
add %o4, 64, %o4
bne,pt %xcc, 1b
LOAD(prefetch, %o4 + 64, #one_read)
andn %o2, 0xf, %o4
and %o2, 0xf, %o2
1: subcc %o4, 0x10, %o4
- EX_LD(LOAD(ldx, %o1, %o5))
+ EX_LD(LOAD(ldx, %o1, %o5), NG2_retl_o2_plus_o4_plus_16)
add %o1, 0x08, %o1
- EX_LD(LOAD(ldx, %o1, %g1))
+ EX_LD(LOAD(ldx, %o1, %g1), NG2_retl_o2_plus_o4_plus_16)
sub %o1, 0x08, %o1
- EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE))
+ EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_o4_plus_16)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + GLOBAL_SPARE))
+ EX_ST(STORE(stx, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_o4_plus_8)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x8, %o2
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldx, %o1, %o5), NG2_retl_o2_plus_8)
+ EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(lduw, %o1, %o5), NG2_retl_o2_plus_4)
+ EX_ST(STORE(stw, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
sub %o2, %g1, %o2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %o1, %o5))
- EX_ST(STORE(stb, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldub, %o1, %o5), NG2_retl_o2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
8: mov 64, GLOBAL_SPARE
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), NG2_retl_o2)
sub GLOBAL_SPARE, %g1, GLOBAL_SPARE
andn %o2, 0x7, %o4
sllx %g2, %g1, %g2
1: add %o1, 0x8, %o1
- EX_LD(LOAD(ldx, %o1, %g3))
+ EX_LD(LOAD(ldx, %o1, %g3), NG2_retl_o2_and_7_plus_o4)
subcc %o4, 0x8, %o4
srlx %g3, GLOBAL_SPARE, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), NG2_retl_o2_and_7_plus_o4_plus_8)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(lduw, %o1, %g1), NG2_retl_o2_plus_4)
+ EX_ST(STORE(stw, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldub, %o1, %g1), NG2_retl_o2_plus_1)
+ EX_ST(STORE(stb, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
* Copyright (C) 2012 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x, y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
* Copyright (C) 2012 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
+#define EX_ST_FP(x,y) x
#endif
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
-#endif
#ifndef LOAD
#define LOAD(type,addr,dest) type [addr], dest
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_asi_fp:
+ VISExitHalf
+__restore_asi:
+ retl
+ wr %g0, ASI_AIUS, %asi
+
+ENTRY(NG4_retl_o2)
+ ba,pt %xcc, __restore_asi
+ mov %o2, %o0
+ENDPROC(NG4_retl_o2)
+ENTRY(NG4_retl_o2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %o2, 1, %o0
+ENDPROC(NG4_retl_o2_plus_1)
+ENTRY(NG4_retl_o2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %o2, 4, %o0
+ENDPROC(NG4_retl_o2_plus_4)
+ENTRY(NG4_retl_o2_plus_o5)
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5)
+ENTRY(NG4_retl_o2_plus_o5_plus_4)
+ add %o5, 4, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_4)
+ENTRY(NG4_retl_o2_plus_o5_plus_8)
+ add %o5, 8, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_8)
+ENTRY(NG4_retl_o2_plus_o5_plus_16)
+ add %o5, 16, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_16)
+ENTRY(NG4_retl_o2_plus_o5_plus_24)
+ add %o5, 24, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_24)
+ENTRY(NG4_retl_o2_plus_o5_plus_32)
+ add %o5, 32, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_32)
+ENTRY(NG4_retl_o2_plus_g1)
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1)
+ENTRY(NG4_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1_plus_1)
+ENTRY(NG4_retl_o2_plus_g1_plus_8)
+ add %g1, 8, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1_plus_8)
+ENTRY(NG4_retl_o2_plus_o4)
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4)
+ENTRY(NG4_retl_o2_plus_o4_plus_8)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_8)
+ENTRY(NG4_retl_o2_plus_o4_plus_16)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_16)
+ENTRY(NG4_retl_o2_plus_o4_plus_24)
+ add %o4, 24, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_24)
+ENTRY(NG4_retl_o2_plus_o4_plus_32)
+ add %o4, 32, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_32)
+ENTRY(NG4_retl_o2_plus_o4_plus_40)
+ add %o4, 40, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_40)
+ENTRY(NG4_retl_o2_plus_o4_plus_48)
+ add %o4, 48, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_48)
+ENTRY(NG4_retl_o2_plus_o4_plus_56)
+ add %o4, 56, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_56)
+ENTRY(NG4_retl_o2_plus_o4_plus_64)
+ add %o4, 64, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_64)
+ENTRY(NG4_retl_o2_plus_o4_fp)
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_8_fp)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_8_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_16_fp)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_16_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_24_fp)
+ add %o4, 24, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_24_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_32_fp)
+ add %o4, 32, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_32_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_40_fp)
+ add %o4, 40, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_40_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_48_fp)
+ add %o4, 48, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_48_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_56_fp)
+ add %o4, 56, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_56_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_64_fp)
+ add %o4, 64, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_64_fp)
+#endif
.align 64
.globl FUNC_NAME
brz,pt %g1, 51f
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2))
+
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 1, %o1
subcc %g1, 1, %g1
add %o0, 1, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g2, %o0 - 0x01))
+ EX_ST(STORE(stb, %g2, %o0 - 0x01), NG4_retl_o2_plus_g1_plus_1)
51: LOAD(prefetch, %o1 + 0x040, #n_reads_strong)
LOAD(prefetch, %o1 + 0x080, #n_reads_strong)
brz,pt %g1, .Llarge_aligned
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g2))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 8, %o1
subcc %g1, 8, %g1
add %o0, 8, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stx, %g2, %o0 - 0x08))
+ EX_ST(STORE(stx, %g2, %o0 - 0x08), NG4_retl_o2_plus_g1_plus_8)
.Llarge_aligned:
/* len >= 0x80 && src 8-byte aligned && dest 8-byte aligned */
andn %o2, 0x3f, %o4
sub %o2, %o4, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o4)
add %o1, 0x40, %o1
- EX_LD(LOAD(ldx, %o1 - 0x38, %g2))
+ EX_LD(LOAD(ldx, %o1 - 0x38, %g2), NG4_retl_o2_plus_o4)
subcc %o4, 0x40, %o4
- EX_LD(LOAD(ldx, %o1 - 0x30, %g3))
- EX_LD(LOAD(ldx, %o1 - 0x28, GLOBAL_SPARE))
- EX_LD(LOAD(ldx, %o1 - 0x20, %o5))
- EX_ST(STORE_INIT(%g1, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x30, %g3), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD(LOAD(ldx, %o1 - 0x28, GLOBAL_SPARE), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD(LOAD(ldx, %o1 - 0x20, %o5), NG4_retl_o2_plus_o4_plus_64)
+ EX_ST(STORE_INIT(%g1, %o0), NG4_retl_o2_plus_o4_plus_64)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g2, %o0))
+ EX_ST(STORE_INIT(%g2, %o0), NG4_retl_o2_plus_o4_plus_56)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x18, %g2))
- EX_ST(STORE_INIT(%g3, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x18, %g2), NG4_retl_o2_plus_o4_plus_48)
+ EX_ST(STORE_INIT(%g3, %o0), NG4_retl_o2_plus_o4_plus_48)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x10, %g3))
- EX_ST(STORE_INIT(GLOBAL_SPARE, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x10, %g3), NG4_retl_o2_plus_o4_plus_40)
+ EX_ST(STORE_INIT(GLOBAL_SPARE, %o0), NG4_retl_o2_plus_o4_plus_40)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x08, GLOBAL_SPARE))
- EX_ST(STORE_INIT(%o5, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x08, GLOBAL_SPARE), NG4_retl_o2_plus_o4_plus_32)
+ EX_ST(STORE_INIT(%o5, %o0), NG4_retl_o2_plus_o4_plus_32)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g2, %o0))
+ EX_ST(STORE_INIT(%g2, %o0), NG4_retl_o2_plus_o4_plus_24)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g3, %o0))
+ EX_ST(STORE_INIT(%g3, %o0), NG4_retl_o2_plus_o4_plus_16)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(GLOBAL_SPARE, %o0))
+ EX_ST(STORE_INIT(GLOBAL_SPARE, %o0), NG4_retl_o2_plus_o4_plus_8)
add %o0, 0x08, %o0
bne,pt %icc, 1b
LOAD(prefetch, %o1 + 0x200, #n_reads_strong)
sub %o2, %o4, %o2
alignaddr %o1, %g0, %g1
add %o1, %o4, %o1
- EX_LD_FP(LOAD(ldd, %g1 + 0x00, %f0))
-1: EX_LD_FP(LOAD(ldd, %g1 + 0x08, %f2))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x00, %f0), NG4_retl_o2_plus_o4)
+1: EX_LD_FP(LOAD(ldd, %g1 + 0x08, %f2), NG4_retl_o2_plus_o4)
subcc %o4, 0x40, %o4
- EX_LD_FP(LOAD(ldd, %g1 + 0x10, %f4))
- EX_LD_FP(LOAD(ldd, %g1 + 0x18, %f6))
- EX_LD_FP(LOAD(ldd, %g1 + 0x20, %f8))
- EX_LD_FP(LOAD(ldd, %g1 + 0x28, %f10))
- EX_LD_FP(LOAD(ldd, %g1 + 0x30, %f12))
- EX_LD_FP(LOAD(ldd, %g1 + 0x38, %f14))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x10, %f4), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x18, %f6), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x20, %f8), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x28, %f10), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x30, %f12), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x38, %f14), NG4_retl_o2_plus_o4_plus_64)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %g1 + 0x40, %f0))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x40, %f0), NG4_retl_o2_plus_o4_plus_64)
faligndata %f2, %f4, %f18
add %g1, 0x40, %g1
faligndata %f4, %f6, %f20
faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE(std, %f16, %o0 + 0x00))
- EX_ST_FP(STORE(std, %f18, %o0 + 0x08))
- EX_ST_FP(STORE(std, %f20, %o0 + 0x10))
- EX_ST_FP(STORE(std, %f22, %o0 + 0x18))
- EX_ST_FP(STORE(std, %f24, %o0 + 0x20))
- EX_ST_FP(STORE(std, %f26, %o0 + 0x28))
- EX_ST_FP(STORE(std, %f28, %o0 + 0x30))
- EX_ST_FP(STORE(std, %f30, %o0 + 0x38))
+ EX_ST_FP(STORE(std, %f16, %o0 + 0x00), NG4_retl_o2_plus_o4_plus_64)
+ EX_ST_FP(STORE(std, %f18, %o0 + 0x08), NG4_retl_o2_plus_o4_plus_56)
+ EX_ST_FP(STORE(std, %f20, %o0 + 0x10), NG4_retl_o2_plus_o4_plus_48)
+ EX_ST_FP(STORE(std, %f22, %o0 + 0x18), NG4_retl_o2_plus_o4_plus_40)
+ EX_ST_FP(STORE(std, %f24, %o0 + 0x20), NG4_retl_o2_plus_o4_plus_32)
+ EX_ST_FP(STORE(std, %f26, %o0 + 0x28), NG4_retl_o2_plus_o4_plus_24)
+ EX_ST_FP(STORE(std, %f28, %o0 + 0x30), NG4_retl_o2_plus_o4_plus_16)
+ EX_ST_FP(STORE(std, %f30, %o0 + 0x38), NG4_retl_o2_plus_o4_plus_8)
add %o0, 0x40, %o0
bne,pt %icc, 1b
LOAD(prefetch, %g1 + 0x200, #n_reads_strong)
andncc %o2, 0x20 - 1, %o5
be,pn %icc, 2f
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g2))
- EX_LD(LOAD(ldx, %o1 + 0x10, GLOBAL_SPARE))
- EX_LD(LOAD(ldx, %o1 + 0x18, %o4))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g2), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x10, GLOBAL_SPARE), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x18, %o4), NG4_retl_o2_plus_o5)
add %o1, 0x20, %o1
subcc %o5, 0x20, %o5
- EX_ST(STORE(stx, %g1, %o0 + 0x00))
- EX_ST(STORE(stx, %g2, %o0 + 0x08))
- EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x10))
- EX_ST(STORE(stx, %o4, %o0 + 0x18))
+ EX_ST(STORE(stx, %g1, %o0 + 0x00), NG4_retl_o2_plus_o5_plus_32)
+ EX_ST(STORE(stx, %g2, %o0 + 0x08), NG4_retl_o2_plus_o5_plus_24)
+ EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x10), NG4_retl_o2_plus_o5_plus_24)
+ EX_ST(STORE(stx, %o4, %o0 + 0x18), NG4_retl_o2_plus_o5_plus_8)
bne,pt %icc, 1b
add %o0, 0x20, %o0
2: andcc %o2, 0x18, %o5
be,pt %icc, 3f
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
+
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
add %o1, 0x08, %o1
add %o0, 0x08, %o0
subcc %o5, 0x08, %o5
bne,pt %icc, 1b
- EX_ST(STORE(stx, %g1, %o0 - 0x08))
+ EX_ST(STORE(stx, %g1, %o0 - 0x08), NG4_retl_o2_plus_o5_plus_8)
3: brz,pt %o2, .Lexit
cmp %o2, 0x04
bl,pn %icc, .Ltiny
nop
- EX_LD(LOAD(lduw, %o1 + 0x00, %g1))
+ EX_LD(LOAD(lduw, %o1 + 0x00, %g1), NG4_retl_o2)
add %o1, 0x04, %o1
add %o0, 0x04, %o0
subcc %o2, 0x04, %o2
bne,pn %icc, .Ltiny
- EX_ST(STORE(stw, %g1, %o0 - 0x04))
+ EX_ST(STORE(stw, %g1, %o0 - 0x04), NG4_retl_o2_plus_4)
ba,a,pt %icc, .Lexit
.Lmedium_unaligned:
/* First get dest 8 byte aligned. */
brz,pt %g1, 2f
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2))
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 1, %o1
subcc %g1, 1, %g1
add %o0, 1, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g2, %o0 - 0x01))
+ EX_ST(STORE(stb, %g2, %o0 - 0x01), NG4_retl_o2_plus_g1_plus_1)
2:
and %o1, 0x7, %g1
brz,pn %g1, .Lmedium_noprefetch
mov 64, %g2
sub %g2, %g1, %g2
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1 + 0x00, %o4))
+ EX_LD(LOAD(ldx, %o1 + 0x00, %o4), NG4_retl_o2)
sllx %o4, %g1, %o4
andn %o2, 0x08 - 1, %o5
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x08, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x08, %g3), NG4_retl_o2_plus_o5)
add %o1, 0x08, %o1
subcc %o5, 0x08, %o5
srlx %g3, %g2, GLOBAL_SPARE
or GLOBAL_SPARE, %o4, GLOBAL_SPARE
- EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x00))
+ EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x00), NG4_retl_o2_plus_o5_plus_8)
add %o0, 0x08, %o0
bne,pt %icc, 1b
sllx %g3, %g1, %o4
ba,pt %icc, .Lsmall_unaligned
.Ltiny:
- EX_LD(LOAD(ldub, %o1 + 0x00, %g1))
+ EX_LD(LOAD(ldub, %o1 + 0x00, %g1), NG4_retl_o2)
subcc %o2, 1, %o2
be,pn %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x00))
- EX_LD(LOAD(ldub, %o1 + 0x01, %g1))
+ EX_ST(STORE(stb, %g1, %o0 + 0x00), NG4_retl_o2_plus_1)
+ EX_LD(LOAD(ldub, %o1 + 0x01, %g1), NG4_retl_o2)
subcc %o2, 1, %o2
be,pn %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x01))
- EX_LD(LOAD(ldub, %o1 + 0x02, %g1))
+ EX_ST(STORE(stb, %g1, %o0 + 0x01), NG4_retl_o2_plus_1)
+ EX_LD(LOAD(ldub, %o1 + 0x02, %g1), NG4_retl_o2)
ba,pt %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x02))
+ EX_ST(STORE(stb, %g1, %o0 + 0x02), NG4_retl_o2)
.Lsmall:
andcc %g2, 0x3, %g0
andn %o2, 0x4 - 1, %o5
sub %o2, %o5, %o2
1:
- EX_LD(LOAD(lduw, %o1 + 0x00, %g1))
+ EX_LD(LOAD(lduw, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
add %o1, 0x04, %o1
subcc %o5, 0x04, %o5
add %o0, 0x04, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stw, %g1, %o0 - 0x04))
+ EX_ST(STORE(stw, %g1, %o0 - 0x04), NG4_retl_o2_plus_o5_plus_4)
brz,pt %o2, .Lexit
nop
ba,a,pt %icc, .Ltiny
.Lsmall_unaligned:
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g1))
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g1), NG4_retl_o2)
add %o1, 1, %o1
add %o0, 1, %o0
subcc %o2, 1, %o2
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g1, %o0 - 0x01))
+ EX_ST(STORE(stb, %g1, %o0 - 0x01), NG4_retl_o2_plus_1)
ba,a,pt %icc, .Lexit
.size FUNC_NAME, .-FUNC_NAME
* Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __ret_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
* Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __ret_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/asi.h>
#include <asm/thread_info.h>
#define GLOBAL_SPARE %g7
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST(x,y) x
#endif
#ifndef LOAD
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_asi:
+ ret
+ wr %g0, ASI_AIUS, %asi
+ restore
+ENTRY(NG_ret_i2_plus_i4_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %i2, %i5, %i0
+ENDPROC(NG_ret_i2_plus_i4_plus_1)
+ENTRY(NG_ret_i2_plus_g1)
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1)
+ENTRY(NG_ret_i2_plus_g1_minus_8)
+ sub %g1, 8, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_8)
+ENTRY(NG_ret_i2_plus_g1_minus_16)
+ sub %g1, 16, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_16)
+ENTRY(NG_ret_i2_plus_g1_minus_24)
+ sub %g1, 24, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_24)
+ENTRY(NG_ret_i2_plus_g1_minus_32)
+ sub %g1, 32, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_32)
+ENTRY(NG_ret_i2_plus_g1_minus_40)
+ sub %g1, 40, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_40)
+ENTRY(NG_ret_i2_plus_g1_minus_48)
+ sub %g1, 48, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_48)
+ENTRY(NG_ret_i2_plus_g1_minus_56)
+ sub %g1, 56, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_56)
+ENTRY(NG_ret_i2_plus_i4)
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_plus_i4)
+ENTRY(NG_ret_i2_plus_i4_minus_8)
+ sub %i4, 8, %i4
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_plus_i4_minus_8)
+ENTRY(NG_ret_i2_plus_8)
+ ba,pt %xcc, __restore_asi
+ add %i2, 8, %i0
+ENDPROC(NG_ret_i2_plus_8)
+ENTRY(NG_ret_i2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %i2, 4, %i0
+ENDPROC(NG_ret_i2_plus_4)
+ENTRY(NG_ret_i2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %i2, 1, %i0
+ENDPROC(NG_ret_i2_plus_1)
+ENTRY(NG_ret_i2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_plus_1)
+ENTRY(NG_ret_i2)
+ ba,pt %xcc, __restore_asi
+ mov %i2, %i0
+ENDPROC(NG_ret_i2)
+ENTRY(NG_ret_i2_and_7_plus_i4)
+ and %i2, 7, %i2
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_and_7_plus_i4)
+#endif
+
.align 64
.globl FUNC_NAME
sub %g0, %i4, %i4 ! bytes to align dst
sub %i2, %i4, %i2
1: subcc %i4, 1, %i4
- EX_LD(LOAD(ldub, %i1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %i1, %g1), NG_ret_i2_plus_i4_plus_1)
+ EX_ST(STORE(stb, %g1, %o0), NG_ret_i2_plus_i4_plus_1)
add %i1, 1, %i1
bne,pt %XCC, 1b
add %o0, 1, %o0
and %i4, 0x7, GLOBAL_SPARE
sll GLOBAL_SPARE, 3, GLOBAL_SPARE
mov 64, %i5
- EX_LD(LOAD_TWIN(%i1, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1, %g2, %g3), NG_ret_i2_plus_g1)
sub %i5, GLOBAL_SPARE, %i5
mov 16, %o4
mov 32, %o5
srlx WORD3, PRE_SHIFT, TMP; \
or WORD2, TMP, WORD2;
-8: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3))
+8: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3), NG_ret_i2_plus_g1)
MIX_THREE_WORDS(%g2, %g3, %o2, %i5, GLOBAL_SPARE, %o1)
LOAD(prefetch, %i1 + %i3, #one_read)
- EX_ST(STORE_INIT(%g2, %o0 + 0x00))
- EX_ST(STORE_INIT(%g3, %o0 + 0x08))
+ EX_ST(STORE_INIT(%g2, %o0 + 0x00), NG_ret_i2_plus_g1)
+ EX_ST(STORE_INIT(%g3, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
- EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3), NG_ret_i2_plus_g1_minus_16)
MIX_THREE_WORDS(%o2, %o3, %g2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o2, %o0 + 0x10))
- EX_ST(STORE_INIT(%o3, %o0 + 0x18))
+ EX_ST(STORE_INIT(%o2, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
MIX_THREE_WORDS(%g2, %g3, %o2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%g2, %o0 + 0x20))
- EX_ST(STORE_INIT(%g3, %o0 + 0x28))
+ EX_ST(STORE_INIT(%g2, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%g3, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
- EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
MIX_THREE_WORDS(%o2, %o3, %g2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o2, %o0 + 0x30))
- EX_ST(STORE_INIT(%o3, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o2, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 8b
ba,pt %XCC, 60f
add %i1, %i4, %i1
-9: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3))
+9: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3), NG_ret_i2_plus_g1)
MIX_THREE_WORDS(%g3, %o2, %o3, %i5, GLOBAL_SPARE, %o1)
LOAD(prefetch, %i1 + %i3, #one_read)
- EX_ST(STORE_INIT(%g3, %o0 + 0x00))
- EX_ST(STORE_INIT(%o2, %o0 + 0x08))
+ EX_ST(STORE_INIT(%g3, %o0 + 0x00), NG_ret_i2_plus_g1)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
- EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3), NG_ret_i2_plus_g1_minus_16)
MIX_THREE_WORDS(%o3, %g2, %g3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o3, %o0 + 0x10))
- EX_ST(STORE_INIT(%g2, %o0 + 0x18))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%g2, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
MIX_THREE_WORDS(%g3, %o2, %o3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%g3, %o0 + 0x20))
- EX_ST(STORE_INIT(%o2, %o0 + 0x28))
+ EX_ST(STORE_INIT(%g3, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
- EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
MIX_THREE_WORDS(%o3, %g2, %g3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o3, %o0 + 0x30))
- EX_ST(STORE_INIT(%g2, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%g2, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 9b
* one twin load ahead, then add 8 back into source when
* we finish the loop.
*/
- EX_LD(LOAD_TWIN(%i1, %o4, %o5))
+ EX_LD(LOAD_TWIN(%i1, %o4, %o5), NG_ret_i2_plus_g1)
mov 16, %o7
mov 32, %g2
mov 48, %g3
mov 64, %o1
-1: EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+1: EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1)
LOAD(prefetch, %i1 + %o1, #one_read)
- EX_ST(STORE_INIT(%o5, %o0 + 0x00)) ! initializes cache line
- EX_ST(STORE_INIT(%o2, %o0 + 0x08))
- EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5))
- EX_ST(STORE_INIT(%o3, %o0 + 0x10))
- EX_ST(STORE_INIT(%o4, %o0 + 0x18))
- EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3))
- EX_ST(STORE_INIT(%o5, %o0 + 0x20))
- EX_ST(STORE_INIT(%o2, %o0 + 0x28))
- EX_LD(LOAD_TWIN(%i1 + %o1, %o4, %o5))
+ EX_ST(STORE_INIT(%o5, %o0 + 0x00), NG_ret_i2_plus_g1) ! initializes cache line
+ EX_ST(STORE_INIT(%o2, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
+ EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o4, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
+ EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o5, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
+ EX_LD(LOAD_TWIN(%i1 + %o1, %o4, %o5), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
- EX_ST(STORE_INIT(%o3, %o0 + 0x30))
- EX_ST(STORE_INIT(%o4, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o4, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 1b
add %o0, 64, %o0
mov 32, %g2
mov 48, %g3
mov 64, %o1
-1: EX_LD(LOAD_TWIN(%i1 + %g0, %o4, %o5))
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+1: EX_LD(LOAD_TWIN(%i1 + %g0, %o4, %o5), NG_ret_i2_plus_g1)
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1)
LOAD(prefetch, %i1 + %o1, #one_read)
- EX_ST(STORE_INIT(%o4, %o0 + 0x00)) ! initializes cache line
- EX_ST(STORE_INIT(%o5, %o0 + 0x08))
- EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5))
- EX_ST(STORE_INIT(%o2, %o0 + 0x10))
- EX_ST(STORE_INIT(%o3, %o0 + 0x18))
- EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3))
+ EX_ST(STORE_INIT(%o4, %o0 + 0x00), NG_ret_i2_plus_g1) ! initializes cache line
+ EX_ST(STORE_INIT(%o5, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
+ EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
+ EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
add %i1, 64, %i1
- EX_ST(STORE_INIT(%o4, %o0 + 0x20))
- EX_ST(STORE_INIT(%o5, %o0 + 0x28))
- EX_ST(STORE_INIT(%o2, %o0 + 0x30))
- EX_ST(STORE_INIT(%o3, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o4, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o5, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 1b
add %o0, 64, %o0
andn %i2, 0xf, %i4
and %i2, 0xf, %i2
1: subcc %i4, 0x10, %i4
- EX_LD(LOAD(ldx, %i1, %o4))
+ EX_LD(LOAD(ldx, %i1, %o4), NG_ret_i2_plus_i4)
add %i1, 0x08, %i1
- EX_LD(LOAD(ldx, %i1, %g1))
+ EX_LD(LOAD(ldx, %i1, %g1), NG_ret_i2_plus_i4)
sub %i1, 0x08, %i1
- EX_ST(STORE(stx, %o4, %i1 + %i3))
+ EX_ST(STORE(stx, %o4, %i1 + %i3), NG_ret_i2_plus_i4)
add %i1, 0x8, %i1
- EX_ST(STORE(stx, %g1, %i1 + %i3))
+ EX_ST(STORE(stx, %g1, %i1 + %i3), NG_ret_i2_plus_i4_minus_8)
bgu,pt %XCC, 1b
add %i1, 0x8, %i1
73: andcc %i2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %i2, 0x8, %i2
- EX_LD(LOAD(ldx, %i1, %o4))
- EX_ST(STORE(stx, %o4, %i1 + %i3))
+ EX_LD(LOAD(ldx, %i1, %o4), NG_ret_i2_plus_8)
+ EX_ST(STORE(stx, %o4, %i1 + %i3), NG_ret_i2_plus_8)
add %i1, 0x8, %i1
1: andcc %i2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %i2, 0x4, %i2
- EX_LD(LOAD(lduw, %i1, %i5))
- EX_ST(STORE(stw, %i5, %i1 + %i3))
+ EX_LD(LOAD(lduw, %i1, %i5), NG_ret_i2_plus_4)
+ EX_ST(STORE(stw, %i5, %i1 + %i3), NG_ret_i2_plus_4)
add %i1, 0x4, %i1
1: cmp %i2, 0
be,pt %XCC, 85f
sub %i2, %g1, %i2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %i1, %i5))
- EX_ST(STORE(stb, %i5, %i1 + %i3))
+ EX_LD(LOAD(ldub, %i1, %i5), NG_ret_i2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %i5, %i1 + %i3), NG_ret_i2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %i1, 1, %i1
8: mov 64, %i3
andn %i1, 0x7, %i1
- EX_LD(LOAD(ldx, %i1, %g2))
+ EX_LD(LOAD(ldx, %i1, %g2), NG_ret_i2)
sub %i3, %g1, %i3
andn %i2, 0x7, %i4
sllx %g2, %g1, %g2
1: add %i1, 0x8, %i1
- EX_LD(LOAD(ldx, %i1, %g3))
+ EX_LD(LOAD(ldx, %i1, %g3), NG_ret_i2_and_7_plus_i4)
subcc %i4, 0x8, %i4
srlx %g3, %i3, %i5
or %i5, %g2, %i5
- EX_ST(STORE(stx, %i5, %o0))
+ EX_ST(STORE(stx, %i5, %o0), NG_ret_i2_and_7_plus_i4)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
1:
subcc %i2, 4, %i2
- EX_LD(LOAD(lduw, %i1, %g1))
- EX_ST(STORE(stw, %g1, %i1 + %i3))
+ EX_LD(LOAD(lduw, %i1, %g1), NG_ret_i2_plus_4)
+ EX_ST(STORE(stw, %g1, %i1 + %i3), NG_ret_i2_plus_4)
bgu,pt %XCC, 1b
add %i1, 4, %i1
.align 32
90:
subcc %i2, 1, %i2
- EX_LD(LOAD(ldub, %i1, %g1))
- EX_ST(STORE(stb, %g1, %i1 + %i3))
+ EX_LD(LOAD(ldub, %i1, %g1), NG_ret_i2_plus_1)
+ EX_ST(STORE(stb, %g1, %i1 + %i3), NG_ret_i2_plus_1)
bgu,pt %XCC, 90b
add %i1, 1, %i1
ret
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y; \
.text; \
.align 4;
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#include <asm/export.h>
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
faligndata %f7, %f8, %f60; \
faligndata %f8, %f9, %f62;
-#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \
- EX_LD_FP(LOAD_BLK(%src, %fdest)); \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
- add %src, 0x40, %src; \
- subcc %len, 0x40, %len; \
- be,pn %xcc, jmptgt; \
- add %dest, 0x40, %dest; \
-
-#define LOOP_CHUNK1(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest)
-#define LOOP_CHUNK2(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest)
-#define LOOP_CHUNK3(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest)
+#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, jmptgt) \
+ EX_LD_FP(LOAD_BLK(%src, %fdest), U1_gs_80_fp); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_80_fp); \
+ add %src, 0x40, %src; \
+ subcc %GLOBAL_SPARE, 0x40, %GLOBAL_SPARE; \
+ be,pn %xcc, jmptgt; \
+ add %dest, 0x40, %dest; \
+
+#define LOOP_CHUNK1(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f0, f48, branch_dest)
+#define LOOP_CHUNK2(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f16, f48, branch_dest)
+#define LOOP_CHUNK3(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f32, f48, branch_dest)
#define DO_SYNC membar #Sync;
#define STORE_SYNC(dest, fsrc) \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_80_fp); \
add %dest, 0x40, %dest; \
DO_SYNC
#define STORE_JUMP(dest, fsrc, target) \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_40_fp); \
add %dest, 0x40, %dest; \
ba,pt %xcc, target; \
nop;
-#define FINISH_VISCHUNK(dest, f0, f1, left) \
- subcc %left, 8, %left;\
- bl,pn %xcc, 95f; \
- faligndata %f0, %f1, %f48; \
- EX_ST_FP(STORE(std, %f48, %dest)); \
+#define FINISH_VISCHUNK(dest, f0, f1) \
+ subcc %g3, 8, %g3; \
+ bl,pn %xcc, 95f; \
+ faligndata %f0, %f1, %f48; \
+ EX_ST_FP(STORE(std, %f48, %dest), U1_g3_8_fp); \
add %dest, 8, %dest;
-#define UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \
- subcc %left, 8, %left; \
- bl,pn %xcc, 95f; \
+#define UNEVEN_VISCHUNK_LAST(dest, f0, f1) \
+ subcc %g3, 8, %g3; \
+ bl,pn %xcc, 95f; \
fsrc2 %f0, %f1;
-#define UNEVEN_VISCHUNK(dest, f0, f1, left) \
- UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \
+#define UNEVEN_VISCHUNK(dest, f0, f1) \
+ UNEVEN_VISCHUNK_LAST(dest, f0, f1) \
ba,a,pt %xcc, 93f;
.register %g2,#scratch
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+ENTRY(U1_g1_1_fp)
+ VISExitHalf
+ add %g1, 1, %g1
+ add %g1, %g2, %g1
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_1_fp)
+ENTRY(U1_g2_0_fp)
+ VISExitHalf
+ retl
+ add %g2, %o2, %o0
+ENDPROC(U1_g2_0_fp)
+ENTRY(U1_g2_8_fp)
+ VISExitHalf
+ add %g2, 8, %g2
+ retl
+ add %g2, %o2, %o0
+ENDPROC(U1_g2_8_fp)
+ENTRY(U1_gs_0_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_0_fp)
+ENTRY(U1_gs_80_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, 0x80, %GLOBAL_SPARE
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_80_fp)
+ENTRY(U1_gs_40_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, 0x40, %GLOBAL_SPARE
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_40_fp)
+ENTRY(U1_g3_0_fp)
+ VISExitHalf
+ retl
+ add %g3, %o2, %o0
+ENDPROC(U1_g3_0_fp)
+ENTRY(U1_g3_8_fp)
+ VISExitHalf
+ add %g3, 8, %g3
+ retl
+ add %g3, %o2, %o0
+ENDPROC(U1_g3_8_fp)
+ENTRY(U1_o2_0_fp)
+ VISExitHalf
+ retl
+ mov %o2, %o0
+ENDPROC(U1_o2_0_fp)
+ENTRY(U1_o2_1_fp)
+ VISExitHalf
+ retl
+ add %o2, 1, %o0
+ENDPROC(U1_o2_1_fp)
+ENTRY(U1_gs_0)
+ VISExitHalf
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_0)
+ENTRY(U1_gs_8)
+ VISExitHalf
+ add %GLOBAL_SPARE, %o2, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, 0x8, %o0
+ENDPROC(U1_gs_8)
+ENTRY(U1_gs_10)
+ VISExitHalf
+ add %GLOBAL_SPARE, %o2, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, 0x10, %o0
+ENDPROC(U1_gs_10)
+ENTRY(U1_o2_0)
+ retl
+ mov %o2, %o0
+ENDPROC(U1_o2_0)
+ENTRY(U1_o2_8)
+ retl
+ add %o2, 8, %o0
+ENDPROC(U1_o2_8)
+ENTRY(U1_o2_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(U1_o2_4)
+ENTRY(U1_o2_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(U1_o2_1)
+ENTRY(U1_g1_0)
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_0)
+ENTRY(U1_g1_1)
+ add %g1, 1, %g1
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_1)
+ENTRY(U1_gs_0_o2_adj)
+ and %o2, 7, %o2
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_0_o2_adj)
+ENTRY(U1_gs_8_o2_adj)
+ and %o2, 7, %o2
+ add %GLOBAL_SPARE, 8, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_8_o2_adj)
+#endif
+
.align 64
.globl FUNC_NAME
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST_FP(STORE(stb, %o3, %o1 + %GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3), U1_g1_1_fp)
+ EX_ST_FP(STORE(stb, %o3, %o1 + %GLOBAL_SPARE), U1_g1_1_fp)
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD_FP(LOAD(ldd, %o1, %f4))
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4), U1_g2_0_fp)
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6), U1_g2_0_fp)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U1_g2_8_fp)
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4), U1_g2_0_fp)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U1_g2_8_fp)
bne,pt %icc, 1b
add %o0, 0x8, %o0
add %g1, %GLOBAL_SPARE, %g1
subcc %o2, %g3, %o2
- EX_LD_FP(LOAD_BLK(%o1, %f0))
+ EX_LD_FP(LOAD_BLK(%o1, %f0), U1_gs_0_fp)
add %o1, 0x40, %o1
add %g1, %g3, %g1
- EX_LD_FP(LOAD_BLK(%o1, %f16))
+ EX_LD_FP(LOAD_BLK(%o1, %f16), U1_gs_0_fp)
add %o1, 0x40, %o1
sub %GLOBAL_SPARE, 0x80, %GLOBAL_SPARE
- EX_LD_FP(LOAD_BLK(%o1, %f32))
+ EX_LD_FP(LOAD_BLK(%o1, %f32), U1_gs_80_fp)
add %o1, 0x40, %o1
/* There are 8 instances of the unrolled loop,
.align 64
1: FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f0, %f2, %f48
1: FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32)
STORE_JUMP(o0, f48, 56f)
1: FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f2, %f4, %f48
1: FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34)
STORE_JUMP(o0, f48, 57f)
1: FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f4, %f6, %f48
1: FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36)
STORE_JUMP(o0, f48, 58f)
1: FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f6, %f8, %f48
1: FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38)
STORE_JUMP(o0, f48, 59f)
1: FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f8, %f10, %f48
1: FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40)
STORE_JUMP(o0, f48, 60f)
1: FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f10, %f12, %f48
1: FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42)
STORE_JUMP(o0, f48, 61f)
1: FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f12, %f14, %f48
1: FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44)
STORE_JUMP(o0, f48, 62f)
1: FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f14, %f16, %f48
1: FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
STORE_JUMP(o0, f48, 63f)
-40: FINISH_VISCHUNK(o0, f0, f2, g3)
-41: FINISH_VISCHUNK(o0, f2, f4, g3)
-42: FINISH_VISCHUNK(o0, f4, f6, g3)
-43: FINISH_VISCHUNK(o0, f6, f8, g3)
-44: FINISH_VISCHUNK(o0, f8, f10, g3)
-45: FINISH_VISCHUNK(o0, f10, f12, g3)
-46: FINISH_VISCHUNK(o0, f12, f14, g3)
-47: UNEVEN_VISCHUNK(o0, f14, f0, g3)
-48: FINISH_VISCHUNK(o0, f16, f18, g3)
-49: FINISH_VISCHUNK(o0, f18, f20, g3)
-50: FINISH_VISCHUNK(o0, f20, f22, g3)
-51: FINISH_VISCHUNK(o0, f22, f24, g3)
-52: FINISH_VISCHUNK(o0, f24, f26, g3)
-53: FINISH_VISCHUNK(o0, f26, f28, g3)
-54: FINISH_VISCHUNK(o0, f28, f30, g3)
-55: UNEVEN_VISCHUNK(o0, f30, f0, g3)
-56: FINISH_VISCHUNK(o0, f32, f34, g3)
-57: FINISH_VISCHUNK(o0, f34, f36, g3)
-58: FINISH_VISCHUNK(o0, f36, f38, g3)
-59: FINISH_VISCHUNK(o0, f38, f40, g3)
-60: FINISH_VISCHUNK(o0, f40, f42, g3)
-61: FINISH_VISCHUNK(o0, f42, f44, g3)
-62: FINISH_VISCHUNK(o0, f44, f46, g3)
-63: UNEVEN_VISCHUNK_LAST(o0, f46, f0, g3)
-
-93: EX_LD_FP(LOAD(ldd, %o1, %f2))
+40: FINISH_VISCHUNK(o0, f0, f2)
+41: FINISH_VISCHUNK(o0, f2, f4)
+42: FINISH_VISCHUNK(o0, f4, f6)
+43: FINISH_VISCHUNK(o0, f6, f8)
+44: FINISH_VISCHUNK(o0, f8, f10)
+45: FINISH_VISCHUNK(o0, f10, f12)
+46: FINISH_VISCHUNK(o0, f12, f14)
+47: UNEVEN_VISCHUNK(o0, f14, f0)
+48: FINISH_VISCHUNK(o0, f16, f18)
+49: FINISH_VISCHUNK(o0, f18, f20)
+50: FINISH_VISCHUNK(o0, f20, f22)
+51: FINISH_VISCHUNK(o0, f22, f24)
+52: FINISH_VISCHUNK(o0, f24, f26)
+53: FINISH_VISCHUNK(o0, f26, f28)
+54: FINISH_VISCHUNK(o0, f28, f30)
+55: UNEVEN_VISCHUNK(o0, f30, f0)
+56: FINISH_VISCHUNK(o0, f32, f34)
+57: FINISH_VISCHUNK(o0, f34, f36)
+58: FINISH_VISCHUNK(o0, f36, f38)
+59: FINISH_VISCHUNK(o0, f38, f40)
+60: FINISH_VISCHUNK(o0, f40, f42)
+61: FINISH_VISCHUNK(o0, f42, f44)
+62: FINISH_VISCHUNK(o0, f44, f46)
+63: UNEVEN_VISCHUNK_LAST(o0, f46, f0)
+
+93: EX_LD_FP(LOAD(ldd, %o1, %f2), U1_g3_0_fp)
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f0, %f2, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U1_g3_8_fp)
bl,pn %xcc, 95f
add %o0, 8, %o0
- EX_LD_FP(LOAD(ldd, %o1, %f0))
+ EX_LD_FP(LOAD(ldd, %o1, %f0), U1_g3_0_fp)
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f2, %f0, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U1_g3_8_fp)
bge,pt %xcc, 93b
add %o0, 8, %o0
95: brz,pt %o2, 2f
mov %g1, %o1
-1: EX_LD_FP(LOAD(ldub, %o1, %o3))
+1: EX_LD_FP(LOAD(ldub, %o1, %o3), U1_o2_0_fp)
add %o1, 1, %o1
subcc %o2, 1, %o2
- EX_ST_FP(STORE(stb, %o3, %o0))
+ EX_ST_FP(STORE(stb, %o3, %o0), U1_o2_1_fp)
bne,pt %xcc, 1b
add %o0, 1, %o0
72: andn %o2, 0xf, %GLOBAL_SPARE
and %o2, 0xf, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %o5))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g1))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %o5), U1_gs_0)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g1), U1_gs_0)
subcc %GLOBAL_SPARE, 0x10, %GLOBAL_SPARE
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U1_gs_10)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + %o3))
+ EX_ST(STORE(stx, %g1, %o1 + %o3), U1_gs_8)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
- EX_LD(LOAD(ldx, %o1, %o5))
+ EX_LD(LOAD(ldx, %o1, %o5), U1_o2_0)
sub %o2, 0x8, %o2
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U1_o2_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
- EX_LD(LOAD(lduw, %o1, %o5))
+ EX_LD(LOAD(lduw, %o1, %o5), U1_o2_0)
sub %o2, 0x4, %o2
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U1_o2_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
sub %g0, %g1, %g1
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1, %o5))
+1: EX_LD(LOAD(ldub, %o1, %o5), U1_g1_0)
subcc %g1, 1, %g1
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U1_g1_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
8: mov 64, %o3
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), U1_o2_0)
sub %o3, %g1, %o3
andn %o2, 0x7, %GLOBAL_SPARE
sllx %g2, %g1, %g2
-1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3), U1_gs_0_o2_adj)
subcc %GLOBAL_SPARE, 0x8, %GLOBAL_SPARE
add %o1, 0x8, %o1
srlx %g3, %o3, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), U1_gs_8_o2_adj)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
bne,pn %XCC, 90f
sub %o0, %o1, %o3
-1: EX_LD(LOAD(lduw, %o1, %g1))
+1: EX_LD(LOAD(lduw, %o1, %g1), U1_o2_0)
subcc %o2, 4, %o2
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_ST(STORE(stw, %g1, %o1 + %o3), U1_o2_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
mov EX_RETVAL(%o4), %o0
.align 32
-90: EX_LD(LOAD(ldub, %o1, %g1))
+90: EX_LD(LOAD(ldub, %o1, %g1), U1_o2_0)
subcc %o2, 1, %o2
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_ST(STORE(stb, %g1, %o1 + %o3), U1_o2_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
*/
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_fp:
+ VISExitHalf
+ retl
+ nop
+ENTRY(U3_retl_o2_plus_g2_plus_g1_plus_1_fp)
+ add %g1, 1, %g1
+ add %g2, %g1, %g2
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_plus_g1_plus_1_fp)
+ENTRY(U3_retl_o2_plus_g2_fp)
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_fp)
+ENTRY(U3_retl_o2_plus_g2_plus_8_fp)
+ add %g2, 8, %g2
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_plus_8_fp)
+ENTRY(U3_retl_o2)
+ retl
+ mov %o2, %o0
+ENDPROC(U3_retl_o2)
+ENTRY(U3_retl_o2_plus_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(U3_retl_o2_plus_1)
+ENTRY(U3_retl_o2_plus_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(U3_retl_o2_plus_4)
+ENTRY(U3_retl_o2_plus_8)
+ retl
+ add %o2, 8, %o0
+ENDPROC(U3_retl_o2_plus_8)
+ENTRY(U3_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ retl
+ add %o2, %g1, %o0
+ENDPROC(U3_retl_o2_plus_g1_plus_1)
+ENTRY(U3_retl_o2_fp)
+ ba,pt %xcc, __restore_fp
+ mov %o2, %o0
+ENDPROC(U3_retl_o2_fp)
+ENTRY(U3_retl_o2_plus_o3_sll_6_plus_0x80_fp)
+ sll %o3, 6, %o3
+ add %o3, 0x80, %o3
+ ba,pt %xcc, __restore_fp
+ add %o2, %o3, %o0
+ENDPROC(U3_retl_o2_plus_o3_sll_6_plus_0x80_fp)
+ENTRY(U3_retl_o2_plus_o3_sll_6_plus_0x40_fp)
+ sll %o3, 6, %o3
+ add %o3, 0x40, %o3
+ ba,pt %xcc, __restore_fp
+ add %o2, %o3, %o0
+ENDPROC(U3_retl_o2_plus_o3_sll_6_plus_0x40_fp)
+ENTRY(U3_retl_o2_plus_GS_plus_0x10)
+ add GLOBAL_SPARE, 0x10, GLOBAL_SPARE
+ retl
+ add %o2, GLOBAL_SPARE, %o0
+ENDPROC(U3_retl_o2_plus_GS_plus_0x10)
+ENTRY(U3_retl_o2_plus_GS_plus_0x08)
+ add GLOBAL_SPARE, 0x08, GLOBAL_SPARE
+ retl
+ add %o2, GLOBAL_SPARE, %o0
+ENDPROC(U3_retl_o2_plus_GS_plus_0x08)
+ENTRY(U3_retl_o2_and_7_plus_GS)
+ and %o2, 7, %o2
+ retl
+ add %o2, GLOBAL_SPARE, %o2
+ENDPROC(U3_retl_o2_and_7_plus_GS)
+ENTRY(U3_retl_o2_and_7_plus_GS_plus_8)
+ add GLOBAL_SPARE, 8, GLOBAL_SPARE
+ and %o2, 7, %o2
+ retl
+ add %o2, GLOBAL_SPARE, %o2
+ENDPROC(U3_retl_o2_and_7_plus_GS_plus_8)
+#endif
+
.align 64
/* The cheetah's flexible spine, oversized liver, enlarged heart,
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST_FP(STORE(stb, %o3, %o1 + GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3), U3_retl_o2_plus_g2_plus_g1_plus_1)
+ EX_ST_FP(STORE(stb, %o3, %o1 + GLOBAL_SPARE), U3_retl_o2_plus_g2_plus_g1_plus_1)
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD_FP(LOAD(ldd, %o1, %f4))
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4), U3_retl_o2_plus_g2)
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U3_retl_o2_plus_g2_plus_8)
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f2
- EX_ST_FP(STORE(std, %f2, %o0))
+ EX_ST_FP(STORE(std, %f2, %o0), U3_retl_o2_plus_g2_plus_8)
bne,pt %icc, 1b
add %o0, 0x8, %o0
LOAD(prefetch, %o1 + 0x080, #one_read)
LOAD(prefetch, %o1 + 0x0c0, #one_read)
LOAD(prefetch, %o1 + 0x100, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x000, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x000, %f0), U3_retl_o2)
LOAD(prefetch, %o1 + 0x140, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2)
LOAD(prefetch, %o1 + 0x180, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2)
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2)
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2)
faligndata %f8, %f10, %f24
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2)
faligndata %f10, %f12, %f26
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2)
subcc GLOBAL_SPARE, 0x80, GLOBAL_SPARE
add %o1, 0x40, %o1
.align 64
1:
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f12, %f14, %f28
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f0, %f2, %f16
add %o0, 0x40, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2_plus_o3_sll_6_plus_0x40)
subcc %o3, 0x01, %o3
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f8, %f10, %f24
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f10, %f12, %f26
bg,pt %XCC, 1b
/* Finally we copy the last full 64-byte block. */
2:
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f12, %f14, %f28
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f8, %f10, %f24
cmp %g1, 0
be,pt %XCC, 1f
add %o0, 0x40, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2_plus_o3_sll_6_plus_0x40)
1: faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x40)
add %o0, 0x40, %o0
add %o1, 0x40, %o1
membar #Sync
sub %o2, %g2, %o2
be,a,pt %XCC, 1f
- EX_LD_FP(LOAD(ldd, %o1 + 0x00, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x00, %f0), U3_retl_o2_plus_g2)
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f2))
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f2), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f0, %f2, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U3_retl_o2_plus_g2_plus_8)
be,pn %XCC, 2f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f0), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f2, %f0, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U3_retl_o2_plus_g2_plus_8)
bne,pn %XCC, 1b
add %o0, 0x8, %o0
andcc %o2, 0x8, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x8, %o1
+ sub %o2, 8, %o2
1: andcc %o2, 0x4, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x4, %o1
+ sub %o2, 4, %o2
1: andcc %o2, 0x2, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(lduh, %o1, %o5))
- EX_ST(STORE(sth, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduh, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(sth, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x2, %o1
+ sub %o2, 2, %o2
1: andcc %o2, 0x1, %g0
be,pt %icc, 85f
nop
- EX_LD(LOAD(ldub, %o1, %o5))
+ EX_LD(LOAD(ldub, %o1, %o5), U3_retl_o2)
ba,pt %xcc, 85f
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U3_retl_o2)
.align 64
70: /* 16 < len <= 64 */
andn %o2, 0xf, GLOBAL_SPARE
and %o2, 0xf, %o2
1: subcc GLOBAL_SPARE, 0x10, GLOBAL_SPARE
- EX_LD(LOAD(ldx, %o1 + 0x00, %o5))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g1))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1 + 0x00, %o5), U3_retl_o2_plus_GS_plus_0x10)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g1), U3_retl_o2_plus_GS_plus_0x10)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2_plus_GS_plus_0x10)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + %o3))
+ EX_ST(STORE(stx, %g1, %o1 + %o3), U3_retl_o2_plus_GS_plus_0x08)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x8, %o2
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1, %o5), U3_retl_o2_plus_8)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2_plus_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %o5), U3_retl_o2_plus_4)
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U3_retl_o2_plus_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
sub %o2, %g1, %o2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %o1, %o5))
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %o5), U3_retl_o2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U3_retl_o2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
8: mov 64, %o3
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), U3_retl_o2)
sub %o3, %g1, %o3
andn %o2, 0x7, GLOBAL_SPARE
sllx %g2, %g1, %g2
-1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3), U3_retl_o2_and_7_plus_GS)
subcc GLOBAL_SPARE, 0x8, GLOBAL_SPARE
add %o1, 0x8, %o1
srlx %g3, %o3, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), U3_retl_o2_and_7_plus_GS_plus_8)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %g1), U3_retl_o2_plus_4)
+ EX_ST(STORE(stw, %g1, %o1 + %o3), U3_retl_o2_plus_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %g1), U3_retl_o2_plus_1)
+ EX_ST(STORE(stb, %g1, %o1 + %o3), U3_retl_o2_plus_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
#define XCC xcc
-#define EX(x,y) \
+#define EX(x,y,z) \
98: x,y; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, z; \
.text; \
.align 4;
+#define EX_O4(x,y) EX(x,y,__retl_o4_plus_8)
+#define EX_O2_4(x,y) EX(x,y,__retl_o2_plus_4)
+#define EX_O2_1(x,y) EX(x,y,__retl_o2_plus_1)
+
.register %g2,#scratch
.register %g3,#scratch
.text
+__retl_o4_plus_8:
+ add %o4, %o2, %o4
+ retl
+ add %o4, 8, %o0
+__retl_o2_plus_4:
+ retl
+ add %o2, 4, %o0
+__retl_o2_plus_1:
+ retl
+ add %o2, 1, %o0
+
.align 32
/* Don't try to get too fancy here, just nice and
andn %o2, 0x7, %o4
and %o2, 0x7, %o2
1: subcc %o4, 0x8, %o4
- EX(ldxa [%o1] %asi, %o5)
- EX(stxa %o5, [%o0] %asi)
+ EX_O4(ldxa [%o1] %asi, %o5)
+ EX_O4(stxa %o5, [%o0] %asi)
add %o1, 0x8, %o1
bgu,pt %XCC, 1b
add %o0, 0x8, %o0
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX(lduwa [%o1] %asi, %o5)
- EX(stwa %o5, [%o0] %asi)
+ EX_O2_4(lduwa [%o1] %asi, %o5)
+ EX_O2_4(stwa %o5, [%o0] %asi)
add %o1, 0x4, %o1
add %o0, 0x4, %o0
1: cmp %o2, 0
82:
subcc %o2, 4, %o2
- EX(lduwa [%o1] %asi, %g1)
- EX(stwa %g1, [%o0] %asi)
+ EX_O2_4(lduwa [%o1] %asi, %g1)
+ EX_O2_4(stwa %g1, [%o0] %asi)
add %o1, 4, %o1
bgu,pt %XCC, 82b
add %o0, 4, %o0
.align 32
90:
subcc %o2, 1, %o2
- EX(lduba [%o1] %asi, %g1)
- EX(stba %g1, [%o0] %asi)
+ EX_O2_1(lduba [%o1] %asi, %g1)
+ EX_O2_1(stba %g1, [%o0] %asi)
add %o1, 1, %o1
bgu,pt %XCC, 90b
add %o0, 1, %o0
+++ /dev/null
-/* user_fixup.c: Fix up user copy faults.
- *
- * Copyright (C) 2004 David S. Miller <davem@redhat.com>
- */
-
-#include <linux/compiler.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-
-#include <asm/uaccess.h>
-
-/* Calculating the exact fault address when using
- * block loads and stores can be very complicated.
- *
- * Instead of trying to be clever and handling all
- * of the cases, just fix things up simply here.
- */
-
-static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset)
-{
- unsigned long fault_addr = current_thread_info()->fault_address;
- unsigned long end = start + size;
-
- if (fault_addr < start || fault_addr >= end) {
- *offset = 0;
- } else {
- *offset = fault_addr - start;
- size = end - fault_addr;
- }
- return size;
-}
-
-unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
-{
- unsigned long offset;
-
- size = compute_size((unsigned long) from, size, &offset);
- if (likely(size))
- memset(to + offset, 0, size);
-
- return size;
-}
-EXPORT_SYMBOL(copy_from_user_fixup);
-
-unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size)
-{
- unsigned long offset;
-
- return compute_size((unsigned long) to, size, &offset);
-}
-EXPORT_SYMBOL(copy_to_user_fixup);
-
-unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size)
-{
- unsigned long fault_addr = current_thread_info()->fault_address;
- unsigned long start = (unsigned long) to;
- unsigned long end = start + size;
-
- if (fault_addr >= start && fault_addr < end)
- return end - fault_addr;
-
- start = (unsigned long) from;
- end = start + size;
- if (fault_addr >= start && fault_addr < end)
- return end - fault_addr;
-
- return size;
-}
-EXPORT_SYMBOL(copy_in_user_fixup);
return (tag == (vaddr >> 22));
}
+static void flush_tsb_kernel_range_scan(unsigned long start, unsigned long end)
+{
+ unsigned long idx;
+
+ for (idx = 0; idx < KERNEL_TSB_NENTRIES; idx++) {
+ struct tsb *ent = &swapper_tsb[idx];
+ unsigned long match = idx << 13;
+
+ match |= (ent->tag << 22);
+ if (match >= start && match < end)
+ ent->tag = (1UL << TSB_TAG_INVALID_BIT);
+ }
+}
+
/* TSB flushes need only occur on the processor initiating the address
* space modification, not on each cpu the address space has run on.
* Only the TLB flush needs that treatment.
{
unsigned long v;
+ if ((end - start) >> PAGE_SHIFT >= 2 * KERNEL_TSB_NENTRIES)
+ return flush_tsb_kernel_range_scan(start, end);
+
for (v = start; v < end; v += PAGE_SIZE) {
unsigned long hash = tsb_hash(v, PAGE_SHIFT,
KERNEL_TSB_NENTRIES);
.text
.align 32
.globl __flush_tlb_mm
-__flush_tlb_mm: /* 18 insns */
+__flush_tlb_mm: /* 19 insns */
/* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
ldxa [%o1] ASI_DMMU, %g2
cmp %g2, %o0
.align 32
.globl __flush_tlb_pending
-__flush_tlb_pending: /* 26 insns */
+__flush_tlb_pending: /* 27 insns */
/* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
rdpr %pstate, %g7
sllx %o1, 3, %o1
.align 32
.globl __flush_tlb_kernel_range
-__flush_tlb_kernel_range: /* 16 insns */
+__flush_tlb_kernel_range: /* 31 insns */
/* %o0=start, %o1=end */
cmp %o0, %o1
be,pn %xcc, 2f
+ sub %o1, %o0, %o3
+ srlx %o3, 18, %o4
+ brnz,pn %o4, __spitfire_flush_tlb_kernel_range_slow
sethi %hi(PAGE_SIZE), %o4
- sub %o1, %o0, %o3
sub %o3, %o4, %o3
or %o0, 0x20, %o0 ! Nucleus
1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
retl
nop
nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+__spitfire_flush_tlb_kernel_range_slow:
+ mov 63 * 8, %o4
+1: ldxa [%o4] ASI_ITLB_DATA_ACCESS, %o3
+ andcc %o3, 0x40, %g0 /* _PAGE_L_4U */
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %o3
+ stxa %g0, [%o3] ASI_IMMU
+ stxa %g0, [%o4] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+2: ldxa [%o4] ASI_DTLB_DATA_ACCESS, %o3
+ andcc %o3, 0x40, %g0
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %o3
+ stxa %g0, [%o3] ASI_DMMU
+ stxa %g0, [%o4] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2: sub %o4, 8, %o4
+ brgez,pt %o4, 1b
+ nop
+ retl
+ nop
__spitfire_flush_tlb_mm_slow:
rdpr %pstate, %g1
retl
wrpr %g7, 0x0, %pstate
+__cheetah_flush_tlb_kernel_range: /* 31 insns */
+ /* %o0=start, %o1=end */
+ cmp %o0, %o1
+ be,pn %xcc, 2f
+ sub %o1, %o0, %o3
+ srlx %o3, 18, %o4
+ brnz,pn %o4, 3f
+ sethi %hi(PAGE_SIZE), %o4
+ sub %o3, %o4, %o3
+ or %o0, 0x20, %o0 ! Nucleus
+1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
+ stxa %g0, [%o0 + %o3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %o3, 1b
+ sub %o3, %o4, %o3
+2: sethi %hi(KERNBASE), %o3
+ flush %o3
+ retl
+ nop
+3: mov 0x80, %o4
+ stxa %g0, [%o4] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g0, [%o4] ASI_IMMU_DEMAP
+ membar #Sync
+ retl
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
#ifdef DCACHE_ALIASING_POSSIBLE
__cheetah_flush_dcache_page: /* 11 insns */
sethi %hi(PAGE_OFFSET), %g1
ret
restore
-__hypervisor_flush_tlb_mm: /* 10 insns */
+__hypervisor_flush_tlb_mm: /* 19 insns */
mov %o0, %o2 /* ARG2: mmu context */
mov 0, %o0 /* ARG0: CPU lists unimplemented */
mov 0, %o1 /* ARG1: CPU lists unimplemented */
mov HV_MMU_ALL, %o3 /* ARG3: flags */
mov HV_FAST_MMU_DEMAP_CTX, %o5
ta HV_FAST_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_FAST_MMU_DEMAP_CTX, %o1
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o5
+ jmpl %o5 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_page: /* 11 insns */
+__hypervisor_flush_tlb_page: /* 22 insns */
/* %o0 = context, %o1 = vaddr */
mov %o0, %g2
mov %o1, %o0 /* ARG0: vaddr + IMMU-bit */
srlx %o0, PAGE_SHIFT, %o0
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_pending: /* 16 insns */
+__hypervisor_flush_tlb_pending: /* 27 insns */
/* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
sllx %o1, 3, %g1
mov %o2, %g2
srlx %o0, PAGE_SHIFT, %o0
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
brnz,pt %g1, 1b
nop
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_kernel_range: /* 16 insns */
+__hypervisor_flush_tlb_kernel_range: /* 31 insns */
/* %o0=start, %o1=end */
cmp %o0, %o1
be,pn %xcc, 2f
- sethi %hi(PAGE_SIZE), %g3
- mov %o0, %g1
- sub %o1, %g1, %g2
+ sub %o1, %o0, %g2
+ srlx %g2, 18, %g3
+ brnz,pn %g3, 4f
+ mov %o0, %g1
+ sethi %hi(PAGE_SIZE), %g3
sub %g2, %g3, %g2
1: add %g1, %g2, %o0 /* ARG0: virtual address */
mov 0, %o1 /* ARG1: mmu context */
mov HV_MMU_ALL, %o2 /* ARG2: flags */
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 3f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
brnz,pt %g2, 1b
sub %g2, %g3, %g2
2: retl
nop
+3: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+4: mov 0, %o0 /* ARG0: CPU lists unimplemented */
+ mov 0, %o1 /* ARG1: CPU lists unimplemented */
+ mov 0, %o2 /* ARG2: mmu context == nucleus */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ brnz,pn %o0, 3b
+ mov HV_FAST_MMU_DEMAP_CTX, %o1
+ retl
+ nop
#ifdef DCACHE_ALIASING_POSSIBLE
/* XXX Niagara and friends have an 8K cache, so no aliasing is
retl
nop
- .globl cheetah_patch_cachetlbops
-cheetah_patch_cachetlbops:
- save %sp, -128, %sp
-
- sethi %hi(__flush_tlb_mm), %o0
- or %o0, %lo(__flush_tlb_mm), %o0
- sethi %hi(__cheetah_flush_tlb_mm), %o1
- or %o1, %lo(__cheetah_flush_tlb_mm), %o1
- call tlb_patch_one
- mov 19, %o2
-
- sethi %hi(__flush_tlb_page), %o0
- or %o0, %lo(__flush_tlb_page), %o0
- sethi %hi(__cheetah_flush_tlb_page), %o1
- or %o1, %lo(__cheetah_flush_tlb_page), %o1
- call tlb_patch_one
- mov 22, %o2
-
- sethi %hi(__flush_tlb_pending), %o0
- or %o0, %lo(__flush_tlb_pending), %o0
- sethi %hi(__cheetah_flush_tlb_pending), %o1
- or %o1, %lo(__cheetah_flush_tlb_pending), %o1
- call tlb_patch_one
- mov 27, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- sethi %hi(__flush_dcache_page), %o0
- or %o0, %lo(__flush_dcache_page), %o0
- sethi %hi(__cheetah_flush_dcache_page), %o1
- or %o1, %lo(__cheetah_flush_dcache_page), %o1
- call tlb_patch_one
- mov 11, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- ret
- restore
-
#ifdef CONFIG_SMP
/* These are all called by the slaves of a cross call, at
* trap level 1, with interrupts fully disabled.
*/
.align 32
.globl xcall_flush_tlb_mm
-xcall_flush_tlb_mm: /* 21 insns */
+xcall_flush_tlb_mm: /* 24 insns */
mov PRIMARY_CONTEXT, %g2
ldxa [%g2] ASI_DMMU, %g3
srlx %g3, CTX_PGSZ1_NUC_SHIFT, %g4
nop
nop
nop
+ nop
+ nop
+ nop
.globl xcall_flush_tlb_page
-xcall_flush_tlb_page: /* 17 insns */
+xcall_flush_tlb_page: /* 20 insns */
/* %g5=context, %g1=vaddr */
mov PRIMARY_CONTEXT, %g4
ldxa [%g4] ASI_DMMU, %g2
retry
nop
nop
+ nop
+ nop
+ nop
.globl xcall_flush_tlb_kernel_range
-xcall_flush_tlb_kernel_range: /* 25 insns */
+xcall_flush_tlb_kernel_range: /* 44 insns */
sethi %hi(PAGE_SIZE - 1), %g2
or %g2, %lo(PAGE_SIZE - 1), %g2
andn %g1, %g2, %g1
andn %g7, %g2, %g7
sub %g7, %g1, %g3
- add %g2, 1, %g2
+ srlx %g3, 18, %g2
+ brnz,pn %g2, 2f
+ add %g2, 1, %g2
sub %g3, %g2, %g3
or %g1, 0x20, %g1 ! Nucleus
1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
brnz,pt %g3, 1b
sub %g3, %g2, %g3
retry
- nop
- nop
+2: mov 63 * 8, %g1
+1: ldxa [%g1] ASI_ITLB_DATA_ACCESS, %g2
+ andcc %g2, 0x40, %g0 /* _PAGE_L_4U */
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %g2
+ stxa %g0, [%g2] ASI_IMMU
+ stxa %g0, [%g1] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+2: ldxa [%g1] ASI_DTLB_DATA_ACCESS, %g2
+ andcc %g2, 0x40, %g0
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %g2
+ stxa %g0, [%g2] ASI_DMMU
+ stxa %g0, [%g1] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2: sub %g1, 8, %g1
+ brgez,pt %g1, 1b
+ nop
+ retry
nop
nop
nop
retry
+__cheetah_xcall_flush_tlb_kernel_range: /* 44 insns */
+ sethi %hi(PAGE_SIZE - 1), %g2
+ or %g2, %lo(PAGE_SIZE - 1), %g2
+ andn %g1, %g2, %g1
+ andn %g7, %g2, %g7
+ sub %g7, %g1, %g3
+ srlx %g3, 18, %g2
+ brnz,pn %g2, 2f
+ add %g2, 1, %g2
+ sub %g3, %g2, %g3
+ or %g1, 0x20, %g1 ! Nucleus
+1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %g3, 1b
+ sub %g3, %g2, %g3
+ retry
+2: mov 0x80, %g2
+ stxa %g0, [%g2] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g0, [%g2] ASI_IMMU_DEMAP
+ membar #Sync
+ retry
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
#ifdef DCACHE_ALIASING_POSSIBLE
.align 32
.globl xcall_flush_dcache_page_cheetah
ba,a,pt %xcc, rtrap
.globl __hypervisor_xcall_flush_tlb_mm
-__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
+__hypervisor_xcall_flush_tlb_mm: /* 24 insns */
/* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
mov %o0, %g2
mov %o1, %g3
mov HV_FAST_MMU_DEMAP_CTX, %o5
ta HV_FAST_TRAP
mov HV_FAST_MMU_DEMAP_CTX, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,pn %o0, 1f
mov %o0, %g5
mov %g2, %o0
mov %g3, %o1
mov %g7, %o5
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
.globl __hypervisor_xcall_flush_tlb_page
-__hypervisor_xcall_flush_tlb_page: /* 17 insns */
+__hypervisor_xcall_flush_tlb_page: /* 20 insns */
/* %g5=ctx, %g1=vaddr */
mov %o0, %g2
mov %o1, %g3
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,a,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,a,pn %o0, 1f
mov %o0, %g5
mov %g2, %o0
mov %g3, %o1
mov %g4, %o2
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
.globl __hypervisor_xcall_flush_tlb_kernel_range
-__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
+__hypervisor_xcall_flush_tlb_kernel_range: /* 44 insns */
/* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
sethi %hi(PAGE_SIZE - 1), %g2
or %g2, %lo(PAGE_SIZE - 1), %g2
andn %g1, %g2, %g1
andn %g7, %g2, %g7
sub %g7, %g1, %g3
+ srlx %g3, 18, %g7
add %g2, 1, %g2
sub %g3, %g2, %g3
mov %o0, %g2
mov %o1, %g4
- mov %o2, %g7
+ brnz,pn %g7, 2f
+ mov %o2, %g7
1: add %g1, %g3, %o0 /* ARG0: virtual address */
mov 0, %o1 /* ARG1: mmu context */
mov HV_MMU_ALL, %o2 /* ARG2: flags */
ta HV_MMU_UNMAP_ADDR_TRAP
mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,pn %o0, 1f
mov %o0, %g5
sethi %hi(PAGE_SIZE), %o2
brnz,pt %g3, 1b
sub %g3, %o2, %g3
- mov %g2, %o0
+5: mov %g2, %o0
mov %g4, %o1
mov %g7, %o2
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
+2: mov %o3, %g1
+ mov %o5, %g3
+ mov 0, %o0 /* ARG0: CPU lists unimplemented */
+ mov 0, %o1 /* ARG1: CPU lists unimplemented */
+ mov 0, %o2 /* ARG2: mmu context == nucleus */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ mov %g1, %o3
+ brz,pt %o0, 5b
+ mov %g3, %o5
+ mov HV_FAST_MMU_DEMAP_CTX, %g6
+ ba,pt %xcc, 1b
+ clr %g5
/* These just get rescheduled to PIL vectors. */
.globl xcall_call_function
#endif /* CONFIG_SMP */
+ .globl cheetah_patch_cachetlbops
+cheetah_patch_cachetlbops:
+ save %sp, -128, %sp
+
+ sethi %hi(__flush_tlb_mm), %o0
+ or %o0, %lo(__flush_tlb_mm), %o0
+ sethi %hi(__cheetah_flush_tlb_mm), %o1
+ or %o1, %lo(__cheetah_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 19, %o2
+
+ sethi %hi(__flush_tlb_page), %o0
+ or %o0, %lo(__flush_tlb_page), %o0
+ sethi %hi(__cheetah_flush_tlb_page), %o1
+ or %o1, %lo(__cheetah_flush_tlb_page), %o1
+ call tlb_patch_one
+ mov 22, %o2
+
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__cheetah_flush_tlb_pending), %o1
+ or %o1, %lo(__cheetah_flush_tlb_pending), %o1
+ call tlb_patch_one
+ mov 27, %o2
+
+ sethi %hi(__flush_tlb_kernel_range), %o0
+ or %o0, %lo(__flush_tlb_kernel_range), %o0
+ sethi %hi(__cheetah_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__cheetah_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 31, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ sethi %hi(__flush_dcache_page), %o0
+ or %o0, %lo(__flush_dcache_page), %o0
+ sethi %hi(__cheetah_flush_dcache_page), %o1
+ or %o1, %lo(__cheetah_flush_dcache_page), %o1
+ call tlb_patch_one
+ mov 11, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+#ifdef CONFIG_SMP
+ sethi %hi(xcall_flush_tlb_kernel_range), %o0
+ or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
+ sethi %hi(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 44, %o2
+#endif /* CONFIG_SMP */
+
+ ret
+ restore
.globl hypervisor_patch_cachetlbops
hypervisor_patch_cachetlbops:
sethi %hi(__hypervisor_flush_tlb_mm), %o1
or %o1, %lo(__hypervisor_flush_tlb_mm), %o1
call tlb_patch_one
- mov 10, %o2
+ mov 19, %o2
sethi %hi(__flush_tlb_page), %o0
or %o0, %lo(__flush_tlb_page), %o0
sethi %hi(__hypervisor_flush_tlb_page), %o1
or %o1, %lo(__hypervisor_flush_tlb_page), %o1
call tlb_patch_one
- mov 11, %o2
+ mov 22, %o2
sethi %hi(__flush_tlb_pending), %o0
or %o0, %lo(__flush_tlb_pending), %o0
sethi %hi(__hypervisor_flush_tlb_pending), %o1
or %o1, %lo(__hypervisor_flush_tlb_pending), %o1
call tlb_patch_one
- mov 16, %o2
+ mov 27, %o2
sethi %hi(__flush_tlb_kernel_range), %o0
or %o0, %lo(__flush_tlb_kernel_range), %o0
sethi %hi(__hypervisor_flush_tlb_kernel_range), %o1
or %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
call tlb_patch_one
- mov 16, %o2
+ mov 31, %o2
#ifdef DCACHE_ALIASING_POSSIBLE
sethi %hi(__flush_dcache_page), %o0
sethi %hi(__hypervisor_xcall_flush_tlb_mm), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
call tlb_patch_one
- mov 21, %o2
+ mov 24, %o2
sethi %hi(xcall_flush_tlb_page), %o0
or %o0, %lo(xcall_flush_tlb_page), %o0
sethi %hi(__hypervisor_xcall_flush_tlb_page), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_page), %o1
call tlb_patch_one
- mov 17, %o2
+ mov 20, %o2
sethi %hi(xcall_flush_tlb_kernel_range), %o0
or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
sethi %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
call tlb_patch_one
- mov 25, %o2
+ mov 44, %o2
#endif /* CONFIG_SMP */
ret
int (*get_lpage_level)(void);
bool (*rdtscp_supported)(void);
bool (*invpcid_supported)(void);
- void (*adjust_tsc_offset_guest)(struct kvm_vcpu *vcpu, s64 adjustment);
void (*set_tdp_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3);
void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
- u64 (*read_l1_tsc)(struct kvm_vcpu *vcpu, u64 host_tsc);
-
void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2);
int (*check_intercept)(struct kvm_vcpu *vcpu,
/* Decode and fetch the destination operand: register or memory. */
rc = decode_operand(ctxt, &ctxt->dst, (ctxt->d >> DstShift) & OpMask);
- if (ctxt->rip_relative)
+ if (ctxt->rip_relative && likely(ctxt->memopp))
ctxt->memopp->addr.mem.ea = address_mask(ctxt,
ctxt->memopp->addr.mem.ea + ctxt->_eip);
mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
}
-static void svm_adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, s64 adjustment)
-{
- struct vcpu_svm *svm = to_svm(vcpu);
-
- svm->vmcb->control.tsc_offset += adjustment;
- if (is_guest_mode(vcpu))
- svm->nested.hsave->control.tsc_offset += adjustment;
- else
- trace_kvm_write_tsc_offset(vcpu->vcpu_id,
- svm->vmcb->control.tsc_offset - adjustment,
- svm->vmcb->control.tsc_offset);
-
- mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
-}
-
static void avic_init_vmcb(struct vcpu_svm *svm)
{
struct vmcb *vmcb = svm->vmcb;
return 0;
}
-static u64 svm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc)
-{
- struct vmcb *vmcb = get_host_vmcb(to_svm(vcpu));
- return vmcb->control.tsc_offset + host_tsc;
-}
-
static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
{
struct vcpu_svm *svm = to_svm(vcpu);
.has_wbinvd_exit = svm_has_wbinvd_exit,
.write_tsc_offset = svm_write_tsc_offset,
- .adjust_tsc_offset_guest = svm_adjust_tsc_offset_guest,
- .read_l1_tsc = svm_read_l1_tsc,
.set_tdp_cr3 = set_tdp_cr3,
*/
struct loaded_vmcs {
struct vmcs *vmcs;
+ struct vmcs *shadow_vmcs;
int cpu;
int launched;
struct list_head loaded_vmcss_on_cpu_link;
* memory during VMXOFF, VMCLEAR, VMPTRLD.
*/
struct vmcs12 *cached_vmcs12;
- struct vmcs *current_shadow_vmcs;
/*
* Indicates if the shadow vmcs must be updated with the
* data hold by vmcs12
/* vmcs02_list cache of VMCSs recently used to run L2 guests */
struct list_head vmcs02_pool;
int vmcs02_num;
- u64 vmcs01_tsc_offset;
bool change_vmcs01_virtual_x2apic_mode;
/* L2 must run next, and mustn't decide to exit to L1. */
bool nested_run_pending;
static inline void loaded_vmcs_init(struct loaded_vmcs *loaded_vmcs)
{
vmcs_clear(loaded_vmcs->vmcs);
+ if (loaded_vmcs->shadow_vmcs && loaded_vmcs->launched)
+ vmcs_clear(loaded_vmcs->shadow_vmcs);
loaded_vmcs->cpu = -1;
loaded_vmcs->launched = 0;
}
return kvm_scale_tsc(vcpu, host_tsc) + tsc_offset;
}
-/*
- * Like guest_read_tsc, but always returns L1's notion of the timestamp
- * counter, even if a nested guest (L2) is currently running.
- */
-static u64 vmx_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc)
-{
- u64 tsc_offset;
-
- tsc_offset = is_guest_mode(vcpu) ?
- to_vmx(vcpu)->nested.vmcs01_tsc_offset :
- vmcs_read64(TSC_OFFSET);
- return host_tsc + tsc_offset;
-}
-
/*
* writes 'offset' into guest's timestamp counter offset register
*/
* to the newly set TSC to get L2's TSC.
*/
struct vmcs12 *vmcs12;
- to_vmx(vcpu)->nested.vmcs01_tsc_offset = offset;
/* recalculate vmcs02.TSC_OFFSET: */
vmcs12 = get_vmcs12(vcpu);
vmcs_write64(TSC_OFFSET, offset +
}
}
-static void vmx_adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, s64 adjustment)
-{
- u64 offset = vmcs_read64(TSC_OFFSET);
-
- vmcs_write64(TSC_OFFSET, offset + adjustment);
- if (is_guest_mode(vcpu)) {
- /* Even when running L2, the adjustment needs to apply to L1 */
- to_vmx(vcpu)->nested.vmcs01_tsc_offset += adjustment;
- } else
- trace_kvm_write_tsc_offset(vcpu->vcpu_id, offset,
- offset + adjustment);
-}
-
static bool guest_cpuid_has_vmx(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best = kvm_find_cpuid_entry(vcpu, 1, 0);
loaded_vmcs_clear(loaded_vmcs);
free_vmcs(loaded_vmcs->vmcs);
loaded_vmcs->vmcs = NULL;
+ WARN_ON(loaded_vmcs->shadow_vmcs != NULL);
}
static void free_kvm_area(void)
if (!item)
return NULL;
item->vmcs02.vmcs = alloc_vmcs();
+ item->vmcs02.shadow_vmcs = NULL;
if (!item->vmcs02.vmcs) {
kfree(item);
return NULL;
shadow_vmcs->revision_id |= (1u << 31);
/* init shadow vmcs */
vmcs_clear(shadow_vmcs);
- vmx->nested.current_shadow_vmcs = shadow_vmcs;
+ vmx->vmcs01.shadow_vmcs = shadow_vmcs;
}
INIT_LIST_HEAD(&(vmx->nested.vmcs02_pool));
free_page((unsigned long)vmx->nested.msr_bitmap);
vmx->nested.msr_bitmap = NULL;
}
- if (enable_shadow_vmcs)
- free_vmcs(vmx->nested.current_shadow_vmcs);
+ if (enable_shadow_vmcs) {
+ vmcs_clear(vmx->vmcs01.shadow_vmcs);
+ free_vmcs(vmx->vmcs01.shadow_vmcs);
+ vmx->vmcs01.shadow_vmcs = NULL;
+ }
kfree(vmx->nested.cached_vmcs12);
/* Unpin physical memory we referred to in current vmcs02 */
if (vmx->nested.apic_access_page) {
int i;
unsigned long field;
u64 field_value;
- struct vmcs *shadow_vmcs = vmx->nested.current_shadow_vmcs;
+ struct vmcs *shadow_vmcs = vmx->vmcs01.shadow_vmcs;
const unsigned long *fields = shadow_read_write_fields;
const int num_fields = max_shadow_read_write_fields;
int i, q;
unsigned long field;
u64 field_value = 0;
- struct vmcs *shadow_vmcs = vmx->nested.current_shadow_vmcs;
+ struct vmcs *shadow_vmcs = vmx->vmcs01.shadow_vmcs;
vmcs_load(shadow_vmcs);
vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL,
SECONDARY_EXEC_SHADOW_VMCS);
vmcs_write64(VMCS_LINK_POINTER,
- __pa(vmx->nested.current_shadow_vmcs));
+ __pa(vmx->vmcs01.shadow_vmcs));
vmx->nested.sync_shadow_vmcs = true;
}
}
types = (vmx->nested.nested_vmx_ept_caps >> VMX_EPT_EXTENT_SHIFT) & 6;
- if (!(types & (1UL << type))) {
+ if (type >= 32 || !(types & (1 << type))) {
nested_vmx_failValid(vcpu,
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
skip_emulated_instruction(vcpu);
types = (vmx->nested.nested_vmx_vpid_caps >> 8) & 0x7;
- if (!(types & (1UL << type))) {
+ if (type >= 32 || !(types & (1 << type))) {
nested_vmx_failValid(vcpu,
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
skip_emulated_instruction(vcpu);
vmx->loaded_vmcs = &vmx->vmcs01;
vmx->loaded_vmcs->vmcs = alloc_vmcs();
+ vmx->loaded_vmcs->shadow_vmcs = NULL;
if (!vmx->loaded_vmcs->vmcs)
goto free_msrs;
if (!vmm_exclusive)
if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETING)
vmcs_write64(TSC_OFFSET,
- vmx->nested.vmcs01_tsc_offset + vmcs12->tsc_offset);
+ vcpu->arch.tsc_offset + vmcs12->tsc_offset);
else
- vmcs_write64(TSC_OFFSET, vmx->nested.vmcs01_tsc_offset);
+ vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset);
if (kvm_has_tsc_control)
decache_tsc_multiplier(vmx);
enter_guest_mode(vcpu);
- vmx->nested.vmcs01_tsc_offset = vmcs_read64(TSC_OFFSET);
-
if (!(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS))
vmx->nested.vmcs01_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL);
load_vmcs12_host_state(vcpu, vmcs12);
/* Update any VMCS fields that might have changed while L2 ran */
- vmcs_write64(TSC_OFFSET, vmx->nested.vmcs01_tsc_offset);
+ vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset);
if (vmx->hv_deadline_tsc == -1)
vmcs_clear_bits(PIN_BASED_VM_EXEC_CONTROL,
PIN_BASED_VMX_PREEMPTION_TIMER);
.has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
.write_tsc_offset = vmx_write_tsc_offset,
- .adjust_tsc_offset_guest = vmx_adjust_tsc_offset_guest,
- .read_l1_tsc = vmx_read_l1_tsc,
.set_tdp_cr3 = vmx_set_cr3,
u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc)
{
- return kvm_x86_ops->read_l1_tsc(vcpu, kvm_scale_tsc(vcpu, host_tsc));
+ return vcpu->arch.tsc_offset + kvm_scale_tsc(vcpu, host_tsc);
}
EXPORT_SYMBOL_GPL(kvm_read_l1_tsc);
static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu,
s64 adjustment)
{
- kvm_x86_ops->adjust_tsc_offset_guest(vcpu, adjustment);
+ kvm_vcpu_write_tsc_offset(vcpu, vcpu->arch.tsc_offset + adjustment);
}
static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)
if (vcpu->arch.tsc_scaling_ratio != kvm_default_tsc_scaling_ratio)
WARN_ON(adjustment < 0);
adjustment = kvm_scale_tsc(vcpu, (u64) adjustment);
- kvm_x86_ops->adjust_tsc_offset_guest(vcpu, adjustment);
+ adjust_tsc_offset_guest(vcpu, adjustment);
}
#ifdef CONFIG_X86_64
/* Drop writes to this legacy MSR -- see rdmsr
* counterpart for further detail.
*/
- vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", msr, data);
+ vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data 0x%llx\n", msr, data);
break;
case MSR_AMD64_OSVW_ID_LENGTH:
if (!guest_cpuid_has_osvw(vcpu))
if (kvm_pmu_is_valid_msr(vcpu, msr))
return kvm_pmu_set_msr(vcpu, msr_info);
if (!ignore_msrs) {
- vcpu_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n",
+ vcpu_unimpl(vcpu, "unhandled wrmsr: 0x%x data 0x%llx\n",
msr, data);
return 1;
} else {
- vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n",
+ vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data 0x%llx\n",
msr, data);
break;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ void *wbinvd_dirty_mask = vcpu->arch.wbinvd_dirty_mask;
+
kvmclock_reset(vcpu);
- free_cpumask_var(vcpu->arch.wbinvd_dirty_mask);
kvm_x86_ops->vcpu_free(vcpu);
+ free_cpumask_var(wbinvd_dirty_mask);
}
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
static int init_vq(struct virtio_blk *vblk)
{
- int err = 0;
+ int err;
int i;
vq_callback_t **callbacks;
const char **names;
if (err)
num_vqs = 1;
- vblk->vqs = kmalloc(sizeof(*vblk->vqs) * num_vqs, GFP_KERNEL);
+ vblk->vqs = kmalloc_array(num_vqs, sizeof(*vblk->vqs), GFP_KERNEL);
if (!vblk->vqs)
return -ENOMEM;
- names = kmalloc(sizeof(*names) * num_vqs, GFP_KERNEL);
- callbacks = kmalloc(sizeof(*callbacks) * num_vqs, GFP_KERNEL);
- vqs = kmalloc(sizeof(*vqs) * num_vqs, GFP_KERNEL);
+ names = kmalloc_array(num_vqs, sizeof(*names), GFP_KERNEL);
+ callbacks = kmalloc_array(num_vqs, sizeof(*callbacks), GFP_KERNEL);
+ vqs = kmalloc_array(num_vqs, sizeof(*vqs), GFP_KERNEL);
if (!names || !callbacks || !vqs) {
err = -ENOMEM;
goto out;
BT_DBG("HCI device registered (hdev %p)", hdev);
dev_set_drvdata(&pdev->dev, hst);
- return err;
+ return 0;
}
static int bt_ti_remove(struct platform_device *pdev)
},
.driver_data = &acpi_active_low,
},
+ { /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
+ .ident = "Lenovo ThinkPad 8",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+ },
+ .driver_data = &acpi_active_low,
+ },
{ }
};
continue;
}
- if (rc < TPM_HEADER_SIZE)
- return -EFAULT;
-
if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
dev_info(&chip->dev,
"TPM is disabled/deactivated (0x%X)\n", rc);
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
+ spin_unlock_irq(&port->inbuf_lock);
/* Remove buffers we queued up for the Host to send us data in. */
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->inbuf_lock);
+ do {
+ spin_lock_irq(&port->inbuf_lock);
+ buf = virtqueue_detach_unused_buf(port->in_vq);
+ spin_unlock_irq(&port->inbuf_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
+ spin_unlock_irq(&port->outvq_lock);
/* Free pending buffers from the out-queue. */
- while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->outvq_lock);
+ do {
+ spin_lock_irq(&port->outvq_lock);
+ buf = virtqueue_detach_unused_buf(port->out_vq);
+ spin_unlock_irq(&port->outvq_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
}
/*
#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
-#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16)
+#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)
#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
-#define fwnet_set_hdr_lf(lf) ((lf) << 30)
+#define fwnet_set_hdr_lf(lf) ((lf) << 30)
#define fwnet_set_hdr_ether_type(et) (et)
-#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16)
+#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)
#define fwnet_set_hdr_fg_off(fgo) (fgo)
#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
int retval;
u16 ether_type;
+ if (len <= RFC2374_UNFRAG_HDR_SIZE)
+ return 0;
+
hdr.w0 = be32_to_cpu(buf[0]);
lf = fwnet_get_hdr_lf(&hdr);
if (lf == RFC2374_HDR_UNFRAG) {
return fwnet_finish_incoming_packet(net, skb, source_node_id,
is_broadcast, ether_type);
}
+
/* A datagram fragment has been received, now the fun begins. */
+
+ if (len <= RFC2374_FRAG_HDR_SIZE)
+ return 0;
+
hdr.w1 = ntohl(buf[1]);
buf += 2;
len -= RFC2374_FRAG_HDR_SIZE;
fg_off = fwnet_get_hdr_fg_off(&hdr);
}
datagram_label = fwnet_get_hdr_dgl(&hdr);
- dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
+ dg_size = fwnet_get_hdr_dg_size(&hdr);
+
+ if (fg_off + len > dg_size)
+ return 0;
spin_lock_irqsave(&dev->lock, flags);
fw_send_response(card, r, rcode);
}
+static int gasp_source_id(__be32 *p)
+{
+ return be32_to_cpu(p[0]) >> 16;
+}
+
+static u32 gasp_specifier_id(__be32 *p)
+{
+ return (be32_to_cpu(p[0]) & 0xffff) << 8 |
+ (be32_to_cpu(p[1]) & 0xff000000) >> 24;
+}
+
+static u32 gasp_version(__be32 *p)
+{
+ return be32_to_cpu(p[1]) & 0xffffff;
+}
+
static void fwnet_receive_broadcast(struct fw_iso_context *context,
u32 cycle, size_t header_length, void *header, void *data)
{
__be32 *buf_ptr;
int retval;
u32 length;
- u16 source_node_id;
- u32 specifier_id;
- u32 ver;
unsigned long offset;
unsigned long flags;
spin_unlock_irqrestore(&dev->lock, flags);
- specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8
- | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24;
- ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
- source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
-
- if (specifier_id == IANA_SPECIFIER_ID &&
- (ver == RFC2734_SW_VERSION
+ if (length > IEEE1394_GASP_HDR_SIZE &&
+ gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
+ (gasp_version(buf_ptr) == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6)
- || ver == RFC3146_SW_VERSION
+ || gasp_version(buf_ptr) == RFC3146_SW_VERSION
#endif
- )) {
- buf_ptr += 2;
- length -= IEEE1394_GASP_HDR_SIZE;
- fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
+ ))
+ fwnet_incoming_packet(dev, buf_ptr + 2,
+ length - IEEE1394_GASP_HDR_SIZE,
+ gasp_source_id(buf_ptr),
context->card->generation, true);
- }
packet.payload_length = dev->rcv_buffer_size;
packet.interrupt = 1;
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
- u32 mask = ~(1 << (d->irq - gc->irq_base));
+ u32 mask = d->mask;
irq_gc_lock(gc);
- writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip));
+ writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip));
irq_gc_unlock(gc);
}
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d);
- u32 mask = 1 << (d->irq - gc->irq_base);
+ u32 mask = d->mask;
irq_gc_lock(gc);
ct->mask_cache_priv &= ~mask;
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d);
-
- u32 mask = 1 << (d->irq - gc->irq_base);
+ u32 mask = d->mask;
irq_gc_lock(gc);
ct->mask_cache_priv |= mask;
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d);
-
- u32 mask = 1 << (d->irq - gc->irq_base);
+ u32 mask = d->mask;
irq_gc_lock(gc);
ct->mask_cache_priv &= ~mask;
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d);
-
- u32 mask = 1 << (d->irq - gc->irq_base);
+ u32 mask = d->mask;
irq_gc_lock(gc);
ct->mask_cache_priv |= mask;
for (i = 0; i < mvchip->chip.ngpio; i++) {
int irq;
- irq = mvchip->irqbase + i;
+ irq = irq_find_mapping(mvchip->domain, i);
if (!(cause & (1 << i)))
continue;
struct irq_chip_type *ct;
struct clk *clk;
unsigned int ngpios;
+ bool have_irqs;
int soc_variant;
int i, cpu, id;
int err;
else
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
+ /* Some gpio controllers do not provide irq support */
+ have_irqs = of_irq_count(np) != 0;
+
mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
GFP_KERNEL);
if (!mvchip)
mvchip->chip.get = mvebu_gpio_get;
mvchip->chip.direction_output = mvebu_gpio_direction_output;
mvchip->chip.set = mvebu_gpio_set;
- mvchip->chip.to_irq = mvebu_gpio_to_irq;
+ if (have_irqs)
+ mvchip->chip.to_irq = mvebu_gpio_to_irq;
mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK;
mvchip->chip.ngpio = ngpios;
mvchip->chip.can_sleep = false;
devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
/* Some gpio controllers do not provide irq support */
- if (!of_irq_count(np))
+ if (!have_irqs)
return 0;
- /* Setup the interrupt handlers. Each chip can have up to 4
- * interrupt handlers, with each handler dealing with 8 GPIO
- * pins. */
- for (i = 0; i < 4; i++) {
- int irq = platform_get_irq(pdev, i);
-
- if (irq < 0)
- continue;
- irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
- mvchip);
- }
-
- mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
- if (mvchip->irqbase < 0) {
- dev_err(&pdev->dev, "no irqs\n");
- return mvchip->irqbase;
+ mvchip->domain =
+ irq_domain_add_linear(np, ngpios, &irq_generic_chip_ops, NULL);
+ if (!mvchip->domain) {
+ dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
+ mvchip->chip.label);
+ return -ENODEV;
}
- gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
- mvchip->membase, handle_level_irq);
- if (!gc) {
- dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
- return -ENOMEM;
+ err = irq_alloc_domain_generic_chips(
+ mvchip->domain, ngpios, 2, np->name, handle_level_irq,
+ IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0);
+ if (err) {
+ dev_err(&pdev->dev, "couldn't allocate irq chips %s (DT).\n",
+ mvchip->chip.label);
+ goto err_domain;
}
+ /* NOTE: The common accessors cannot be used because of the percpu
+ * access to the mask registers
+ */
+ gc = irq_get_domain_generic_chip(mvchip->domain, 0);
gc->private = mvchip;
ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->handler = handle_edge_irq;
ct->chip.name = mvchip->chip.label;
- irq_setup_generic_chip(gc, IRQ_MSK(ngpios), 0,
- IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
+ /* Setup the interrupt handlers. Each chip can have up to 4
+ * interrupt handlers, with each handler dealing with 8 GPIO
+ * pins.
+ */
+ for (i = 0; i < 4; i++) {
+ int irq = platform_get_irq(pdev, i);
- /* Setup irq domain on top of the generic chip. */
- mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio,
- mvchip->irqbase,
- &irq_domain_simple_ops,
- mvchip);
- if (!mvchip->domain) {
- dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
- mvchip->chip.label);
- err = -ENODEV;
- goto err_generic_chip;
+ if (irq < 0)
+ continue;
+ irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
+ mvchip);
}
return 0;
-err_generic_chip:
- irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
- IRQ_LEVEL | IRQ_NOPROBE);
- kfree(gc);
+err_domain:
+ irq_domain_remove(mvchip->domain);
return err;
}
#include "gpiolib.h"
-static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
+static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
{
- return chip->gpiodev->dev.of_node == data;
+ struct of_phandle_args *gpiospec = data;
+
+ return chip->gpiodev->dev.of_node == gpiospec->np &&
+ chip->of_xlate(chip, gpiospec, NULL) >= 0;
}
-static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
+static struct gpio_chip *of_find_gpiochip_by_xlate(
+ struct of_phandle_args *gpiospec)
{
- return gpiochip_find(np, of_gpiochip_match_node);
+ return gpiochip_find(gpiospec, of_gpiochip_match_node_and_xlate);
}
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
return ERR_PTR(ret);
}
- chip = of_find_gpiochip_by_node(gpiospec.np);
+ chip = of_find_gpiochip_by_xlate(&gpiospec);
if (!chip) {
desc = ERR_PTR(-EPROBE_DEFER);
goto out;
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
+#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/timekeeping.h>
{
struct gpiohandle_request handlereq;
struct linehandle_state *lh;
+ struct file *file;
int fd, i, ret;
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
i--;
lh->numdescs = handlereq.lines;
- fd = anon_inode_getfd("gpio-linehandle",
- &linehandle_fileops,
- lh,
- O_RDONLY | O_CLOEXEC);
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto out_free_descs;
}
+ file = anon_inode_getfile("gpio-linehandle",
+ &linehandle_fileops,
+ lh,
+ O_RDONLY | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_put_unused_fd;
+ }
+
handlereq.fd = fd;
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
- ret = -EFAULT;
- goto out_free_descs;
+ /*
+ * fput() will trigger the release() callback, so do not go onto
+ * the regular error cleanup path here.
+ */
+ fput(file);
+ put_unused_fd(fd);
+ return -EFAULT;
}
+ fd_install(fd, file);
+
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lh->numdescs);
return 0;
+out_put_unused_fd:
+ put_unused_fd(fd);
out_free_descs:
for (; i >= 0; i--)
gpiod_free(lh->descs[i]);
struct gpioevent_request eventreq;
struct lineevent_state *le;
struct gpio_desc *desc;
+ struct file *file;
u32 offset;
u32 lflags;
u32 eflags;
if (ret)
goto out_free_desc;
- fd = anon_inode_getfd("gpio-event",
- &lineevent_fileops,
- le,
- O_RDONLY | O_CLOEXEC);
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto out_free_irq;
}
+ file = anon_inode_getfile("gpio-event",
+ &lineevent_fileops,
+ le,
+ O_RDONLY | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_put_unused_fd;
+ }
+
eventreq.fd = fd;
if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
- ret = -EFAULT;
- goto out_free_irq;
+ /*
+ * fput() will trigger the release() callback, so do not go onto
+ * the regular error cleanup path here.
+ */
+ fput(file);
+ put_unused_fd(fd);
+ return -EFAULT;
}
+ fd_install(fd, file);
+
return 0;
+out_put_unused_fd:
+ put_unused_fd(fd);
out_free_irq:
free_irq(le->irq, le);
out_free_desc:
r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true,
&duplicates);
if (unlikely(r != 0)) {
- DRM_ERROR("ttm_eu_reserve_buffers failed.\n");
+ if (r != -ERESTARTSYS)
+ DRM_ERROR("ttm_eu_reserve_buffers failed.\n");
goto error_free_pages;
}
/* evict remaining vram memory */
amdgpu_bo_evict_vram(adev);
+ amdgpu_atombios_scratch_regs_save(adev);
pci_save_state(dev->pdev);
if (suspend) {
/* Shut down the device */
return r;
}
}
+ amdgpu_atombios_scratch_regs_restore(adev);
/* post card */
if (!amdgpu_card_posted(adev) || !resume) {
}
if (need_full_reset) {
- /* save scratch */
- amdgpu_atombios_scratch_regs_save(adev);
r = amdgpu_suspend(adev);
retry:
amdgpu_display_stop_mc_access(adev, &save);
amdgpu_wait_for_idle(adev, AMD_IP_BLOCK_TYPE_GMC);
}
-
+ amdgpu_atombios_scratch_regs_save(adev);
r = amdgpu_asic_reset(adev);
+ amdgpu_atombios_scratch_regs_restore(adev);
/* post card */
amdgpu_atom_asic_init(adev->mode_info.atom_context);
dev_info(adev->dev, "GPU reset succeeded, trying to resume\n");
r = amdgpu_resume(adev);
}
- /* restore scratch */
- amdgpu_atombios_scratch_regs_restore(adev);
}
if (!r) {
amdgpu_irq_gpu_reset_resume_helper(adev);
void amdgpu_fence_slab_fini(void)
{
+ rcu_barrier();
kmem_cache_destroy(amdgpu_fence_slab);
}
/*
if (r) {
adev->irq.installed = false;
flush_work(&adev->hotplug_work);
+ cancel_work_sync(&adev->reset_work);
return r;
}
if (adev->irq.msi_enabled)
pci_disable_msi(adev->pdev);
flush_work(&adev->hotplug_work);
+ cancel_work_sync(&adev->reset_work);
}
for (i = 0; i < AMDGPU_MAX_IRQ_SRC_ID; ++i) {
/* return all clocks in KHz */
dev_info.gpu_counter_freq = amdgpu_asic_get_xclk(adev) * 10;
if (adev->pm.dpm_enabled) {
- dev_info.max_engine_clock =
- adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk * 10;
- dev_info.max_memory_clock =
- adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk * 10;
+ dev_info.max_engine_clock = amdgpu_dpm_get_sclk(adev, false) * 10;
+ dev_info.max_memory_clock = amdgpu_dpm_get_mclk(adev, false) * 10;
} else {
dev_info.max_engine_clock = adev->pm.default_sclk * 10;
dev_info.max_memory_clock = adev->pm.default_mclk * 10;
fence_put(adev->vm_manager.ids[i].first);
amdgpu_sync_free(&adev->vm_manager.ids[i].active);
fence_put(id->flushed_updates);
+ fence_put(id->last_flush);
}
}
pi->dpm_level_enable_mask.mclk_dpm_enable_mask);
}
} else {
- if (pi->last_mclk_dpm_enable_mask & 0x1) {
+ if (pi->uvd_enabled) {
pi->uvd_enabled = false;
pi->dpm_level_enable_mask.mclk_dpm_enable_mask |= 1;
amdgpu_ci_send_msg_to_smc_with_parameter(adev,
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ flush_work(&adev->pm.dpm.thermal.work);
+
mutex_lock(&adev->pm.mutex);
amdgpu_pm_sysfs_fini(adev);
ci_dpm_fini(adev);
static int dce_v10_0_suspend(void *handle)
{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_atombios_scratch_regs_save(adev);
-
return dce_v10_0_hw_fini(handle);
}
ret = dce_v10_0_hw_init(handle);
- amdgpu_atombios_scratch_regs_restore(adev);
-
/* turn on the BL */
if (adev->mode_info.bl_encoder) {
u8 bl_level = amdgpu_display_backlight_get_level(adev,
static int dce_v11_0_suspend(void *handle)
{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_atombios_scratch_regs_save(adev);
-
return dce_v11_0_hw_fini(handle);
}
ret = dce_v11_0_hw_init(handle);
- amdgpu_atombios_scratch_regs_restore(adev);
-
/* turn on the BL */
if (adev->mode_info.bl_encoder) {
u8 bl_level = amdgpu_display_backlight_get_level(adev,
static int dce_v6_0_suspend(void *handle)
{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_atombios_scratch_regs_save(adev);
-
return dce_v6_0_hw_fini(handle);
}
ret = dce_v6_0_hw_init(handle);
- amdgpu_atombios_scratch_regs_restore(adev);
-
/* turn on the BL */
if (adev->mode_info.bl_encoder) {
u8 bl_level = amdgpu_display_backlight_get_level(adev,
static int dce_v8_0_suspend(void *handle)
{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_atombios_scratch_regs_save(adev);
-
return dce_v8_0_hw_fini(handle);
}
ret = dce_v8_0_hw_init(handle);
- amdgpu_atombios_scratch_regs_restore(adev);
-
/* turn on the BL */
if (adev->mode_info.bl_encoder) {
u8 bl_level = amdgpu_display_backlight_get_level(adev,
mmCP_MEM_SLP_CNTL, 0xffffffff, 0x00020201,
mmRLC_MEM_SLP_CNTL, 0xffffffff, 0x00020201,
mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200,
- mmATC_MISC_CG, 0xffffffff, 0x000c0200,
};
static void gfx_v8_0_set_ring_funcs(struct amdgpu_device *adev);
static const u32 stoney_mgcg_cgcg_init[] =
{
+ mmATC_MISC_CG, 0xffffffff, 0x000c0200,
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ flush_work(&adev->pm.dpm.thermal.work);
+
mutex_lock(&adev->pm.mutex);
amdgpu_pm_sysfs_fini(adev);
kv_dpm_fini(adev);
int i;
struct si_dpm_quirk *p = si_dpm_quirk_list;
+ /* limit all SI kickers */
+ if (adev->asic_type == CHIP_PITCAIRN) {
+ if ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->device == 0x6810) ||
+ (adev->pdev->device == 0x6811) ||
+ (adev->pdev->device == 0x6816) ||
+ (adev->pdev->device == 0x6817) ||
+ (adev->pdev->device == 0x6806))
+ max_mclk = 120000;
+ } else if (adev->asic_type == CHIP_VERDE) {
+ if ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0x87) ||
+ (adev->pdev->device == 0x6820) ||
+ (adev->pdev->device == 0x6821) ||
+ (adev->pdev->device == 0x6822) ||
+ (adev->pdev->device == 0x6823) ||
+ (adev->pdev->device == 0x682A) ||
+ (adev->pdev->device == 0x682B)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (adev->asic_type == CHIP_OLAND) {
+ if ((adev->pdev->revision == 0xC7) ||
+ (adev->pdev->revision == 0x80) ||
+ (adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83) ||
+ (adev->pdev->device == 0x6604) ||
+ (adev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (adev->asic_type == CHIP_HAINAN) {
+ if ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0xC3) ||
+ (adev->pdev->device == 0x6664) ||
+ (adev->pdev->device == 0x6665) ||
+ (adev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ }
/* Apply dpm quirks */
while (p && p->chip_device != 0) {
if (adev->pdev->vendor == p->chip_vendor &&
}
++p;
}
- /* limit mclk on all R7 370 parts for stability */
- if (adev->pdev->device == 0x6811 &&
- adev->pdev->revision == 0x81)
- max_mclk = 120000;
- /* limit sclk/mclk on Jet parts for stability */
- if (adev->pdev->device == 0x6665 &&
- adev->pdev->revision == 0xc3) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
- /* Limit clocks for some HD8600 parts */
- if (adev->pdev->device == 0x6660 &&
- adev->pdev->revision == 0x83) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
if (rps->vce_active) {
rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk;
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ flush_work(&adev->pm.dpm.thermal.work);
+
mutex_lock(&adev->pm.mutex);
amdgpu_pm_sysfs_fini(adev);
si_dpm_fini(adev);
#define VCE_V3_0_STACK_SIZE (64 * 1024)
#define VCE_V3_0_DATA_SIZE ((16 * 1024 * AMDGPU_MAX_VCE_HANDLES) + (52 * 1024))
+#define FW_52_8_3 ((52 << 24) | (8 << 16) | (3 << 8))
+
static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx);
static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev);
static void vce_v3_0_set_irq_funcs(struct amdgpu_device *adev);
if (r)
return r;
+ /* 52.8.3 required for 3 ring support */
+ if (adev->vce.fw_version < FW_52_8_3)
+ adev->vce.num_rings = 2;
+
r = amdgpu_vce_resume(adev);
if (r)
return r;
AMD_CG_SUPPORT_SDMA_MGCG |
AMD_CG_SUPPORT_SDMA_LS |
AMD_CG_SUPPORT_VCE_MGCG;
- adev->pg_flags |= AMD_PG_SUPPORT_GFX_PG |
+ adev->pg_flags = AMD_PG_SUPPORT_GFX_PG |
AMD_PG_SUPPORT_GFX_SMG |
AMD_PG_SUPPORT_GFX_PIPELINE |
AMD_PG_SUPPORT_UVD |
*voltage = 1150;
} else {
ret = atomctrl_get_voltage_evv_on_sclk_ai(hwmgr, voltage_type, sclk, id, &vol);
- *voltage = (uint16_t)vol/100;
+ *voltage = (uint16_t)(vol/100);
}
return ret;
}
if (0 != result)
return result;
- *voltage = le32_to_cpu(((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_3 *)(&get_voltage_info_param_space))->ulVoltageLevel);
+ *voltage = le32_to_cpu(((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_3 *)
+ (&get_voltage_info_param_space))->ulVoltageLevel);
return result;
}
static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr)
{
const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
- const ATOM_Tonga_VCE_State_Table *vce_state_table =
- (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pp_table) + le16_to_cpu(pp_table->usVCEStateTableOffset));
+ const ATOM_Tonga_VCE_State_Table *vce_state_table;
- if (vce_state_table == NULL)
+
+ if (pp_table == NULL)
return 0;
+ vce_state_table = (void *)pp_table +
+ le16_to_cpu(pp_table->usVCEStateTableOffset);
+
return vce_state_table->ucNumEntries;
}
tmp_result = (!smum_is_dpm_running(hwmgr)) ? 0 : -1;
PP_ASSERT_WITH_CODE(tmp_result == 0,
- "DPM is already running right now, no need to enable DPM!",
- return 0);
+ "DPM is already running",
+ );
if (smu7_voltage_control(hwmgr)) {
tmp_result = smu7_enable_voltage_control(hwmgr);
}
static int smu7_patch_limits_vddc(struct pp_hwmgr *hwmgr,
- struct phm_clock_and_voltage_limits *tab)
+ struct phm_clock_and_voltage_limits *tab)
{
+ uint32_t vddc, vddci;
struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
if (tab) {
- smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, (uint32_t *)&tab->vddc,
- &data->vddc_leakage);
- smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, (uint32_t *)&tab->vddci,
- &data->vddci_leakage);
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddc,
+ &data->vddc_leakage);
+ tab->vddc = vddc;
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddci,
+ &data->vddci_leakage);
+ tab->vddci = vddci;
}
return 0;
{
if (sched->thread)
kthread_stop(sched->thread);
+ rcu_barrier();
if (atomic_dec_and_test(&sched_fence_slab_ref))
kmem_cache_destroy(sched_fence_slab);
}
}
/**
- * amd_sched_fence_release - callback that fence can be freed
+ * amd_sched_fence_release_scheduled - callback that fence can be freed
*
* @fence: fence
*
}
/**
- * amd_sched_fence_release_scheduled - drop extra reference
+ * amd_sched_fence_release_finished - drop extra reference
*
* @f: fence
*
ssize_t expected_size,
bool *replaced)
{
- struct drm_device *dev = crtc->dev;
struct drm_property_blob *new_blob = NULL;
if (blob_id != 0) {
- new_blob = drm_property_lookup_blob(dev, blob_id);
+ new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
if (new_blob == NULL)
return -EINVAL;
- if (expected_size > 0 && expected_size != new_blob->length)
+
+ if (expected_size > 0 && expected_size != new_blob->length) {
+ drm_property_unreference_blob(new_blob);
return -EINVAL;
+ }
}
drm_atomic_replace_property_blob(blob, new_blob, replaced);
+ drm_property_unreference_blob(new_blob);
return 0;
}
struct drm_plane_state *plane_state;
int i, ret = 0;
- ret = drm_atomic_normalize_zpos(dev, state);
- if (ret)
- return ret;
-
for_each_plane_in_state(state, plane, plane_state, i) {
const struct drm_plane_helper_funcs *funcs;
/* no need to clean up vcpi
* as if we have no connector we never setup a vcpi */
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
}
kfree(port);
}
drm_dp_put_port(port);
goto out;
}
- if (port->port_num >= DP_MST_LOGICAL_PORT_0) {
+ if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
+ port->pdt == DP_PEER_DEVICE_SST_SINK) &&
+ port->port_num >= DP_MST_LOGICAL_PORT_0) {
port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
drm_mode_connector_set_tile_property(port->connector);
}
mgr->cbs->destroy_connector(mgr, port->connector);
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
if (!port->input && port->vcpi.vcpi > 0) {
drm_dp_mst_reset_vcpi_slots(mgr, port);
return 0;
fail:
for (i = 0; i < fb_helper->connector_count; i++) {
- kfree(fb_helper->connector_info[i]);
+ struct drm_fb_helper_connector *fb_helper_connector =
+ fb_helper->connector_info[i];
+
+ drm_connector_unreference(fb_helper_connector->connector);
+
+ kfree(fb_helper_connector);
fb_helper->connector_info[i] = NULL;
}
fb_helper->connector_count = 0;
}
EXPORT_SYMBOL(drm_fb_helper_blank);
+static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
+ struct drm_mode_set *modeset)
+{
+ int i;
+
+ for (i = 0; i < modeset->num_connectors; i++) {
+ drm_connector_unreference(modeset->connectors[i]);
+ modeset->connectors[i] = NULL;
+ }
+ modeset->num_connectors = 0;
+
+ drm_mode_destroy(helper->dev, modeset->mode);
+ modeset->mode = NULL;
+
+ /* FIXME should hold a ref? */
+ modeset->fb = NULL;
+}
+
static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
{
int i;
kfree(helper->connector_info[i]);
}
kfree(helper->connector_info);
+
for (i = 0; i < helper->crtc_count; i++) {
- kfree(helper->crtc_info[i].mode_set.connectors);
- if (helper->crtc_info[i].mode_set.mode)
- drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
+ struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
+
+ drm_fb_helper_modeset_release(helper, modeset);
+ kfree(modeset->connectors);
}
kfree(helper->crtc_info);
}
clip->x2 = clip->y2 = 0;
spin_unlock_irqrestore(&helper->dirty_lock, flags);
- helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
+ /* call dirty callback only when it has been really touched */
+ if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
+ helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
}
/**
struct drm_fb_helper_crtc **crtcs;
struct drm_display_mode **modes;
struct drm_fb_offset *offsets;
- struct drm_mode_set *modeset;
bool *enabled;
int width, height;
int i;
/* need to set the modesets up here for use later */
/* fill out the connector<->crtc mappings into the modesets */
- for (i = 0; i < fb_helper->crtc_count; i++) {
- modeset = &fb_helper->crtc_info[i].mode_set;
- modeset->num_connectors = 0;
- modeset->fb = NULL;
- }
+ for (i = 0; i < fb_helper->crtc_count; i++)
+ drm_fb_helper_modeset_release(fb_helper,
+ &fb_helper->crtc_info[i].mode_set);
for (i = 0; i < fb_helper->connector_count; i++) {
struct drm_display_mode *mode = modes[i];
struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
struct drm_fb_offset *offset = &offsets[i];
- modeset = &fb_crtc->mode_set;
+ struct drm_mode_set *modeset = &fb_crtc->mode_set;
if (mode && fb_crtc) {
+ struct drm_connector *connector =
+ fb_helper->connector_info[i]->connector;
+
DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
+
fb_crtc->desired_mode = mode;
fb_crtc->x = offset->x;
fb_crtc->y = offset->y;
- if (modeset->mode)
- drm_mode_destroy(dev, modeset->mode);
modeset->mode = drm_mode_duplicate(dev,
fb_crtc->desired_mode);
- modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
+ drm_connector_reference(connector);
+ modeset->connectors[modeset->num_connectors++] = connector;
modeset->fb = fb_helper->fb;
modeset->x = offset->x;
modeset->y = offset->y;
}
}
-
- /* Clear out any old modes if there are no more connected outputs. */
- for (i = 0; i < fb_helper->crtc_count; i++) {
- modeset = &fb_helper->crtc_info[i].mode_set;
- if (modeset->num_connectors == 0) {
- BUG_ON(modeset->fb);
- if (modeset->mode)
- drm_mode_destroy(dev, modeset->mode);
- modeset->mode = NULL;
- }
- }
out:
kfree(crtcs);
kfree(modes);
return 0;
}
+int exynos_atomic_check(struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ int ret;
+
+ ret = drm_atomic_helper_check_modeset(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_normalize_zpos(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_helper_check_planes(dev, state);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_exynos_file_private *file_priv;
int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
bool nonblock);
+int exynos_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
extern struct platform_driver fimd_driver;
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
- .atomic_check = drm_atomic_helper_check,
+ .atomic_check = exynos_atomic_check,
.atomic_commit = exynos_atomic_commit,
};
dev_priv->suspend_count++;
- intel_display_set_init_power(dev_priv, false);
-
intel_csr_ucode_suspend(dev_priv);
out:
disable_rpm_wakeref_asserts(dev_priv);
+ intel_display_set_init_power(dev_priv, false);
+
fw_csr = !IS_BROXTON(dev_priv) &&
suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
/*
extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
#endif
+extern const struct dev_pm_ops i915_pm_ops;
+
+extern int i915_driver_load(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+extern void i915_driver_unload(struct drm_device *dev);
extern int intel_gpu_reset(struct drm_i915_private *dev_priv, u32 engine_mask);
extern bool intel_has_gpu_reset(struct drm_i915_private *dev_priv);
extern void i915_reset(struct drm_i915_private *dev_priv);
vma->display_alignment = max_t(u64, vma->display_alignment, alignment);
- WARN_ON(obj->pin_display > i915_vma_pin_count(vma));
-
i915_gem_object_flush_cpu_write_domain(obj);
old_write_domain = obj->base.write_domain;
list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
i915_vma_unpin(vma);
- WARN_ON(vma->obj->pin_display > i915_vma_pin_count(vma));
}
/**
mappable = (vma->node.start + fence_size <=
dev_priv->ggtt.mappable_end);
- if (mappable && fenceable)
+ /*
+ * Explicitly disable for rotated VMA since the display does not
+ * need the fence and the VMA is not accessible to other users.
+ */
+ if (mappable && fenceable &&
+ vma->ggtt_view.type != I915_GGTT_VIEW_ROTATED)
vma->flags |= I915_VMA_CAN_FENCE;
else
vma->flags &= ~I915_VMA_CAN_FENCE;
{
struct drm_i915_fence_reg *fence = vma->fence;
+ assert_rpm_wakelock_held(to_i915(vma->vm->dev));
+
if (!fence)
return 0;
struct drm_i915_fence_reg *fence;
struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
+ assert_rpm_wakelock_held(to_i915(vma->vm->dev));
+
/* Just update our place in the LRU if our fence is getting reused. */
if (vma->fence) {
fence = vma->fence;
struct drm_i915_private *dev_priv = to_i915(dev);
int i;
+ /* Note that this may be called outside of struct_mutex, by
+ * runtime suspend/resume. The barrier we require is enforced by
+ * rpm itself - all access to fences/GTT are only within an rpm
+ * wakeref, and to acquire that wakeref you must pass through here.
+ */
+
for (i = 0; i < dev_priv->num_fence_regs; i++) {
struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i];
struct i915_vma *vma = reg->vma;
* Commit delayed tiling changes if we have an object still
* attached to the fence, otherwise just clear the fence.
*/
- if (vma && !i915_gem_object_is_tiled(vma->obj))
+ if (vma && !i915_gem_object_is_tiled(vma->obj)) {
+ GEM_BUG_ON(!reg->dirty);
+ GEM_BUG_ON(vma->obj->fault_mappable);
+
+ list_move(®->link, &dev_priv->mm.fence_list);
+ vma->fence = NULL;
vma = NULL;
+ }
- fence_update(reg, vma);
+ fence_write(reg, vma);
+ reg->vma = vma;
}
}
};
MODULE_DEVICE_TABLE(pci, pciidlist);
-extern int i915_driver_load(struct pci_dev *pdev,
- const struct pci_device_id *ent);
-
static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct intel_device_info *intel_info =
return i915_driver_load(pdev, ent);
}
-extern void i915_driver_unload(struct drm_device *dev);
-
static void i915_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_dev_unref(dev);
}
-extern const struct dev_pm_ops i915_pm_ops;
-
static struct pci_driver i915_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
return mapping[val];
}
+static void sanitize_ddc_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ const struct ddi_vbt_port_info *info =
+ &dev_priv->vbt.ddi_port_info[port];
+ enum port p;
+
+ if (!info->alternate_ddc_pin)
+ return;
+
+ for_each_port_masked(p, (1 << port) - 1) {
+ struct ddi_vbt_port_info *i = &dev_priv->vbt.ddi_port_info[p];
+
+ if (info->alternate_ddc_pin != i->alternate_ddc_pin)
+ continue;
+
+ DRM_DEBUG_KMS("port %c trying to use the same DDC pin (0x%x) as port %c, "
+ "disabling port %c DVI/HDMI support\n",
+ port_name(p), i->alternate_ddc_pin,
+ port_name(port), port_name(p));
+
+ /*
+ * If we have multiple ports supposedly sharing the
+ * pin, then dvi/hdmi couldn't exist on the shared
+ * port. Otherwise they share the same ddc bin and
+ * system couldn't communicate with them separately.
+ *
+ * Due to parsing the ports in alphabetical order,
+ * a higher port will always clobber a lower one.
+ */
+ i->supports_dvi = false;
+ i->supports_hdmi = false;
+ i->alternate_ddc_pin = 0;
+ }
+}
+
+static void sanitize_aux_ch(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ const struct ddi_vbt_port_info *info =
+ &dev_priv->vbt.ddi_port_info[port];
+ enum port p;
+
+ if (!info->alternate_aux_channel)
+ return;
+
+ for_each_port_masked(p, (1 << port) - 1) {
+ struct ddi_vbt_port_info *i = &dev_priv->vbt.ddi_port_info[p];
+
+ if (info->alternate_aux_channel != i->alternate_aux_channel)
+ continue;
+
+ DRM_DEBUG_KMS("port %c trying to use the same AUX CH (0x%x) as port %c, "
+ "disabling port %c DP support\n",
+ port_name(p), i->alternate_aux_channel,
+ port_name(port), port_name(p));
+
+ /*
+ * If we have multiple ports supposedlt sharing the
+ * aux channel, then DP couldn't exist on the shared
+ * port. Otherwise they share the same aux channel
+ * and system couldn't communicate with them separately.
+ *
+ * Due to parsing the ports in alphabetical order,
+ * a higher port will always clobber a lower one.
+ */
+ i->supports_dp = false;
+ i->alternate_aux_channel = 0;
+ }
+}
+
static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
const struct bdb_header *bdb)
{
DRM_DEBUG_KMS("Port %c is internal DP\n", port_name(port));
if (is_dvi) {
- if (port == PORT_E) {
- info->alternate_ddc_pin = ddc_pin;
- /* if DDIE share ddc pin with other port, then
- * dvi/hdmi couldn't exist on the shared port.
- * Otherwise they share the same ddc bin and system
- * couldn't communicate with them seperately. */
- if (ddc_pin == DDC_PIN_B) {
- dev_priv->vbt.ddi_port_info[PORT_B].supports_dvi = 0;
- dev_priv->vbt.ddi_port_info[PORT_B].supports_hdmi = 0;
- } else if (ddc_pin == DDC_PIN_C) {
- dev_priv->vbt.ddi_port_info[PORT_C].supports_dvi = 0;
- dev_priv->vbt.ddi_port_info[PORT_C].supports_hdmi = 0;
- } else if (ddc_pin == DDC_PIN_D) {
- dev_priv->vbt.ddi_port_info[PORT_D].supports_dvi = 0;
- dev_priv->vbt.ddi_port_info[PORT_D].supports_hdmi = 0;
- }
- } else if (ddc_pin == DDC_PIN_B && port != PORT_B)
- DRM_DEBUG_KMS("Unexpected DDC pin for port B\n");
- else if (ddc_pin == DDC_PIN_C && port != PORT_C)
- DRM_DEBUG_KMS("Unexpected DDC pin for port C\n");
- else if (ddc_pin == DDC_PIN_D && port != PORT_D)
- DRM_DEBUG_KMS("Unexpected DDC pin for port D\n");
+ info->alternate_ddc_pin = ddc_pin;
+
+ sanitize_ddc_pin(dev_priv, port);
}
if (is_dp) {
- if (port == PORT_E) {
- info->alternate_aux_channel = aux_channel;
- /* if DDIE share aux channel with other port, then
- * DP couldn't exist on the shared port. Otherwise
- * they share the same aux channel and system
- * couldn't communicate with them seperately. */
- if (aux_channel == DP_AUX_A)
- dev_priv->vbt.ddi_port_info[PORT_A].supports_dp = 0;
- else if (aux_channel == DP_AUX_B)
- dev_priv->vbt.ddi_port_info[PORT_B].supports_dp = 0;
- else if (aux_channel == DP_AUX_C)
- dev_priv->vbt.ddi_port_info[PORT_C].supports_dp = 0;
- else if (aux_channel == DP_AUX_D)
- dev_priv->vbt.ddi_port_info[PORT_D].supports_dp = 0;
- }
- else if (aux_channel == DP_AUX_A && port != PORT_A)
- DRM_DEBUG_KMS("Unexpected AUX channel for port A\n");
- else if (aux_channel == DP_AUX_B && port != PORT_B)
- DRM_DEBUG_KMS("Unexpected AUX channel for port B\n");
- else if (aux_channel == DP_AUX_C && port != PORT_C)
- DRM_DEBUG_KMS("Unexpected AUX channel for port C\n");
- else if (aux_channel == DP_AUX_D && port != PORT_D)
- DRM_DEBUG_KMS("Unexpected AUX channel for port D\n");
+ info->alternate_aux_channel = aux_channel;
+
+ sanitize_aux_ch(dev_priv, port);
}
if (bdb->version >= 158) {
struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu;
const int s_max = 3, ss_max = 3, eu_max = 8;
int s, ss;
- u32 fuse2, eu_disable[s_max];
+ u32 fuse2, eu_disable[3]; /* s_max */
fuse2 = I915_READ(GEN8_FUSE2);
sseu->slice_mask = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
/* Rotate src coordinates to match rotated GTT view */
if (intel_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->base.src,
- fb->width, fb->height, DRM_ROTATE_270);
+ fb->width << 16, fb->height << 16,
+ DRM_ROTATE_270);
/*
* Handle the AUX surface first since
for_each_plane_in_state(state, plane, plane_state, i) {
struct intel_plane_state *intel_plane_state =
- to_intel_plane_state(plane_state);
+ to_intel_plane_state(plane->state);
if (!intel_plane_state->wait_req)
continue;
return ret;
}
+static enum port intel_aux_port(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ const struct ddi_vbt_port_info *info =
+ &dev_priv->vbt.ddi_port_info[port];
+ enum port aux_port;
+
+ if (!info->alternate_aux_channel) {
+ DRM_DEBUG_KMS("using AUX %c for port %c (platform default)\n",
+ port_name(port), port_name(port));
+ return port;
+ }
+
+ switch (info->alternate_aux_channel) {
+ case DP_AUX_A:
+ aux_port = PORT_A;
+ break;
+ case DP_AUX_B:
+ aux_port = PORT_B;
+ break;
+ case DP_AUX_C:
+ aux_port = PORT_C;
+ break;
+ case DP_AUX_D:
+ aux_port = PORT_D;
+ break;
+ default:
+ MISSING_CASE(info->alternate_aux_channel);
+ aux_port = PORT_A;
+ break;
+ }
+
+ DRM_DEBUG_KMS("using AUX %c for port %c (VBT)\n",
+ port_name(aux_port), port_name(port));
+
+ return aux_port;
+}
+
static i915_reg_t g4x_aux_ctl_reg(struct drm_i915_private *dev_priv,
enum port port)
{
}
}
-/*
- * On SKL we don't have Aux for port E so we rely
- * on VBT to set a proper alternate aux channel.
- */
-static enum port skl_porte_aux_port(struct drm_i915_private *dev_priv)
-{
- const struct ddi_vbt_port_info *info =
- &dev_priv->vbt.ddi_port_info[PORT_E];
-
- switch (info->alternate_aux_channel) {
- case DP_AUX_A:
- return PORT_A;
- case DP_AUX_B:
- return PORT_B;
- case DP_AUX_C:
- return PORT_C;
- case DP_AUX_D:
- return PORT_D;
- default:
- MISSING_CASE(info->alternate_aux_channel);
- return PORT_A;
- }
-}
-
static i915_reg_t skl_aux_ctl_reg(struct drm_i915_private *dev_priv,
enum port port)
{
- if (port == PORT_E)
- port = skl_porte_aux_port(dev_priv);
-
switch (port) {
case PORT_A:
case PORT_B:
static i915_reg_t skl_aux_data_reg(struct drm_i915_private *dev_priv,
enum port port, int index)
{
- if (port == PORT_E)
- port = skl_porte_aux_port(dev_priv);
-
switch (port) {
case PORT_A:
case PORT_B:
static void intel_aux_reg_init(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
- enum port port = dp_to_dig_port(intel_dp)->port;
+ enum port port = intel_aux_port(dev_priv,
+ dp_to_dig_port(intel_dp)->port);
int i;
intel_dp->aux_ch_ctl_reg = intel_aux_ctl_reg(dev_priv, port);
/* Read the eDP Display control capabilities registers */
if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) &&
drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
- intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd) ==
- sizeof(intel_dp->edp_dpcd)))
+ intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd)) ==
+ sizeof(intel_dp->edp_dpcd))
DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd),
intel_dp->edp_dpcd);
int lines;
intel_fbc_get_plane_source_size(cache, NULL, &lines);
- if (INTEL_INFO(dev_priv)->gen >= 7)
+ if (INTEL_GEN(dev_priv) == 7)
lines = min(lines, 2048);
+ else if (INTEL_GEN(dev_priv) >= 8)
+ lines = min(lines, 2560);
/* Hardware needs the full buffer stride, not just the active area. */
return lines * cache->fb.stride;
int num_active;
int id, i;
+ /* Clear the partitioning for disabled planes. */
+ memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe]));
+ memset(ddb->y_plane[pipe], 0, sizeof(ddb->y_plane[pipe]));
+
if (WARN_ON(!state))
return 0;
if (!cstate->base.active) {
ddb->pipe[pipe].start = ddb->pipe[pipe].end = 0;
- memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe]));
- memset(ddb->y_plane[pipe], 0, sizeof(ddb->y_plane[pipe]));
return 0;
}
return 0;
}
-static uint32_t skl_pipe_pixel_rate(const struct intel_crtc_state *config)
-{
- /* TODO: Take into account the scalers once we support them */
- return config->base.adjusted_mode.crtc_clock;
-}
-
/*
* The max latency should be 257 (max the punit can code is 255 and we add 2us
* for the read latency) and cpp should always be <= 8, so that
* Adjusted plane pixel rate is just the pipe's adjusted pixel rate
* with additional adjustments for plane-specific scaling.
*/
- adjusted_pixel_rate = skl_pipe_pixel_rate(cstate);
+ adjusted_pixel_rate = ilk_pipe_pixel_rate(cstate);
downscale_amount = skl_plane_downscale_amount(pstate);
pixel_rate = adjusted_pixel_rate * downscale_amount >> 16;
if (!cstate->base.active)
return 0;
- if (WARN_ON(skl_pipe_pixel_rate(cstate) == 0))
+ if (WARN_ON(ilk_pipe_pixel_rate(cstate) == 0))
return 0;
return DIV_ROUND_UP(8 * cstate->base.adjusted_mode.crtc_htotal * 1000,
- skl_pipe_pixel_rate(cstate));
+ ilk_pipe_pixel_rate(cstate));
}
static void skl_compute_transition_wm(struct intel_crtc_state *cstate,
intel_state->wm_results.dirty_pipes = ~0;
}
+ /*
+ * We're not recomputing for the pipes not included in the commit, so
+ * make sure we start with the current state.
+ */
+ memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb));
+
for_each_intel_crtc_mask(dev, intel_crtc, realloc_pipes) {
struct intel_crtc_state *cstate;
int ret;
drm = drm_dev_alloc(&imx_drm_driver, dev);
- if (!drm)
- return -ENOMEM;
+ if (IS_ERR(drm))
+ return PTR_ERR(drm);
imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL);
if (!imxdrm) {
err_fbhelper:
drm_kms_helper_poll_fini(drm);
+#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
if (imxdrm->fbhelper)
drm_fbdev_cma_fini(imxdrm->fbhelper);
err_unbind:
+#endif
component_unbind_all(drm->dev, drm);
err_vblank:
drm_vblank_cleanup(drm);
(state->src_x >> 16) / 2 - eba;
}
-static void ipu_plane_atomic_set_base(struct ipu_plane *ipu_plane,
- struct drm_plane_state *old_state)
+static void ipu_plane_atomic_set_base(struct ipu_plane *ipu_plane)
{
struct drm_plane *plane = &ipu_plane->base;
struct drm_plane_state *state = plane->state;
+ struct drm_crtc_state *crtc_state = state->crtc->state;
struct drm_framebuffer *fb = state->fb;
unsigned long eba, ubo, vbo;
int active;
switch (fb->pixel_format) {
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
- if (old_state->fb)
+ if (!drm_atomic_crtc_needs_modeset(crtc_state))
break;
/*
break;
}
- if (old_state->fb) {
+ if (!drm_atomic_crtc_needs_modeset(crtc_state)) {
active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
struct drm_framebuffer *fb = state->fb;
struct drm_framebuffer *old_fb = old_state->fb;
unsigned long eba, ubo, vbo, old_ubo, old_vbo;
+ int hsub, vsub;
/* Ok to disable */
if (!fb)
if ((ubo > 0xfffff8) || (vbo > 0xfffff8))
return -EINVAL;
- if (old_fb) {
+ if (old_fb &&
+ (old_fb->pixel_format == DRM_FORMAT_YUV420 ||
+ old_fb->pixel_format == DRM_FORMAT_YVU420)) {
old_ubo = drm_plane_state_to_ubo(old_state);
old_vbo = drm_plane_state_to_vbo(old_state);
if (ubo != old_ubo || vbo != old_vbo)
if (old_fb && old_fb->pitches[1] != fb->pitches[1])
crtc_state->mode_changed = true;
+
+ /*
+ * The x/y offsets must be even in case of horizontal/vertical
+ * chroma subsampling.
+ */
+ hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
+ vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
+ if (((state->src_x >> 16) & (hsub - 1)) ||
+ ((state->src_y >> 16) & (vsub - 1)))
+ return -EINVAL;
}
return 0;
struct drm_crtc_state *crtc_state = state->crtc->state;
if (!drm_atomic_crtc_needs_modeset(crtc_state)) {
- ipu_plane_atomic_set_base(ipu_plane, old_state);
+ ipu_plane_atomic_set_base(ipu_plane);
return;
}
}
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
break;
default:
+ ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
break;
}
}
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]);
- ipu_plane_atomic_set_base(ipu_plane, old_state);
+ ipu_plane_atomic_set_base(ipu_plane);
ipu_plane_enable(ipu_plane);
}
if (!parent_adev)
return false;
- return acpi_has_method(parent_adev->handle, "_PR3");
+ return parent_adev->power.flags.power_resources &&
+ acpi_has_method(parent_adev->handle, "_PR3");
}
static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
int ring, u32 cp_int_cntl)
{
- u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3;
-
- WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3));
+ WREG32(SRBM_GFX_CNTL, RINGID(ring));
WREG32(CP_INT_CNTL, cp_int_cntl);
}
tmp &= AUX_HPD_SEL(0x7);
tmp |= AUX_HPD_SEL(chan->rec.hpd);
- tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1);
+ tmp |= AUX_EN | AUX_LS_READ_EN;
WREG32(AUX_CONTROL + aux_offset[instance], tmp);
int i;
struct si_dpm_quirk *p = si_dpm_quirk_list;
+ /* limit all SI kickers */
+ if (rdev->family == CHIP_PITCAIRN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->device == 0x6810) ||
+ (rdev->pdev->device == 0x6811) ||
+ (rdev->pdev->device == 0x6816) ||
+ (rdev->pdev->device == 0x6817) ||
+ (rdev->pdev->device == 0x6806))
+ max_mclk = 120000;
+ } else if (rdev->family == CHIP_VERDE) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
+ (rdev->pdev->device == 0x6820) ||
+ (rdev->pdev->device == 0x6821) ||
+ (rdev->pdev->device == 0x6822) ||
+ (rdev->pdev->device == 0x6823) ||
+ (rdev->pdev->device == 0x682A) ||
+ (rdev->pdev->device == 0x682B)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_OLAND) {
+ if ((rdev->pdev->revision == 0xC7) ||
+ (rdev->pdev->revision == 0x80) ||
+ (rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->device == 0x6604) ||
+ (rdev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_HAINAN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0xC3) ||
+ (rdev->pdev->device == 0x6664) ||
+ (rdev->pdev->device == 0x6665) ||
+ (rdev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ }
/* Apply dpm quirks */
while (p && p->chip_device != 0) {
if (rdev->pdev->vendor == p->chip_vendor &&
}
++p;
}
- /* limit mclk on all R7 370 parts for stability */
- if (rdev->pdev->device == 0x6811 &&
- rdev->pdev->revision == 0x81)
- max_mclk = 120000;
- /* limit sclk/mclk on Jet parts for stability */
- if (rdev->pdev->device == 0x6665 &&
- rdev->pdev->revision == 0xc3) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
if (rps->vce_active) {
rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
struct rcar_du_device *rcdu = dev->dev_private;
int ret;
- ret = drm_atomic_helper_check(dev, state);
- if (ret < 0)
+ ret = drm_atomic_helper_check_modeset(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_normalize_zpos(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_helper_check_planes(dev, state);
+ if (ret)
return ret;
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
sti_atomic_complete(private, private->commit.state);
}
+static int sti_atomic_check(struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ int ret;
+
+ ret = drm_atomic_helper_check_modeset(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_normalize_zpos(dev, state);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_helper_check_planes(dev, state);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
static int sti_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *state, bool nonblock)
{
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
.output_poll_changed = sti_output_poll_changed,
- .atomic_check = drm_atomic_helper_check,
+ .atomic_check = sti_atomic_check,
.atomic_commit = sti_atomic_commit,
};
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
- drm_atomic_helper_commit_planes(dev, state,
- DRM_PLANE_COMMIT_ACTIVE_ONLY);
+ drm_atomic_helper_commit_planes(dev, state, 0);
drm_atomic_helper_commit_hw_done(state);
ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode,
complete, complete_context);
if (IS_ERR(ctx))
- return ERR_PTR(PTR_ERR(ctx));
+ return ERR_CAST(ctx);
run = kzalloc(sizeof(*run), GFP_KERNEL);
if (!run) {
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
+ INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
pr_debug("driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
resp.qp_tab_size = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp);
if (mlx5_core_is_pf(dev->mdev) && MLX5_CAP_GEN(dev->mdev, bf))
resp.bf_reg_size = 1 << MLX5_CAP_GEN(dev->mdev, log_bf_reg_size);
- resp.cache_line_size = L1_CACHE_BYTES;
+ resp.cache_line_size = cache_line_size();
resp.max_sq_desc_sz = MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq);
resp.max_rq_desc_sz = MLX5_CAP_GEN(dev->mdev, max_wqe_sz_rq);
resp.max_send_wqebb = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz);
enum {
MLX5_IB_SQ_STRIDE = 6,
- MLX5_IB_CACHE_LINE_SIZE = 64,
};
static const u32 mlx5_ib_opcode[] = {
tristate "QLogic RoCE driver"
depends on 64BIT && QEDE
select QED_LL2
+ select QED_RDMA
---help---
This driver provides low-level InfiniBand over Ethernet
support for QLogic QED host channel adapters (HCAs).
enum {
IPOIB_ENCAP_LEN = 4,
+ IPOIB_PSEUDO_LEN = 20,
+ IPOIB_HARD_LEN = IPOIB_ENCAP_LEN + IPOIB_PSEUDO_LEN,
IPOIB_UD_HEAD_SIZE = IB_GRH_BYTES + IPOIB_ENCAP_LEN,
IPOIB_UD_RX_SG = 2, /* max buffer needed for 4K mtu */
u16 reserved;
};
-struct ipoib_cb {
- struct qdisc_skb_cb qdisc_cb;
- u8 hwaddr[INFINIBAND_ALEN];
+struct ipoib_pseudo_header {
+ u8 hwaddr[INFINIBAND_ALEN];
};
-static inline struct ipoib_cb *ipoib_skb_cb(const struct sk_buff *skb)
+static inline void skb_add_pseudo_hdr(struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct ipoib_cb));
- return (struct ipoib_cb *)skb->cb;
+ char *data = skb_push(skb, IPOIB_PSEUDO_LEN);
+
+ /*
+ * only the ipoib header is present now, make room for a dummy
+ * pseudo header and set skb field accordingly
+ */
+ memset(data, 0, IPOIB_PSEUDO_LEN);
+ skb_reset_mac_header(skb);
+ skb_pull(skb, IPOIB_HARD_LEN);
}
/* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */
#define IPOIB_CM_RX_DELAY (3 * 256 * HZ)
#define IPOIB_CM_RX_UPDATE_MASK (0x3)
+#define IPOIB_CM_RX_RESERVE (ALIGN(IPOIB_HARD_LEN, 16) - IPOIB_ENCAP_LEN)
+
static struct ib_qp_attr ipoib_cm_err_attr = {
.qp_state = IB_QPS_ERR
};
struct sk_buff *skb;
int i;
- skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12);
+ skb = dev_alloc_skb(ALIGN(IPOIB_CM_HEAD_SIZE + IPOIB_PSEUDO_LEN, 16));
if (unlikely(!skb))
return NULL;
/*
- * IPoIB adds a 4 byte header. So we need 12 more bytes to align the
+ * IPoIB adds a IPOIB_ENCAP_LEN byte header, this will align the
* IP header to a multiple of 16.
*/
- skb_reserve(skb, 12);
+ skb_reserve(skb, IPOIB_CM_RX_RESERVE);
mapping[0] = ib_dma_map_single(priv->ca, skb->data, IPOIB_CM_HEAD_SIZE,
DMA_FROM_DEVICE);
if (wc->byte_len < IPOIB_CM_COPYBREAK) {
int dlen = wc->byte_len;
- small_skb = dev_alloc_skb(dlen + 12);
+ small_skb = dev_alloc_skb(dlen + IPOIB_CM_RX_RESERVE);
if (small_skb) {
- skb_reserve(small_skb, 12);
+ skb_reserve(small_skb, IPOIB_CM_RX_RESERVE);
ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0],
dlen, DMA_FROM_DEVICE);
skb_copy_from_linear_data(skb, small_skb->data, dlen);
copied:
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
buf_size = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu);
- skb = dev_alloc_skb(buf_size + IPOIB_ENCAP_LEN);
+ skb = dev_alloc_skb(buf_size + IPOIB_HARD_LEN);
if (unlikely(!skb))
return NULL;
/*
- * IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte
- * header. So we need 4 more bytes to get to 48 and align the
- * IP header to a multiple of 16.
+ * the IP header will be at IPOIP_HARD_LEN + IB_GRH_BYTES, that is
+ * 64 bytes aligned
*/
- skb_reserve(skb, 4);
+ skb_reserve(skb, sizeof(struct ipoib_pseudo_header));
mapping = priv->rx_ring[id].mapping;
mapping[0] = ib_dma_map_single(priv->ca, skb->data, buf_size,
skb_pull(skb, IB_GRH_BYTES);
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
ipoib_neigh_free(neigh);
goto err_drop;
}
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+ if (skb_queue_len(&neigh->queue) <
+ IPOIB_MAX_PATH_REC_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, IPOIB_PSEUDO_LEN);
__skb_queue_tail(&neigh->queue, skb);
- else {
+ } else {
ipoib_warn(priv, "queue length limit %d. Packet drop.\n",
skb_queue_len(&neigh->queue));
goto err_drop;
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
- struct ipoib_cb *cb)
+ struct ipoib_pseudo_header *phdr)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
spin_lock_irqsave(&priv->lock, flags);
- path = __path_find(dev, cb->hwaddr + 4);
+ path = __path_find(dev, phdr->hwaddr + 4);
if (!path || !path->valid) {
int new_path = 0;
if (!path) {
- path = path_rec_create(dev, cb->hwaddr + 4);
+ path = path_rec_create(dev, phdr->hwaddr + 4);
new_path = 1;
}
if (path) {
if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, IPOIB_PSEUDO_LEN);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
be16_to_cpu(path->pathrec.dlid));
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr));
return;
} else if ((path->query || !path_rec_start(dev, path)) &&
skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, IPOIB_PSEUDO_LEN);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
+ struct ipoib_pseudo_header *phdr;
struct ipoib_header *header;
unsigned long flags;
+ phdr = (struct ipoib_pseudo_header *) skb->data;
+ skb_pull(skb, sizeof(*phdr));
header = (struct ipoib_header *) skb->data;
- if (unlikely(cb->hwaddr[4] == 0xff)) {
+ if (unlikely(phdr->hwaddr[4] == 0xff)) {
/* multicast, arrange "if" according to probability */
if ((header->proto != htons(ETH_P_IP)) &&
(header->proto != htons(ETH_P_IPV6)) &&
return NETDEV_TX_OK;
}
/* Add in the P_Key for multicast*/
- cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
- cb->hwaddr[9] = priv->pkey & 0xff;
+ phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+ phdr->hwaddr[9] = priv->pkey & 0xff;
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (likely(neigh))
goto send_using_neigh;
- ipoib_mcast_send(dev, cb->hwaddr, skb);
+ ipoib_mcast_send(dev, phdr->hwaddr, skb);
return NETDEV_TX_OK;
}
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
case htons(ETH_P_TIPC):
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (unlikely(!neigh)) {
- neigh_add_path(skb, cb->hwaddr, dev);
+ neigh_add_path(skb, phdr->hwaddr, dev);
return NETDEV_TX_OK;
}
break;
case htons(ETH_P_ARP):
case htons(ETH_P_RARP):
/* for unicast ARP and RARP should always perform path find */
- unicast_arp_send(skb, dev, cb);
+ unicast_arp_send(skb, dev, phdr);
return NETDEV_TX_OK;
default:
/* ethertype not supported by IPoIB */
goto unref;
}
} else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(phdr->hwaddr));
goto unref;
}
if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, sizeof(*phdr));
spin_lock_irqsave(&priv->lock, flags);
__skb_queue_tail(&neigh->queue, skb);
spin_unlock_irqrestore(&priv->lock, flags);
unsigned short type,
const void *daddr, const void *saddr, unsigned len)
{
+ struct ipoib_pseudo_header *phdr;
struct ipoib_header *header;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
/*
* we don't rely on dst_entry structure, always stuff the
- * destination address into skb->cb so we can figure out where
+ * destination address into skb hard header so we can figure out where
* to send the packet later.
*/
- memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
+ phdr = (struct ipoib_pseudo_header *) skb_push(skb, sizeof(*phdr));
+ memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
- return sizeof *header;
+ return IPOIB_HARD_LEN;
}
static void ipoib_set_mcast_list(struct net_device *dev)
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- dev->hard_header_len = IPOIB_ENCAP_LEN;
+ dev->hard_header_len = IPOIB_HARD_LEN;
dev->addr_len = INFINIBAND_ALEN;
dev->type = ARPHRD_INFINIBAND;
dev->tx_queue_len = ipoib_sendq_size * 2;
__ipoib_mcast_add(dev, mcast);
list_add_tail(&mcast->list, &priv->multicast_list);
}
- if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE)
+ if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, sizeof(struct ipoib_pseudo_header));
skb_queue_tail(&mcast->pkt_queue, skb);
- else {
+ } else {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
}
if (set_properties) {
psmouse->vendor = "FocalTech";
- psmouse->name = "FocalTech Touchpad";
+ psmouse->name = "Touchpad";
}
return 0;
}
input_mt_report_pointer_emulation(dev, true);
- input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
- input_sync(psmouse->dev);
+ input_report_key(dev, BTN_LEFT, state->pressed);
+ input_sync(dev);
}
static void focaltech_process_touch_packet(struct psmouse *psmouse,
DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
},
},
+ {
+ /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+ },
+ },
{ }
};
st = read_auto;
break;
case 0:
- if (mddev->in_sync)
- st = clean;
- else if (test_bit(MD_CHANGE_PENDING, &mddev->flags))
+ if (test_bit(MD_CHANGE_PENDING, &mddev->flags))
st = write_pending;
+ else if (mddev->in_sync)
+ st = clean;
else if (mddev->safemode)
st = active_idle;
else
if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
- mddev->curr_resync > 2) {
+ mddev->curr_resync > 3) {
mddev->curr_resync_completed = mddev->curr_resync;
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
}
mddev->pers->sync_request(mddev, max_sectors, &skipped);
if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
- mddev->curr_resync > 2) {
+ mddev->curr_resync > 3) {
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
if (mddev->curr_resync >= mddev->recovery_cp) {
struct bio *to_put = NULL;
int mirror = find_bio_disk(r1_bio, bio);
struct md_rdev *rdev = conf->mirrors[mirror].rdev;
+ bool discard_error;
+
+ discard_error = bio->bi_error && bio_op(bio) == REQ_OP_DISCARD;
/*
* 'one mirror IO has finished' event handler:
*/
- if (bio->bi_error) {
+ if (bio->bi_error && !discard_error) {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &
/* Maybe we can clear some bad blocks. */
if (is_badblock(rdev, r1_bio->sector, r1_bio->sectors,
- &first_bad, &bad_sectors)) {
+ &first_bad, &bad_sectors) && !discard_error) {
r1_bio->bios[mirror] = IO_MADE_GOOD;
set_bit(R1BIO_MadeGood, &r1_bio->state);
}
* This is all done synchronously while the array is
* frozen
*/
+
+ bio = r1_bio->bios[r1_bio->read_disk];
+ bdevname(bio->bi_bdev, b);
+ bio_put(bio);
+ r1_bio->bios[r1_bio->read_disk] = NULL;
+
if (mddev->ro == 0) {
freeze_array(conf, 1);
fix_read_error(conf, r1_bio->read_disk,
r1_bio->sector, r1_bio->sectors);
unfreeze_array(conf);
- } else
- md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+ } else {
+ r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED;
+ }
+
rdev_dec_pending(conf->mirrors[r1_bio->read_disk].rdev, conf->mddev);
- bio = r1_bio->bios[r1_bio->read_disk];
- bdevname(bio->bi_bdev, b);
read_more:
disk = read_balance(conf, r1_bio, &max_sectors);
if (disk == -1) {
} else {
const unsigned long do_sync
= r1_bio->master_bio->bi_opf & REQ_SYNC;
- if (bio) {
- r1_bio->bios[r1_bio->read_disk] =
- mddev->ro ? IO_BLOCKED : NULL;
- bio_put(bio);
- }
r1_bio->read_disk = disk;
bio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev);
bio_trim(bio, r1_bio->sector - bio->bi_iter.bi_sector,
struct r10conf *conf = r10_bio->mddev->private;
int slot, repl;
struct md_rdev *rdev = NULL;
+ bool discard_error;
+
+ discard_error = bio->bi_error && bio_op(bio) == REQ_OP_DISCARD;
dev = find_bio_disk(conf, r10_bio, bio, &slot, &repl);
/*
* this branch is our 'one mirror IO has finished' event handler:
*/
- if (bio->bi_error) {
+ if (bio->bi_error && !discard_error) {
if (repl)
/* Never record new bad blocks to replacement,
* just fail it.
if (is_badblock(rdev,
r10_bio->devs[slot].addr,
r10_bio->sectors,
- &first_bad, &bad_sectors)) {
+ &first_bad, &bad_sectors) && !discard_error) {
bio_put(bio);
if (repl)
r10_bio->devs[slot].repl_bio = IO_MADE_GOOD;
* 1's seq + 10 and let superblock points to meta2. The same recovery will
* not think meta 3 is a valid meta, because its seq doesn't match
*/
- if (ctx.seq > log->last_cp_seq + 1) {
+ if (ctx.seq > log->last_cp_seq) {
int ret;
ret = r5l_log_write_empty_meta_block(log, ctx.pos, ctx.seq + 10);
log->seq = ctx.seq + 11;
log->log_start = r5l_ring_add(log, ctx.pos, BLOCK_SECTORS);
r5l_write_super(log, ctx.pos);
+ log->last_checkpoint = ctx.pos;
+ log->next_checkpoint = ctx.pos;
} else {
log->log_start = ctx.pos;
log->seq = ctx.seq;
if (create_super) {
log->last_cp_seq = prandom_u32();
cp = 0;
+ r5l_log_write_empty_meta_block(log, cp, log->last_cp_seq);
/*
* Make sure super points to correct address. Log might have
* data very soon. If super hasn't correct log tail address,
if (log->max_free_space > RECLAIM_MAX_FREE_SPACE)
log->max_free_space = RECLAIM_MAX_FREE_SPACE;
log->last_checkpoint = cp;
+ log->next_checkpoint = cp;
__free_page(page);
u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
(read ? 0x80 : 0);
+ int ret;
+
+ mutex_lock(&fc_usb->data_mutex);
+ if (!read)
+ memcpy(fc_usb->data, val, sizeof(*val));
- int len = usb_control_msg(fc_usb->udev,
+ ret = usb_control_msg(fc_usb->udev,
read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
request,
request_type, /* 0xc0 read or 0x40 write */
wAddress,
0,
- val,
+ fc_usb->data,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * HZ);
- if (len != sizeof(u32)) {
+ if (ret != sizeof(u32)) {
err("error while %s dword from %d (%d).", read ? "reading" :
"writing", wAddress, wRegOffsPCI);
- return -EIO;
+ if (ret >= 0)
+ ret = -EIO;
}
- return 0;
+
+ if (read && ret >= 0)
+ memcpy(val, fc_usb->data, sizeof(*val));
+ mutex_unlock(&fc_usb->data_mutex);
+
+ return ret;
}
/*
* DKT 010817 - add support for V8 memory read/write and flash update
{
u8 request_type = USB_TYPE_VENDOR;
u16 wIndex;
- int nWaitTime, pipe, len;
+ int nWaitTime, pipe, ret;
wIndex = page << 8;
+ if (buflen > sizeof(fc_usb->data)) {
+ err("Buffer size bigger than max URB control message\n");
+ return -EIO;
+ }
+
switch (req) {
case B2C2_USB_READ_V8_MEM:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
wAddress, wIndex, buflen);
- len = usb_control_msg(fc_usb->udev, pipe,
+ mutex_lock(&fc_usb->data_mutex);
+
+ if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+ memcpy(fc_usb->data, pbBuffer, buflen);
+
+ ret = usb_control_msg(fc_usb->udev, pipe,
req,
request_type,
wAddress,
wIndex,
- pbBuffer,
+ fc_usb->data,
buflen,
nWaitTime * HZ);
+ if (ret != buflen)
+ ret = -EIO;
+
+ if (ret >= 0) {
+ ret = 0;
+ if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ memcpy(pbBuffer, fc_usb->data, buflen);
+ }
- debug_dump(pbBuffer, len, deb_v8);
- return len == buflen ? 0 : -EIO;
+ mutex_unlock(&fc_usb->data_mutex);
+
+ debug_dump(pbBuffer, ret, deb_v8);
+ return ret;
}
#define bytes_left_to_read_on_page(paddr,buflen) \
fc->dvb_adapter.proposed_mac, 6);
}
-#if 0
-static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
- flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
- u16 buflen, u8 *pvBuffer)
-{
- u16 wValue;
- u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
- int nWaitTime = 2,
- pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;
- wValue = (func << 8) | extra;
-
- len = usb_control_msg(fc_usb->udev,pipe,
- B2C2_USB_UTILITY,
- request_type,
- wValue,
- wIndex,
- pvBuffer,
- buflen,
- nWaitTime * HZ);
- return len == buflen ? 0 : -EIO;
-}
-#endif
-
/* usb i2c stuff */
static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
{
struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
u16 wValue, wIndex;
- int nWaitTime,pipe,len;
+ int nWaitTime, pipe, ret;
u8 request_type = USB_TYPE_VENDOR;
+ if (buflen > sizeof(fc_usb->data)) {
+ err("Buffer size bigger than max URB control message\n");
+ return -EIO;
+ }
+
switch (func) {
case USB_FUNC_I2C_WRITE:
case USB_FUNC_I2C_MULTIWRITE:
wValue & 0xff, wValue >> 8,
wIndex & 0xff, wIndex >> 8);
- len = usb_control_msg(fc_usb->udev,pipe,
+ mutex_lock(&fc_usb->data_mutex);
+
+ if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+ memcpy(fc_usb->data, buf, buflen);
+
+ ret = usb_control_msg(fc_usb->udev, pipe,
req,
request_type,
wValue,
wIndex,
- buf,
+ fc_usb->data,
buflen,
nWaitTime * HZ);
- return len == buflen ? 0 : -EREMOTEIO;
+
+ if (ret != buflen)
+ ret = -EIO;
+
+ if (ret >= 0) {
+ ret = 0;
+ if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ memcpy(buf, fc_usb->data, buflen);
+ }
+
+ mutex_unlock(&fc_usb->data_mutex);
+
+ return 0;
}
/* actual bus specific access functions,
/* general flexcop init */
fc_usb = fc->bus_specific;
fc_usb->fc_dev = fc;
+ mutex_init(&fc_usb->data_mutex);
fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
u8 tmp_buffer[1023+190];
int tmp_buffer_length;
+
+ /* for URB control messages */
+ u8 data[80];
+ struct mutex data_mutex;
};
#if 0
static int write_packet(struct usb_device *udev,
u8 request, u8 * registers, u16 start, size_t size)
{
+ unsigned char *buf;
+ int ret;
+
if (!registers || size <= 0)
return -EINVAL;
- return usb_control_msg(udev,
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, registers, size);
+
+ ret = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
start, /* value */
0, /* index */
- registers, /* buffer */
+ buf, /* buffer */
size,
HZ);
+
+ kfree(buf);
+ return ret;
}
/****************************************************************************
static int read_packet(struct usb_device *udev,
u8 request, u8 * registers, u16 start, size_t size)
{
+ unsigned char *buf;
+ int ret;
+
if (!registers || size <= 0)
return -EINVAL;
- return usb_control_msg(udev,
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
request,
USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
start, /* value */
0, /* index */
- registers, /* buffer */
+ buf, /* buffer */
size,
HZ);
+
+ if (ret >= 0)
+ memcpy(registers, buf, size);
+
+ kfree(buf);
+
+ return ret;
}
/******************************************************************************
struct af9005_device_state {
u8 sequence;
int led_state;
+ unsigned char data[256];
+ struct mutex data_mutex;
};
static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg,
int readwrite, int type, u8 * values, int len)
{
struct af9005_device_state *st = d->priv;
- u8 obuf[16] = { 0 };
- u8 ibuf[17] = { 0 };
- u8 command;
- int i;
- int ret;
+ u8 command, seq;
+ int i, ret;
if (len < 1) {
err("generic read/write, less than 1 byte. Makes no sense.");
return -EINVAL;
}
- obuf[0] = 14; /* rest of buffer length low */
- obuf[1] = 0; /* rest of buffer length high */
+ mutex_lock(&st->data_mutex);
+ st->data[0] = 14; /* rest of buffer length low */
+ st->data[1] = 0; /* rest of buffer length high */
- obuf[2] = AF9005_REGISTER_RW; /* register operation */
- obuf[3] = 12; /* rest of buffer length */
+ st->data[2] = AF9005_REGISTER_RW; /* register operation */
+ st->data[3] = 12; /* rest of buffer length */
- obuf[4] = st->sequence++; /* sequence number */
+ st->data[4] = seq = st->sequence++; /* sequence number */
- obuf[5] = (u8) (reg >> 8); /* register address */
- obuf[6] = (u8) (reg & 0xff);
+ st->data[5] = (u8) (reg >> 8); /* register address */
+ st->data[6] = (u8) (reg & 0xff);
if (type == AF9005_OFDM_REG) {
command = AF9005_CMD_OFDM_REG;
command |= readwrite;
if (readwrite == AF9005_CMD_WRITE)
for (i = 0; i < len; i++)
- obuf[8 + i] = values[i];
+ st->data[8 + i] = values[i];
else if (type == AF9005_TUNER_REG)
/* read command for tuner, the first byte contains the i2c address */
- obuf[8] = values[0];
- obuf[7] = command;
+ st->data[8] = values[0];
+ st->data[7] = command;
- ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 17, 0);
+ ret = dvb_usb_generic_rw(d, st->data, 16, st->data, 17, 0);
if (ret)
- return ret;
+ goto ret;
/* sanity check */
- if (ibuf[2] != AF9005_REGISTER_RW_ACK) {
+ if (st->data[2] != AF9005_REGISTER_RW_ACK) {
err("generic read/write, wrong reply code.");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
- if (ibuf[3] != 0x0d) {
+ if (st->data[3] != 0x0d) {
err("generic read/write, wrong length in reply.");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
- if (ibuf[4] != obuf[4]) {
+ if (st->data[4] != seq) {
err("generic read/write, wrong sequence in reply.");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
/*
- Windows driver doesn't check these fields, in fact sometimes
- the register in the reply is different that what has been sent
-
- if (ibuf[5] != obuf[5] || ibuf[6] != obuf[6]) {
- err("generic read/write, wrong register in reply.");
- return -EIO;
- }
- if (ibuf[7] != command) {
- err("generic read/write wrong command in reply.");
- return -EIO;
- }
+ * In thesis, both input and output buffers should have
+ * identical values for st->data[5] to st->data[8].
+ * However, windows driver doesn't check these fields, in fact
+ * sometimes the register in the reply is different that what
+ * has been sent
*/
- if (ibuf[16] != 0x01) {
+ if (st->data[16] != 0x01) {
err("generic read/write wrong status code in reply.");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
+
if (readwrite == AF9005_CMD_READ)
for (i = 0; i < len; i++)
- values[i] = ibuf[8 + i];
+ values[i] = st->data[8 + i];
- return 0;
+ret:
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
struct af9005_device_state *st = d->priv;
int ret, i, packet_len;
- u8 buf[64];
- u8 ibuf[64];
+ u8 seq;
if (wlen < 0) {
err("send command, wlen less than 0 bytes. Makes no sense.");
return -EINVAL;
}
packet_len = wlen + 5;
- buf[0] = (u8) (packet_len & 0xff);
- buf[1] = (u8) ((packet_len & 0xff00) >> 8);
-
- buf[2] = 0x26; /* packet type */
- buf[3] = wlen + 3;
- buf[4] = st->sequence++;
- buf[5] = command;
- buf[6] = wlen;
+
+ mutex_lock(&st->data_mutex);
+
+ st->data[0] = (u8) (packet_len & 0xff);
+ st->data[1] = (u8) ((packet_len & 0xff00) >> 8);
+
+ st->data[2] = 0x26; /* packet type */
+ st->data[3] = wlen + 3;
+ st->data[4] = seq = st->sequence++;
+ st->data[5] = command;
+ st->data[6] = wlen;
for (i = 0; i < wlen; i++)
- buf[7 + i] = wbuf[i];
- ret = dvb_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0);
- if (ret)
- return ret;
- if (ibuf[2] != 0x27) {
+ st->data[7 + i] = wbuf[i];
+ ret = dvb_usb_generic_rw(d, st->data, wlen + 7, st->data, rlen + 7, 0);
+ if (st->data[2] != 0x27) {
err("send command, wrong reply code.");
- return -EIO;
- }
- if (ibuf[4] != buf[4]) {
+ ret = -EIO;
+ } else if (st->data[4] != seq) {
err("send command, wrong sequence in reply.");
- return -EIO;
- }
- if (ibuf[5] != 0x01) {
+ ret = -EIO;
+ } else if (st->data[5] != 0x01) {
err("send command, wrong status code in reply.");
- return -EIO;
- }
- if (ibuf[6] != rlen) {
+ ret = -EIO;
+ } else if (st->data[6] != rlen) {
err("send command, invalid data length in reply.");
- return -EIO;
+ ret = -EIO;
}
- for (i = 0; i < rlen; i++)
- rbuf[i] = ibuf[i + 7];
- return 0;
+ if (!ret) {
+ for (i = 0; i < rlen; i++)
+ rbuf[i] = st->data[i + 7];
+ }
+
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values,
int len)
{
struct af9005_device_state *st = d->priv;
- u8 obuf[16], ibuf[14];
+ u8 seq;
int ret, i;
- memset(obuf, 0, sizeof(obuf));
- memset(ibuf, 0, sizeof(ibuf));
+ mutex_lock(&st->data_mutex);
- obuf[0] = 14; /* length of rest of packet low */
- obuf[1] = 0; /* length of rest of packer high */
+ memset(st->data, 0, sizeof(st->data));
- obuf[2] = 0x2a; /* read/write eeprom */
+ st->data[0] = 14; /* length of rest of packet low */
+ st->data[1] = 0; /* length of rest of packer high */
- obuf[3] = 12; /* size */
+ st->data[2] = 0x2a; /* read/write eeprom */
- obuf[4] = st->sequence++;
+ st->data[3] = 12; /* size */
- obuf[5] = 0; /* read */
+ st->data[4] = seq = st->sequence++;
- obuf[6] = len;
- obuf[7] = address;
- ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 14, 0);
- if (ret)
- return ret;
- if (ibuf[2] != 0x2b) {
+ st->data[5] = 0; /* read */
+
+ st->data[6] = len;
+ st->data[7] = address;
+ ret = dvb_usb_generic_rw(d, st->data, 16, st->data, 14, 0);
+ if (st->data[2] != 0x2b) {
err("Read eeprom, invalid reply code");
- return -EIO;
- }
- if (ibuf[3] != 10) {
+ ret = -EIO;
+ } else if (st->data[3] != 10) {
err("Read eeprom, invalid reply length");
- return -EIO;
- }
- if (ibuf[4] != obuf[4]) {
+ ret = -EIO;
+ } else if (st->data[4] != seq) {
err("Read eeprom, wrong sequence in reply ");
- return -EIO;
- }
- if (ibuf[5] != 1) {
+ ret = -EIO;
+ } else if (st->data[5] != 1) {
err("Read eeprom, wrong status in reply ");
- return -EIO;
+ ret = -EIO;
}
- for (i = 0; i < len; i++) {
- values[i] = ibuf[6 + i];
+
+ if (!ret) {
+ for (i = 0; i < len; i++)
+ values[i] = st->data[6 + i];
}
- return 0;
+ mutex_unlock(&st->data_mutex);
+
+ return ret;
}
-static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply)
+static int af9005_boot_packet(struct usb_device *udev, int type, u8 *reply,
+ u8 *buf, int size)
{
- u8 buf[FW_BULKOUT_SIZE + 2];
u16 checksum;
int act_len, i, ret;
- memset(buf, 0, sizeof(buf));
+
+ memset(buf, 0, size);
buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff);
buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff);
switch (type) {
{
int i, packets, ret, act_len;
- u8 buf[FW_BULKOUT_SIZE + 2];
+ u8 *buf;
u8 reply;
- ret = af9005_boot_packet(udev, FW_CONFIG, &reply);
+ buf = kmalloc(FW_BULKOUT_SIZE + 2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = af9005_boot_packet(udev, FW_CONFIG, &reply, buf,
+ FW_BULKOUT_SIZE + 2);
if (ret)
- return ret;
+ goto err;
if (reply != 0x01) {
err("before downloading firmware, FW_CONFIG expected 0x01, received 0x%x", reply);
- return -EIO;
+ ret = -EIO;
+ goto err;
}
packets = fw->size / FW_BULKOUT_SIZE;
buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff);
buf, FW_BULKOUT_SIZE + 2, &act_len, 1000);
if (ret) {
err("firmware download failed at packet %d with code %d", i, ret);
- return ret;
+ goto err;
}
}
- ret = af9005_boot_packet(udev, FW_CONFIRM, &reply);
+ ret = af9005_boot_packet(udev, FW_CONFIRM, &reply,
+ buf, FW_BULKOUT_SIZE + 2);
if (ret)
- return ret;
+ goto err;
if (reply != (u8) (packets & 0xff)) {
err("after downloading firmware, FW_CONFIRM expected 0x%x, received 0x%x", packets & 0xff, reply);
- return -EIO;
+ ret = -EIO;
+ goto err;
}
- ret = af9005_boot_packet(udev, FW_BOOT, &reply);
+ ret = af9005_boot_packet(udev, FW_BOOT, &reply, buf,
+ FW_BULKOUT_SIZE + 2);
if (ret)
- return ret;
- ret = af9005_boot_packet(udev, FW_CONFIG, &reply);
+ goto err;
+ ret = af9005_boot_packet(udev, FW_CONFIG, &reply, buf,
+ FW_BULKOUT_SIZE + 2);
if (ret)
- return ret;
+ goto err;
if (reply != 0x02) {
err("after downloading firmware, FW_CONFIG expected 0x02, received 0x%x", reply);
- return -EIO;
+ ret = -EIO;
+ goto err;
}
- return 0;
+err:
+ kfree(buf);
+ return ret;
}
{
struct af9005_device_state *st = d->priv;
int ret, len;
-
- u8 obuf[5];
- u8 ibuf[256];
+ u8 seq;
*state = REMOTE_NO_KEY_PRESSED;
if (rc_decode == NULL) {
/* it shouldn't never come here */
return 0;
}
+
+ mutex_lock(&st->data_mutex);
+
/* deb_info("rc_query\n"); */
- obuf[0] = 3; /* rest of packet length low */
- obuf[1] = 0; /* rest of packet lentgh high */
- obuf[2] = 0x40; /* read remote */
- obuf[3] = 1; /* rest of packet length */
- obuf[4] = st->sequence++; /* sequence number */
- ret = dvb_usb_generic_rw(d, obuf, 5, ibuf, 256, 0);
+ st->data[0] = 3; /* rest of packet length low */
+ st->data[1] = 0; /* rest of packet lentgh high */
+ st->data[2] = 0x40; /* read remote */
+ st->data[3] = 1; /* rest of packet length */
+ st->data[4] = seq = st->sequence++; /* sequence number */
+ ret = dvb_usb_generic_rw(d, st->data, 5, st->data, 256, 0);
if (ret) {
err("rc query failed");
- return ret;
+ goto ret;
}
- if (ibuf[2] != 0x41) {
+ if (st->data[2] != 0x41) {
err("rc query bad header.");
- return -EIO;
- }
- if (ibuf[4] != obuf[4]) {
+ ret = -EIO;
+ goto ret;
+ } else if (st->data[4] != seq) {
err("rc query bad sequence.");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
- len = ibuf[5];
+ len = st->data[5];
if (len > 246) {
err("rc query invalid length");
- return -EIO;
+ ret = -EIO;
+ goto ret;
}
if (len > 0) {
deb_rc("rc data (%d) ", len);
- debug_dump((ibuf + 6), len, deb_rc);
- ret = rc_decode(d, &ibuf[6], len, event, state);
+ debug_dump((st->data + 6), len, deb_rc);
+ ret = rc_decode(d, &st->data[6], len, event, state);
if (ret) {
err("rc_decode failed");
- return ret;
+ goto ret;
} else {
deb_rc("rc_decode state %x event %x\n", *state, *event);
if (*state == REMOTE_KEY_REPEAT)
*event = d->last_event;
}
}
- return 0;
+
+ret:
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
static int af9005_power_ctrl(struct dvb_usb_device *d, int onoff)
int *cold)
{
int ret;
- u8 reply;
- ret = af9005_boot_packet(udev, FW_CONFIG, &reply);
+ u8 reply, *buf;
+
+ buf = kmalloc(FW_BULKOUT_SIZE + 2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = af9005_boot_packet(udev, FW_CONFIG, &reply,
+ buf, FW_BULKOUT_SIZE + 2);
if (ret)
- return ret;
+ goto err;
deb_info("result of FW_CONFIG in identify state %d\n", reply);
if (reply == 0x01)
*cold = 1;
else
return -EIO;
deb_info("Identify state cold = %d\n", *cold);
- return 0;
+
+err:
+ kfree(buf);
+ return ret;
}
static struct dvb_usb_device_properties af9005_properties;
static int af9005_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- return dvb_usb_device_init(intf, &af9005_properties,
- THIS_MODULE, NULL, adapter_nr);
+ struct dvb_usb_device *d;
+ struct af9005_device_state *st;
+ int ret;
+
+ ret = dvb_usb_device_init(intf, &af9005_properties,
+ THIS_MODULE, &d, adapter_nr);
+
+ if (ret < 0)
+ return ret;
+
+ st = d->priv;
+ mutex_init(&st->data_mutex);
+
+ return 0;
}
enum af9005_usb_table_entry {
struct cinergyt2_state {
u8 rc_counter;
+ unsigned char data[64];
+ struct mutex data_mutex;
};
/* We are missing a release hook with usb_device data */
static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable)
{
- char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
- char result[64];
- return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result,
- sizeof(result), 0);
+ struct dvb_usb_device *d = adap->dev;
+ struct cinergyt2_state *st = d->priv;
+ int ret;
+
+ mutex_lock(&st->data_mutex);
+ st->data[0] = CINERGYT2_EP1_CONTROL_STREAM_TRANSFER;
+ st->data[1] = enable ? 1 : 0;
+
+ ret = dvb_usb_generic_rw(d, st->data, 2, st->data, 64, 0);
+ mutex_unlock(&st->data_mutex);
+
+ return ret;
}
static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable)
{
- char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 };
- char state[3];
- return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0);
+ struct cinergyt2_state *st = d->priv;
+ int ret;
+
+ mutex_lock(&st->data_mutex);
+ st->data[0] = CINERGYT2_EP1_SLEEP_MODE;
+ st->data[1] = enable ? 0 : 1;
+
+ ret = dvb_usb_generic_rw(d, st->data, 2, st->data, 3, 0);
+ mutex_unlock(&st->data_mutex);
+
+ return ret;
}
static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
{
- char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION };
- char state[3];
+ struct dvb_usb_device *d = adap->dev;
+ struct cinergyt2_state *st = d->priv;
int ret;
adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev);
- ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
- sizeof(state), 0);
+ mutex_lock(&st->data_mutex);
+ st->data[0] = CINERGYT2_EP1_GET_FIRMWARE_VERSION;
+
+ ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 3, 0);
if (ret < 0) {
deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep "
"state info\n");
}
+ mutex_unlock(&st->data_mutex);
/* Copy this pointer as we are gonna need it in the release phase */
cinergyt2_usb_device = adap->dev;
- return 0;
+ return ret;
}
static struct rc_map_table rc_map_cinergyt2_table[] = {
static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
struct cinergyt2_state *st = d->priv;
- u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS;
- int i;
+ int i, ret;
*state = REMOTE_NO_KEY_PRESSED;
- dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0);
- if (key[4] == 0xff) {
+ mutex_lock(&st->data_mutex);
+ st->data[0] = CINERGYT2_EP1_GET_RC_EVENTS;
+
+ ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0);
+ if (ret < 0)
+ goto ret;
+
+ if (st->data[4] == 0xff) {
/* key repeat */
st->rc_counter++;
if (st->rc_counter > RC_REPEAT_DELAY) {
*event = d->last_event;
deb_rc("repeat key, event %x\n",
*event);
- return 0;
+ goto ret;
}
}
deb_rc("repeated key (non repeatable)\n");
}
- return 0;
+ goto ret;
}
/* hack to pass checksum on the custom field */
- key[2] = ~key[1];
- dvb_usb_nec_rc_key_to_event(d, key, event, state);
- if (key[0] != 0) {
+ st->data[2] = ~st->data[1];
+ dvb_usb_nec_rc_key_to_event(d, st->data, event, state);
+ if (st->data[0] != 0) {
if (*event != d->last_event)
st->rc_counter = 0;
- deb_rc("key: %*ph\n", 5, key);
+ deb_rc("key: %*ph\n", 5, st->data);
}
- return 0;
+
+ret:
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
static int cinergyt2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- return dvb_usb_device_init(intf, &cinergyt2_properties,
- THIS_MODULE, NULL, adapter_nr);
+ struct dvb_usb_device *d;
+ struct cinergyt2_state *st;
+ int ret;
+
+ ret = dvb_usb_device_init(intf, &cinergyt2_properties,
+ THIS_MODULE, &d, adapter_nr);
+ if (ret < 0)
+ return ret;
+
+ st = d->priv;
+ mutex_init(&st->data_mutex);
+
+ return 0;
}
struct cinergyt2_fe_state {
struct dvb_frontend fe;
struct dvb_usb_device *d;
+
+ unsigned char data[64];
+ struct mutex data_mutex;
+
+ struct dvbt_get_status_msg status;
};
static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
enum fe_status *status)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_get_status_msg result;
- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result,
- sizeof(result), 0);
+ mutex_lock(&state->data_mutex);
+ state->data[0] = CINERGYT2_EP1_GET_TUNER_STATUS;
+
+ ret = dvb_usb_generic_rw(state->d, state->data, 1,
+ state->data, sizeof(state->status), 0);
+ if (!ret)
+ memcpy(&state->status, state->data, sizeof(state->status));
+ mutex_unlock(&state->data_mutex);
+
if (ret < 0)
return ret;
*status = 0;
- if (0xffff - le16_to_cpu(result.gain) > 30)
+ if (0xffff - le16_to_cpu(state->status.gain) > 30)
*status |= FE_HAS_SIGNAL;
- if (result.lock_bits & (1 << 6))
+ if (state->status.lock_bits & (1 << 6))
*status |= FE_HAS_LOCK;
- if (result.lock_bits & (1 << 5))
+ if (state->status.lock_bits & (1 << 5))
*status |= FE_HAS_SYNC;
- if (result.lock_bits & (1 << 4))
+ if (state->status.lock_bits & (1 << 4))
*status |= FE_HAS_CARRIER;
- if (result.lock_bits & (1 << 1))
+ if (state->status.lock_bits & (1 << 1))
*status |= FE_HAS_VITERBI;
if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_get_status_msg status;
- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
- int ret;
-
- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
- sizeof(status), 0);
- if (ret < 0)
- return ret;
- *ber = le32_to_cpu(status.viterbi_error_rate);
+ *ber = le32_to_cpu(state->status.viterbi_error_rate);
return 0;
}
static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_get_status_msg status;
- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
- int ret;
- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status,
- sizeof(status), 0);
- if (ret < 0) {
- err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n",
- ret);
- return ret;
- }
- *unc = le32_to_cpu(status.uncorrected_block_count);
+ *unc = le32_to_cpu(state->status.uncorrected_block_count);
return 0;
}
u16 *strength)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_get_status_msg status;
- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
- int ret;
- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
- sizeof(status), 0);
- if (ret < 0) {
- err("cinergyt2_fe_read_signal_strength() Failed!"
- " (Error=%d)\n", ret);
- return ret;
- }
- *strength = (0xffff - le16_to_cpu(status.gain));
+ *strength = (0xffff - le16_to_cpu(state->status.gain));
return 0;
}
static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_get_status_msg status;
- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
- int ret;
- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
- sizeof(status), 0);
- if (ret < 0) {
- err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret);
- return ret;
- }
- *snr = (status.snr << 8) | status.snr;
+ *snr = (state->status.snr << 8) | state->status.snr;
return 0;
}
{
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct cinergyt2_fe_state *state = fe->demodulator_priv;
- struct dvbt_set_parameters_msg param;
- char result[2];
+ struct dvbt_set_parameters_msg *param;
int err;
- param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
- param.tps = cpu_to_le16(compute_tps(fep));
- param.freq = cpu_to_le32(fep->frequency / 1000);
- param.flags = 0;
+ mutex_lock(&state->data_mutex);
+
+ param = (void *)state->data;
+ param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+ param->tps = cpu_to_le16(compute_tps(fep));
+ param->freq = cpu_to_le32(fep->frequency / 1000);
+ param->flags = 0;
switch (fep->bandwidth_hz) {
default:
case 8000000:
- param.bandwidth = 8;
+ param->bandwidth = 8;
break;
case 7000000:
- param.bandwidth = 7;
+ param->bandwidth = 7;
break;
case 6000000:
- param.bandwidth = 6;
+ param->bandwidth = 6;
break;
}
- err = dvb_usb_generic_rw(state->d,
- (char *)¶m, sizeof(param),
- result, sizeof(result), 0);
+ err = dvb_usb_generic_rw(state->d, state->data, sizeof(*param),
+ state->data, 2, 0);
if (err < 0)
err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
+ mutex_unlock(&state->data_mutex);
return (err < 0) ? err : 0;
}
s->d = d;
memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
s->fe.demodulator_priv = s;
+ mutex_init(&s->data_mutex);
return &s->fe;
}
#include "si2168.h"
#include "si2157.h"
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE 80
-
/* debug */
static int dvb_usb_cxusb_debug;
module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
static int cxusb_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
- int wo = (rbuf == NULL || rlen == 0); /* write-only */
- u8 sndbuf[MAX_XFER_SIZE];
+ struct cxusb_state *st = d->priv;
+ int ret, wo;
- if (1 + wlen > sizeof(sndbuf)) {
- warn("i2c wr: len=%d is too big!\n",
- wlen);
+ if (1 + wlen > MAX_XFER_SIZE) {
+ warn("i2c wr: len=%d is too big!\n", wlen);
return -EOPNOTSUPP;
}
- memset(sndbuf, 0, 1+wlen);
+ wo = (rbuf == NULL || rlen == 0); /* write-only */
- sndbuf[0] = cmd;
- memcpy(&sndbuf[1], wbuf, wlen);
+ mutex_lock(&st->data_mutex);
+ st->data[0] = cmd;
+ memcpy(&st->data[1], wbuf, wlen);
if (wo)
- return dvb_usb_generic_write(d, sndbuf, 1+wlen);
+ ret = dvb_usb_generic_write(d, st->data, 1 + wlen);
else
- return dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
+ ret = dvb_usb_generic_rw(d, st->data, 1 + wlen,
+ rbuf, rlen, 0);
+
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
/* GPIO */
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ struct dvb_usb_device *d;
+ struct cxusb_state *st;
+
if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf,
&cxusb_bluebird_nano2_needsfirmware_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf,
&cxusb_bluebird_dualdig4_rev2_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
- THIS_MODULE, NULL, adapter_nr) ||
- 0)
+ THIS_MODULE, &d, adapter_nr) ||
+ 0) {
+ st = d->priv;
+ mutex_init(&st->data_mutex);
+
return 0;
+ }
return -EINVAL;
}
#define CMD_ANALOG 0x50
#define CMD_DIGITAL 0x51
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 80
+
struct cxusb_state {
u8 gpio_write_state[3];
struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
+
+ unsigned char data[MAX_XFER_SIZE];
+ struct mutex data_mutex;
};
#endif
usb_rcvctrlpipe(d->udev, 0),
REQUEST_NEW_I2C_READ,
USB_TYPE_VENDOR | USB_DIR_IN,
- value, index, msg[i].buf,
+ value, index, st->buf,
msg[i].len,
USB_CTRL_GET_TIMEOUT);
if (result < 0) {
break;
}
+ if (msg[i].len > sizeof(st->buf)) {
+ deb_info("buffer too small to fit %d bytes\n",
+ msg[i].len);
+ return -EIO;
+ }
+
+ memcpy(msg[i].buf, st->buf, msg[i].len);
+
deb_data("<<< ");
debug_dump(msg[i].buf, msg[i].len, deb_data);
/* I2C ctrl + FE bus; */
st->buf[3] = ((gen_mode << 6) & 0xC0) |
((bus_mode << 4) & 0x30);
+
+ if (msg[i].len > sizeof(st->buf) - 4) {
+ deb_info("i2c message to big: %d\n",
+ msg[i].len);
+ return -EIO;
+ }
+
/* The Actual i2c payload */
memcpy(&st->buf[4], msg[i].buf, msg[i].len);
/* fill in the address */
st->buf[1] = msg[i].addr << 1;
/* fill the buffer */
+ if (msg[i].len > sizeof(st->buf) - 2) {
+ deb_info("i2c xfer to big: %d\n",
+ msg[i].len);
+ return -EIO;
+ }
memcpy(&st->buf[2], msg[i].buf, msg[i].len);
/* write/read request */
/* special thing in the current firmware: when length is zero the read-failed */
len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
- msg[i+1].buf, msg[i+1].len);
+ st->buf, msg[i + 1].len);
if (len <= 0) {
deb_info("I2C read failed on address 0x%02x\n",
msg[i].addr);
break;
}
+ if (msg[i + 1].len > sizeof(st->buf)) {
+ deb_info("i2c xfer buffer to small for %d\n",
+ msg[i].len);
+ return -EIO;
+ }
+ memcpy(msg[i + 1].buf, st->buf, msg[i + 1].len);
+
msg[i+1].len = len;
i++;
#define DEFAULT_RC_INTERVAL 50
-static u8 rc_request[] = { REQUEST_POLL_RC, 0 };
-
/*
* This function is used only when firmware is < 1.20 version. Newer
* firmwares use bulk mode, with functions implemented at dib0700_core,
*/
static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d)
{
- u8 key[4];
enum rc_type protocol;
u32 scancode;
u8 toggle;
return 0;
}
- i = dib0700_ctrl_rd(d, rc_request, 2, key, 4);
+ st->buf[0] = REQUEST_POLL_RC;
+ st->buf[1] = 0;
+
+ i = dib0700_ctrl_rd(d, st->buf, 2, st->buf, 4);
if (i <= 0) {
err("RC Query Failed");
- return -1;
+ return -EIO;
}
/* losing half of KEY_0 events from Philipps rc5 remotes.. */
- if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0)
+ if (st->buf[0] == 0 && st->buf[1] == 0
+ && st->buf[2] == 0 && st->buf[3] == 0)
return 0;
- /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]); */
+ /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)st->buf[3 - 2],(int)st->buf[3 - 3],(int)st->buf[3 - 1],(int)st->buf[3]); */
dib0700_rc_setup(d, NULL); /* reset ir sensor data to prevent false events */
switch (d->props.rc.core.protocol) {
case RC_BIT_NEC:
/* NEC protocol sends repeat code as 0 0 0 FF */
- if ((key[3-2] == 0x00) && (key[3-3] == 0x00) &&
- (key[3] == 0xff)) {
+ if ((st->buf[3 - 2] == 0x00) && (st->buf[3 - 3] == 0x00) &&
+ (st->buf[3] == 0xff)) {
rc_repeat(d->rc_dev);
return 0;
}
protocol = RC_TYPE_NEC;
- scancode = RC_SCANCODE_NEC(key[3-2], key[3-3]);
+ scancode = RC_SCANCODE_NEC(st->buf[3 - 2], st->buf[3 - 3]);
toggle = 0;
break;
default:
/* RC-5 protocol changes toggle bit on new keypress */
protocol = RC_TYPE_RC5;
- scancode = RC_SCANCODE_RC5(key[3-2], key[3-3]);
- toggle = key[3-1];
+ scancode = RC_SCANCODE_RC5(st->buf[3 - 2], st->buf[3 - 3]);
+ toggle = st->buf[3 - 1];
break;
}
int dibusb_power_ctrl(struct dvb_usb_device *d, int onoff)
{
- u8 b[3];
+ u8 *b;
int ret;
+
+ b = kmalloc(3, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
b[0] = DIBUSB_REQ_SET_IOCTL;
b[1] = DIBUSB_IOCTL_CMD_POWER_MODE;
b[2] = onoff ? DIBUSB_IOCTL_POWER_WAKEUP : DIBUSB_IOCTL_POWER_SLEEP;
- ret = dvb_usb_generic_write(d,b,3);
+
+ ret = dvb_usb_generic_write(d, b, 3);
+
+ kfree(b);
+
msleep(10);
+
return ret;
}
EXPORT_SYMBOL(dibusb_power_ctrl);
int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
- u8 b[3] = { 0 };
int ret;
+ u8 *b;
+
+ b = kmalloc(3, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
if ((ret = dibusb_streaming_ctrl(adap,onoff)) < 0)
- return ret;
+ goto ret;
if (onoff) {
b[0] = DIBUSB_REQ_SET_STREAMING_MODE;
b[1] = 0x00;
- if ((ret = dvb_usb_generic_write(adap->dev,b,2)) < 0)
- return ret;
+ ret = dvb_usb_generic_write(adap->dev, b, 2);
+ if (ret < 0)
+ goto ret;
}
b[0] = DIBUSB_REQ_SET_IOCTL;
b[1] = onoff ? DIBUSB_IOCTL_CMD_ENABLE_STREAM : DIBUSB_IOCTL_CMD_DISABLE_STREAM;
- return dvb_usb_generic_write(adap->dev,b,3);
+ ret = dvb_usb_generic_write(adap->dev, b, 3);
+
+ret:
+ kfree(b);
+ return ret;
}
EXPORT_SYMBOL(dibusb2_0_streaming_ctrl);
int dibusb2_0_power_ctrl(struct dvb_usb_device *d, int onoff)
{
- if (onoff) {
- u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP };
- return dvb_usb_generic_write(d,b,3);
- } else
+ u8 *b;
+ int ret;
+
+ if (!onoff)
return 0;
+
+ b = kmalloc(3, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ b[0] = DIBUSB_REQ_SET_IOCTL;
+ b[1] = DIBUSB_IOCTL_CMD_POWER_MODE;
+ b[2] = DIBUSB_IOCTL_POWER_WAKEUP;
+
+ ret = dvb_usb_generic_write(d, b, 3);
+
+ kfree(b);
+
+ return ret;
}
EXPORT_SYMBOL(dibusb2_0_power_ctrl);
static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
- u8 sndbuf[MAX_XFER_SIZE]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
+ u8 *sndbuf;
+ int ret, wo, len;
+
/* write only ? */
- int wo = (rbuf == NULL || rlen == 0),
- len = 2 + wlen + (wo ? 0 : 2);
+ wo = (rbuf == NULL || rlen == 0);
+
+ len = 2 + wlen + (wo ? 0 : 2);
+
+ sndbuf = kmalloc(MAX_XFER_SIZE, GFP_KERNEL);
+ if (!sndbuf)
+ return -ENOMEM;
- if (4 + wlen > sizeof(sndbuf)) {
+ if (4 + wlen > MAX_XFER_SIZE) {
warn("i2c wr: len=%d is too big!\n", wlen);
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto ret;
}
sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
- memcpy(&sndbuf[2],wbuf,wlen);
+ memcpy(&sndbuf[2], wbuf, wlen);
if (!wo) {
- sndbuf[wlen+2] = (rlen >> 8) & 0xff;
- sndbuf[wlen+3] = rlen & 0xff;
+ sndbuf[wlen + 2] = (rlen >> 8) & 0xff;
+ sndbuf[wlen + 3] = rlen & 0xff;
}
- return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0);
+ ret = dvb_usb_generic_rw(d, sndbuf, len, rbuf, rlen, 0);
+
+ret:
+ kfree(sndbuf);
+ return ret;
}
/*
int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
- u8 key[5],cmd = DIBUSB_REQ_POLL_REMOTE;
- dvb_usb_generic_rw(d,&cmd,1,key,5,0);
- dvb_usb_nec_rc_key_to_event(d,key,event,state);
- if (key[0] != 0)
- deb_info("key: %*ph\n", 5, key);
- return 0;
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(5, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = DIBUSB_REQ_POLL_REMOTE;
+
+ ret = dvb_usb_generic_rw(d, buf, 1, buf, 5, 0);
+ if (ret < 0)
+ goto ret;
+
+ dvb_usb_nec_rc_key_to_event(d, buf, event, state);
+
+ if (buf[0] != 0)
+ deb_info("key: %*ph\n", 5, buf);
+
+ kfree(buf);
+
+ret:
+ return ret;
}
EXPORT_SYMBOL(dibusb_rc_query);
#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01
#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 64
+
struct dibusb_state {
struct dib_fe_xfer_ops ops;
int mt2060_present;
static int digitv_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
- int wo = (rbuf == NULL || rlen == 0); /* write-only */
- u8 sndbuf[7],rcvbuf[7];
- memset(sndbuf,0,7); memset(rcvbuf,0,7);
+ struct digitv_state *st = d->priv;
+ int ret, wo;
- sndbuf[0] = cmd;
- sndbuf[1] = vv;
- sndbuf[2] = wo ? wlen : rlen;
+ wo = (rbuf == NULL || rlen == 0); /* write-only */
+
+ memset(st->sndbuf, 0, 7);
+ memset(st->rcvbuf, 0, 7);
+
+ st->sndbuf[0] = cmd;
+ st->sndbuf[1] = vv;
+ st->sndbuf[2] = wo ? wlen : rlen;
if (wo) {
- memcpy(&sndbuf[3],wbuf,wlen);
- dvb_usb_generic_write(d,sndbuf,7);
+ memcpy(&st->sndbuf[3], wbuf, wlen);
+ ret = dvb_usb_generic_write(d, st->sndbuf, 7);
} else {
- dvb_usb_generic_rw(d,sndbuf,7,rcvbuf,7,10);
- memcpy(rbuf,&rcvbuf[3],rlen);
+ ret = dvb_usb_generic_rw(d, st->sndbuf, 7, st->rcvbuf, 7, 10);
+ memcpy(rbuf, &st->rcvbuf[3], rlen);
}
- return 0;
+ return ret;
}
/* I2C */
#include "dvb-usb.h"
struct digitv_state {
- int is_nxt6000;
+ int is_nxt6000;
+
+ unsigned char sndbuf[7];
+ unsigned char rcvbuf[7];
};
/* protocol (from usblogging and the SDK:
struct dtv_frontend_properties fep;
struct dvb_frontend frontend;
+
+ unsigned char data[80];
+ struct mutex data_mutex;
};
static int dtt200u_fe_read_status(struct dvb_frontend *fe,
enum fe_status *stat)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 st = GET_TUNE_STATUS, b[3];
+ int ret;
+
+ mutex_lock(&state->data_mutex);
+ state->data[0] = GET_TUNE_STATUS;
- dvb_usb_generic_rw(state->d,&st,1,b,3,0);
+ ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
+ if (ret < 0) {
+ *stat = 0;
+ mutex_unlock(&state->data_mutex);
+ return ret;
+ }
- switch (b[0]) {
+ switch (state->data[0]) {
case 0x01:
*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
*stat = 0;
break;
}
+ mutex_unlock(&state->data_mutex);
return 0;
}
static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 bw = GET_VIT_ERR_CNT,b[3];
- dvb_usb_generic_rw(state->d,&bw,1,b,3,0);
- *ber = (b[0] << 16) | (b[1] << 8) | b[2];
- return 0;
+ int ret;
+
+ mutex_lock(&state->data_mutex);
+ state->data[0] = GET_VIT_ERR_CNT;
+
+ ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
+ if (ret >= 0)
+ *ber = (state->data[0] << 16) | (state->data[1] << 8) | state->data[2];
+
+ mutex_unlock(&state->data_mutex);
+ return ret;
}
static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 bw = GET_RS_UNCOR_BLK_CNT,b[2];
+ int ret;
- dvb_usb_generic_rw(state->d,&bw,1,b,2,0);
- *unc = (b[0] << 8) | b[1];
- return 0;
+ mutex_lock(&state->data_mutex);
+ state->data[0] = GET_RS_UNCOR_BLK_CNT;
+
+ ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 2, 0);
+ if (ret >= 0)
+ *unc = (state->data[0] << 8) | state->data[1];
+
+ mutex_unlock(&state->data_mutex);
+ return ret;
}
static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 bw = GET_AGC, b;
- dvb_usb_generic_rw(state->d,&bw,1,&b,1,0);
- *strength = (b << 8) | b;
- return 0;
+ int ret;
+
+ mutex_lock(&state->data_mutex);
+ state->data[0] = GET_AGC;
+
+ ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
+ if (ret >= 0)
+ *strength = (state->data[0] << 8) | state->data[0];
+
+ mutex_unlock(&state->data_mutex);
+ return ret;
}
static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 bw = GET_SNR,br;
- dvb_usb_generic_rw(state->d,&bw,1,&br,1,0);
- *snr = ~((br << 8) | br);
- return 0;
+ int ret;
+
+ mutex_lock(&state->data_mutex);
+ state->data[0] = GET_SNR;
+
+ ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
+ if (ret >= 0)
+ *snr = ~((state->data[0] << 8) | state->data[0]);
+
+ mutex_unlock(&state->data_mutex);
+ return ret;
}
static int dtt200u_fe_init(struct dvb_frontend* fe)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
- u8 b = SET_INIT;
- return dvb_usb_generic_write(state->d,&b,1);
+ int ret;
+
+ mutex_lock(&state->data_mutex);
+ state->data[0] = SET_INIT;
+
+ ret = dvb_usb_generic_write(state->d, state->data, 1);
+ mutex_unlock(&state->data_mutex);
+
+ return ret;
}
static int dtt200u_fe_sleep(struct dvb_frontend* fe)
{
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dtt200u_fe_state *state = fe->demodulator_priv;
- int i;
- enum fe_status st;
+ int ret;
u16 freq = fep->frequency / 250000;
- u8 bwbuf[2] = { SET_BANDWIDTH, 0 },freqbuf[3] = { SET_RF_FREQ, 0, 0 };
+ mutex_lock(&state->data_mutex);
+ state->data[0] = SET_BANDWIDTH;
switch (fep->bandwidth_hz) {
case 8000000:
- bwbuf[1] = 8;
+ state->data[1] = 8;
break;
case 7000000:
- bwbuf[1] = 7;
+ state->data[1] = 7;
break;
case 6000000:
- bwbuf[1] = 6;
+ state->data[1] = 6;
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto ret;
}
- dvb_usb_generic_write(state->d,bwbuf,2);
+ ret = dvb_usb_generic_write(state->d, state->data, 2);
+ if (ret < 0)
+ goto ret;
- freqbuf[1] = freq & 0xff;
- freqbuf[2] = (freq >> 8) & 0xff;
- dvb_usb_generic_write(state->d,freqbuf,3);
+ state->data[0] = SET_RF_FREQ;
+ state->data[1] = freq & 0xff;
+ state->data[2] = (freq >> 8) & 0xff;
+ ret = dvb_usb_generic_write(state->d, state->data, 3);
+ if (ret < 0)
+ goto ret;
- for (i = 0; i < 30; i++) {
- msleep(20);
- dtt200u_fe_read_status(fe, &st);
- if (st & FE_TIMEDOUT)
- continue;
- }
-
- return 0;
+ret:
+ mutex_unlock(&state->data_mutex);
+ return ret;
}
static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
deb_info("attaching frontend dtt200u\n");
state->d = d;
+ mutex_init(&state->data_mutex);
memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+struct dtt200u_state {
+ unsigned char data[80];
+ struct mutex data_mutex;
+};
+
static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff)
{
- u8 b = SET_INIT;
+ struct dtt200u_state *st = d->priv;
+ int ret = 0;
+
+ mutex_lock(&st->data_mutex);
+
+ st->data[0] = SET_INIT;
if (onoff)
- dvb_usb_generic_write(d,&b,2);
+ ret = dvb_usb_generic_write(d, st->data, 2);
- return 0;
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
- u8 b_streaming[2] = { SET_STREAMING, onoff };
- u8 b_rst_pid = RESET_PID_FILTER;
+ struct dtt200u_state *st = adap->dev->priv;
+ int ret;
- dvb_usb_generic_write(adap->dev, b_streaming, 2);
+ mutex_lock(&st->data_mutex);
+ st->data[0] = SET_STREAMING;
+ st->data[1] = onoff;
- if (onoff == 0)
- dvb_usb_generic_write(adap->dev, &b_rst_pid, 1);
- return 0;
+ ret = dvb_usb_generic_write(adap->dev, st->data, 2);
+ if (ret < 0)
+ goto ret;
+
+ if (onoff)
+ goto ret;
+
+ st->data[0] = RESET_PID_FILTER;
+ ret = dvb_usb_generic_write(adap->dev, st->data, 1);
+
+ret:
+ mutex_unlock(&st->data_mutex);
+
+ return ret;
}
static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff)
{
- u8 b_pid[4];
+ struct dtt200u_state *st = adap->dev->priv;
+ int ret;
+
pid = onoff ? pid : 0;
- b_pid[0] = SET_PID_FILTER;
- b_pid[1] = index;
- b_pid[2] = pid & 0xff;
- b_pid[3] = (pid >> 8) & 0x1f;
+ mutex_lock(&st->data_mutex);
+ st->data[0] = SET_PID_FILTER;
+ st->data[1] = index;
+ st->data[2] = pid & 0xff;
+ st->data[3] = (pid >> 8) & 0x1f;
- return dvb_usb_generic_write(adap->dev, b_pid, 4);
+ ret = dvb_usb_generic_write(adap->dev, st->data, 4);
+ mutex_unlock(&st->data_mutex);
+
+ return ret;
}
static int dtt200u_rc_query(struct dvb_usb_device *d)
{
- u8 key[5],cmd = GET_RC_CODE;
+ struct dtt200u_state *st = d->priv;
u32 scancode;
+ int ret;
+
+ mutex_lock(&st->data_mutex);
+ st->data[0] = GET_RC_CODE;
+
+ ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0);
+ if (ret < 0)
+ goto ret;
- dvb_usb_generic_rw(d,&cmd,1,key,5,0);
- if (key[0] == 1) {
+ if (st->data[0] == 1) {
enum rc_type proto = RC_TYPE_NEC;
- scancode = key[1];
- if ((u8) ~key[1] != key[2]) {
+ scancode = st->data[1];
+ if ((u8) ~st->data[1] != st->data[2]) {
/* Extended NEC */
scancode = scancode << 8;
- scancode |= key[2];
+ scancode |= st->data[2];
proto = RC_TYPE_NECX;
}
scancode = scancode << 8;
- scancode |= key[3];
+ scancode |= st->data[3];
/* Check command checksum is ok */
- if ((u8) ~key[3] == key[4])
+ if ((u8) ~st->data[3] == st->data[4])
rc_keydown(d->rc_dev, proto, scancode, 0);
else
rc_keyup(d->rc_dev);
- } else if (key[0] == 2) {
+ } else if (st->data[0] == 2) {
rc_repeat(d->rc_dev);
} else {
rc_keyup(d->rc_dev);
}
- if (key[0] != 0)
- deb_info("key: %*ph\n", 5, key);
+ if (st->data[0] != 0)
+ deb_info("st->data: %*ph\n", 5, st->data);
- return 0;
+ret:
+ mutex_unlock(&st->data_mutex);
+ return ret;
}
static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
static int dtt200u_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ struct dvb_usb_device *d;
+ struct dtt200u_state *st;
+
if (0 == dvb_usb_device_init(intf, &dtt200u_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_fc_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties,
- THIS_MODULE, NULL, adapter_nr) ||
+ THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_miglia_properties,
- THIS_MODULE, NULL, adapter_nr))
+ THIS_MODULE, &d, adapter_nr)) {
+ st = d->priv;
+ mutex_init(&st->data_mutex);
+
return 0;
+ }
return -ENODEV;
}
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-dtt200u-01.fw",
+ .size_of_priv = sizeof(struct dtt200u_state),
+
.num_adapters = 1,
.adapter = {
{
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-02.fw",
+ .size_of_priv = sizeof(struct dtt200u_state),
+
.num_adapters = 1,
.adapter = {
{
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-fc03.fw",
+ .size_of_priv = sizeof(struct dtt200u_state),
+
.num_adapters = 1,
.adapter = {
{
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-zl0353-01.fw",
+ .size_of_priv = sizeof(struct dtt200u_state),
+
.num_adapters = 1,
.adapter = {
{
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-miglia-01.fw",
+ .size_of_priv = sizeof(struct dtt200u_state),
+
.num_adapters = 1,
.generic_bulk_ctrl_endpoint = 0x01,
MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+struct dtv5100_state {
+ unsigned char data[80];
+};
+
static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
+ struct dtv5100_state *st = d->priv;
u8 request;
u8 type;
u16 value;
}
index = (addr << 8) + wbuf[0];
+ memcpy(st->data, rbuf, rlen);
msleep(1); /* avoid I2C errors */
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request,
- type, value, index, rbuf, rlen,
+ type, value, index, st->data, rlen,
DTV5100_USB_TIMEOUT);
}
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
- .size_of_priv = 0,
+ .size_of_priv = sizeof(struct dtv5100_state),
.num_adapters = 1,
.adapter = {{
if (i && !state->initialized) {
state->initialized = 1;
/* reset board */
- dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0);
+ return dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0);
}
return 0;
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+struct gp8psk_state {
+ unsigned char data[80];
+};
+
static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers)
{
return (gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6));
int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
{
+ struct gp8psk_state *st = d->priv;
int ret = 0,try = 0;
+ if (blen > sizeof(st->data))
+ return -EIO;
+
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
while (ret >= 0 && ret != blen && try < 3) {
+ memcpy(st->data, b, blen);
ret = usb_control_msg(d->udev,
usb_rcvctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_IN,
- value,index,b,blen,
+ value, index, st->data, blen,
2000);
deb_info("reading number %d (ret: %d)\n",try,ret);
try++;
int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
+ struct gp8psk_state *st = d->priv;
int ret;
deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
debug_dump(b,blen,deb_xfer);
+ if (blen > sizeof(st->data))
+ return -EIO;
+
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
+ memcpy(st->data, b, blen);
if (usb_control_msg(d->udev,
usb_sndctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_OUT,
- value,index,b,blen,
+ value, index, st->data, blen,
2000) != blen) {
warn("usb out operation failed.");
ret = -EIO;
err("failed to load bcm4500 firmware.");
goto out_free;
}
+ if (buflen > 64) {
+ err("firmare chunk size bigger than 64 bytes.");
+ goto out_free;
+ }
+
memcpy(buf, ptr, buflen);
if (dvb_usb_generic_write(d, buf, buflen)) {
err("failed to load bcm4500 firmware.");
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-gp8psk-01.fw",
+ .size_of_priv = sizeof(struct gp8psk_state),
+
.num_adapters = 1,
.adapter = {
{
*/
static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
- u8 key[5],cmd[2] = { DIBUSB_REQ_POLL_REMOTE, 0x35 }, data,toggle,custom;
+ u8 *buf, data, toggle, custom;
u16 raw;
- int i;
+ int i, ret;
struct dibusb_device_state *st = d->priv;
- dvb_usb_generic_rw(d,cmd,2,key,5,0);
+ buf = kmalloc(5, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = DIBUSB_REQ_POLL_REMOTE;
+ buf[1] = 0x35;
+ ret = dvb_usb_generic_rw(d, buf, 2, buf, 5, 0);
+ if (ret < 0)
+ goto ret;
*state = REMOTE_NO_KEY_PRESSED;
- switch (key[0]) {
+ switch (buf[0]) {
case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
- raw = ((key[1] << 8) | key[2]) >> 3;
+ raw = ((buf[1] << 8) | buf[2]) >> 3;
toggle = !!(raw & 0x800);
data = raw & 0x3f;
custom = (raw >> 6) & 0x1f;
- deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle);
+ deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",
+ buf[1], buf[2], buf[3], custom, data, toggle);
for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) {
if (rc5_data(&rc_map_haupp_table[i]) == data &&
break;
}
- return 0;
+ret:
+ kfree(buf);
+ return ret;
}
static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6])
u8 c; /* transaction counter, wraps around... */
u8 initialized; /* set to 1 if 0x15 has been sent */
u16 last_rc_key;
+
+ unsigned char data[80];
};
static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
unsigned int write_len, unsigned int read_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 buf[64];
u8 id;
unsigned int rlen;
int ret;
- BUG_ON(NULL == data && 0 != (write_len | read_len));
- BUG_ON(write_len > 64 - 4);
- BUG_ON(read_len > 64 - 4);
+ if (!data || (write_len > 64 - 4) || (read_len > 64 - 4)) {
+ err("%s: transfer data invalid", __func__);
+ return -EIO;
+ }
+ mutex_lock(&state->ca_mutex);
id = state->c++;
- buf[0] = SYNC_BYTE_OUT;
- buf[1] = id;
- buf[2] = cmd;
- buf[3] = write_len;
+ state->data[0] = SYNC_BYTE_OUT;
+ state->data[1] = id;
+ state->data[2] = cmd;
+ state->data[3] = write_len;
- memcpy(buf + 4, data, write_len);
+ memcpy(state->data + 4, data, write_len);
rlen = (read_len > 0) ? 64 : 0;
- ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
- buf, rlen, /* delay_ms */ 0);
+ ret = dvb_usb_generic_rw(d, state->data, 4 + write_len,
+ state->data, rlen, /* delay_ms */ 0);
if (0 != ret)
goto failed;
ret = -EIO;
- if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ if (SYNC_BYTE_IN != state->data[0] || id != state->data[1])
goto failed;
- memcpy(data, buf + 4, read_len);
+ memcpy(data, state->data + 4, read_len);
+ mutex_unlock(&state->ca_mutex);
return 0;
failed:
err("CI error %d; %02X %02X %02X -> %*ph.",
- ret, SYNC_BYTE_OUT, id, cmd, 3, buf);
+ ret, SYNC_BYTE_OUT, id, cmd, 3, state->data);
+ mutex_unlock(&state->ca_mutex);
return ret;
}
u8 *rcv_buf, u8 rcv_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 buf[64];
u8 id;
int ret;
+ mutex_lock(&state->ca_mutex);
id = state->c++;
ret = -EINVAL;
if (snd_len > 64 - 7 || rcv_len > 64 - 7)
goto failed;
- buf[0] = SYNC_BYTE_OUT;
- buf[1] = id;
- buf[2] = PCTV_CMD_I2C;
- buf[3] = snd_len + 3;
- buf[4] = addr << 1;
- buf[5] = snd_len;
- buf[6] = rcv_len;
+ state->data[0] = SYNC_BYTE_OUT;
+ state->data[1] = id;
+ state->data[2] = PCTV_CMD_I2C;
+ state->data[3] = snd_len + 3;
+ state->data[4] = addr << 1;
+ state->data[5] = snd_len;
+ state->data[6] = rcv_len;
- memcpy(buf + 7, snd_buf, snd_len);
+ memcpy(state->data + 7, snd_buf, snd_len);
- ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
- buf, /* rcv_len */ 64,
+ ret = dvb_usb_generic_rw(d, state->data, 7 + snd_len,
+ state->data, /* rcv_len */ 64,
/* delay_ms */ 0);
if (ret < 0)
goto failed;
/* TT USB protocol error. */
ret = -EIO;
- if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ if (SYNC_BYTE_IN != state->data[0] || id != state->data[1])
goto failed;
/* I2C device didn't respond as expected. */
ret = -EREMOTEIO;
- if (buf[5] < snd_len || buf[6] < rcv_len)
+ if (state->data[5] < snd_len || state->data[6] < rcv_len)
goto failed;
- memcpy(rcv_buf, buf + 7, rcv_len);
+ memcpy(rcv_buf, state->data + 7, rcv_len);
+ mutex_unlock(&state->ca_mutex);
return rcv_len;
failed:
- err("I2C error %d; %02X %02X %02X %02X %02X -> "
- "%02X %02X %02X %02X %02X.",
+ err("I2C error %d; %02X %02X %02X %02X %02X -> %*ph",
ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
- buf[0], buf[1], buf[4], buf[5], buf[6]);
+ 7, state->data);
+ mutex_unlock(&state->ca_mutex);
return ret;
}
static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 };
- u8 rx[PCTV_ANSWER_LEN];
+ u8 *rx;
int ret;
info("%s: %d\n", __func__, i);
if (state->initialized)
return 0;
+ rx = kmalloc(PCTV_ANSWER_LEN, GFP_KERNEL);
+ if (!rx)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
/* hmm where shoud this should go? */
ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
if (ret != 0)
__func__, ret);
/* this is a one-time initialization, dont know where to put */
- b0[1] = state->c++;
+ state->data[0] = 0xaa;
+ state->data[1] = state->c++;
+ state->data[2] = PCTV_CMD_RESET;
+ state->data[3] = 1;
+ state->data[4] = 0;
/* reset board */
- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
- return ret;
+ goto ret;
- b0[1] = state->c++;
- b0[4] = 1;
+ state->data[1] = state->c++;
+ state->data[4] = 1;
/* reset board (again?) */
- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
- return ret;
+ goto ret;
state->initialized = 1;
- return 0;
+ret:
+ mutex_unlock(&state->ca_mutex);
+ kfree(rx);
+ return ret;
}
static int pctv452e_rc_query(struct dvb_usb_device *d)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 b[CMD_BUFFER_SIZE];
- u8 rx[PCTV_ANSWER_LEN];
int ret, i;
- u8 id = state->c++;
+ u8 id;
+
+ mutex_lock(&state->ca_mutex);
+ id = state->c++;
/* prepare command header */
- b[0] = SYNC_BYTE_OUT;
- b[1] = id;
- b[2] = PCTV_CMD_IR;
- b[3] = 0;
+ state->data[0] = SYNC_BYTE_OUT;
+ state->data[1] = id;
+ state->data[2] = PCTV_CMD_IR;
+ state->data[3] = 0;
/* send ir request */
- ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, state->data, 4,
+ state->data, PCTV_ANSWER_LEN, 0);
if (ret != 0)
- return ret;
+ goto ret;
if (debug > 3) {
- info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx);
- for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
- info(" %02x", rx[i+3]);
+ info("%s: read: %2d: %*ph: ", __func__, ret, 3, state->data);
+ for (i = 0; (i < state->data[3]) && ((i + 3) < PCTV_ANSWER_LEN); i++)
+ info(" %02x", state->data[i + 3]);
info("\n");
}
- if ((rx[3] == 9) && (rx[12] & 0x01)) {
+ if ((state->data[3] == 9) && (state->data[12] & 0x01)) {
/* got a "press" event */
- state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]);
+ state->last_rc_key = RC_SCANCODE_RC5(state->data[7], state->data[6]);
if (debug > 2)
info("%s: cmd=0x%02x sys=0x%02x\n",
- __func__, rx[6], rx[7]);
+ __func__, state->data[6], state->data[7]);
rc_keydown(d->rc_dev, RC_TYPE_RC5, state->last_rc_key, 0);
} else if (state->last_rc_key) {
rc_keyup(d->rc_dev);
state->last_rc_key = 0;
}
-
- return 0;
+ret:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
}
static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
static int technisat_usb2_i2c_access(struct usb_device *udev,
u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
{
- u8 b[64];
+ u8 *b;
int ret, actual_length;
+ b = kmalloc(64, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
deb_i2c("i2c-access: %02x, tx: ", device_addr);
debug_dump(tx, txlen, deb_i2c);
deb_i2c(" ");
if (ret < 0) {
err("i2c-error: out failed %02x = %d", device_addr, ret);
- return -ENODEV;
+ goto err;
}
ret = usb_bulk_msg(udev,
b, 64, &actual_length, 1000);
if (ret < 0) {
err("i2c-error: in failed %02x = %d", device_addr, ret);
- return -ENODEV;
+ goto err;
}
if (b[0] != I2C_STATUS_OK) {
if (!(b[0] == I2C_STATUS_NAK &&
device_addr == 0x60
/* && device_is_technisat_usb2 */))
- return -ENODEV;
+ goto err;
}
deb_i2c("status: %d, ", b[0]);
deb_i2c("\n");
- return 0;
+err:
+ kfree(b);
+ return ret;
}
static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
s32 TransferBufferLength, int bOut)
{
int r;
+ unsigned char *buf;
+
+ buf = kmalloc(TransferBufferLength, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
if (!bOut) {
r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
Request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE |
USB_DIR_IN,
- Value, Index, TransferBuffer,
+ Value, Index, buf,
TransferBufferLength, HZ * 5);
+
+ if (r >= 0)
+ memcpy(TransferBuffer, buf, TransferBufferLength);
} else {
+ memcpy(buf, TransferBuffer, TransferBufferLength);
r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- Value, Index, TransferBuffer,
+ Value, Index, buf,
TransferBufferLength, HZ * 5);
}
+ kfree(buf);
return r;
}
int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value)
{
struct usb_device *udev = dev->udev;
+ unsigned char *buf;
int ret;
+ buf = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00,
index,
- (u8 *) value,
+ buf,
sizeof(u8),
500);
- if (ret < 0)
- return ret;
- else
- return 0;
+ if (ret >= 0)
+ memcpy(value, buf, sizeof(u8));
+
+ kfree(buf);
+ return ret;
}
static int stk_start_stream(struct stk_camera *dev)
host->pdata = pdev->dev.platform_data;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- /* Get registers' physical base address */
- host->phy_regs = regs->start;
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
+ /* Get registers' physical base address */
+ host->phy_regs = regs->start;
+
platform_set_drvdata(pdev, host);
return dw_mci_probe(host);
}
if (msm_host->pwr_irq < 0) {
dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
msm_host->pwr_irq);
+ ret = msm_host->pwr_irq;
goto clk_disable;
}
ret = gpmi_enable_clk(this);
if (ret)
- goto err_out;
+ return ret;
ret = gpmi_reset_block(r->gpmi_regs, false);
if (ret)
goto err_out;
gpmi_disable_clk(this);
return 0;
err_out:
+ gpmi_disable_clk(this);
return ret;
}
ret = gpmi_enable_clk(this);
if (ret)
- goto err_out;
+ return ret;
/*
* Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
gpmi_disable_clk(this);
return 0;
err_out:
+ gpmi_disable_clk(this);
return ret;
}
struct completion done;
struct mutex lock;
u32 sectors;
+
+ u8 eccdata[112];
};
static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
u8 *data, u32 bytes)
{
dma_addr_t addr;
- u8 *p;
- u32 len, i, val;
- int ret = 0;
+ u32 len;
+ int ret;
addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
ret = dma_mapping_error(ecc->dev, addr);
/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
- p = data + bytes;
- /* write the parity bytes generated by the ECC back to the OOB region */
- for (i = 0; i < len; i++) {
- if ((i % 4) == 0)
- val = readl(ecc->regs + ECC_ENCPAR(i / 4));
- p[i] = (val >> ((i % 4) * 8)) & 0xff;
- }
+ /* write the parity bytes generated by the ECC back to temp buffer */
+ __ioread32_copy(ecc->eccdata, ecc->regs + ECC_ENCPAR(0), round_up(len, 4));
+
+ /* copy into possibly unaligned OOB region with actual length */
+ memcpy(data + bytes, ecc->eccdata, len);
timeout:
dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
/**
* nand_reset - Reset and initialize a NAND device
* @chip: The NAND chip
+ * @chipnr: Internal die id
*
* Returns 0 for success or negative error code otherwise
*/
-int nand_reset(struct nand_chip *chip)
+int nand_reset(struct nand_chip *chip, int chipnr)
{
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
if (ret)
return ret;
+ /*
+ * The CS line has to be released before we can apply the new NAND
+ * interface settings, hence this weird ->select_chip() dance.
+ */
+ chip->select_chip(mtd, chipnr);
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
ret = nand_setup_data_interface(chip);
+ chip->select_chip(mtd, -1);
if (ret)
return ret;
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(mtd, chipnr);
-
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- nand_reset(chip);
+ nand_reset(chip, chipnr);
+
+ chip->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(mtd, chipnr);
-
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- nand_reset(chip);
+ nand_reset(chip, chipnr);
+
+ chip->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
}
chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
/*
* Reset the chip. Some chips (like the Toshiba TC5832DC found in one
* if we don't do this. I have no clue why, but I seem to have 'fixed'
* it in the doc2000 driver in August 1999. dwmw2.
*/
- nand_reset(chip);
+ nand_reset(chip, chipnr);
+
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
int i, maf_idx;
u8 id_data[8];
- /* Select the device */
- chip->select_chip(mtd, 0);
-
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
*/
- nand_reset(chip);
+ nand_reset(chip, 0);
+
+ /* Select the device */
+ chip->select_chip(mtd, 0);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
return PTR_ERR(type);
}
+ /* Initialize the ->data_interface field. */
ret = nand_init_data_interface(chip);
if (ret)
return ret;
+ /*
+ * Setup the data interface correctly on the chip and controller side.
+ * This explicit call to nand_setup_data_interface() is only required
+ * for the first die, because nand_reset() has been called before
+ * ->data_interface and ->default_onfi_timing_mode were set.
+ * For the other dies, nand_reset() will automatically switch to the
+ * best mode for us.
+ */
+ ret = nand_setup_data_interface(chip);
+ if (ret)
+ return ret;
+
chip->select_chip(mtd, -1);
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
- chip->select_chip(mtd, i);
/* See comment in nand_get_flash_type for reset */
- nand_reset(chip);
+ nand_reset(chip, i);
+
+ chip->select_chip(mtd, i);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
{ .compatible = "brcm,bcm63xx-switch" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, b53_mmap_of_table);
static struct platform_driver b53_mmap_driver = {
.probe = b53_mmap_probe,
return 0;
}
+static void bcm_sf2_sw_shutdown(struct platform_device *pdev)
+{
+ struct bcm_sf2_priv *priv = platform_get_drvdata(pdev);
+
+ /* For a kernel about to be kexec'd we want to keep the GPHY on for a
+ * successful MDIO bus scan to occur. If we did turn off the GPHY
+ * before (e.g: port_disable), this will also power it back on.
+ *
+ * Do not rely on kexec_in_progress, just power the PHY on.
+ */
+ if (priv->hw_params.num_gphy == 1)
+ bcm_sf2_gphy_enable_set(priv->dev->ds, true);
+}
+
#ifdef CONFIG_PM_SLEEP
static int bcm_sf2_suspend(struct device *dev)
{
{ .compatible = "brcm,bcm7445-switch-v4.0" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, bcm_sf2_of_match);
static struct platform_driver bcm_sf2_driver = {
.probe = bcm_sf2_sw_probe,
.remove = bcm_sf2_sw_remove,
+ .shutdown = bcm_sf2_sw_shutdown,
.driver = {
.name = "brcm-sf2",
.of_match_table = bcm_sf2_of_match,
},
{ }
};
+MODULE_DEVICE_TABLE(of, nb8800_dt_ids);
static int nb8800_probe(struct platform_device *pdev)
{
free_irq(dev->irq, dev);
out_phy_disconnect:
- phy_disconnect(phydev);
+ if (priv->has_phy)
+ phy_disconnect(phydev);
return ret;
}
phy_dev = phy_connect(bgmac->net_dev, bus_id, &bgmac_adjust_link,
PHY_INTERFACE_MODE_MII);
if (IS_ERR(phy_dev)) {
- dev_err(bgmac->dev, "PHY connecton failed\n");
+ dev_err(bgmac->dev, "PHY connection failed\n");
return PTR_ERR(phy_dev);
}
static u32
bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
{
+ unsigned long flags;
u32 val;
- spin_lock_bh(&bp->indirect_lock);
+ spin_lock_irqsave(&bp->indirect_lock, flags);
BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
val = BNX2_RD(bp, BNX2_PCICFG_REG_WINDOW);
- spin_unlock_bh(&bp->indirect_lock);
+ spin_unlock_irqrestore(&bp->indirect_lock, flags);
return val;
}
static void
bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
{
- spin_lock_bh(&bp->indirect_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bp->indirect_lock, flags);
BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW, val);
- spin_unlock_bh(&bp->indirect_lock);
+ spin_unlock_irqrestore(&bp->indirect_lock, flags);
}
static void
static void
bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
{
+ unsigned long flags;
+
offset += cid_addr;
- spin_lock_bh(&bp->indirect_lock);
+ spin_lock_irqsave(&bp->indirect_lock, flags);
if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
int i;
BNX2_WR(bp, BNX2_CTX_DATA_ADR, offset);
BNX2_WR(bp, BNX2_CTX_DATA, val);
}
- spin_unlock_bh(&bp->indirect_lock);
+ spin_unlock_irqrestore(&bp->indirect_lock, flags);
}
#ifdef BCM_CNIC
memset(&bp->cyclecounter, 0, sizeof(bp->cyclecounter));
bp->cyclecounter.read = bnx2x_cyclecounter_read;
bp->cyclecounter.mask = CYCLECOUNTER_MASK(64);
- bp->cyclecounter.shift = 1;
+ bp->cyclecounter.shift = 0;
bp->cyclecounter.mult = 1;
}
* capped by the number of available cores.
*/
if (n10g) {
- i = num_online_cpus();
+ i = min_t(int, MAX_OFLD_QSETS, num_online_cpus());
s->ofldqsets = roundup(i, adap->params.nports);
} else {
s->ofldqsets = adap->params.nports;
}
static int alloc_uld_rxqs(struct adapter *adap,
- struct sge_uld_rxq_info *rxq_info,
- unsigned int nq, unsigned int offset, bool lro)
+ struct sge_uld_rxq_info *rxq_info, bool lro)
{
struct sge *s = &adap->sge;
- struct sge_ofld_rxq *q = rxq_info->uldrxq + offset;
- unsigned short *ids = rxq_info->rspq_id + offset;
- unsigned int per_chan = nq / adap->params.nports;
+ unsigned int nq = rxq_info->nrxq + rxq_info->nciq;
+ struct sge_ofld_rxq *q = rxq_info->uldrxq;
+ unsigned short *ids = rxq_info->rspq_id;
unsigned int bmap_idx = 0;
- int i, err, msi_idx;
+ unsigned int per_chan;
+ int i, err, msi_idx, que_idx = 0;
+
+ per_chan = rxq_info->nrxq / adap->params.nports;
if (adap->flags & USING_MSIX)
msi_idx = 1;
msi_idx = -((int)s->intrq.abs_id + 1);
for (i = 0; i < nq; i++, q++) {
+ if (i == rxq_info->nrxq) {
+ /* start allocation of concentrator queues */
+ per_chan = rxq_info->nciq / adap->params.nports;
+ que_idx = 0;
+ }
+
if (msi_idx >= 0) {
bmap_idx = get_msix_idx_from_bmap(adap);
msi_idx = adap->msix_info_ulds[bmap_idx].idx;
}
err = t4_sge_alloc_rxq(adap, &q->rspq, false,
- adap->port[i / per_chan],
+ adap->port[que_idx++ / per_chan],
msi_idx,
q->fl.size ? &q->fl : NULL,
uldrx_handler,
if (err)
goto freeout;
if (msi_idx >= 0)
- rxq_info->msix_tbl[i + offset] = bmap_idx;
+ rxq_info->msix_tbl[i] = bmap_idx;
memset(&q->stats, 0, sizeof(q->stats));
if (ids)
ids[i] = q->rspq.abs_id;
}
return 0;
freeout:
- q = rxq_info->uldrxq + offset;
+ q = rxq_info->uldrxq;
for ( ; i; i--, q++) {
if (q->rspq.desc)
free_rspq_fl(adap, &q->rspq,
q->fl.size ? &q->fl : NULL);
}
-
- /* We need to free rxq also in case of ciq allocation failure */
- if (offset) {
- q = rxq_info->uldrxq + offset;
- for ( ; i; i--, q++) {
- if (q->rspq.desc)
- free_rspq_fl(adap, &q->rspq,
- q->fl.size ? &q->fl : NULL);
- }
- }
return err;
}
return -ENOMEM;
}
- ret = !(!alloc_uld_rxqs(adap, rxq_info, rxq_info->nrxq, 0, lro) &&
- !alloc_uld_rxqs(adap, rxq_info, rxq_info->nciq,
- rxq_info->nrxq, lro));
+ ret = !(!alloc_uld_rxqs(adap, rxq_info, lro));
/* Tell uP to route control queue completions to rdma rspq */
if (adap->flags & FULL_INIT_DONE &&
/* Unbind queue from any existing class */
err = t4_sched_queue_unbind(pi, p);
- if (err)
+ if (err) {
+ t4_free_mem(qe);
goto out;
+ }
/* Bind queue to specified class */
memset(qe, 0, sizeof(*qe));
{
unsigned int wait;
struct vnic_dev *vdev = rq->vdev;
+ int i;
- iowrite32(0, &rq->ctrl->enable);
+ /* Due to a race condition with clearing RQ "mini-cache" in hw, we need
+ * to disable the RQ twice to guarantee that stale descriptors are not
+ * used when this RQ is re-enabled.
+ */
+ for (i = 0; i < 2; i++) {
+ iowrite32(0, &rq->ctrl->enable);
- /* Wait for HW to ACK disable request */
- for (wait = 0; wait < 1000; wait++) {
- if (!(ioread32(&rq->ctrl->running)))
- return 0;
- udelay(10);
- }
+ /* Wait for HW to ACK disable request */
+ for (wait = 20000; wait > 0; wait--)
+ if (!ioread32(&rq->ctrl->running))
+ break;
+ if (!wait) {
+ vdev_neterr(vdev, "Failed to disable RQ[%d]\n",
+ rq->index);
- vdev_neterr(vdev, "Failed to disable RQ[%d]\n", rq->index);
+ return -ETIMEDOUT;
+ }
+ }
- return -ETIMEDOUT;
+ return 0;
}
void vnic_rq_clean(struct vnic_rq *rq,
[fetch_index % VNIC_RQ_BUF_BLK_ENTRIES(count)];
iowrite32(fetch_index, &rq->ctrl->posted_index);
+ /* Anytime we write fetch_index, we need to re-write 0 to rq->enable
+ * to re-sync internal VIC state.
+ */
+ iowrite32(0, &rq->ctrl->enable);
+
vnic_dev_clear_desc_ring(&rq->ring);
}
{ .compatible = "ezchip,nps-mgt-enet" },
{ /* Sentinel */ }
};
+MODULE_DEVICE_TABLE(of, nps_enet_dt_ids);
static struct platform_driver nps_enet_driver = {
.probe = nps_enet_probe,
skb_put(skb, pkt_len - 4);
data = skb->data;
+ if (!is_copybreak && need_swap)
+ swap_buffer(data, pkt_len);
+
#if !defined(CONFIG_M5272)
if (fep->quirks & FEC_QUIRK_HAS_RACC)
data = skb_pull_inline(skb, 2);
#endif
- if (!is_copybreak && need_swap)
- swap_buffer(data, pkt_len);
-
/* Extract the enhanced buffer descriptor */
ebdp = NULL;
if (fep->bufdesc_ex)
{.compatible = "hisilicon,hns-dsaf-v2"},
{}
};
+MODULE_DEVICE_TABLE(of, g_dsaf_match);
static struct platform_driver g_dsaf_driver = {
.probe = hns_dsaf_probe,
{.compatible = "hisilicon,hns-mdio"},
{}
};
+MODULE_DEVICE_TABLE(of, hns_mdio_match);
static const struct acpi_device_id hns_mdio_acpi_match[] = {
{ "HISI0141", 0 },
if (!scrq)
return NULL;
- scrq->msgs = (union sub_crq *)__get_free_pages(GFP_KERNEL, 2);
+ scrq->msgs = (union sub_crq *)__get_free_pages(GFP_ATOMIC, 2);
memset(scrq->msgs, 0, 4 * PAGE_SIZE);
if (!scrq->msgs) {
dev_warn(dev, "Couldn't allocate crq queue messages page\n");
return rc;
req_rx_irq_failed:
- for (j = 0; j < i; j++)
+ for (j = 0; j < i; j++) {
free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+ }
i = adapter->req_tx_queues;
req_tx_irq_failed:
- for (j = 0; j < i; j++)
+ for (j = 0; j < i; j++) {
free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+ }
release_sub_crqs_no_irqs(adapter);
return rc;
}
spin_unlock_irqrestore(&adapter->inflight_lock, flags);
}
+static void ibmvnic_xport_event(struct work_struct *work)
+{
+ struct ibmvnic_adapter *adapter = container_of(work,
+ struct ibmvnic_adapter,
+ ibmvnic_xport);
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ ibmvnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ if (adapter->migrated) {
+ rc = ibmvnic_reenable_crq_queue(adapter);
+ if (rc)
+ dev_err(dev, "Error after enable rc=%ld\n", rc);
+ adapter->migrated = false;
+ rc = ibmvnic_send_crq_init(adapter);
+ if (rc)
+ dev_err(dev, "Error sending init rc=%ld\n", rc);
+ }
+}
+
static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
struct ibmvnic_adapter *adapter)
{
if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
dev_info(dev, "Re-enabling adapter\n");
adapter->migrated = true;
- ibmvnic_free_inflight(adapter);
- release_sub_crqs(adapter);
- rc = ibmvnic_reenable_crq_queue(adapter);
- if (rc)
- dev_err(dev, "Error after enable rc=%ld\n", rc);
- adapter->migrated = false;
- rc = ibmvnic_send_crq_init(adapter);
- if (rc)
- dev_err(dev, "Error sending init rc=%ld\n", rc);
+ schedule_work(&adapter->ibmvnic_xport);
} else if (gen_crq->cmd == IBMVNIC_DEVICE_FAILOVER) {
dev_info(dev, "Backing device failover detected\n");
netif_carrier_off(netdev);
/* The adapter lost the connection */
dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
gen_crq->cmd);
- ibmvnic_free_inflight(adapter);
- release_sub_crqs(adapter);
+ schedule_work(&adapter->ibmvnic_xport);
}
return;
case IBMVNIC_CRQ_CMD_RSP:
goto task_failed;
netdev->real_num_tx_queues = adapter->req_tx_queues;
+ netdev->mtu = adapter->req_mtu;
if (adapter->failover) {
adapter->failover = false;
SET_NETDEV_DEV(netdev, &dev->dev);
INIT_WORK(&adapter->vnic_crq_init, handle_crq_init_rsp);
+ INIT_WORK(&adapter->ibmvnic_xport, ibmvnic_xport_event);
spin_lock_init(&adapter->stats_lock);
}
netdev->real_num_tx_queues = adapter->req_tx_queues;
+ netdev->mtu = adapter->req_mtu;
rc = register_netdev(netdev);
if (rc) {
/**************************************************************************/
#define IBMVNIC_NAME "ibmvnic"
-#define IBMVNIC_DRIVER_VERSION "1.0"
+#define IBMVNIC_DRIVER_VERSION "1.0.1"
#define IBMVNIC_INVALID_MAP -1
#define IBMVNIC_STATS_TIMEOUT 1
/* basic structures plus 100 2k buffers */
u8 map_id;
struct work_struct vnic_crq_init;
+ struct work_struct ibmvnic_xport;
bool failover;
};
#define I40E_AQ_LEN 256
#define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */
#define I40E_MAX_USER_PRIORITY 8
+#define I40E_DEFAULT_TRAFFIC_CLASS BIT(0)
#define I40E_DEFAULT_MSG_ENABLE 4
#define I40E_QUEUE_WAIT_RETRY_LIMIT 10
#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16)
return num_tc;
}
-/**
- * i40e_pf_get_default_tc - Get bitmap for first enabled TC
- * @pf: PF being queried
- *
- * Return a bitmap for first enabled traffic class for this PF.
- **/
-static u8 i40e_pf_get_default_tc(struct i40e_pf *pf)
-{
- u8 enabled_tc = pf->hw.func_caps.enabled_tcmap;
- u8 i = 0;
-
- if (!enabled_tc)
- return 0x1; /* TC0 */
-
- /* Find the first enabled TC */
- for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT(i))
- break;
- }
-
- return BIT(i);
-}
-
/**
* i40e_pf_get_pf_tc_map - Get bitmap for enabled traffic classes
* @pf: PF being queried
{
/* If DCB is not enabled for this PF then just return default TC */
if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
- return i40e_pf_get_default_tc(pf);
+ return I40E_DEFAULT_TRAFFIC_CLASS;
/* SFP mode we want PF to be enabled for all TCs */
if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
if (pf->hw.func_caps.iscsi)
return i40e_get_iscsi_tc_map(pf);
else
- return i40e_pf_get_default_tc(pf);
+ return I40E_DEFAULT_TRAFFIC_CLASS;
}
/**
if (v == pf->lan_vsi)
tc_map = i40e_pf_get_tc_map(pf);
else
- tc_map = i40e_pf_get_default_tc(pf);
+ tc_map = I40E_DEFAULT_TRAFFIC_CLASS;
#ifdef I40E_FCOE
if (pf->vsi[v]->type == I40E_VSI_FCOE)
tc_map = i40e_get_fcoe_tc_map(pf);
u8 type;
/* Not DCB capable or capability disabled */
- if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
+ if (!(pf->flags & I40E_FLAG_DCB_CAPABLE))
return ret;
/* Ignore if event is not for Nearest Bridge */
pf->flags &= ~I40E_FLAG_MSIX_ENABLED;
kfree(pf->msix_entries);
pf->msix_entries = NULL;
+ pci_disable_msix(pf->pdev);
return -ENODEV;
} else if (v_actual == I40E_MIN_MSIX) {
return 0;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
- nlflags, 0, 0, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL);
}
/* Hardware supports L4 tunnel length of 128B (=2^7) which includes
goto fwd_add_err;
fwd_adapter->pool = pool;
fwd_adapter->real_adapter = adapter;
- err = ixgbe_fwd_ring_up(vdev, fwd_adapter);
- if (err)
- goto fwd_add_err;
- netif_tx_start_all_queues(vdev);
+
+ if (netif_running(pdev)) {
+ err = ixgbe_fwd_ring_up(vdev, fwd_adapter);
+ if (err)
+ goto fwd_add_err;
+ netif_tx_start_all_queues(vdev);
+ }
+
return fwd_adapter;
fwd_add_err:
/* unwind counter and free adapter struct */
mp->txq_count = pd->tx_queue_count ? : 1;
}
+static int get_phy_mode(struct mv643xx_eth_private *mp)
+{
+ struct device *dev = mp->dev->dev.parent;
+ int iface = -1;
+
+ if (dev->of_node)
+ iface = of_get_phy_mode(dev->of_node);
+
+ /* Historical default if unspecified. We could also read/write
+ * the interface state in the PSC1
+ */
+ if (iface < 0)
+ iface = PHY_INTERFACE_MODE_GMII;
+ return iface;
+}
+
static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
int phy_addr)
{
"orion-mdio-mii", addr);
phydev = phy_connect(mp->dev, phy_id, mv643xx_eth_adjust_link,
- PHY_INTERFACE_MODE_GMII);
+ get_phy_mode(mp));
if (!IS_ERR(phydev)) {
phy_addr_set(mp, addr);
break;
if (!dev)
return -ENOMEM;
+ SET_NETDEV_DEV(dev, &pdev->dev);
mp = netdev_priv(dev);
platform_set_drvdata(pdev, mp);
if (pd->phy_node) {
mp->phy = of_phy_connect(mp->dev, pd->phy_node,
mv643xx_eth_adjust_link, 0,
- PHY_INTERFACE_MODE_GMII);
+ get_phy_mode(mp));
if (!mp->phy)
err = -ENODEV;
else
dev->priv_flags |= IFF_UNICAST_FLT;
dev->gso_max_segs = MV643XX_MAX_TSO_SEGS;
- SET_NETDEV_DEV(dev, &pdev->dev);
-
if (mp->shared->win_protect)
wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect);
kfree(priv->mfunc.master.slave_state);
err_comm:
iounmap(priv->mfunc.comm);
+ priv->mfunc.comm = NULL;
err_vhcr:
dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE,
priv->mfunc.vhcr,
int slave;
u32 slave_read;
+ /* If the comm channel has not yet been initialized,
+ * skip reporting the internal error event to all
+ * the communication channels.
+ */
+ if (!priv->mfunc.comm)
+ return;
+
/* Report an internal error event to all
* communication channels.
*/
}
iounmap(priv->mfunc.comm);
+ priv->mfunc.comm = NULL;
}
void mlx4_cmd_cleanup(struct mlx4_dev *dev, int cleanup_mask)
{
u32 freq_khz = freq * 1000;
u64 max_val_cycles = freq_khz * 1000 * MLX4_EN_WRAP_AROUND_SEC;
+ u64 tmp_rounded =
+ roundup_pow_of_two(max_val_cycles) > max_val_cycles ?
+ roundup_pow_of_two(max_val_cycles) - 1 : UINT_MAX;
u64 max_val_cycles_rounded = is_power_of_2(max_val_cycles + 1) ?
- max_val_cycles : roundup_pow_of_two(max_val_cycles) - 1;
+ max_val_cycles : tmp_rounded;
/* calculate max possible multiplier in order to fit in 64bit */
u64 max_mul = div_u64(0xffffffffffffffffULL, max_val_cycles_rounded);
/* For TX we use the same irq per
ring we assigned for the RX */
struct mlx4_en_cq *rx_cq;
-
+ int xdp_index;
+
+ /* The xdp tx irq must align with the rx ring that forwards to
+ * it, so reindex these from 0. This should only happen when
+ * tx_ring_num is not a multiple of rx_ring_num.
+ */
+ xdp_index = (priv->xdp_ring_num - priv->tx_ring_num) + cq_idx;
+ if (xdp_index >= 0)
+ cq_idx = xdp_index;
cq_idx = cq_idx % priv->rx_ring_num;
rx_cq = priv->rx_cq[cq_idx];
cq->vector = rx_cq->vector;
udp_tunnel_get_rx_info(dev);
priv->port_up = true;
+
+ /* Process all completions if exist to prevent
+ * the queues freezing if they are full
+ */
+ for (i = 0; i < priv->rx_ring_num; i++)
+ napi_schedule(&priv->rx_cq[i]->napi);
+
netif_tx_start_all_queues(dev);
netif_device_attach(dev);
struct mlx4_en_dev *mdev = priv->mdev;
int i;
- if (mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 1))
- en_dbg(HW, priv, "Failed dumping statistics\n");
+ if (!mlx4_is_slave(mdev->dev))
+ if (mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 1))
+ en_dbg(HW, priv, "Failed dumping statistics\n");
memset(&priv->pstats, 0, sizeof(priv->pstats));
memset(&priv->pkstats, 0, sizeof(priv->pkstats));
if (!shutdown)
free_netdev(dev);
+ dev->ethtool_ops = NULL;
}
static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu)
return PTR_ERR(mailbox);
err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, in_mod, 0,
MLX4_CMD_DUMP_ETH_STATS, MLX4_CMD_TIME_CLASS_B,
- MLX4_CMD_WRAPPED);
+ MLX4_CMD_NATIVE);
if (err)
goto out;
err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma,
in_mod | MLX4_DUMP_ETH_STATS_FLOW_CONTROL,
0, MLX4_CMD_DUMP_ETH_STATS,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
if (err)
goto out;
}
return !loopback_ok;
}
+static int mlx4_en_test_interrupts(struct mlx4_en_priv *priv)
+{
+ struct mlx4_en_dev *mdev = priv->mdev;
+ int err = 0;
+ int i = 0;
+
+ err = mlx4_test_async(mdev->dev);
+ /* When not in MSI_X or slave, test only async */
+ if (!(mdev->dev->flags & MLX4_FLAG_MSI_X) || mlx4_is_slave(mdev->dev))
+ return err;
+
+ /* A loop over all completion vectors of current port,
+ * for each vector check whether it works by mapping command
+ * completions to that vector and performing a NOP command
+ */
+ for (i = 0; i < priv->rx_ring_num; i++) {
+ err = mlx4_test_interrupt(mdev->dev, priv->rx_cq[i]->vector);
+ if (err)
+ break;
+ }
+
+ return err;
+}
static int mlx4_en_test_link(struct mlx4_en_priv *priv)
{
void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_dev *mdev = priv->mdev;
int i, carrier_ok;
memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST);
netif_carrier_on(dev);
}
- buf[0] = mlx4_test_interrupts(mdev->dev);
+ buf[0] = mlx4_en_test_interrupts(priv);
buf[1] = mlx4_en_test_link(priv);
buf[2] = mlx4_en_test_speed(priv);
kfree(priv->eq_table.uar_map);
}
-/* A test that verifies that we can accept interrupts on all
- * the irq vectors of the device.
+/* A test that verifies that we can accept interrupts
+ * on the vector allocated for asynchronous events
+ */
+int mlx4_test_async(struct mlx4_dev *dev)
+{
+ return mlx4_NOP(dev);
+}
+EXPORT_SYMBOL(mlx4_test_async);
+
+/* A test that verifies that we can accept interrupts
+ * on the given irq vector of the tested port.
* Interrupts are checked using the NOP command.
*/
-int mlx4_test_interrupts(struct mlx4_dev *dev)
+int mlx4_test_interrupt(struct mlx4_dev *dev, int vector)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- int i;
int err;
- err = mlx4_NOP(dev);
- /* When not in MSI_X, there is only one irq to check */
- if (!(dev->flags & MLX4_FLAG_MSI_X) || mlx4_is_slave(dev))
- return err;
-
- /* A loop over all completion vectors, for each vector we will check
- * whether it works by mapping command completions to that vector
- * and performing a NOP command
- */
- for(i = 0; !err && (i < dev->caps.num_comp_vectors); ++i) {
- /* Make sure request_irq was called */
- if (!priv->eq_table.eq[i].have_irq)
- continue;
-
- /* Temporary use polling for command completions */
- mlx4_cmd_use_polling(dev);
-
- /* Map the new eq to handle all asynchronous events */
- err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0,
- priv->eq_table.eq[i].eqn);
- if (err) {
- mlx4_warn(dev, "Failed mapping eq for interrupt test\n");
- mlx4_cmd_use_events(dev);
- break;
- }
+ /* Temporary use polling for command completions */
+ mlx4_cmd_use_polling(dev);
- /* Go back to using events */
- mlx4_cmd_use_events(dev);
- err = mlx4_NOP(dev);
+ /* Map the new eq to handle all asynchronous events */
+ err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0,
+ priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(vector)].eqn);
+ if (err) {
+ mlx4_warn(dev, "Failed mapping eq for interrupt test\n");
+ goto out;
}
+ /* Go back to using events */
+ mlx4_cmd_use_events(dev);
+ err = mlx4_NOP(dev);
+
/* Return to default */
+ mlx4_cmd_use_polling(dev);
+out:
mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0,
priv->eq_table.eq[MLX4_EQ_ASYNC].eqn);
+ mlx4_cmd_use_events(dev);
+
return err;
}
-EXPORT_SYMBOL(mlx4_test_interrupts);
+EXPORT_SYMBOL(mlx4_test_interrupt);
bool mlx4_is_eq_vector_valid(struct mlx4_dev *dev, u8 port, int vector)
{
extern void __buggy_use_of_MLX4_GET(void);
extern void __buggy_use_of_MLX4_PUT(void);
-static bool enable_qos = true;
+static bool enable_qos;
module_param(enable_qos, bool, 0444);
-MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: on)");
+MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: off)");
#define MLX4_GET(dest, source, offset) \
do { \
int i;
int err = 0;
+ if ((port_type & mdev->caps.supported_type[info->port]) != port_type) {
+ mlx4_err(mdev,
+ "Requested port type for port %d is not supported on this HCA\n",
+ info->port);
+ err = -EINVAL;
+ goto err_sup;
+ }
+
mlx4_stop_sense(mdev);
mutex_lock(&priv->port_mutex);
info->tmp_type = port_type;
out:
mlx4_start_sense(mdev);
mutex_unlock(&priv->port_mutex);
-
+err_sup:
return err;
}
RES_MTT,
RES_MAC,
RES_VLAN,
- RES_EQ,
+ RES_NPORT_ID,
RES_COUNTER,
RES_FS_RULE,
+ RES_EQ,
MLX4_NUM_OF_RESOURCE_TYPE
};
struct mlx4_cmd_info *cmd);
int mlx4_common_set_vlan_fltr(struct mlx4_dev *dev, int function,
int port, void *buf);
-int mlx4_common_dump_eth_stats(struct mlx4_dev *dev, int slave, u32 in_mod,
- struct mlx4_cmd_mailbox *outbox);
int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
return err;
}
-int mlx4_common_dump_eth_stats(struct mlx4_dev *dev, int slave,
- u32 in_mod, struct mlx4_cmd_mailbox *outbox)
-{
- return mlx4_cmd_box(dev, 0, outbox->dma, in_mod, 0,
- MLX4_CMD_DUMP_ETH_STATS, MLX4_CMD_TIME_CLASS_B,
- MLX4_CMD_NATIVE);
-}
-
int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
struct mlx4_cmd_mailbox *outbox,
struct mlx4_cmd_info *cmd)
{
- if (slave != dev->caps.function)
- return 0;
- return mlx4_common_dump_eth_stats(dev, slave,
- vhcr->in_modifier, outbox);
+ return 0;
}
int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid,
r->com.from_state = r->com.state;
r->com.to_state = state;
r->com.state = RES_EQ_BUSY;
- if (eq)
- *eq = r;
}
}
spin_unlock_irq(mlx4_tlock(dev));
+ if (!err && eq)
+ *eq = r;
+
return err;
}
#include "mlx5_core.h"
+struct mlx5_db_pgdir {
+ struct list_head list;
+ unsigned long *bitmap;
+ __be32 *db_page;
+ dma_addr_t db_dma;
+};
+
/* Handling for queue buffers -- we allocate a bunch of memory and
* register it in a memory region at HCA virtual address 0.
*/
static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct mlx5_core_dev *dev,
int node)
{
+ u32 db_per_page = PAGE_SIZE / cache_line_size();
struct mlx5_db_pgdir *pgdir;
pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);
if (!pgdir)
return NULL;
- bitmap_fill(pgdir->bitmap, MLX5_DB_PER_PAGE);
+ pgdir->bitmap = kcalloc(BITS_TO_LONGS(db_per_page),
+ sizeof(unsigned long),
+ GFP_KERNEL);
+
+ if (!pgdir->bitmap) {
+ kfree(pgdir);
+ return NULL;
+ }
+
+ bitmap_fill(pgdir->bitmap, db_per_page);
pgdir->db_page = mlx5_dma_zalloc_coherent_node(dev, PAGE_SIZE,
&pgdir->db_dma, node);
if (!pgdir->db_page) {
+ kfree(pgdir->bitmap);
kfree(pgdir);
return NULL;
}
static int mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir *pgdir,
struct mlx5_db *db)
{
+ u32 db_per_page = PAGE_SIZE / cache_line_size();
int offset;
int i;
- i = find_first_bit(pgdir->bitmap, MLX5_DB_PER_PAGE);
- if (i >= MLX5_DB_PER_PAGE)
+ i = find_first_bit(pgdir->bitmap, db_per_page);
+ if (i >= db_per_page)
return -ENOMEM;
__clear_bit(i, pgdir->bitmap);
db->u.pgdir = pgdir;
db->index = i;
- offset = db->index * L1_CACHE_BYTES;
+ offset = db->index * cache_line_size();
db->db = pgdir->db_page + offset / sizeof(*pgdir->db_page);
db->dma = pgdir->db_dma + offset;
void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db)
{
+ u32 db_per_page = PAGE_SIZE / cache_line_size();
mutex_lock(&dev->priv.pgdir_mutex);
__set_bit(db->index, db->u.pgdir->bitmap);
- if (bitmap_full(db->u.pgdir->bitmap, MLX5_DB_PER_PAGE)) {
+ if (bitmap_full(db->u.pgdir->bitmap, db_per_page)) {
dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE,
db->u.pgdir->db_page, db->u.pgdir->db_dma);
list_del(&db->u.pgdir->list);
+ kfree(db->u.pgdir->bitmap);
kfree(db->u.pgdir);
}
#define MLX5_MPWRQ_SMALL_PACKET_THRESHOLD (128)
#define MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ (64 * 1024)
+#define MLX5E_DEFAULT_LRO_TIMEOUT 32
+#define MLX5E_LRO_TIMEOUT_ARR_SIZE 4
+
#define MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC 0x10
#define MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC_FROM_CQE 0x3
#define MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_PKTS 0x20
struct ieee_ets ets;
#endif
bool rx_am_enabled;
+ u32 lro_timeout;
};
struct mlx5e_tstamp {
void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev);
struct rtnl_link_stats64 *
mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
+u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout);
#endif /* __MLX5_EN_H__ */
MLX5_SET(tirc, tirc, lro_max_ip_payload_size,
(priv->params.lro_wqe_sz -
ROUGH_MAX_L2_L3_HDR_SZ) >> 8);
- MLX5_SET(tirc, tirc, lro_timeout_period_usecs,
- MLX5_CAP_ETH(priv->mdev,
- lro_timer_supported_periods[2]));
+ MLX5_SET(tirc, tirc, lro_timeout_period_usecs, priv->params.lro_timeout);
}
void mlx5e_build_tir_ctx_hash(void *tirc, struct mlx5e_priv *priv)
}
}
+u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout)
+{
+ int i;
+
+ /* The supported periods are organized in ascending order */
+ for (i = 0; i < MLX5E_LRO_TIMEOUT_ARR_SIZE - 1; i++)
+ if (MLX5_CAP_ETH(mdev, lro_timer_supported_periods[i]) >= wanted_timeout)
+ break;
+
+ return MLX5_CAP_ETH(mdev, lro_timer_supported_periods[i]);
+}
+
static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev,
struct net_device *netdev,
const struct mlx5e_profile *profile,
priv->profile = profile;
priv->ppriv = ppriv;
+ priv->params.lro_timeout =
+ mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT);
+
priv->params.log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
/* set CQE compression */
const struct mlx5e_profile *profile = priv->profile;
struct net_device *netdev = priv->netdev;
- unregister_netdev(netdev);
destroy_workqueue(priv->wq);
if (profile->cleanup)
profile->cleanup(priv);
for (vport = 1; vport < total_vfs; vport++)
mlx5_eswitch_unregister_vport_rep(esw, vport);
+ unregister_netdev(priv->netdev);
mlx5e_detach(mdev, vpriv);
mlx5e_destroy_netdev(mdev, priv);
}
struct mlx5e_priv *priv = rep->priv_data;
struct net_device *netdev = priv->netdev;
+ unregister_netdev(netdev);
mlx5e_detach_netdev(esw->dev, netdev);
mlx5e_destroy_netdev(esw->dev, priv);
}
mutex_unlock(&esw->state_lock);
}
-static void esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
- struct mlx5_vport *vport)
+static int esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
+ struct mlx5_vport *vport)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *vlan_grp = NULL;
int table_size = 2;
int err = 0;
- if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support) ||
- !IS_ERR_OR_NULL(vport->egress.acl))
- return;
+ if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
+ return -EOPNOTSUPP;
+
+ if (!IS_ERR_OR_NULL(vport->egress.acl))
+ return 0;
esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n",
vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size));
root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS);
if (!root_ns) {
esw_warn(dev, "Failed to get E-Switch egress flow namespace\n");
- return;
+ return -EIO;
}
flow_group_in = mlx5_vzalloc(inlen);
if (!flow_group_in)
- return;
+ return -ENOMEM;
acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
if (IS_ERR(acl)) {
mlx5_destroy_flow_group(vlan_grp);
if (err && !IS_ERR_OR_NULL(acl))
mlx5_destroy_flow_table(acl);
+ return err;
}
static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
vport->egress.acl = NULL;
}
-static void esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
- struct mlx5_vport *vport)
+static int esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
+ struct mlx5_vport *vport)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_core_dev *dev = esw->dev;
int table_size = 4;
int err = 0;
- if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support) ||
- !IS_ERR_OR_NULL(vport->ingress.acl))
- return;
+ if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
+ return -EOPNOTSUPP;
+
+ if (!IS_ERR_OR_NULL(vport->ingress.acl))
+ return 0;
esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n",
vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size));
root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS);
if (!root_ns) {
esw_warn(dev, "Failed to get E-Switch ingress flow namespace\n");
- return;
+ return -EIO;
}
flow_group_in = mlx5_vzalloc(inlen);
if (!flow_group_in)
- return;
+ return -ENOMEM;
acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
if (IS_ERR(acl)) {
}
kvfree(flow_group_in);
+ return err;
}
static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw,
return 0;
}
- esw_vport_enable_ingress_acl(esw, vport);
+ err = esw_vport_enable_ingress_acl(esw, vport);
+ if (err) {
+ mlx5_core_warn(esw->dev,
+ "failed to enable ingress acl (%d) on vport[%d]\n",
+ err, vport->vport);
+ return err;
+ }
esw_debug(esw->dev,
"vport[%d] configure ingress rules, vlan(%d) qos(%d)\n",
return 0;
}
- esw_vport_enable_egress_acl(esw, vport);
+ err = esw_vport_enable_egress_acl(esw, vport);
+ if (err) {
+ mlx5_core_warn(esw->dev,
+ "failed to enable egress acl (%d) on vport[%d]\n",
+ err, vport->vport);
+ return err;
+ }
esw_debug(esw->dev,
"vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
fs_get_obj(ft, fg->node.parent);
dev = get_dev(&ft->node);
+ if (ft->autogroup.active)
+ ft->autogroup.num_groups--;
+
if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n",
fg->id, ft->id);
tree_init_node(&fg->node, !is_auto_fg, del_flow_group);
tree_add_node(&fg->node, &ft->node);
/* Add node to group list */
- list_add(&fg->node.list, ft->node.children.prev);
+ list_add(&fg->node.list, prev_fg);
return fg;
}
return ERR_PTR(-EPERM);
lock_ref_node(&ft->node);
- fg = create_flow_group_common(ft, fg_in, &ft->node.children, false);
+ fg = create_flow_group_common(ft, fg_in, ft->node.children.prev, false);
unlock_ref_node(&ft->node);
return fg;
u32 *match_criteria)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
- struct list_head *prev = &ft->node.children;
+ struct list_head *prev = ft->node.children.prev;
unsigned int candidate_index = 0;
struct mlx5_flow_group *fg;
void *match_criteria_addr;
goto err_out;
if (aging) {
+ counter->cache.lastuse = jiffies;
counter->aging = true;
spin_lock(&fc_stats->addlist_lock);
enum {
MLX5_NIC_IFC_FULL = 0,
MLX5_NIC_IFC_DISABLED = 1,
- MLX5_NIC_IFC_NO_DRAM_NIC = 2
+ MLX5_NIC_IFC_NO_DRAM_NIC = 2,
+ MLX5_NIC_IFC_INVALID = 3
};
-static u8 get_nic_interface(struct mlx5_core_dev *dev)
+enum {
+ MLX5_DROP_NEW_HEALTH_WORK,
+};
+
+static u8 get_nic_state(struct mlx5_core_dev *dev)
{
return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3;
}
struct mlx5_core_health *health = &dev->priv.health;
struct health_buffer __iomem *h = health->health;
- if (get_nic_interface(dev) == MLX5_NIC_IFC_DISABLED)
+ if (get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
return 1;
if (ioread32be(&h->fw_ver) == 0xffffffff)
static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
{
- u8 nic_interface = get_nic_interface(dev);
+ u8 nic_interface = get_nic_state(dev);
switch (nic_interface) {
case MLX5_NIC_IFC_FULL:
mlx5_disable_device(dev);
}
+static void health_recover(struct work_struct *work)
+{
+ struct mlx5_core_health *health;
+ struct delayed_work *dwork;
+ struct mlx5_core_dev *dev;
+ struct mlx5_priv *priv;
+ u8 nic_state;
+
+ dwork = container_of(work, struct delayed_work, work);
+ health = container_of(dwork, struct mlx5_core_health, recover_work);
+ priv = container_of(health, struct mlx5_priv, health);
+ dev = container_of(priv, struct mlx5_core_dev, priv);
+
+ nic_state = get_nic_state(dev);
+ if (nic_state == MLX5_NIC_IFC_INVALID) {
+ dev_err(&dev->pdev->dev, "health recovery flow aborted since the nic state is invalid\n");
+ return;
+ }
+
+ dev_err(&dev->pdev->dev, "starting health recovery flow\n");
+ mlx5_recover_device(dev);
+}
+
+/* How much time to wait until health resetting the driver (in msecs) */
+#define MLX5_RECOVERY_DELAY_MSECS 60000
static void health_care(struct work_struct *work)
{
+ unsigned long recover_delay = msecs_to_jiffies(MLX5_RECOVERY_DELAY_MSECS);
struct mlx5_core_health *health;
struct mlx5_core_dev *dev;
struct mlx5_priv *priv;
dev = container_of(priv, struct mlx5_core_dev, priv);
mlx5_core_warn(dev, "handling bad device here\n");
mlx5_handle_bad_state(dev);
+
+ spin_lock(&health->wq_lock);
+ if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
+ schedule_delayed_work(&health->recover_work, recover_delay);
+ else
+ dev_err(&dev->pdev->dev,
+ "new health works are not permitted at this stage\n");
+ spin_unlock(&health->wq_lock);
}
static const char *hsynd_str(u8 synd)
if (in_fatal(dev) && !health->sick) {
health->sick = true;
print_health_info(dev);
- schedule_work(&health->work);
+ spin_lock(&health->wq_lock);
+ if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
+ queue_work(health->wq, &health->work);
+ else
+ dev_err(&dev->pdev->dev,
+ "new health works are not permitted at this stage\n");
+ spin_unlock(&health->wq_lock);
}
}
struct mlx5_core_health *health = &dev->priv.health;
init_timer(&health->timer);
+ health->sick = 0;
+ clear_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
health->health = &dev->iseg->health;
health->health_counter = &dev->iseg->health_counter;
del_timer_sync(&health->timer);
}
+void mlx5_drain_health_wq(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_health *health = &dev->priv.health;
+
+ spin_lock(&health->wq_lock);
+ set_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
+ spin_unlock(&health->wq_lock);
+ cancel_delayed_work_sync(&health->recover_work);
+ cancel_work_sync(&health->work);
+}
+
void mlx5_health_cleanup(struct mlx5_core_dev *dev)
{
struct mlx5_core_health *health = &dev->priv.health;
- flush_work(&health->work);
+ destroy_workqueue(health->wq);
}
int mlx5_health_init(struct mlx5_core_dev *dev)
strcpy(name, "mlx5_health");
strcat(name, dev_name(&dev->pdev->dev));
+ health->wq = create_singlethread_workqueue(name);
kfree(name);
-
+ if (!health->wq)
+ return -ENOMEM;
+ spin_lock_init(&health->wq_lock);
INIT_WORK(&health->work, health_care);
+ INIT_DELAYED_WORK(&health->recover_work, health_recover);
return 0;
}
struct pci_dev *pdev = dev->pdev;
int err;
- err = mlx5_query_hca_caps(dev);
- if (err) {
- dev_err(&pdev->dev, "query hca failed\n");
- goto out;
- }
-
err = mlx5_query_board_id(dev);
if (err) {
dev_err(&pdev->dev, "query board id failed\n");
mlx5_start_health_poll(dev);
+ err = mlx5_query_hca_caps(dev);
+ if (err) {
+ dev_err(&pdev->dev, "query hca failed\n");
+ goto err_stop_poll;
+ }
+
if (boot && mlx5_init_once(dev, priv)) {
dev_err(&pdev->dev, "sw objs init failed\n");
goto err_stop_poll;
struct mlx5_priv *priv = &dev->priv;
dev_info(&pdev->dev, "%s was called\n", __func__);
+
mlx5_enter_error_state(dev);
mlx5_unload_one(dev, priv, false);
- pci_save_state(pdev);
- mlx5_pci_disable_device(dev);
+ /* In case of kernel call save the pci state and drain health wq */
+ if (state) {
+ pci_save_state(pdev);
+ mlx5_drain_health_wq(dev);
+ mlx5_pci_disable_device(dev);
+ }
+
return state == pci_channel_io_perm_failure ?
PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
}
return PCI_ERS_RESULT_RECOVERED;
}
-void mlx5_disable_device(struct mlx5_core_dev *dev)
-{
- mlx5_pci_err_detected(dev->pdev, 0);
-}
-
static void mlx5_pci_resume(struct pci_dev *pdev)
{
struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table);
+void mlx5_disable_device(struct mlx5_core_dev *dev)
+{
+ mlx5_pci_err_detected(dev->pdev, 0);
+}
+
+void mlx5_recover_device(struct mlx5_core_dev *dev)
+{
+ mlx5_pci_disable_device(dev);
+ if (mlx5_pci_slot_reset(dev->pdev) == PCI_ERS_RESULT_RECOVERED)
+ mlx5_pci_resume(dev->pdev);
+}
+
static struct pci_driver mlx5_core_driver = {
.name = DRIVER_NAME,
.id_table = mlx5_core_pci_table,
unsigned long param);
void mlx5_enter_error_state(struct mlx5_core_dev *dev);
void mlx5_disable_device(struct mlx5_core_dev *dev);
+void mlx5_recover_device(struct mlx5_core_dev *dev);
int mlx5_sriov_init(struct mlx5_core_dev *dev);
void mlx5_sriov_cleanup(struct mlx5_core_dev *dev);
int mlx5_sriov_attach(struct mlx5_core_dev *dev);
static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id)
{
struct page *page;
+ u64 zero_addr = 1;
u64 addr;
int err;
int nid = dev_to_node(&dev->pdev->dev);
mlx5_core_warn(dev, "failed to allocate page\n");
return -ENOMEM;
}
+map:
addr = dma_map_page(&dev->pdev->dev, page, 0,
PAGE_SIZE, DMA_BIDIRECTIONAL);
if (dma_mapping_error(&dev->pdev->dev, addr)) {
mlx5_core_warn(dev, "failed dma mapping page\n");
err = -ENOMEM;
- goto out_alloc;
+ goto err_mapping;
}
+
+ /* Firmware doesn't support page with physical address 0 */
+ if (addr == 0) {
+ zero_addr = addr;
+ goto map;
+ }
+
err = insert_page(dev, addr, page, func_id);
if (err) {
mlx5_core_err(dev, "failed to track allocated page\n");
- goto out_mapping;
+ dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
}
- return 0;
-
-out_mapping:
- dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+err_mapping:
+ if (err)
+ __free_page(page);
-out_alloc:
- __free_page(page);
+ if (zero_addr == 0)
+ dma_unmap_page(&dev->pdev->dev, zero_addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
return err;
}
.cmd_exec = mlxsw_pci_cmd_exec,
};
-static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci)
+static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci,
+ const struct pci_device_id *id)
{
unsigned long end;
mlxsw_pci_write32(mlxsw_pci, SW_RESET, MLXSW_PCI_SW_RESET_RST_BIT);
+ if (id->device == PCI_DEVICE_ID_MELLANOX_SWITCHX2) {
+ msleep(MLXSW_PCI_SW_RESET_TIMEOUT_MSECS);
+ return 0;
+ }
+
wmb(); /* reset needs to be written before we read control register */
end = jiffies + msecs_to_jiffies(MLXSW_PCI_SW_RESET_TIMEOUT_MSECS);
do {
mlxsw_pci->pdev = pdev;
pci_set_drvdata(pdev, mlxsw_pci);
- err = mlxsw_pci_sw_reset(mlxsw_pci);
+ err = mlxsw_pci_sw_reset(mlxsw_pci, id);
if (err) {
dev_err(&pdev->dev, "Software reset failed\n");
goto err_sw_reset;
lpm_tree);
if (err)
goto err_left_struct_set;
+ memcpy(&lpm_tree->prefix_usage, prefix_usage,
+ sizeof(lpm_tree->prefix_usage));
return lpm_tree;
err_left_struct_set:
for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
lpm_tree = &mlxsw_sp->router.lpm_trees[i];
- if (lpm_tree->proto == proto &&
+ if (lpm_tree->ref_count != 0 &&
+ lpm_tree->proto == proto &&
mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
prefix_usage))
goto inc_ref_count;
return err;
}
-static int mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
- struct fib_entry_notifier_info *fen_info)
+static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
+ struct fib_entry_notifier_info *fen_info)
{
struct mlxsw_sp_fib_entry *fib_entry;
if (mlxsw_sp->router.aborted)
- return 0;
+ return;
fib_entry = mlxsw_sp_fib_entry_find(mlxsw_sp, fen_info);
- if (!fib_entry) {
- dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
- return -ENOENT;
- }
+ if (!fib_entry)
+ return;
if (fib_entry->ref_count == 1) {
mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
}
mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
- return 0;
}
static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
if (err)
return err;
- mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4, 0);
+ mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4,
+ MLXSW_SP_LPM_TREE_MIN);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
if (err)
return err;
err_port_admin_status_set:
err_port_mtu_set:
err_port_speed_set:
+ mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
err_port_swid_set:
err_port_system_port_mapping_set:
port_not_usable:
---help---
This enables the support for ...
+config QED_RDMA
+ bool
+
endif # NET_VENDOR_QLOGIC
qed_selftest.o qed_dcbx.o qed_debug.o
qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o
qed-$(CONFIG_QED_LL2) += qed_ll2.o
-qed-$(CONFIG_INFINIBAND_QEDR) += qed_roce.o
+qed-$(CONFIG_QED_RDMA) += qed_roce.o
#define TM_ALIGN BIT(TM_SHIFT)
#define TM_ELEM_SIZE 4
-/* ILT constants */
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
/* For RoCE we configure to 64K to cover for RoCE max tasks 256K purpose. */
-#define ILT_DEFAULT_HW_P_SIZE 4
-#else
-#define ILT_DEFAULT_HW_P_SIZE 3
-#endif
+#define ILT_DEFAULT_HW_P_SIZE (IS_ENABLED(CONFIG_QED_RDMA) ? 4 : 3)
#define ILT_PAGE_IN_BYTES(hw_p_size) (1U << ((hw_p_size) + 12))
#define ILT_CFG_REG(cli, reg) PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET
return NULL;
}
-void qed_cxt_set_srq_count(struct qed_hwfn *p_hwfn, u32 num_srqs)
+static void qed_cxt_set_srq_count(struct qed_hwfn *p_hwfn, u32 num_srqs)
{
struct qed_cxt_mngr *p_mgr = p_hwfn->p_cxt_mngr;
p_mgr->srq_count = num_srqs;
}
-u32 qed_cxt_get_srq_count(struct qed_hwfn *p_hwfn)
+static u32 qed_cxt_get_srq_count(struct qed_hwfn *p_hwfn)
{
struct qed_cxt_mngr *p_mgr = p_hwfn->p_cxt_mngr;
return 0;
}
-void qed_rdma_set_pf_params(struct qed_hwfn *p_hwfn,
- struct qed_rdma_pf_params *p_params)
+static void qed_rdma_set_pf_params(struct qed_hwfn *p_hwfn,
+ struct qed_rdma_pf_params *p_params)
{
u32 num_cons, num_tasks, num_qps, num_mrs, num_srqs;
enum protocol_type proto;
if (!dcbx_info)
return -ENOMEM;
+ memset(dcbx_info, 0, sizeof(*dcbx_info));
rc = qed_dcbx_query_params(p_hwfn, dcbx_info, QED_DCBX_OPERATIONAL_MIB);
if (rc) {
kfree(dcbx_info);
if (!dcbx_info)
return NULL;
+ memset(dcbx_info, 0, sizeof(*dcbx_info));
if (qed_dcbx_query_params(hwfn, dcbx_info, type)) {
kfree(dcbx_info);
return NULL;
/***************************** Constant Arrays *******************************/
/* Debug arrays */
-static struct dbg_array s_dbg_arrays[MAX_BIN_DBG_BUFFER_TYPE] = { {0} };
+static struct dbg_array s_dbg_arrays[MAX_BIN_DBG_BUFFER_TYPE] = { {NULL} };
/* Chip constant definitions array */
static struct chip_defs s_chip_defs[MAX_CHIP_IDS] = {
}
/* Dump MCP Trace */
-enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *dump_buf,
- bool dump, u32 *num_dumped_dwords)
+static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *dump_buf,
+ bool dump, u32 *num_dumped_dwords)
{
u32 trace_data_grc_addr, trace_data_size_bytes, trace_data_size_dwords;
u32 trace_meta_size_dwords, running_bundle_id, offset = 0;
}
/* Dump GRC FIFO */
-enum dbg_status qed_reg_fifo_dump(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *dump_buf,
- bool dump, u32 *num_dumped_dwords)
+static enum dbg_status qed_reg_fifo_dump(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *dump_buf,
+ bool dump, u32 *num_dumped_dwords)
{
u32 offset = 0, dwords_read, size_param_offset;
bool fifo_has_data;
}
/* Dump IGU FIFO */
-enum dbg_status qed_igu_fifo_dump(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *dump_buf,
- bool dump, u32 *num_dumped_dwords)
+static enum dbg_status qed_igu_fifo_dump(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *dump_buf,
+ bool dump, u32 *num_dumped_dwords)
{
u32 offset = 0, dwords_read, size_param_offset;
bool fifo_has_data;
}
/* Protection Override dump */
-enum dbg_status qed_protection_override_dump(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *dump_buf,
- bool dump, u32 *num_dumped_dwords)
+static enum dbg_status qed_protection_override_dump(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *dump_buf,
+ bool dump,
+ u32 *num_dumped_dwords)
{
u32 offset = 0, size_param_offset, override_window_dwords;
}
/* Wrapper for unifying the idle_chk and mcp_trace api */
-enum dbg_status qed_print_idle_chk_results_wrapper(struct qed_hwfn *p_hwfn,
- u32 *dump_buf,
- u32 num_dumped_dwords,
- char *results_buf)
+static enum dbg_status
+qed_print_idle_chk_results_wrapper(struct qed_hwfn *p_hwfn,
+ u32 *dump_buf,
+ u32 num_dumped_dwords,
+ char *results_buf)
{
u32 num_errors, num_warnnings;
#define QED_RESULTS_BUF_MIN_SIZE 16
/* Generic function for decoding debug feature info */
-enum dbg_status format_feature(struct qed_hwfn *p_hwfn,
- enum qed_dbg_features feature_idx)
+static enum dbg_status format_feature(struct qed_hwfn *p_hwfn,
+ enum qed_dbg_features feature_idx)
{
struct qed_dbg_feature *feature =
&p_hwfn->cdev->dbg_params.features[feature_idx];
}
/* Generic function for performing the dump of a debug feature. */
-enum dbg_status qed_dbg_dump(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
- enum qed_dbg_features feature_idx)
+static enum dbg_status qed_dbg_dump(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_dbg_features feature_idx)
{
struct qed_dbg_feature *feature =
&p_hwfn->cdev->dbg_params.features[feature_idx];
if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) {
num_cons = qed_cxt_get_proto_cid_count(p_hwfn,
PROTOCOLID_ROCE,
- 0) * 2;
+ NULL) * 2;
n_eqes += num_cons + 2 * MAX_NUM_VFS_BB;
} else if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) {
num_cons =
qed_cxt_get_proto_cid_count(p_hwfn,
- PROTOCOLID_ISCSI, 0);
+ PROTOCOLID_ISCSI,
+ NULL);
n_eqes += 2 * num_cons;
}
u32 *feat_num = p_hwfn->hw_info.feat_num;
int num_features = 1;
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
- /* Roce CNQ each requires: 1 status block + 1 CNQ. We divide the
- * status blocks equally between L2 / RoCE but with consideration as
- * to how many l2 queues / cnqs we have
- */
- if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) {
+ if (IS_ENABLED(CONFIG_QED_RDMA) &&
+ p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) {
+ /* Roce CNQ each requires: 1 status block + 1 CNQ. We divide
+ * the status blocks equally between L2 / RoCE but with
+ * consideration as to how many l2 queues / cnqs we have.
+ */
num_features++;
feat_num[QED_RDMA_CNQ] =
min_t(u32, RESC_NUM(p_hwfn, QED_SB) / num_features,
RESC_NUM(p_hwfn, QED_RDMA_CNQ_RAM));
}
-#endif
+
feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) /
num_features,
RESC_NUM(p_hwfn, QED_L2_QUEUE));
#include "qed_mcp.h"
#include "qed_reg_addr.h"
#include "qed_sp.h"
+#include "qed_roce.h"
#define QED_LL2_RX_REGISTERED(ll2) ((ll2)->rx_queue.b_cb_registred)
#define QED_LL2_TX_REGISTERED(ll2) ((ll2)->tx_queue.b_cb_registred)
qed_ll2_dealloc_buffer(cdev, buffer);
}
-void qed_ll2b_complete_rx_packet(struct qed_hwfn *p_hwfn,
- u8 connection_handle,
- struct qed_ll2_rx_packet *p_pkt,
- struct core_rx_fast_path_cqe *p_cqe,
- bool b_last_packet)
+static void qed_ll2b_complete_rx_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ struct qed_ll2_rx_packet *p_pkt,
+ struct core_rx_fast_path_cqe *p_cqe,
+ bool b_last_packet)
{
u16 packet_length = le16_to_cpu(p_cqe->packet_length);
struct qed_ll2_buffer *buffer = p_pkt->cookie;
return rc;
}
-void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
+static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
{
struct qed_ll2_info *p_ll2_conn = NULL;
struct qed_ll2_rx_packet *p_pkt = NULL;
if (!p_pkt)
break;
- list_del(&p_pkt->list_entry);
- list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
+ list_move_tail(&p_pkt->list_entry, &p_rx->free_descq);
rx_buf_addr = p_pkt->rx_buf_addr;
cookie = p_pkt->cookie;
p_posting_packet = list_first_entry(&p_rx->posting_descq,
struct qed_ll2_rx_packet,
list_entry);
- list_del(&p_posting_packet->list_entry);
- list_add_tail(&p_posting_packet->list_entry,
- &p_rx->active_descq);
+ list_move_tail(&p_posting_packet->list_entry,
+ &p_rx->active_descq);
b_notify_fw = true;
}
DMA_REGPAIR_LE(start_bd->addr, first_frag);
start_bd->nbytes = cpu_to_le16(first_frag_len);
- SET_FIELD(start_bd->bd_flags.as_bitfield, CORE_TX_BD_FLAGS_ROCE_FLAV,
- type);
-
DP_VERBOSE(p_hwfn,
(NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
"LL2 [q 0x%02x cid 0x%08x type 0x%08x] Tx Producer at [0x%04x] - set with a %04x bytes %02x BDs buffer at %08x:%08x\n",
if (!p_pkt)
break;
- list_del(&p_pkt->list_entry);
- list_add_tail(&p_pkt->list_entry, &p_tx->active_descq);
+ list_move_tail(&p_pkt->list_entry, &p_tx->active_descq);
}
SET_FIELD(db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
*/
void qed_ll2_free(struct qed_hwfn *p_hwfn,
struct qed_ll2_info *p_ll2_connections);
-void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn,
- u8 connection_handle,
- void *cookie,
- dma_addr_t rx_buf_addr,
- u16 data_length,
- u8 data_length_error,
- u16 parse_flags,
- u16 vlan,
- u32 src_mac_addr_hi,
- u16 src_mac_addr_lo, bool b_last_packet);
-void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn,
- u8 connection_handle,
- void *cookie,
- dma_addr_t first_frag_addr,
- bool b_last_fragment, bool b_last_packet);
-void qed_ll2b_release_tx_gsi_packet(struct qed_hwfn *p_hwfn,
- u8 connection_handle,
- void *cookie,
- dma_addr_t first_frag_addr,
- bool b_last_fragment, bool b_last_packet);
#endif
#include "qed_hw.h"
#include "qed_selftest.h"
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
#define QED_ROCE_QPS (8192)
#define QED_ROCE_DPIS (8)
-#endif
static char version[] =
"QLogic FastLinQ 4xxxx Core Module qed " DRV_MODULE_VERSION "\n";
enum qed_int_mode int_mode)
{
struct qed_sb_cnt_info sb_cnt_info;
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
- int num_l2_queues;
-#endif
+ int num_l2_queues = 0;
int rc;
int i;
cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors -
cdev->num_hwfns;
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
- num_l2_queues = 0;
+ if (!IS_ENABLED(CONFIG_QED_RDMA))
+ return 0;
+
for_each_hwfn(cdev, i)
num_l2_queues += FEAT_NUM(&cdev->hwfns[i], QED_PF_L2_QUE);
DP_VERBOSE(cdev, QED_MSG_RDMA, "roce_msix_cnt=%d roce_msix_base=%d\n",
cdev->int_params.rdma_msix_cnt,
cdev->int_params.rdma_msix_base);
-#endif
return 0;
}
{
int i;
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
- params->rdma_pf_params.num_qps = QED_ROCE_QPS;
- params->rdma_pf_params.min_dpis = QED_ROCE_DPIS;
- /* divide by 3 the MRs to avoid MF ILT overflow */
- params->rdma_pf_params.num_mrs = RDMA_MAX_TIDS;
- params->rdma_pf_params.gl_pi = QED_ROCE_PROTOCOL_INDEX;
-#endif
for (i = 0; i < cdev->num_hwfns; i++) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
p_hwfn->pf_params = *params;
}
+
+ if (!IS_ENABLED(CONFIG_QED_RDMA))
+ return;
+
+ params->rdma_pf_params.num_qps = QED_ROCE_QPS;
+ params->rdma_pf_params.min_dpis = QED_ROCE_DPIS;
+ /* divide by 3 the MRs to avoid MF ILT overflow */
+ params->rdma_pf_params.num_mrs = RDMA_MAX_TIDS;
+ params->rdma_pf_params.gl_pi = QED_ROCE_PROTOCOL_INDEX;
}
static int qed_slowpath_start(struct qed_dev *cdev,
}
}
+ cdev->rx_coalesce_usecs = QED_DEFAULT_RX_USECS;
rc = qed_nic_setup(cdev);
if (rc)
goto err;
return status;
}
-struct qed_selftest_ops qed_selftest_ops_pass = {
+static struct qed_selftest_ops qed_selftest_ops_pass = {
.selftest_memory = &qed_selftest_memory,
.selftest_interrupt = &qed_selftest_interrupt,
.selftest_register = &qed_selftest_register,
}
}
-u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id)
+static u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id)
{
/* First sb id for RoCE is after all the l2 sb */
return FEAT_NUM((struct qed_hwfn *)p_hwfn, QED_PF_L2_QUE) + rel_sb_id;
}
-u32 qed_rdma_query_cau_timer_res(void *rdma_cxt)
-{
- return QED_CAU_DEF_RX_TIMER_RES;
-}
-
static int qed_rdma_alloc(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_rdma_start_in_params *params)
p_hwfn->p_rdma_info = p_rdma_info;
p_rdma_info->proto = PROTOCOLID_ROCE;
- num_cons = qed_cxt_get_proto_cid_count(p_hwfn, p_rdma_info->proto, 0);
+ num_cons = qed_cxt_get_proto_cid_count(p_hwfn, p_rdma_info->proto,
+ NULL);
p_rdma_info->num_qps = num_cons / 2;
return rc;
}
-void qed_rdma_resc_free(struct qed_hwfn *p_hwfn)
+static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn)
{
struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
+static int qed_rdma_alloc_tid(void *rdma_cxt, u32 *itid)
+{
+ struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+ int rc;
+
+ DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID\n");
+
+ spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+ rc = qed_rdma_bmap_alloc_id(p_hwfn,
+ &p_hwfn->p_rdma_info->tid_map, itid);
+ spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+ if (rc)
+ goto out;
+
+ rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_TASK, *itid);
+out:
+ DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID - done, rc = %d\n", rc);
+ return rc;
+}
+
static int qed_rdma_reserve_lkey(struct qed_hwfn *p_hwfn)
{
struct qed_rdma_device *dev = p_hwfn->p_rdma_info->dev;
return qed_rdma_start_fw(p_hwfn, params, p_ptt);
}
-int qed_rdma_stop(void *rdma_cxt)
+static int qed_rdma_stop(void *rdma_cxt)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct rdma_close_func_ramrod_data *p_ramrod;
return rc;
}
-int qed_rdma_add_user(void *rdma_cxt,
- struct qed_rdma_add_user_out_params *out_params)
+static int qed_rdma_add_user(void *rdma_cxt,
+ struct qed_rdma_add_user_out_params *out_params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
u32 dpi_start_offset;
return rc;
}
-struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt)
+static struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct qed_rdma_port *p_port = p_hwfn->p_rdma_info->port;
return p_port;
}
-struct qed_rdma_device *qed_rdma_query_device(void *rdma_cxt)
+static struct qed_rdma_device *qed_rdma_query_device(void *rdma_cxt)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
return p_hwfn->p_rdma_info->dev;
}
-void qed_rdma_free_tid(void *rdma_cxt, u32 itid)
+static void qed_rdma_free_tid(void *rdma_cxt, u32 itid)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
}
-int qed_rdma_alloc_tid(void *rdma_cxt, u32 *itid)
-{
- struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
- int rc;
-
- DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID\n");
-
- spin_lock_bh(&p_hwfn->p_rdma_info->lock);
- rc = qed_rdma_bmap_alloc_id(p_hwfn,
- &p_hwfn->p_rdma_info->tid_map, itid);
- spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
- if (rc)
- goto out;
-
- rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_TASK, *itid);
-out:
- DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID - done, rc = %d\n", rc);
- return rc;
-}
-
-void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 qz_offset, u16 prod)
+static void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 qz_offset, u16 prod)
{
struct qed_hwfn *p_hwfn;
u16 qz_num;
return 0;
}
-int qed_rdma_alloc_pd(void *rdma_cxt, u16 *pd)
+static int qed_rdma_alloc_pd(void *rdma_cxt, u16 *pd)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
u32 returned_id;
return rc;
}
-void qed_rdma_free_pd(void *rdma_cxt, u16 pd)
+static void qed_rdma_free_pd(void *rdma_cxt, u16 pd)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
return toggle_bit;
}
-int qed_rdma_create_cq(void *rdma_cxt,
- struct qed_rdma_create_cq_in_params *params, u16 *icid)
+static int qed_rdma_create_cq(void *rdma_cxt,
+ struct qed_rdma_create_cq_in_params *params,
+ u16 *icid)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct qed_rdma_info *p_info = p_hwfn->p_rdma_info;
return rc;
}
-int qed_rdma_resize_cq(void *rdma_cxt,
- struct qed_rdma_resize_cq_in_params *in_params,
- struct qed_rdma_resize_cq_out_params *out_params)
-{
- struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
- struct rdma_resize_cq_output_params *p_ramrod_res;
- struct rdma_resize_cq_ramrod_data *p_ramrod;
- enum qed_rdma_toggle_bit toggle_bit;
- struct qed_sp_init_data init_data;
- struct qed_spq_entry *p_ent;
- dma_addr_t ramrod_res_phys;
- u8 fw_return_code;
- int rc = -ENOMEM;
-
- DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", in_params->icid);
-
- p_ramrod_res =
- (struct rdma_resize_cq_output_params *)
- dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
- sizeof(struct rdma_resize_cq_output_params),
- &ramrod_res_phys, GFP_KERNEL);
- if (!p_ramrod_res) {
- DP_NOTICE(p_hwfn,
- "qed resize cq failed: cannot allocate memory (ramrod)\n");
- return rc;
- }
-
- /* Get SPQ entry */
- memset(&init_data, 0, sizeof(init_data));
- init_data.cid = in_params->icid;
- init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
- init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
-
- rc = qed_sp_init_request(p_hwfn, &p_ent,
- RDMA_RAMROD_RESIZE_CQ,
- p_hwfn->p_rdma_info->proto, &init_data);
- if (rc)
- goto err;
-
- p_ramrod = &p_ent->ramrod.rdma_resize_cq;
-
- p_ramrod->flags = 0;
-
- /* toggle the bit for every resize or create cq for a given icid */
- toggle_bit = qed_rdma_toggle_bit_create_resize_cq(p_hwfn,
- in_params->icid);
-
- SET_FIELD(p_ramrod->flags,
- RDMA_RESIZE_CQ_RAMROD_DATA_TOGGLE_BIT, toggle_bit);
-
- SET_FIELD(p_ramrod->flags,
- RDMA_RESIZE_CQ_RAMROD_DATA_IS_TWO_LEVEL_PBL,
- in_params->pbl_two_level);
-
- p_ramrod->pbl_log_page_size = in_params->pbl_page_size_log - 12;
- p_ramrod->pbl_num_pages = cpu_to_le16(in_params->pbl_num_pages);
- p_ramrod->max_cqes = cpu_to_le32(in_params->cq_size);
- DMA_REGPAIR_LE(p_ramrod->pbl_addr, in_params->pbl_ptr);
- DMA_REGPAIR_LE(p_ramrod->output_params_addr, ramrod_res_phys);
-
- rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code);
- if (rc)
- goto err;
-
- if (fw_return_code != RDMA_RETURN_OK) {
- DP_NOTICE(p_hwfn, "fw_return_code = %d\n", fw_return_code);
- rc = -EINVAL;
- goto err;
- }
-
- out_params->prod = le32_to_cpu(p_ramrod_res->old_cq_prod);
- out_params->cons = le32_to_cpu(p_ramrod_res->old_cq_cons);
-
- dma_free_coherent(&p_hwfn->cdev->pdev->dev,
- sizeof(struct rdma_resize_cq_output_params),
- p_ramrod_res, ramrod_res_phys);
-
- DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Resized CQ, rc = %d\n", rc);
-
- return rc;
-
-err: dma_free_coherent(&p_hwfn->cdev->pdev->dev,
- sizeof(struct rdma_resize_cq_output_params),
- p_ramrod_res, ramrod_res_phys);
- DP_NOTICE(p_hwfn, "Resized CQ, Failed - rc = %d\n", rc);
-
- return rc;
-}
-
-int qed_rdma_destroy_cq(void *rdma_cxt,
- struct qed_rdma_destroy_cq_in_params *in_params,
- struct qed_rdma_destroy_cq_out_params *out_params)
+static int
+qed_rdma_destroy_cq(void *rdma_cxt,
+ struct qed_rdma_destroy_cq_in_params *in_params,
+ struct qed_rdma_destroy_cq_out_params *out_params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct rdma_destroy_cq_output_params *p_ramrod_res;
return flavor;
}
-int qed_roce_alloc_cid(struct qed_hwfn *p_hwfn, u16 *cid)
+static int qed_roce_alloc_cid(struct qed_hwfn *p_hwfn, u16 *cid)
{
struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
u32 responder_icid;
return rc;
}
-int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
- struct qed_rdma_qp *qp,
- struct qed_rdma_query_qp_out_params *out_params)
+static int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
+ struct qed_rdma_qp *qp,
+ struct qed_rdma_query_qp_out_params *out_params)
{
struct roce_query_qp_resp_output_params *p_resp_ramrod_res;
struct roce_query_qp_req_output_params *p_req_ramrod_res;
return rc;
}
-int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp)
+static int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp)
{
u32 num_invalidated_mw = 0;
u32 num_bound_mw = 0;
return 0;
}
-int qed_rdma_query_qp(void *rdma_cxt,
- struct qed_rdma_qp *qp,
- struct qed_rdma_query_qp_out_params *out_params)
+static int qed_rdma_query_qp(void *rdma_cxt,
+ struct qed_rdma_qp *qp,
+ struct qed_rdma_query_qp_out_params *out_params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
int rc;
return rc;
}
-int qed_rdma_destroy_qp(void *rdma_cxt, struct qed_rdma_qp *qp)
+static int qed_rdma_destroy_qp(void *rdma_cxt, struct qed_rdma_qp *qp)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
int rc = 0;
return rc;
}
-struct qed_rdma_qp *
+static struct qed_rdma_qp *
qed_rdma_create_qp(void *rdma_cxt,
struct qed_rdma_create_qp_in_params *in_params,
struct qed_rdma_create_qp_out_params *out_params)
return rc;
}
-int qed_rdma_modify_qp(void *rdma_cxt,
- struct qed_rdma_qp *qp,
- struct qed_rdma_modify_qp_in_params *params)
+static int qed_rdma_modify_qp(void *rdma_cxt,
+ struct qed_rdma_qp *qp,
+ struct qed_rdma_modify_qp_in_params *params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
enum qed_roce_qp_state prev_state;
return rc;
}
-int qed_rdma_register_tid(void *rdma_cxt,
- struct qed_rdma_register_tid_in_params *params)
+static int
+qed_rdma_register_tid(void *rdma_cxt,
+ struct qed_rdma_register_tid_in_params *params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct rdma_register_tid_ramrod_data *p_ramrod;
return rc;
}
-int qed_rdma_deregister_tid(void *rdma_cxt, u32 itid)
+static int qed_rdma_deregister_tid(void *rdma_cxt, u32 itid)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct rdma_deregister_tid_ramrod_data *p_ramrod;
qed_rdma_dpm_conf(p_hwfn, p_ptt);
}
-int qed_rdma_start(void *rdma_cxt, struct qed_rdma_start_in_params *params)
+static int qed_rdma_start(void *rdma_cxt,
+ struct qed_rdma_start_in_params *params)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct qed_ptt *p_ptt;
return qed_rdma_start(QED_LEADING_HWFN(cdev), params);
}
-void qed_rdma_remove_user(void *rdma_cxt, u16 dpi)
+static void qed_rdma_remove_user(void *rdma_cxt, u16 dpi)
{
struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
struct qed_roce_ll2_info *roce_ll2 = hwfn->ll2;
int rc;
- if (!cdev) {
- DP_ERR(cdev, "qed roce ll2 stop: invalid cdev\n");
- return -EINVAL;
- }
-
if (roce_ll2->handle == QED_LL2_UNUSED_HANDLE) {
DP_ERR(cdev, "qed roce ll2 stop: cannot stop an unused LL2\n");
return -EINVAL;
int rc;
int i;
- if (!cdev || !pkt || !params) {
+ if (!pkt || !params) {
DP_ERR(cdev,
"roce ll2 tx: failed tx because one of the following is NULL - drv=%p, pkt=%p, params=%p\n",
cdev, pkt, params);
enum protocol_type proto;
};
-struct qed_rdma_resize_cq_in_params {
- u16 icid;
- u32 cq_size;
- bool pbl_two_level;
- u64 pbl_ptr;
- u16 pbl_num_pages;
- u8 pbl_page_size_log;
-};
-
-struct qed_rdma_resize_cq_out_params {
- u32 prod;
- u32 cons;
-};
-
-struct qed_rdma_resize_cnq_in_params {
- u32 cnq_id;
- u32 pbl_page_size_log;
- u64 pbl_ptr;
-};
-
struct qed_rdma_qp {
struct regpair qp_handle;
struct regpair qp_handle_async;
dma_addr_t shared_queue_phys_addr;
};
-int
-qed_rdma_add_user(void *rdma_cxt,
- struct qed_rdma_add_user_out_params *out_params);
-int qed_rdma_alloc_pd(void *rdma_cxt, u16 *pd);
-int qed_rdma_alloc_tid(void *rdma_cxt, u32 *tid);
-int qed_rdma_deregister_tid(void *rdma_cxt, u32 tid);
-void qed_rdma_free_tid(void *rdma_cxt, u32 tid);
-struct qed_rdma_device *qed_rdma_query_device(void *rdma_cxt);
-struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt);
-int
-qed_rdma_register_tid(void *rdma_cxt,
- struct qed_rdma_register_tid_in_params *params);
-void qed_rdma_remove_user(void *rdma_cxt, u16 dpi);
-int qed_rdma_start(void *p_hwfn, struct qed_rdma_start_in_params *params);
-int qed_rdma_stop(void *rdma_cxt);
-u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id);
-u32 qed_rdma_query_cau_timer_res(void *p_hwfn);
-void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 cnq_index, u16 prod);
-void qed_rdma_resc_free(struct qed_hwfn *p_hwfn);
+#if IS_ENABLED(CONFIG_QED_RDMA)
+void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
void qed_async_roce_event(struct qed_hwfn *p_hwfn,
struct event_ring_entry *p_eqe);
-int qed_rdma_destroy_qp(void *rdma_cxt, struct qed_rdma_qp *qp);
-int qed_rdma_modify_qp(void *rdma_cxt, struct qed_rdma_qp *qp,
- struct qed_rdma_modify_qp_in_params *params);
-int qed_rdma_query_qp(void *rdma_cxt, struct qed_rdma_qp *qp,
- struct qed_rdma_query_qp_out_params *out_params);
-
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
-void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t first_frag_addr,
+ bool b_last_fragment, bool b_last_packet);
+void qed_ll2b_release_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t first_frag_addr,
+ bool b_last_fragment, bool b_last_packet);
+void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t rx_buf_addr,
+ u16 data_length,
+ u8 data_length_error,
+ u16 parse_flags,
+ u16 vlan,
+ u32 src_mac_addr_hi,
+ u16 src_mac_addr_lo, bool b_last_packet);
#else
-void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {}
+static inline void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {}
+static inline void qed_async_roce_event(struct qed_hwfn *p_hwfn, struct event_ring_entry *p_eqe) {}
+static inline void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t first_frag_addr,
+ bool b_last_fragment,
+ bool b_last_packet) {}
+static inline void qed_ll2b_release_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t first_frag_addr,
+ bool b_last_fragment,
+ bool b_last_packet) {}
+static inline void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn,
+ u8 connection_handle,
+ void *cookie,
+ dma_addr_t rx_buf_addr,
+ u16 data_length,
+ u8 data_length_error,
+ u16 parse_flags,
+ u16 vlan,
+ u32 src_mac_addr_hi,
+ u16 src_mac_addr_lo,
+ bool b_last_packet) {}
#endif
#endif
struct roce_destroy_qp_resp_ramrod_data roce_destroy_qp_resp;
struct roce_destroy_qp_req_ramrod_data roce_destroy_qp_req;
struct rdma_create_cq_ramrod_data rdma_create_cq;
- struct rdma_resize_cq_ramrod_data rdma_resize_cq;
struct rdma_destroy_cq_ramrod_data rdma_destroy_cq;
struct rdma_srq_create_ramrod_data rdma_create_srq;
struct rdma_srq_destroy_ramrod_data rdma_destroy_srq;
#include "qed_reg_addr.h"
#include "qed_sp.h"
#include "qed_sriov.h"
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
#include "qed_roce.h"
-#endif
/***************************************************************************
* Structures & Definitions
struct event_ring_entry *p_eqe)
{
switch (p_eqe->protocol_id) {
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
case PROTOCOLID_ROCE:
qed_async_roce_event(p_hwfn, p_eqe);
return 0;
-#endif
case PROTOCOLID_COMMON:
return qed_sriov_eqe_event(p_hwfn,
p_eqe->opcode,
qede-y := qede_main.o qede_ethtool.o
qede-$(CONFIG_DCB) += qede_dcbnl.o
-qede-$(CONFIG_INFINIBAND_QEDR) += qede_roce.o
+qede-$(CONFIG_QED_RDMA) += qede_roce.o
int qede_txq_has_work(struct qede_tx_queue *txq);
void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, struct qede_dev *edev,
u8 count);
+void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq);
#define RX_RING_SIZE_POW 13
#define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW))
#define NUM_RX_BDS_MAX (RX_RING_SIZE - 1)
#define NUM_RX_BDS_MIN 128
-#define NUM_RX_BDS_DEF NUM_RX_BDS_MAX
+#define NUM_RX_BDS_DEF ((u16)BIT(10) - 1)
#define TX_RING_SIZE_POW 13
#define TX_RING_SIZE ((u16)BIT(TX_RING_SIZE_POW))
struct qede_dev *edev = netdev_priv(dev);
channels->max_combined = QEDE_MAX_RSS_CNT(edev);
+ channels->max_rx = QEDE_MAX_RSS_CNT(edev);
+ channels->max_tx = QEDE_MAX_RSS_CNT(edev);
channels->combined_count = QEDE_QUEUE_CNT(edev) - edev->fp_num_tx -
edev->fp_num_rx;
channels->tx_count = edev->fp_num_tx;
edev->req_queues = count;
edev->req_num_tx = channels->tx_count;
edev->req_num_rx = channels->rx_count;
+ /* Reset the indirection table if rx queue count is updated */
+ if ((edev->req_queues - edev->req_num_tx) != QEDE_RSS_COUNT(edev)) {
+ edev->rss_params_inited &= ~QEDE_RSS_INDIR_INITED;
+ memset(&edev->rss_params.rss_ind_table, 0,
+ sizeof(edev->rss_params.rss_ind_table));
+ }
+
if (netif_running(dev))
qede_reload(edev, NULL, NULL);
struct qede_dev *edev = netdev_priv(dev);
int i;
+ if (edev->dev_info.common.num_hwfns > 1) {
+ DP_INFO(edev,
+ "RSS configuration is not supported for 100G devices\n");
+ return -EOPNOTSUPP;
+ }
+
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
}
first_bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl);
- dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
- BD_UNMAP_LEN(first_bd), DMA_TO_DEVICE);
+ dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
+ BD_UNMAP_LEN(first_bd), DMA_TO_DEVICE);
txq->sw_tx_cons++;
txq->sw_tx_ring[idx].skb = NULL;
struct qede_rx_queue *rxq = NULL;
struct sw_rx_data *sw_rx_data;
union eth_rx_cqe *cqe;
+ int i, rc = 0;
u8 *data_ptr;
- int i;
for_each_queue(i) {
if (edev->fp_array[i].type & QEDE_FASTPATH_RX) {
* queue and that the loopback traffic is not IP.
*/
for (i = 0; i < QEDE_SELFTEST_POLL_COUNT; i++) {
- if (qede_has_rx_work(rxq))
+ if (!qede_has_rx_work(rxq)) {
+ usleep_range(100, 200);
+ continue;
+ }
+
+ hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
+ sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
+
+ /* Memory barrier to prevent the CPU from doing speculative
+ * reads of CQE/BD before reading hw_comp_cons. If the CQE is
+ * read before it is written by FW, then FW writes CQE and SB,
+ * and then the CPU reads the hw_comp_cons, it will use an old
+ * CQE.
+ */
+ rmb();
+
+ /* Get the CQE from the completion ring */
+ cqe = (union eth_rx_cqe *)qed_chain_consume(&rxq->rx_comp_ring);
+
+ /* Get the data from the SW ring */
+ sw_rx_index = rxq->sw_rx_cons & NUM_RX_BDS_MAX;
+ sw_rx_data = &rxq->sw_rx_ring[sw_rx_index];
+ fp_cqe = &cqe->fast_path_regular;
+ len = le16_to_cpu(fp_cqe->len_on_first_bd);
+ data_ptr = (u8 *)(page_address(sw_rx_data->data) +
+ fp_cqe->placement_offset +
+ sw_rx_data->page_offset);
+ if (ether_addr_equal(data_ptr, edev->ndev->dev_addr) &&
+ ether_addr_equal(data_ptr + ETH_ALEN,
+ edev->ndev->dev_addr)) {
+ for (i = ETH_HLEN; i < len; i++)
+ if (data_ptr[i] != (unsigned char)(i & 0xff)) {
+ rc = -1;
+ break;
+ }
+
+ qede_recycle_rx_bd_ring(rxq, edev, 1);
+ qed_chain_recycle_consumed(&rxq->rx_comp_ring);
break;
- usleep_range(100, 200);
+ }
+
+ DP_INFO(edev, "Not the transmitted packet\n");
+ qede_recycle_rx_bd_ring(rxq, edev, 1);
+ qed_chain_recycle_consumed(&rxq->rx_comp_ring);
}
- if (!qede_has_rx_work(rxq)) {
+ if (i == QEDE_SELFTEST_POLL_COUNT) {
DP_NOTICE(edev, "Failed to receive the traffic\n");
return -1;
}
- hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
- sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
+ qede_update_rx_prod(edev, rxq);
- /* Memory barrier to prevent the CPU from doing speculative reads of CQE
- * / BD before reading hw_comp_cons. If the CQE is read before it is
- * written by FW, then FW writes CQE and SB, and then the CPU reads the
- * hw_comp_cons, it will use an old CQE.
- */
- rmb();
-
- /* Get the CQE from the completion ring */
- cqe = (union eth_rx_cqe *)qed_chain_consume(&rxq->rx_comp_ring);
-
- /* Get the data from the SW ring */
- sw_rx_index = rxq->sw_rx_cons & NUM_RX_BDS_MAX;
- sw_rx_data = &rxq->sw_rx_ring[sw_rx_index];
- fp_cqe = &cqe->fast_path_regular;
- len = le16_to_cpu(fp_cqe->len_on_first_bd);
- data_ptr = (u8 *)(page_address(sw_rx_data->data) +
- fp_cqe->placement_offset + sw_rx_data->page_offset);
- for (i = ETH_HLEN; i < len; i++)
- if (data_ptr[i] != (unsigned char)(i & 0xff)) {
- DP_NOTICE(edev, "Loopback test failed\n");
- qede_recycle_rx_bd_ring(rxq, edev, 1);
- return -1;
- }
-
- qede_recycle_rx_bd_ring(rxq, edev, 1);
-
- return 0;
+ return rc;
}
static int qede_selftest_run_loopback(struct qede_dev *edev, u32 loopback_mode)
split_bd_len = BD_UNMAP_LEN(split);
bds_consumed++;
}
- dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
- BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
+ dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
+ BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
/* Unmap the data of the skb frags */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, bds_consumed++) {
nbd--;
}
- dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
- BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
+ dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
+ BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
/* Unmap the data of the skb frags */
for (i = 0; i < nbd; i++) {
return 0;
}
-static inline void qede_update_rx_prod(struct qede_dev *edev,
- struct qede_rx_queue *rxq)
+void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq)
{
u16 bd_prod = qed_chain_get_prod_idx(&rxq->rx_bd_ring);
u16 cqe_prod = qed_chain_get_prod_idx(&rxq->rx_comp_ring);
txq->num_tx_buffers = edev->q_num_tx_buffers;
/* Allocate the parallel driver ring for Tx buffers */
- size = sizeof(*txq->sw_tx_ring) * NUM_TX_BDS_MAX;
+ size = sizeof(*txq->sw_tx_ring) * TX_RING_SIZE;
txq->sw_tx_ring = kzalloc(size, GFP_KERNEL);
if (!txq->sw_tx_ring) {
DP_NOTICE(edev, "Tx buffers ring allocation failed\n");
QED_CHAIN_USE_TO_CONSUME_PRODUCE,
QED_CHAIN_MODE_PBL,
QED_CHAIN_CNT_TYPE_U16,
- NUM_TX_BDS_MAX,
+ TX_RING_SIZE,
sizeof(*p_virt), &txq->tx_pbl);
if (rc)
goto err;
napi_disable(&adpt->rx_q.napi);
phy_stop(adpt->phydev);
- phy_disconnect(adpt->phydev);
- /* disable mac irq */
+ /* Interrupts must be disabled before the PHY is disconnected, to
+ * avoid a race condition where adjust_link is null when we get
+ * an interrupt.
+ */
writel(DIS_INT, adpt->base + EMAC_INT_STATUS);
writel(0, adpt->base + EMAC_INT_MASK);
synchronize_irq(adpt->irq.irq);
free_irq(adpt->irq.irq, &adpt->irq);
+ phy_disconnect(adpt->phydev);
+
emac_mac_reset(adpt);
emac_tx_q_descs_free(adpt);
},
{}
};
+MODULE_DEVICE_TABLE(of, emac_dt_match);
#if IS_ENABLED(CONFIG_ACPI)
static const struct acpi_device_id emac_acpi_match[] = {
if ((sizeof(dma_addr_t) > 4) &&
(use_dac == 1 || (use_dac == -1 && pci_is_pcie(pdev) &&
tp->mac_version >= RTL_GIGA_MAC_VER_18)) &&
- !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
+ !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
/* CPlusCmd Dual Access Cycle is only needed for non-PCIe */
if (!pci_is_pcie(pdev))
if (rocker->wops) {
if (rocker->wops->mode != mode) {
dev_err(&rocker->pdev->dev, "hardware has ports in different worlds, which is not supported\n");
- return err;
+ return -EINVAL;
}
return 0;
}
spin_lock_irqsave(&ofdpa->neigh_tbl_lock, lock_flags);
found = ofdpa_neigh_tbl_find(ofdpa, ip_addr);
- if (found)
- *index = found->index;
updating = found && adding;
removing = found && !adding;
resolved = false;
} else if (removing) {
ofdpa_neigh_del(trans, found);
+ *index = found->index;
} else if (updating) {
ofdpa_neigh_update(found, trans, NULL, false);
resolved = !is_zero_ether_addr(found->eth_dst);
+ *index = found->index;
} else {
err = -ENOENT;
}
pr_info("%s descriptor ring:\n", rx ? "RX" : "TX");
for (i = 0; i < size; i++) {
- if (p->des0)
- pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
- i, (unsigned int)virt_to_phys(p),
- p->des0, p->des1, p->des2, p->des3);
+ pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
+ i, (unsigned int)virt_to_phys(p),
+ p->des0, p->des1, p->des2, p->des3);
p++;
}
}
int stmmac_mdio_reset(struct mii_bus *mii);
void stmmac_set_ethtool_ops(struct net_device *netdev);
-int stmmac_ptp_register(struct stmmac_priv *priv);
+void stmmac_ptp_register(struct stmmac_priv *priv);
void stmmac_ptp_unregister(struct stmmac_priv *priv);
int stmmac_resume(struct device *dev);
int stmmac_suspend(struct device *dev);
priv->hwts_tx_en = 0;
priv->hwts_rx_en = 0;
- return stmmac_ptp_register(priv);
+ stmmac_ptp_register(priv);
+
+ return 0;
}
static void stmmac_release_ptp(struct stmmac_priv *priv)
if (init_ptp) {
ret = stmmac_init_ptp(priv);
if (ret)
- netdev_warn(priv->dev, "PTP support cannot init.\n");
+ netdev_warn(priv->dev, "fail to init PTP.\n");
}
#ifdef CONFIG_DEBUG_FS
* Description: this function will register the ptp clock driver
* to kernel. It also does some house keeping work.
*/
-int stmmac_ptp_register(struct stmmac_priv *priv)
+void stmmac_ptp_register(struct stmmac_priv *priv)
{
spin_lock_init(&priv->ptp_lock);
priv->ptp_clock_ops = stmmac_ptp_clock_ops;
priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
priv->device);
if (IS_ERR(priv->ptp_clock)) {
+ netdev_err(priv->dev, "ptp_clock_register failed\n");
priv->ptp_clock = NULL;
- return PTR_ERR(priv->ptp_clock);
- }
-
- spin_lock_init(&priv->ptp_lock);
-
- netdev_dbg(priv->dev, "Added PTP HW clock successfully\n");
-
- return 0;
+ } else if (priv->ptp_clock)
+ netdev_info(priv->dev, "registered PTP clock\n");
}
/**
if (netif_msg_probe(lp))
phy_attached_info(phydev);
- phydev->supported &= PHY_GBIT_FEATURES;
+ phydev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause;
lp->link = 0;
lp->speed = 0;
lp->duplex = DUPLEX_UNKNOWN;
+ lp->flowcontrol.autoneg = AUTONEG_ENABLE;
return 0;
}
struct hlist_node hlist; /* vni hash table */
struct net *net; /* netns for packet i/o */
struct net_device *dev; /* netdev for geneve tunnel */
- struct geneve_sock *sock4; /* IPv4 socket used for geneve tunnel */
+ struct geneve_sock __rcu *sock4; /* IPv4 socket used for geneve tunnel */
#if IS_ENABLED(CONFIG_IPV6)
- struct geneve_sock *sock6; /* IPv6 socket used for geneve tunnel */
+ struct geneve_sock __rcu *sock6; /* IPv6 socket used for geneve tunnel */
#endif
u8 vni[3]; /* virtual network ID for tunnel */
u8 ttl; /* TTL override */
skb_gro_pull(skb, gh_len);
skb_gro_postpull_rcsum(skb, gh, gh_len);
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
flush = 0;
out_unlock:
static void geneve_sock_release(struct geneve_dev *geneve)
{
- __geneve_sock_release(geneve->sock4);
+ struct geneve_sock *gs4 = rtnl_dereference(geneve->sock4);
#if IS_ENABLED(CONFIG_IPV6)
- __geneve_sock_release(geneve->sock6);
+ struct geneve_sock *gs6 = rtnl_dereference(geneve->sock6);
+
+ rcu_assign_pointer(geneve->sock6, NULL);
+#endif
+
+ rcu_assign_pointer(geneve->sock4, NULL);
+ synchronize_net();
+
+ __geneve_sock_release(gs4);
+#if IS_ENABLED(CONFIG_IPV6)
+ __geneve_sock_release(gs6);
#endif
}
gs->flags = geneve->flags;
#if IS_ENABLED(CONFIG_IPV6)
if (ipv6)
- geneve->sock6 = gs;
+ rcu_assign_pointer(geneve->sock6, gs);
else
#endif
- geneve->sock4 = gs;
+ rcu_assign_pointer(geneve->sock4, gs);
hash = geneve_net_vni_hash(geneve->vni);
hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
bool metadata = geneve->collect_md;
int ret = 0;
- geneve->sock4 = NULL;
#if IS_ENABLED(CONFIG_IPV6)
- geneve->sock6 = NULL;
if (ipv6 || metadata)
ret = geneve_sock_add(geneve, true);
#endif
struct rtable *rt = NULL;
__u8 tos;
+ if (!rcu_dereference(geneve->sock4))
+ return ERR_PTR(-EIO);
+
memset(fl4, 0, sizeof(*fl4));
fl4->flowi4_mark = skb->mark;
fl4->flowi4_proto = IPPROTO_UDP;
{
bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
struct geneve_dev *geneve = netdev_priv(dev);
- struct geneve_sock *gs6 = geneve->sock6;
struct dst_entry *dst = NULL;
struct dst_cache *dst_cache;
+ struct geneve_sock *gs6;
__u8 prio;
+ gs6 = rcu_dereference(geneve->sock6);
+ if (!gs6)
+ return ERR_PTR(-EIO);
+
memset(fl6, 0, sizeof(*fl6));
fl6->flowi6_mark = skb->mark;
fl6->flowi6_proto = IPPROTO_UDP;
struct ip_tunnel_info *info)
{
struct geneve_dev *geneve = netdev_priv(dev);
- struct geneve_sock *gs4 = geneve->sock4;
+ struct geneve_sock *gs4;
struct rtable *rt = NULL;
const struct iphdr *iip; /* interior IP header */
int err = -EINVAL;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
u32 flags = geneve->flags;
+ gs4 = rcu_dereference(geneve->sock4);
+ if (!gs4)
+ goto tx_error;
+
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
netdev_dbg(dev, "no tunnel metadata\n");
struct ip_tunnel_info *info)
{
struct geneve_dev *geneve = netdev_priv(dev);
- struct geneve_sock *gs6 = geneve->sock6;
struct dst_entry *dst = NULL;
const struct iphdr *iip; /* interior IP header */
+ struct geneve_sock *gs6;
int err = -EINVAL;
struct flowi6 fl6;
__u8 prio, ttl;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
u32 flags = geneve->flags;
+ gs6 = rcu_dereference(geneve->sock6);
+ if (!gs6)
+ goto tx_error;
+
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
netdev_dbg(dev, "no tunnel metadata\n");
* Setup the sendside checksum offload only if this is not a
* GSO packet.
*/
- if (skb_is_gso(skb)) {
+ if ((net_trans_info & (INFO_TCP | INFO_UDP)) && skb_is_gso(skb)) {
struct ndis_tcp_lso_info *lso_info;
rndis_msg_size += NDIS_LSO_PPI_SIZE;
packet->total_data_buflen);
skb->protocol = eth_type_trans(skb, net);
- if (csum_info) {
- /* We only look at the IP checksum here.
- * Should we be dropping the packet if checksum
- * failed? How do we deal with other checksums - TCP/UDP?
- */
- if (csum_info->receive.ip_checksum_succeeded)
+
+ /* skb is already created with CHECKSUM_NONE */
+ skb_checksum_none_assert(skb);
+
+ /*
+ * In Linux, the IP checksum is always checked.
+ * Do L4 checksum offload if enabled and present.
+ */
+ if (csum_info && (net->features & NETIF_F_RXCSUM)) {
+ if (csum_info->receive.tcp_checksum_succeeded ||
+ csum_info->receive.udp_checksum_succeeded)
skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
}
if (vlan_tci & VLAN_TAG_PRESENT)
static void netvsc_get_drvinfo(struct net_device *net,
struct ethtool_drvinfo *info)
{
- struct net_device_context *net_device_ctx = netdev_priv(net);
- struct hv_device *dev = net_device_ctx->device_ctx;
-
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
- strlcpy(info->bus_info, vmbus_dev_name(dev), sizeof(info->bus_info));
}
static void netvsc_get_channels(struct net_device *net,
#define DEFAULT_ENCRYPT false
#define DEFAULT_ENCODING_SA 0
+static bool send_sci(const struct macsec_secy *secy)
+{
+ const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
+
+ return tx_sc->send_sci ||
+ (secy->n_rx_sc > 1 && !tx_sc->end_station && !tx_sc->scb);
+}
+
static sci_t make_sci(u8 *addr, __be16 port)
{
sci_t sci;
/* Fill SecTAG according to IEEE 802.1AE-2006 10.5.3 */
static void macsec_fill_sectag(struct macsec_eth_header *h,
- const struct macsec_secy *secy, u32 pn)
+ const struct macsec_secy *secy, u32 pn,
+ bool sci_present)
{
const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
- memset(&h->tci_an, 0, macsec_sectag_len(tx_sc->send_sci));
+ memset(&h->tci_an, 0, macsec_sectag_len(sci_present));
h->eth.h_proto = htons(ETH_P_MACSEC);
- if (tx_sc->send_sci ||
- (secy->n_rx_sc > 1 && !tx_sc->end_station && !tx_sc->scb)) {
+ if (sci_present) {
h->tci_an |= MACSEC_TCI_SC;
memcpy(&h->secure_channel_id, &secy->sci,
sizeof(h->secure_channel_id));
struct macsec_tx_sc *tx_sc;
struct macsec_tx_sa *tx_sa;
struct macsec_dev *macsec = macsec_priv(dev);
+ bool sci_present;
u32 pn;
secy = &macsec->secy;
unprotected_len = skb->len;
eth = eth_hdr(skb);
- hh = (struct macsec_eth_header *)skb_push(skb, macsec_extra_len(tx_sc->send_sci));
+ sci_present = send_sci(secy);
+ hh = (struct macsec_eth_header *)skb_push(skb, macsec_extra_len(sci_present));
memmove(hh, eth, 2 * ETH_ALEN);
pn = tx_sa_update_pn(tx_sa, secy);
kfree_skb(skb);
return ERR_PTR(-ENOLINK);
}
- macsec_fill_sectag(hh, secy, pn);
+ macsec_fill_sectag(hh, secy, pn, sci_present);
macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN);
skb_put(skb, secy->icv_len);
skb_to_sgvec(skb, sg, 0, skb->len);
if (tx_sc->encrypt) {
- int len = skb->len - macsec_hdr_len(tx_sc->send_sci) -
+ int len = skb->len - macsec_hdr_len(sci_present) -
secy->icv_len;
aead_request_set_crypt(req, sg, sg, len, iv);
- aead_request_set_ad(req, macsec_hdr_len(tx_sc->send_sci));
+ aead_request_set_ad(req, macsec_hdr_len(sci_present));
} else {
aead_request_set_crypt(req, sg, sg, 0, iv);
aead_request_set_ad(req, skb->len - secy->icv_len);
#define AT803X_MMD_ACCESS_CONTROL 0x0D
#define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E
#define AT803X_FUNC_DATA 0x4003
+#define AT803X_REG_CHIP_CONFIG 0x1f
+#define AT803X_BT_BX_REG_SEL 0x8000
#define AT803X_DEBUG_ADDR 0x1D
#define AT803X_DEBUG_DATA 0x1E
+#define AT803X_MODE_CFG_MASK 0x0F
+#define AT803X_MODE_CFG_SGMII 0x01
+
+#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
+#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+
#define AT803X_DEBUG_REG_0 0x00
#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
#define AT803X_DEBUG_REG_5 0x05
#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
-#define AT803X_REG_CHIP_CONFIG 0x1f
-#define AT803X_BT_BX_REG_SEL 0x8000
-
#define ATH8030_PHY_ID 0x004dd076
#define ATH8031_PHY_ID 0x004dd074
#define ATH8035_PHY_ID 0x004dd072
{
int value;
int wol_enabled;
- int ccr;
mutex_lock(&phydev->lock);
phy_write(phydev, MII_BMCR, value);
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
- goto done;
-
- /* also power-down SGMII interface */
- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
- phy_write(phydev, MII_BMCR, phy_read(phydev, MII_BMCR) | BMCR_PDOWN);
- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
-
-done:
mutex_unlock(&phydev->lock);
return 0;
static int at803x_resume(struct phy_device *phydev)
{
int value;
- int ccr;
mutex_lock(&phydev->lock);
value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
phy_write(phydev, MII_BMCR, value);
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
- goto done;
-
- /* also power-up SGMII interface */
- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
- value = phy_read(phydev, MII_BMCR) & ~(BMCR_PDOWN | BMCR_ISOLATE);
- phy_write(phydev, MII_BMCR, value);
- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
-
-done:
mutex_unlock(&phydev->lock);
return 0;
}
}
+static int at803x_aneg_done(struct phy_device *phydev)
+{
+ int ccr;
+
+ int aneg_done = genphy_aneg_done(phydev);
+ if (aneg_done != BMSR_ANEGCOMPLETE)
+ return aneg_done;
+
+ /*
+ * in SGMII mode, if copper side autoneg is successful,
+ * also check SGMII side autoneg result
+ */
+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+ if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
+ return aneg_done;
+
+ /* switch to SGMII/fiber page */
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
+
+ /* check if the SGMII link is OK. */
+ if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
+ pr_warn("803x_aneg_done: SGMII link is not ok\n");
+ aneg_done = 0;
+ }
+ /* switch back to copper page */
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
+
+ return aneg_done;
+}
+
static struct phy_driver at803x_driver[] = {
{
/* ATHEROS 8035 */
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
+ .aneg_done = at803x_aneg_done,
.ack_interrupt = &at803x_ack_interrupt,
.config_intr = &at803x_config_intr,
} };
#define TI_DP83848C_PHY_ID 0x20005ca0
#define NS_DP83848C_PHY_ID 0x20005c90
#define TLK10X_PHY_ID 0x2000a210
+#define TI_DP83822_PHY_ID 0x2000a240
/* Registers */
#define DP83848_MICR 0x11 /* MII Interrupt Control Register */
{ TI_DP83848C_PHY_ID, 0xfffffff0 },
{ NS_DP83848C_PHY_ID, 0xfffffff0 },
{ TLK10X_PHY_ID, 0xfffffff0 },
+ { TI_DP83822_PHY_ID, 0xfffffff0 },
{ }
};
MODULE_DEVICE_TABLE(mdio, dp83848_tbl);
DP83848_PHY_DRIVER(TI_DP83848C_PHY_ID, "TI DP83848C 10/100 Mbps PHY"),
DP83848_PHY_DRIVER(NS_DP83848C_PHY_ID, "NS DP83848C 10/100 Mbps PHY"),
DP83848_PHY_DRIVER(TLK10X_PHY_ID, "TI TLK10X 10/100 Mbps PHY"),
+ DP83848_PHY_DRIVER(TI_DP83822_PHY_ID, "TI DP83822 10/100 Mbps PHY"),
};
module_phy_driver(dp83848_driver);
mutex_lock(&dev->phy_mutex);
do {
ret = asix_set_sw_mii(dev, 0);
- if (ret == -ENODEV)
+ if (ret == -ENODEV || ret == -ETIMEDOUT)
break;
usleep_range(1000, 1100);
ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
0, 0, 1, &smsr, 0);
} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
- if (ret == -ENODEV) {
+ if (ret == -ENODEV || ret == -ETIMEDOUT) {
mutex_unlock(&dev->phy_mutex);
return ret;
}
mutex_lock(&dev->phy_mutex);
do {
ret = asix_set_sw_mii(dev, 1);
- if (ret == -ENODEV)
+ if (ret == -ENODEV || ret == -ETIMEDOUT)
break;
usleep_range(1000, 1100);
ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
0, 0, 1, &smsr, 1);
} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
- if (ret == -ENODEV) {
+ if (ret == -ENODEV || ret == -ETIMEDOUT) {
mutex_unlock(&dev->phy_mutex);
return ret;
}
status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr);
- if (status < 0) {
+ if (status) {
usb_set_intfdata(intf, NULL);
usb_driver_release_interface(driver_of(intf), intf);
return status;
&adapter->shared->devRead.rxFilterConf;
u8 *new_table = NULL;
dma_addr_t new_table_pa = 0;
+ bool new_table_pa_valid = false;
u32 new_mode = VMXNET3_RXM_UCAST;
if (netdev->flags & IFF_PROMISC) {
new_table,
sz,
PCI_DMA_TODEVICE);
+ if (!dma_mapping_error(&adapter->pdev->dev,
+ new_table_pa)) {
+ new_mode |= VMXNET3_RXM_MCAST;
+ new_table_pa_valid = true;
+ rxConf->mfTablePA = cpu_to_le64(
+ new_table_pa);
+ }
}
-
- if (!dma_mapping_error(&adapter->pdev->dev,
- new_table_pa)) {
- new_mode |= VMXNET3_RXM_MCAST;
- rxConf->mfTablePA = cpu_to_le64(new_table_pa);
- } else {
+ if (!new_table_pa_valid) {
netdev_info(netdev,
"failed to copy mcast list, setting ALL_MULTI\n");
new_mode |= VMXNET3_RXM_ALL_MULTI;
VMXNET3_CMD_UPDATE_MAC_FILTERS);
spin_unlock_irqrestore(&adapter->cmd_lock, flags);
- if (new_table_pa)
+ if (new_table_pa_valid)
dma_unmap_single(&adapter->pdev->dev, new_table_pa,
rxConf->mfTableLen, PCI_DMA_TODEVICE);
kfree(new_table);
if (skb->pkt_type == PACKET_LOOPBACK) {
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
+ IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
skb->pkt_type = PACKET_HOST;
goto out;
}
{
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
+ IPCB(skb)->flags |= IPSKB_L3SLAVE;
/* loopback traffic; do not push through packet taps again.
* Reset pkt_type for upper layers to process skb
}
}
- pp = eth_gro_receive(head, skb);
+ pp = call_gro_receive(eth_gro_receive, head, skb);
flush = 0;
out:
static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev)
{
struct vxlan_dev *vxlan;
+ struct vxlan_sock *sock4;
+ struct vxlan_sock *sock6 = NULL;
unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
+ sock4 = rtnl_dereference(dev->vn4_sock);
+
/* The vxlan_sock is only used by dev, leaving group has
* no effect on other vxlan devices.
*/
- if (family == AF_INET && dev->vn4_sock &&
- atomic_read(&dev->vn4_sock->refcnt) == 1)
+ if (family == AF_INET && sock4 && atomic_read(&sock4->refcnt) == 1)
return false;
#if IS_ENABLED(CONFIG_IPV6)
- if (family == AF_INET6 && dev->vn6_sock &&
- atomic_read(&dev->vn6_sock->refcnt) == 1)
+ sock6 = rtnl_dereference(dev->vn6_sock);
+ if (family == AF_INET6 && sock6 && atomic_read(&sock6->refcnt) == 1)
return false;
#endif
if (!netif_running(vxlan->dev) || vxlan == dev)
continue;
- if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock)
+ if (family == AF_INET &&
+ rtnl_dereference(vxlan->vn4_sock) != sock4)
continue;
#if IS_ENABLED(CONFIG_IPV6)
- if (family == AF_INET6 && vxlan->vn6_sock != dev->vn6_sock)
+ if (family == AF_INET6 &&
+ rtnl_dereference(vxlan->vn6_sock) != sock6)
continue;
#endif
static void vxlan_sock_release(struct vxlan_dev *vxlan)
{
- bool ipv4 = __vxlan_sock_release_prep(vxlan->vn4_sock);
+ struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
#if IS_ENABLED(CONFIG_IPV6)
- bool ipv6 = __vxlan_sock_release_prep(vxlan->vn6_sock);
+ struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
+
+ rcu_assign_pointer(vxlan->vn6_sock, NULL);
#endif
+ rcu_assign_pointer(vxlan->vn4_sock, NULL);
synchronize_net();
- if (ipv4) {
- udp_tunnel_sock_release(vxlan->vn4_sock->sock);
- kfree(vxlan->vn4_sock);
+ if (__vxlan_sock_release_prep(sock4)) {
+ udp_tunnel_sock_release(sock4->sock);
+ kfree(sock4);
}
#if IS_ENABLED(CONFIG_IPV6)
- if (ipv6) {
- udp_tunnel_sock_release(vxlan->vn6_sock->sock);
- kfree(vxlan->vn6_sock);
+ if (__vxlan_sock_release_prep(sock6)) {
+ udp_tunnel_sock_release(sock6->sock);
+ kfree(sock6);
}
#endif
}
int ret = -EINVAL;
if (ip->sa.sa_family == AF_INET) {
+ struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
struct ip_mreqn mreq = {
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
.imr_ifindex = ifindex,
};
- sk = vxlan->vn4_sock->sock->sk;
+ sk = sock4->sock->sk;
lock_sock(sk);
ret = ip_mc_join_group(sk, &mreq);
release_sock(sk);
#if IS_ENABLED(CONFIG_IPV6)
} else {
- sk = vxlan->vn6_sock->sock->sk;
+ struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
+
+ sk = sock6->sock->sk;
lock_sock(sk);
ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex,
&ip->sin6.sin6_addr);
int ret = -EINVAL;
if (ip->sa.sa_family == AF_INET) {
+ struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
struct ip_mreqn mreq = {
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
.imr_ifindex = ifindex,
};
- sk = vxlan->vn4_sock->sock->sk;
+ sk = sock4->sock->sk;
lock_sock(sk);
ret = ip_mc_leave_group(sk, &mreq);
release_sock(sk);
#if IS_ENABLED(CONFIG_IPV6)
} else {
- sk = vxlan->vn6_sock->sock->sk;
+ struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
+
+ sk = sock6->sock->sk;
lock_sock(sk);
ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex,
&ip->sin6.sin6_addr);
struct dst_cache *dst_cache,
const struct ip_tunnel_info *info)
{
+ struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
struct dst_entry *ndst;
struct flowi6 fl6;
int err;
+ if (!sock6)
+ return ERR_PTR(-EIO);
+
if (tos && !info)
use_cache = false;
if (use_cache) {
fl6.flowi6_proto = IPPROTO_UDP;
err = ipv6_stub->ipv6_dst_lookup(vxlan->net,
- vxlan->vn6_sock->sock->sk,
+ sock6->sock->sk,
&ndst, &fl6);
if (err < 0)
return ERR_PTR(err);
}
if (dst->sa.sa_family == AF_INET) {
- if (!vxlan->vn4_sock)
+ struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
+
+ if (!sock4)
goto drop;
- sk = vxlan->vn4_sock->sock->sk;
+ sk = sock4->sock->sk;
rt = vxlan_get_route(vxlan, skb,
rdst ? rdst->remote_ifindex : 0, tos,
src_port, dst_port, xnet, !udp_sum);
#if IS_ENABLED(CONFIG_IPV6)
} else {
+ struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
struct dst_entry *ndst;
u32 rt6i_flags;
- if (!vxlan->vn6_sock)
+ if (!sock6)
goto drop;
- sk = vxlan->vn6_sock->sock->sk;
+ sk = sock6->sock->sk;
ndst = vxlan6_get_route(vxlan, skb,
rdst ? rdst->remote_ifindex : 0, tos,
dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
if (ip_tunnel_info_af(info) == AF_INET) {
+ struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
struct rtable *rt;
- if (!vxlan->vn4_sock)
+ if (!sock4)
return -EINVAL;
rt = vxlan_get_route(vxlan, skb, 0, info->key.tos,
info->key.u.ipv4.dst,
#if IS_ENABLED(CONFIG_IPV6)
struct dst_entry *ndst;
- if (!vxlan->vn6_sock)
- return -EINVAL;
ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos,
info->key.label, &info->key.u.ipv6.dst,
&info->key.u.ipv6.src, NULL, info);
return PTR_ERR(vs);
#if IS_ENABLED(CONFIG_IPV6)
if (ipv6)
- vxlan->vn6_sock = vs;
+ rcu_assign_pointer(vxlan->vn6_sock, vs);
else
#endif
- vxlan->vn4_sock = vs;
+ rcu_assign_pointer(vxlan->vn4_sock, vs);
vxlan_vs_add_dev(vs, vxlan);
return 0;
}
bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
int ret = 0;
- vxlan->vn4_sock = NULL;
+ RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
#if IS_ENABLED(CONFIG_IPV6)
- vxlan->vn6_sock = NULL;
+ RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
if (ipv6 || metadata)
ret = __vxlan_sock_add(vxlan, true);
#endif
config SLIC_DS26522
tristate "Slic Maxim ds26522 card support"
depends on SPI
- depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE
+ depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
help
This module initializes and configures the slic maxim card
in T1 or E1 mode.
return ret;
}
+static const struct spi_device_id slic_ds26522_id[] = {
+ { .name = "ds26522" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(spi, slic_ds26522_id);
+
static const struct of_device_id slic_ds26522_match[] = {
{
.compatible = "maxim,ds26522",
},
{},
};
+MODULE_DEVICE_TABLE(of, slic_ds26522_match);
static struct spi_driver slic_ds26522_driver = {
.driver = {
},
.probe = slic_ds26522_probe,
.remove = slic_ds26522_remove,
+ .id_table = slic_ds26522_id,
};
static int __init slic_ds26522_init(void)
u32 pktlog_filter;
u32 reg_addr;
u32 nf_cal_period;
+ void *cal_data;
struct ath10k_fw_crash_data *fw_crash_data;
};
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
+#define ATH10K_DEBUG_CAL_DATA_LEN 12064
+
#define ATH10K_FW_CRASH_DUMP_VERSION 1
/**
.llseek = default_llseek,
};
-static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
+static int ath10k_debug_cal_data_fetch(struct ath10k *ar)
{
- struct ath10k *ar = inode->i_private;
- void *buf;
u32 hi_addr;
__le32 addr;
int ret;
- mutex_lock(&ar->conf_mutex);
-
- if (ar->state != ATH10K_STATE_ON &&
- ar->state != ATH10K_STATE_UTF) {
- ret = -ENETDOWN;
- goto err;
- }
+ lockdep_assert_held(&ar->conf_mutex);
- buf = vmalloc(ar->hw_params.cal_data_len);
- if (!buf) {
- ret = -ENOMEM;
- goto err;
- }
+ if (WARN_ON(ar->hw_params.cal_data_len > ATH10K_DEBUG_CAL_DATA_LEN))
+ return -EINVAL;
hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));
ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));
if (ret) {
- ath10k_warn(ar, "failed to read hi_board_data address: %d\n", ret);
- goto err_vfree;
+ ath10k_warn(ar, "failed to read hi_board_data address: %d\n",
+ ret);
+ return ret;
}
- ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), buf,
+ ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), ar->debug.cal_data,
ar->hw_params.cal_data_len);
if (ret) {
ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
- goto err_vfree;
+ return ret;
}
- file->private_data = buf;
+ return 0;
+}
- mutex_unlock(&ar->conf_mutex);
+static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
- return 0;
+ mutex_lock(&ar->conf_mutex);
-err_vfree:
- vfree(buf);
+ if (ar->state == ATH10K_STATE_ON ||
+ ar->state == ATH10K_STATE_UTF) {
+ ath10k_debug_cal_data_fetch(ar);
+ }
-err:
+ file->private_data = ar;
mutex_unlock(&ar->conf_mutex);
- return ret;
+ return 0;
}
static ssize_t ath10k_debug_cal_data_read(struct file *file,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
- void *buf = file->private_data;
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, ar->hw_params.cal_data_len);
-}
+ mutex_lock(&ar->conf_mutex);
-static int ath10k_debug_cal_data_release(struct inode *inode,
- struct file *file)
-{
- vfree(file->private_data);
+ count = simple_read_from_buffer(user_buf, count, ppos,
+ ar->debug.cal_data,
+ ar->hw_params.cal_data_len);
- return 0;
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
}
static ssize_t ath10k_write_ani_enable(struct file *file,
static const struct file_operations fops_cal_data = {
.open = ath10k_debug_cal_data_open,
.read = ath10k_debug_cal_data_read,
- .release = ath10k_debug_cal_data_release,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
{
lockdep_assert_held(&ar->conf_mutex);
+ ath10k_debug_cal_data_fetch(ar);
+
/* Must not use _sync to avoid deadlock, we do that in
* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
* warning from del_timer(). */
if (!ar->debug.fw_crash_data)
return -ENOMEM;
+ ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
+ if (!ar->debug.cal_data)
+ return -ENOMEM;
+
INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
vfree(ar->debug.fw_crash_data);
ar->debug.fw_crash_data = NULL;
+ vfree(ar->debug.cal_data);
+ ar->debug.cal_data = NULL;
+
ath10k_debug_fw_stats_reset(ar);
kfree(ar->debug.tpc_stats);
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
{},
};
enum ar9003_cal_types {
IQ_MISMATCH_CAL = BIT(0),
- TEMP_COMP_CAL = BIT(1),
};
static void ar9003_hw_setup_calibration(struct ath_hw *ah,
/* Kick-off cal */
REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
break;
- case TEMP_COMP_CAL:
- ath_dbg(common, CALIBRATE,
- "starting Temperature Compensation Calibration\n");
- REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_LOCAL);
- REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_START);
- break;
default:
ath_err(common, "Invalid calibration type\n");
break;
/*
* Accumulate cal measures for active chains
*/
- if (cur_caldata->calCollect)
- cur_caldata->calCollect(ah);
+ cur_caldata->calCollect(ah);
ah->cal_samples++;
if (ah->cal_samples >= cur_caldata->calNumSamples) {
/*
* Process accumulated data
*/
- if (cur_caldata->calPostProc)
- cur_caldata->calPostProc(ah, numChains);
+ cur_caldata->calPostProc(ah, numChains);
/* Calibration has finished. */
caldata->CalValid |= cur_caldata->calType;
ar9003_hw_iqcalibrate
};
-static const struct ath9k_percal_data temp_cal_single_sample = {
- TEMP_COMP_CAL,
- MIN_CAL_SAMPLES,
- PER_MAX_LOG_COUNT,
-};
-
static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
{
ah->iq_caldata.calData = &iq_cal_single_sample;
- ah->temp_caldata.calData = &temp_cal_single_sample;
if (AR_SREV_9300_20_OR_LATER(ah)) {
ah->enabled_cals |= TX_IQ_CAL;
ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
}
- ah->supp_cals = IQ_MISMATCH_CAL | TEMP_COMP_CAL;
+ ah->supp_cals = IQ_MISMATCH_CAL;
}
#define OFF_UPPER_LT 24
INIT_CAL(&ah->iq_caldata);
INSERT_CAL(ah, &ah->iq_caldata);
- INIT_CAL(&ah->temp_caldata);
- INSERT_CAL(ah, &ah->temp_caldata);
-
/* Initialize current pointer to first element in list */
ah->cal_list_curr = ah->cal_list;
/* Calibration */
u32 supp_cals;
struct ath9k_cal_list iq_caldata;
- struct ath9k_cal_list temp_caldata;
struct ath9k_cal_list adcgain_caldata;
struct ath9k_cal_list adcdc_caldata;
struct ath9k_cal_list *cal_list;
skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
- priv->wdev.iftype, 0, false);
+ priv->wdev.iftype, 0, NULL, NULL);
while (!skb_queue_empty(&list)) {
struct rx_packet_hdr *rx_hdr;
u32 pattern1match:1;
u32 pattern0match:1;
#endif
- __le32 tsfl;
+ u32 tsfl;
#if 0
u32 bassn:12;
u32 bavld:1;
u32 ldcp:1;
u32 splcp:1;
#endif
- __le32 tsfl;
+ u32 tsfl;
};
struct rtl8xxxu_txdesc32 {
int count, ret = 0;
/* Turn off RF */
- rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+ val8 = rtl8xxxu_read8(priv, REG_RF_CTRL);
+ val8 &= ~RF_ENABLE;
+ rtl8xxxu_write8(priv, REG_RF_CTRL, val8);
/* Switch DPDT_SEL_P output from register 0x65[2] */
val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
u32 val32;
u8 val8;
+ val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+ val32 |= (BIT(22) | BIT(23));
+ rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+
val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG);
val8 |= BIT(5);
rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8);
u32 val32;
u8 val8;
+ val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+ val32 |= (BIT(22) | BIT(23));
+ rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+
/*
* No indication anywhere as to what 0x0790 does. The 2 antenna
* vendor code preserves bits 6-7 here.
pkt_offset = roundup(pkt_len + drvinfo_sz + desc_shift +
sizeof(struct rtl8xxxu_rxdesc16), 128);
- if (pkt_cnt > 1)
+ /*
+ * Only clone the skb if there's enough data at the end to
+ * at least cover the rx descriptor
+ */
+ if (pkt_cnt > 1 &&
+ urb_len > (pkt_offset + sizeof(struct rtl8xxxu_rxdesc16)))
next_skb = skb_clone(skb, GFP_ATOMIC);
rx_status = IEEE80211_SKB_RXCB(skb);
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
rx_desc->rxmcs);
- rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
if (!rx_desc->swdec)
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
rx_desc->rxmcs);
- rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
if (!rx_desc->swdec)
if (!err)
goto found_alt;
}
- pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name);
+ pr_err("Selected firmware is not available\n");
rtlpriv->max_fw_size = 0;
return;
}
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
u8 tid;
+ char *fw_name;
rtl8188ee_bt_reg_init(hw);
rtlpriv->dm.dm_initialgain_enable = 1;
return 1;
}
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8188efw.bin";
+ fw_name = "rtlwifi/rtl8188efw.bin";
rtlpriv->max_fw_size = 0x8000;
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl88e_pci",
- .fw_name = "rtlwifi/rtl8188efw.bin",
.ops = &rtl8188ee_hal_ops,
.mod_params = &rtl88ee_mod_params,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ char *fw_name = "rtlwifi/rtl8192cfwU.bin";
rtl8192ce_bt_reg_init(hw);
}
/* request fw */
- if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
- !IS_92C_SERIAL(rtlhal->version))
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
- else if (IS_81XXC_VENDOR_UMC_B_CUT(rtlhal->version))
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+ if (IS_81XXC_VENDOR_UMC_B_CUT(rtlhal->version))
+ fw_name = "rtlwifi/rtl8192cfwU_B.bin";
rtlpriv->max_fw_size = 0x4000;
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl92c_pci",
- .fw_name = "rtlwifi/rtl8192cfw.bin",
.ops = &rtl8192ce_hal_ops,
.mod_params = &rtl92ce_mod_params,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
int err;
+ char *fw_name;
rtlpriv->dm.dm_initialgain_enable = true;
rtlpriv->dm.dm_flag = 0;
}
if (IS_VENDOR_UMC_A_CUT(rtlpriv->rtlhal.version) &&
!IS_92C_SERIAL(rtlpriv->rtlhal.version)) {
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_A.bin";
+ fw_name = "rtlwifi/rtl8192cufw_A.bin";
} else if (IS_81XXC_VENDOR_UMC_B_CUT(rtlpriv->rtlhal.version)) {
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_B.bin";
+ fw_name = "rtlwifi/rtl8192cufw_B.bin";
} else {
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_TMSC.bin";
+ fw_name = "rtlwifi/rtl8192cufw_TMSC.bin";
}
/* provide name of alternative file */
rtlpriv->cfg->alt_fw_name = "rtlwifi/rtl8192cufw.bin";
- pr_info("Loading firmware %s\n", rtlpriv->cfg->fw_name);
+ pr_info("Loading firmware %s\n", fw_name);
rtlpriv->max_fw_size = 0x4000;
err = request_firmware_nowait(THIS_MODULE, 1,
- rtlpriv->cfg->fw_name, rtlpriv->io.dev,
+ fw_name, rtlpriv->io.dev,
GFP_KERNEL, hw, rtl_fw_cb);
return err;
}
static struct rtl_hal_cfg rtl92cu_hal_cfg = {
.name = "rtl92c_usb",
- .fw_name = "rtlwifi/rtl8192cufw.bin",
.ops = &rtl8192cu_hal_ops,
.mod_params = &rtl92cu_mod_params,
.usb_interface_cfg = &rtl92cu_interface_cfg,
u8 tid;
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ char *fw_name = "rtlwifi/rtl8192defw.bin";
rtlpriv->dm.dm_initialgain_enable = true;
rtlpriv->dm.dm_flag = 0;
rtlpriv->max_fw_size = 0x8000;
pr_info("Driver for Realtek RTL8192DE WLAN interface\n");
- pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
+ pr_info("Loading firmware file %s\n", fw_name);
/* request fw */
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl8192de",
- .fw_name = "rtlwifi/rtl8192defw.bin",
.ops = &rtl8192de_hal_ops,
.mod_params = &rtl92de_mod_params,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
int err = 0;
+ char *fw_name;
rtl92ee_bt_reg_init(hw);
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
}
/* request fw */
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8192eefw.bin";
+ fw_name = "rtlwifi/rtl8192eefw.bin";
rtlpriv->max_fw_size = 0x8000;
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl92ee_pci",
- .fw_name = "rtlwifi/rtl8192eefw.bin",
.ops = &rtl8192ee_hal_ops,
.mod_params = &rtl92ee_mod_params,
struct ieee80211_hw *hw = context;
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rt_firmware *pfirmware = NULL;
+ char *fw_name = "rtlwifi/rtl8192sefw.bin";
RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
"Firmware callback routine entered!\n");
complete(&rtlpriv->firmware_loading_complete);
if (!firmware) {
- pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name);
+ pr_err("Firmware %s not available\n", fw_name);
rtlpriv->max_fw_size = 0;
return;
}
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
int err = 0;
u16 earlyrxthreshold = 7;
+ char *fw_name = "rtlwifi/rtl8192sefw.bin";
rtlpriv->dm.dm_initialgain_enable = true;
rtlpriv->dm.dm_flag = 0;
rtlpriv->max_fw_size = RTL8190_MAX_FIRMWARE_CODE_SIZE*2 +
sizeof(struct fw_hdr);
pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
- "Loading firmware %s\n", rtlpriv->cfg->fw_name);
+ "Loading firmware %s\n", fw_name);
/* request fw */
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl92se_fw_cb);
if (err) {
.bar_id = 1,
.write_readback = false,
.name = "rtl92s_pci",
- .fw_name = "rtlwifi/rtl8192sefw.bin",
.ops = &rtl8192se_hal_ops,
.mod_params = &rtl92se_mod_params,
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
int err = 0;
+ char *fw_name = "rtlwifi/rtl8723fw.bin";
rtl8723e_bt_reg_init(hw);
return 1;
}
- if (IS_VENDOR_8723_A_CUT(rtlhal->version))
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8723fw.bin";
- else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8723fw_B.bin";
+ if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+ fw_name = "rtlwifi/rtl8723fw_B.bin";
rtlpriv->max_fw_size = 0x6000;
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl8723e_pci",
- .fw_name = "rtlwifi/rtl8723efw.bin",
.ops = &rtl8723e_hal_ops,
.mod_params = &rtl8723e_mod_params,
.maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ char *fw_name = "rtlwifi/rtl8723befw.bin";
rtl8723be_bt_reg_init(hw);
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
}
rtlpriv->max_fw_size = 0x8000;
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl8723be_pci",
- .fw_name = "rtlwifi/rtl8723befw.bin",
.ops = &rtl8723be_hal_ops,
.mod_params = &rtl8723be_mod_params,
.maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ char *fw_name, *wowlan_fw_name;
rtl8821ae_bt_reg_init(hw);
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
}
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8812aefw.bin";
- rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin";
+ fw_name = "rtlwifi/rtl8812aefw.bin";
+ wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin";
} else {
- rtlpriv->cfg->fw_name = "rtlwifi/rtl8821aefw.bin";
- rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin";
+ fw_name = "rtlwifi/rtl8821aefw.bin";
+ wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin";
}
rtlpriv->max_fw_size = 0x8000;
/*load normal firmware*/
- pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
- err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ pr_info("Using firmware %s\n", fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_fw_cb);
if (err) {
return 1;
}
/*load wowlan firmware*/
- pr_info("Using firmware %s\n", rtlpriv->cfg->wowlan_fw_name);
+ pr_info("Using firmware %s\n", wowlan_fw_name);
err = request_firmware_nowait(THIS_MODULE, 1,
- rtlpriv->cfg->wowlan_fw_name,
+ wowlan_fw_name,
rtlpriv->io.dev, GFP_KERNEL, hw,
rtl_wowlan_fw_cb);
if (err) {
.bar_id = 2,
.write_readback = true,
.name = "rtl8821ae_pci",
- .fw_name = "rtlwifi/rtl8821aefw.bin",
.ops = &rtl8821ae_hal_ops,
.mod_params = &rtl8821ae_mod_params,
.maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
u8 bar_id;
bool write_readback;
char *name;
- char *fw_name;
char *alt_fw_name;
- char *wowlan_fw_name;
struct rtl_hal_ops *ops;
struct rtl_mod_params *mod_params;
struct rtl_hal_usbint_cfg *usb_interface_cfg;
pm_runtime_get_noresume(&func->dev);
platform_device_unregister(glue->core);
- kfree(glue);
}
#ifdef CONFIG_PM
}
}
- pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
-
if (pp->ops->host_init)
pp->ops->host_init(pp);
{
u32 val;
+ /* get iATU unroll support */
+ pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
+ dev_dbg(pp->dev, "iATU unroll: %s\n",
+ pp->iatu_unroll_enabled ? "enabled" : "disabled");
+
/* set the number of lanes */
val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
val &= ~PORT_LINK_MODE_MASK;
if (IS_ERR(pcie->phy))
return PTR_ERR(pcie->phy);
+ pp->dev = dev;
ret = pcie->ops->get_resources(pcie);
if (ret)
return ret;
- pp->dev = dev;
pp->root_bus_nr = -1;
pp->ops = &qcom_pcie_dw_ops;
ramp_delay = rdev->desc->ramp_delay;
if (ramp_delay == 0) {
- rdev_warn(rdev, "ramp_delay not set\n");
+ rdev_dbg(rdev, "ramp_delay not set\n");
return 0;
}
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
- int lun = cmd->device->lun;
- uint8_t scsicmd = cmd->cmnd[0];
cmd->scsi_done = done;
cmd->host_scribble = NULL;
cmd->result = 0;
- if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){
- if(acb->devstate[target][lun] == ARECA_RAID_GONE) {
- cmd->result = (DID_NO_CONNECT << 16);
- }
- cmd->scsi_done(cmd);
- return 0;
- }
if (target == 16) {
/* virtual device for iop message transfer */
arcmsr_handle_virtual_command(acb, cmd);
goto out_done;
}
- switch (scmd->cmnd[0]) {
- case SYNCHRONIZE_CACHE:
- /*
- * FW takes care of flush cache on its own
- * No need to send it down
- */
+ /*
+ * FW takes care of flush cache on its own for Virtual Disk.
+ * No need to send it down for VD. For JBOD send SYNCHRONIZE_CACHE to FW.
+ */
+ if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && MEGASAS_IS_LOGICAL(scmd)) {
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
return instance->instancet->build_and_issue_cmd(instance, scmd);
bus_unregister(&pseudo_lld_bus);
root_device_unregister(pseudo_primary);
+ vfree(map_storep);
vfree(dif_storep);
vfree(fake_storep);
kfree(sdebug_q_arr);
#define SPI_SR 0x2c
#define SPI_SR_EOQF 0x10000000
#define SPI_SR_TCFQF 0x80000000
+#define SPI_SR_CLEAR 0xdaad0000
#define SPI_RSER 0x30
#define SPI_RSER_EOQFE 0x10000000
.max_register = 0x88,
};
+static void dspi_init(struct fsl_dspi *dspi)
+{
+ regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR);
+}
+
static int dspi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
return PTR_ERR(dspi->regmap);
}
+ dspi_init(dspi);
dspi->irq = platform_get_irq(pdev, 0);
if (dspi->irq < 0) {
dev_err(&pdev->dev, "can't get platform irq\n");
mspi->len -= rx_nr_bytes;
- if (mspi->rx)
+ if (rx_nr_bytes && mspi->rx)
mspi->get_rx(rx_data, mspi);
}
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(master, nc);
- if (IS_ERR(spi))
+ if (IS_ERR(spi)) {
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
nc->full_name);
+ of_node_clear_flag(nc, OF_POPULATED);
+ }
}
}
#else
if (IS_ERR(spi)) {
pr_err("%s: failed to create for '%s'\n",
__func__, rd->dn->full_name);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
return notifier_from_errno(PTR_ERR(spi));
}
break;
flags);
memset(&bdev->rds_info, 0, sizeof(bdev->rds_info));
}
+ if (err)
+ return err;
return bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM,
bdev->cache_fm_rds_system);
} else if (cmd == VFIO_DEVICE_SET_IRQS) {
struct vfio_irq_set hdr;
+ size_t size;
u8 *data = NULL;
- int ret = 0;
+ int max, ret = 0;
minsz = offsetofend(struct vfio_irq_set, count);
return -EFAULT;
if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.count >= (U32_MAX - hdr.start) ||
hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
VFIO_IRQ_SET_ACTION_TYPE_MASK))
return -EINVAL;
- if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
- size_t size;
- int max = vfio_pci_get_irq_count(vdev, hdr.index);
+ max = vfio_pci_get_irq_count(vdev, hdr.index);
+ if (hdr.start >= max || hdr.start + hdr.count > max)
+ return -EINVAL;
- if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
- size = sizeof(uint8_t);
- else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
- size = sizeof(int32_t);
- else
- return -EINVAL;
+ switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
+ case VFIO_IRQ_SET_DATA_NONE:
+ size = 0;
+ break;
+ case VFIO_IRQ_SET_DATA_BOOL:
+ size = sizeof(uint8_t);
+ break;
+ case VFIO_IRQ_SET_DATA_EVENTFD:
+ size = sizeof(int32_t);
+ break;
+ default:
+ return -EINVAL;
+ }
- if (hdr.argsz - minsz < hdr.count * size ||
- hdr.start >= max || hdr.start + hdr.count > max)
+ if (size) {
+ if (hdr.argsz - minsz < hdr.count * size)
return -EINVAL;
data = memdup_user((void __user *)(arg + minsz),
if (!is_irq_none(vdev))
return -EINVAL;
- vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
if (!vdev->ctx)
return -ENOMEM;
+++ /dev/null
-/* Configuration space parsing helpers for virtio.
- *
- * The configuration is [type][len][... len bytes ...] fields.
- *
- * Copyright 2007 Rusty Russell, IBM Corporation.
- * GPL v2 or later.
- */
-#include <linux/err.h>
-#include <linux/virtio.h>
-#include <linux/virtio_config.h>
-#include <linux/bug.h>
-
virtio_device_ready(vdev);
+ if (towards_target(vb))
+ virtballoon_changed(vdev);
return 0;
out_del_vqs:
return -ENODEV;
}
- rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
- if (rc)
- rc = dma_set_mask_and_coherent(&pci_dev->dev,
- DMA_BIT_MASK(32));
+ rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
+ } else {
+ /*
+ * The virtio ring base address is expressed as a 32-bit PFN,
+ * with a page size of 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT.
+ */
+ dma_set_coherent_mask(&pci_dev->dev,
+ DMA_BIT_MASK(32 + VIRTIO_PCI_QUEUE_ADDR_SHIFT));
+ }
+
if (rc)
dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n");
* making all of the arch DMA ops work on the vring device itself
* is a mess. For now, we use the parent device for DMA ops.
*/
-static struct device *vring_dma_dev(const struct vring_virtqueue *vq)
+static inline struct device *vring_dma_dev(const struct vring_virtqueue *vq)
{
return vq->vq.vdev->dev.parent;
}
if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
}
* entry. Always do both to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
END_USE(vq);
* more to do. */
/* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
- * entry. Always do both to keep code simple. */
+ * entry. Always update the event index to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
/* TODO: tune this threshold */
bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
/* No callback? Tell other side not to bother us. */
if (!callback) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
}
/* Put everything in free lists. */
*/
bool afs_cm_incoming_call(struct afs_call *call)
{
- u32 operation_id = ntohl(call->operation_ID);
+ _enter("{CB.OP %u}", call->operation_ID);
- _enter("{CB.OP %u}", operation_id);
-
- switch (operation_id) {
+ switch (call->operation_ID) {
case CBCallBack:
call->type = &afs_SRXCBCallBack;
return true;
buffer = kmap(page);
ret = afs_extract_data(call, buffer,
call->count, true);
- kunmap(buffer);
+ kunmap(page);
if (ret < 0)
return ret;
}
page = call->reply3;
buffer = kmap(page);
memset(buffer + call->count, 0, PAGE_SIZE - call->count);
- kunmap(buffer);
+ kunmap(page);
}
_leave(" = 0 [done]");
bool need_attention; /* T if RxRPC poked us */
u16 service_id; /* RxRPC service ID to call */
__be16 port; /* target UDP port */
- __be32 operation_ID; /* operation ID for an incoming call */
+ u32 operation_ID; /* operation ID for an incoming call */
u32 count; /* count for use in unmarshalling */
__be32 tmp; /* place to extract temporary data */
afs_dataversion_t store_version; /* updated version expected from store */
ASSERTCMP(call->offset, <, 4);
/* the operation ID forms the first four bytes of the request data */
- ret = afs_extract_data(call, &call->operation_ID, 4, true);
+ ret = afs_extract_data(call, &call->tmp, 4, true);
if (ret < 0)
return ret;
+ call->operation_ID = ntohl(call->tmp);
call->state = AFS_CALL_AWAIT_REQUEST;
call->offset = 0;
btrfs_free_delayed_extent_op(extent_op);
if (ret) {
+ spin_lock(&delayed_refs->lock);
locked_ref->processing = 0;
+ delayed_refs->num_heads_ready++;
+ spin_unlock(&delayed_refs->lock);
btrfs_delayed_ref_unlock(locked_ref);
btrfs_put_delayed_ref(ref);
btrfs_debug(fs_info, "run_one_delayed_ref returned %d",
*p |= mask_to_set;
len -= bits_to_set;
bits_to_set = BITS_PER_BYTE;
- mask_to_set = ~(u8)0;
+ mask_to_set = ~0;
p++;
}
if (len) {
*p &= ~mask_to_clear;
len -= bits_to_clear;
bits_to_clear = BITS_PER_BYTE;
- mask_to_clear = ~(u8)0;
+ mask_to_clear = ~0;
p++;
}
if (len) {
kaddr[offset] |= mask_to_set;
len -= bits_to_set;
bits_to_set = BITS_PER_BYTE;
- mask_to_set = ~(u8)0;
+ mask_to_set = ~0;
if (++offset >= PAGE_SIZE && len > 0) {
offset = 0;
page = eb->pages[++i];
kaddr[offset] &= ~mask_to_clear;
len -= bits_to_clear;
bits_to_clear = BITS_PER_BYTE;
- mask_to_clear = ~(u8)0;
+ mask_to_clear = ~0;
if (++offset >= PAGE_SIZE && len > 0) {
offset = 0;
page = eb->pages[++i];
BUG_ON(ret);
if (btrfs_should_throttle_delayed_refs(trans, root))
btrfs_async_run_delayed_refs(root,
- trans->transid,
- trans->delayed_ref_updates * 2, 0);
+ trans->delayed_ref_updates * 2,
+ trans->transid, 0);
if (be_nice) {
if (truncate_space_check(trans, root,
extent_num_bytes)) {
* So even we call qgroup_free_data(), it won't decrease reserved
* space.
* 2) Not written to disk
- * This means the reserved space should be freed here.
+ * This means the reserved space should be freed here. However,
+ * if a truncate invalidates the page (by clearing PageDirty)
+ * and the page is accounted for while allocating extent
+ * in btrfs_check_data_free_space() we let delayed_ref to
+ * free the entire extent.
*/
- btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE);
+ if (PageDirty(page))
+ btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE);
if (!inode_evicting) {
clear_extent_bit(tree, page_start, page_end,
EXTENT_LOCKED | EXTENT_DIRTY |
}
btrfs_release_path(path);
key.offset = next_key_min_offset;
+
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ goto out;
+ }
}
ret = 0;
bytenr = btrfs_node_blockptr(upper->eb, slot);
if (lowest) {
- BUG_ON(bytenr != node->bytenr);
+ if (bytenr != node->bytenr) {
+ btrfs_err(root->fs_info,
+ "lowest leaf/node mismatch: bytenr %llu node->bytenr %llu slot %d upper %llu",
+ bytenr, node->bytenr, slot,
+ upper->eb->start);
+ err = -EIO;
+ goto next;
+ }
} else {
if (node->eb->start == bytenr)
goto next;
struct list_head client_lru;
struct list_head close_lru;
struct list_head del_recall_lru;
+
+ /* protected by blocked_locks_lock */
struct list_head blocked_locks_lru;
struct delayed_work laundromat_work;
/* client_lock protects the client lru list and session hash table */
spinlock_t client_lock;
+ /* protects blocked_locks_lru */
+ spinlock_t blocked_locks_lock;
+
struct file *rec_file;
bool in_grace;
const struct nfsd4_client_tracking_ops *client_tracking_ops;
{
struct nfsd4_blocked_lock *cur, *found = NULL;
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
if (fh_match(fh, &cur->nbl_fh)) {
list_del_init(&cur->nbl_list);
break;
}
}
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
if (found)
posix_unblock_lock(&found->nbl_lock);
return found;
static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp)
{
- struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
-
- lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
+ lockdep_assert_held(&stp->st_stid.sc_client->cl_lock);
list_del_init(&stp->st_locks);
nfs4_unhash_stid(&stp->st_stid);
static void release_lock_stateid(struct nfs4_ol_stateid *stp)
{
- struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
+ struct nfs4_client *clp = stp->st_stid.sc_client;
bool unhashed;
- spin_lock(&oo->oo_owner.so_client->cl_lock);
+ spin_lock(&clp->cl_lock);
unhashed = unhash_lock_stateid(stp);
- spin_unlock(&oo->oo_owner.so_client->cl_lock);
+ spin_unlock(&clp->cl_lock);
if (unhashed)
nfs4_put_stid(&stp->st_stid);
}
* indefinitely once the lock does become free.
*/
BUG_ON(!list_empty(&reaplist));
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
while (!list_empty(&nn->blocked_locks_lru)) {
nbl = list_first_entry(&nn->blocked_locks_lru,
struct nfsd4_blocked_lock, nbl_lru);
list_move(&nbl->nbl_lru, &reaplist);
list_del_init(&nbl->nbl_list);
}
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
while (!list_empty(&reaplist)) {
nbl = list_first_entry(&nn->blocked_locks_lru,
bool queue = false;
/* An empty list means that something else is going to be using it */
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
if (!list_empty(&nbl->nbl_list)) {
list_del_init(&nbl->nbl_list);
list_del_init(&nbl->nbl_lru);
queue = true;
}
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
if (queue)
nfsd4_run_cb(&nbl->nbl_cb);
if (fl_flags & FL_SLEEP) {
nbl->nbl_time = jiffies;
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
}
err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
if (nbl) {
/* dequeue it if we queued it before */
if (fl_flags & FL_SLEEP) {
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
list_del_init(&nbl->nbl_list);
list_del_init(&nbl->nbl_lru);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
}
free_blocked_lock(nbl);
}
INIT_LIST_HEAD(&nn->client_lru);
INIT_LIST_HEAD(&nn->close_lru);
INIT_LIST_HEAD(&nn->del_recall_lru);
- INIT_LIST_HEAD(&nn->blocked_locks_lru);
spin_lock_init(&nn->client_lock);
+ spin_lock_init(&nn->blocked_locks_lock);
+ INIT_LIST_HEAD(&nn->blocked_locks_lru);
+
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
get_net(net);
}
BUG_ON(!list_empty(&reaplist));
- spin_lock(&nn->client_lock);
+ spin_lock(&nn->blocked_locks_lock);
while (!list_empty(&nn->blocked_locks_lru)) {
nbl = list_first_entry(&nn->blocked_locks_lru,
struct nfsd4_blocked_lock, nbl_lru);
list_move(&nbl->nbl_lru, &reaplist);
list_del_init(&nbl->nbl_list);
}
- spin_unlock(&nn->client_lock);
+ spin_unlock(&nn->blocked_locks_lock);
while (!list_empty(&reaplist)) {
nbl = list_first_entry(&nn->blocked_locks_lru,
len -= bytes;
}
+ if (!error)
+ error = vfs_fsync(new_file, 0);
fput(new_file);
out_fput:
fput(old_file);
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
return NULL;
- if (!realinode->i_op->get_acl)
- return NULL;
-
old_cred = ovl_override_creds(inode->i_sb);
acl = get_acl(realinode, type);
revert_creds(old_cred);
posix_acl_release(acl);
+ /*
+ * Check if sgid bit needs to be cleared (actual setacl operation will
+ * be done with mounter's capabilities and so that won't do it for us).
+ */
+ if (unlikely(inode->i_mode & S_ISGID) &&
+ handler->flags == ACL_TYPE_ACCESS &&
+ !in_group_p(inode->i_gid) &&
+ !capable_wrt_inode_uidgid(inode, CAP_FSETID)) {
+ struct iattr iattr = { .ia_valid = ATTR_KILL_SGID };
+
+ err = ovl_setattr(dentry, &iattr);
+ if (err)
+ return err;
+ }
+
err = ovl_xattr_set(dentry, handler->name, value, size, flags);
if (!err)
ovl_copyattr(ovl_inode_real(inode, NULL), inode);
* @src_h: height of visible portion of plane (in 16.16)
* @rotation: rotation of the plane
* @zpos: priority of the given plane on crtc (optional)
+ * Note that multiple active planes on the same crtc can have an identical
+ * zpos value. The rule to solving the conflict is to compare the plane
+ * object IDs; the plane with a higher ID must be stacked on top of a
+ * plane with a lower ID.
* @normalized_zpos: normalized value of zpos: unique, range from 0 to N-1
- * where N is the number of active planes for given crtc
+ * where N is the number of active planes for given crtc. Note that
+ * the driver must call drm_atomic_normalize_zpos() to update this before
+ * it can be trusted.
* @src: clipped source coordinates of the plane (in 16.16)
* @dst: clipped destination coordinates of the plane
* @visible: visibility of the plane
const char *mod_name);
void vmbus_driver_unregister(struct hv_driver *hv_driver);
-static inline const char *vmbus_dev_name(const struct hv_device *device_obj)
-{
- const struct kobject *kobj = &device_obj->device.kobj;
-
- return kobj->name;
-}
-
void vmbus_hvsock_device_unregister(struct vmbus_channel *channel);
int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
};
#if defined(CONFIG_NET_L3_MASTER_DEV)
-static inline bool skb_l3mdev_slave(__u16 flags)
+static inline bool ipv6_l3mdev_skb(__u16 flags)
{
return flags & IP6SKB_L3SLAVE;
}
#else
-static inline bool skb_l3mdev_slave(__u16 flags)
+static inline bool ipv6_l3mdev_skb(__u16 flags)
{
return false;
}
static inline int inet6_iif(const struct sk_buff *skb)
{
- bool l3_slave = skb_l3mdev_slave(IP6CB(skb)->flags);
+ bool l3_slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
return l3_slave ? skb->skb_iif : IP6CB(skb)->iif;
}
+/* can not be used in TCP layer after tcp_v6_fill_cb */
+static inline bool inet6_exact_dif_match(struct net *net, struct sk_buff *skb)
+{
+#if defined(CONFIG_NET_L3_MASTER_DEV)
+ if (!net->ipv4.sysctl_tcp_l3mdev_accept &&
+ ipv6_l3mdev_skb(IP6CB(skb)->flags))
+ return true;
+#endif
+ return false;
+}
+
struct tcp6_request_sock {
struct tcp_request_sock tcp6rsk_tcp;
};
u32 *lkey, u32 *rkey);
int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr);
int mlx4_SYNC_TPT(struct mlx4_dev *dev);
-int mlx4_test_interrupts(struct mlx4_dev *dev);
+int mlx4_test_interrupt(struct mlx4_dev *dev, int vector);
+int mlx4_test_async(struct mlx4_dev *dev);
int mlx4_query_diag_counters(struct mlx4_dev *dev, u8 op_modifier,
const u32 offset[], u32 value[],
size_t array_len, u8 port);
u32 prev;
int miss_counter;
bool sick;
+ /* wq spinlock to synchronize draining */
+ spinlock_t wq_lock;
struct workqueue_struct *wq;
+ unsigned long flags;
struct work_struct work;
+ struct delayed_work recover_work;
};
struct mlx5_cq_table {
int index;
};
-enum {
- MLX5_DB_PER_PAGE = PAGE_SIZE / L1_CACHE_BYTES,
-};
-
enum {
MLX5_COMP_EQ_SIZE = 1024,
};
MLX5_PTYS_EN = 1 << 2,
};
-struct mlx5_db_pgdir {
- struct list_head list;
- DECLARE_BITMAP(bitmap, MLX5_DB_PER_PAGE);
- __be32 *db_page;
- dma_addr_t db_dma;
-};
-
typedef void (*mlx5_cmd_cbk_t)(int status, void *context);
struct mlx5_cmd_work_ent {
int mlx5_health_init(struct mlx5_core_dev *dev);
void mlx5_start_health_poll(struct mlx5_core_dev *dev);
void mlx5_stop_health_poll(struct mlx5_core_dev *dev);
+void mlx5_drain_health_wq(struct mlx5_core_dev *dev);
int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
struct mlx5_buf *buf, int node);
int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, struct mlx5_buf *buf);
int page);
/* Reset and initialize a NAND device */
-int nand_reset(struct nand_chip *chip);
+int nand_reset(struct nand_chip *chip, int chipnr);
/* Free resources held by the NAND device */
void nand_cleanup(struct nand_chip *chip);
/* Used to determine if flush_id can be ignored */
u8 is_atomic:1;
- /* 5 bit hole */
+ /* Number of gro_receive callbacks this packet already went through */
+ u8 recursion_counter:4;
+
+ /* 1 bit hole */
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
__wsum csum;
#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
+#define GRO_RECURSION_LIMIT 15
+static inline int gro_recursion_inc_test(struct sk_buff *skb)
+{
+ return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
+}
+
+typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
+static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
+ struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (unlikely(gro_recursion_inc_test(skb))) {
+ NAPI_GRO_CB(skb)->flush |= 1;
+ return NULL;
+ }
+
+ return cb(head, skb);
+}
+
+typedef struct sk_buff **(*gro_receive_sk_t)(struct sock *, struct sk_buff **,
+ struct sk_buff *);
+static inline struct sk_buff **call_gro_receive_sk(gro_receive_sk_t cb,
+ struct sock *sk,
+ struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (unlikely(gro_recursion_inc_test(skb))) {
+ NAPI_GRO_CB(skb)->flush |= 1;
+ return NULL;
+ }
+
+ return cb(sk, head, skb);
+}
+
struct packet_type {
__be16 type; /* This is really htons(ether_type). */
struct net_device *dev; /* NULL is wildcarded here */
ldev = netdev_all_lower_get_next(dev, &(iter)))
#define netdev_for_each_all_lower_dev_rcu(dev, ldev, iter) \
- for (iter = (dev)->all_adj_list.lower.next, \
+ for (iter = &(dev)->all_adj_list.lower, \
ldev = netdev_all_lower_get_next_rcu(dev, &(iter)); \
ldev; \
ldev = netdev_all_lower_get_next_rcu(dev, &(iter)))
#define DIRECT_REG_RD(reg_addr) readl((void __iomem *)(reg_addr))
#define QED_COALESCE_MAX 0xFF
+#define QED_DEFAULT_RX_USECS 12
/* forward */
struct qed_dev;
bool qede_roce_supported(struct qede_dev *dev);
-#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+#if IS_ENABLED(CONFIG_QED_RDMA)
int qede_roce_dev_add(struct qede_dev *dev);
void qede_roce_dev_event_open(struct qede_dev *dev);
void qede_roce_dev_event_close(struct qede_dev *dev);
#include <linux/list.h>
#include <linux/rbtree.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/lockdep.h>
#define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_us) \
({ \
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
- int ret; \
+ int pollret; \
might_sleep_if(sleep_us); \
for (;;) { \
- ret = regmap_read((map), (addr), &(val)); \
- if (ret) \
+ pollret = regmap_read((map), (addr), &(val)); \
+ if (pollret) \
break; \
if (cond) \
break; \
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
- ret = regmap_read((map), (addr), &(val)); \
+ pollret = regmap_read((map), (addr), &(val)); \
break; \
} \
if (sleep_us) \
usleep_range((sleep_us >> 2) + 1, sleep_us); \
} \
- ret ?: ((cond) ? 0 : -ETIMEDOUT); \
+ pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
})
#ifdef CONFIG_REGMAP
/**
* skb_fclone_busy - check if fclone is busy
+ * @sk: socket
* @skb: buffer
*
* Returns true if skb is a fast clone, and its clone is not freed.
const struct in6_addr *addr);
int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
+void __ipv6_sock_mc_close(struct sock *sk);
void ipv6_sock_mc_close(struct sock *sk);
bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
const struct in6_addr *src_addr);
* that do not do the 802.11/802.3 conversion on the device.
*/
+/**
+ * ieee80211_data_to_8023_exthdr - convert an 802.11 data frame to 802.3
+ * @skb: the 802.11 data frame
+ * @ehdr: pointer to a &struct ethhdr that will get the header, instead
+ * of it being pushed into the SKB
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ * Return: 0 on success. Non-zero on error.
+ */
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype);
+
/**
* ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
* @skb: the 802.11 data frame
* @iftype: the virtual interface type
* Return: 0 on success. Non-zero on error.
*/
-int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
- enum nl80211_iftype iftype);
+static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
+ enum nl80211_iftype iftype)
+{
+ return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype);
+}
/**
* ieee80211_data_from_8023 - convert an 802.3 frame to 802.11
/**
* ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
*
- * Decode an IEEE 802.11n A-MSDU frame and convert it to a list of
- * 802.3 frames. The @list will be empty if the decode fails. The
- * @skb is consumed after the function returns.
+ * Decode an IEEE 802.11 A-MSDU and convert it to a list of 802.3 frames.
+ * The @list will be empty if the decode fails. The @skb must be fully
+ * header-less before being passed in here; it is freed in this function.
*
- * @skb: The input IEEE 802.11n A-MSDU frame.
+ * @skb: The input A-MSDU frame without any headers.
* @list: The output list of 802.3 frames. It must be allocated and
* initialized by by the caller.
* @addr: The device MAC address.
* @iftype: The device interface type.
* @extra_headroom: The hardware extra headroom for SKBs in the @list.
- * @has_80211_header: Set it true if SKB is with IEEE 802.11 header.
+ * @check_da: DA to check in the inner ethernet header, or NULL
+ * @check_sa: SA to check in the inner ethernet header, or NULL
*/
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype,
const unsigned int extra_headroom,
- bool has_80211_header);
+ const u8 *check_da, const u8 *check_sa);
/**
* cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
__u32 if_flags;
int dead;
+ u32 desync_factor;
u8 rndid[8];
- struct timer_list regen_timer;
struct list_head tempaddr_list;
struct in6_addr token;
struct inet_skb_parm {
int iif;
struct ip_options opt; /* Compiled IP options */
- unsigned char flags;
+ u16 flags;
#define IPSKB_FORWARDED BIT(0)
#define IPSKB_XFRM_TUNNEL_SIZE BIT(1)
#define IPSKB_DOREDIRECT BIT(5)
#define IPSKB_FRAG_PMTU BIT(6)
#define IPSKB_FRAG_SEGS BIT(7)
+#define IPSKB_L3SLAVE BIT(8)
u16 frag_max_size;
};
+static inline bool ipv4_l3mdev_skb(u16 flags)
+{
+ return !!(flags & IPSKB_L3SLAVE);
+}
+
static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
{
return ip_hdr(skb)->ihl * 4;
*/
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
-void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset);
+void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int tlen, int offset);
int ip_cmsg_send(struct sock *sk, struct msghdr *msg,
struct ipcm_cookie *ipc, bool allow_ipv6);
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
{
- ip_cmsg_recv_offset(msg, skb, 0);
+ ip_cmsg_recv_offset(msg, skb, 0, 0);
}
bool icmp_global_allow(void);
rwlock_t tb6_lock;
struct fib6_node tb6_root;
struct inet_peer_base tb6_peers;
+ unsigned int flags;
+#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
};
#define RT6_TABLE_UNSPEC RT_TABLE_UNSPEC
#define RT6_LOOKUP_F_SRCPREF_TMP 0x00000008
#define RT6_LOOKUP_F_SRCPREF_PUBLIC 0x00000010
#define RT6_LOOKUP_F_SRCPREF_COA 0x00000020
+#define RT6_LOOKUP_F_IGNORE_LINKSTATE 0x00000040
/* We do not (yet ?) support IPv6 jumbograms (RFC 2675)
* Unlike IPv4, hdr->seg_len doesn't include the IPv6 header
* in the control information, and it will be filled by the rate
* control algorithm according to what should be sent. For example,
* if this array contains, in the format { <idx>, <count> } the
- * information
+ * information::
+ *
* { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 }
+ *
* then this means that the frame should be transmitted
* up to twice at rate 3, up to twice at rate 2, and up to four
* times at rate 1 if it doesn't get acknowledged. Say it gets
* acknowledged by the peer after the fifth attempt, the status
- * information should then contain
+ * information should then contain::
+ *
* { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ...
+ *
* since it was transmitted twice at rate 3, twice at rate 2
* and once at rate 1 after which we received an acknowledgement.
*/
* @rate_idx: index of data rate into band's supported rates or MCS index if
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
* @vht_nss: number of streams (VHT only)
- * @flag: %RX_FLAG_*
- * @vht_flag: %RX_VHT_FLAG_*
+ * @flag: %RX_FLAG_\*
+ * @vht_flag: %RX_VHT_FLAG_\*
* @rx_flags: internal RX flags for mac80211
* @ampdu_reference: A-MPDU reference number, must be a different value for
* each A-MPDU but the same for each subframe within one A-MPDU
* @probe_req_reg: probe requests should be reported to mac80211 for this
* interface.
* @drv_priv: data area for driver use, will always be aligned to
- * sizeof(void *).
+ * sizeof(void \*).
* @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
*/
struct ieee80211_vif {
* @wme: indicates whether the STA supports QoS/WME (if local devices does,
* otherwise always false)
* @drv_priv: data area for driver use, will always be aligned to
- * sizeof(void *), size is determined in hw information.
+ * sizeof(void \*), size is determined in hw information.
* @uapsd_queues: bitmap of queues configured for uapsd. Only valid
* if wme is supported.
* @max_sp: max Service Period. Only valid if wme is supported.
*
* @radiotap_mcs_details: lists which MCS information can the HW
* reports, by default it is set to _MCS, _GI and _BW but doesn't
- * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only
+ * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_\* values, only
* adding _BW is supported today.
*
* @radiotap_vht_details: lists which VHT MCS information the HW reports,
* the default is _GI | _BANDWIDTH.
- * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
+ * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_\* values.
*
* @radiotap_timestamp: Information for the radiotap timestamp field; if the
* 'units_pos' member is set to a non-negative value it must be set to
* in the software stack cares about, we will, in the future, have mac80211
* tell the driver which information elements are interesting in the sense
* that we want to see changes in them. This will include
+ *
* - a list of information element IDs
* - a list of OUIs for the vendor information element
*
* @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler)
* @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE)
* @sk_sndbuf: size of send buffer in bytes
+ * @sk_padding: unused element for alignment
* @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets
* @sk_no_check_rx: allow zero checksum in RX packets
* @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
* @sk_backlog_rcv: callback to process the backlog
* @sk_destruct: called at sock freeing time, i.e. when all refcnt == 0
* @sk_reuseport_cb: reuseport group container
- */
+ * @sk_rcu: used during RCU grace period
+ */
struct sock {
/*
* Now struct inet_timewait_sock also uses sock_common, so please just
*/
static inline int tcp_v6_iif(const struct sk_buff *skb)
{
- bool l3_slave = skb_l3mdev_slave(TCP_SKB_CB(skb)->header.h6.flags);
+ bool l3_slave = ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags);
return l3_slave ? skb->skb_iif : TCP_SKB_CB(skb)->header.h6.iif;
}
#endif
+/* TCP_SKB_CB reference means this can not be used from early demux */
+static inline bool inet_exact_dif_match(struct net *net, struct sk_buff *skb)
+{
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+ if (!net->ipv4.sysctl_tcp_l3mdev_accept &&
+ ipv4_l3mdev_skb(TCP_SKB_CB(skb)->header.h4.flags))
+ return true;
+#endif
+ return false;
+}
+
/* Due to TSO, an SKB can be composed of multiple actual
* packets. To keep these tracked properly, we use this.
*/
void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
int udp_rcv(struct sk_buff *skb);
int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+int __udp_disconnect(struct sock *sk, int flags);
int udp_disconnect(struct sock *sk, int flags);
unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
struct vxlan_dev {
struct hlist_node hlist; /* vni hash table */
struct list_head next; /* vxlan's per namespace list */
- struct vxlan_sock *vn4_sock; /* listening socket for IPv4 */
+ struct vxlan_sock __rcu *vn4_sock; /* listening socket for IPv4 */
#if IS_ENABLED(CONFIG_IPV6)
- struct vxlan_sock *vn6_sock; /* listening socket for IPv6 */
+ struct vxlan_sock __rcu *vn6_sock; /* listening socket for IPv6 */
#endif
struct net_device *dev;
struct net *net; /* netns for packet i/o */
struct mutex lock;
int device;
#ifdef CONFIG_SND_VERBOSE_PROCFS
+ /* private: */
char id[64];
struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
void __snd_printk(unsigned int level, const char *file, int line,
const char *format, ...);
#else
-#define __snd_printk(level, file, line, format, args...) \
- printk(format, ##args)
+#define __snd_printk(level, file, line, format, ...) \
+ printk(format, ##__VA_ARGS__)
#endif
/**
* Works like printk() but prints the file and the line of the caller
* when configured with CONFIG_SND_VERBOSE_PRINTK.
*/
-#define snd_printk(fmt, args...) \
- __snd_printk(0, __FILE__, __LINE__, fmt, ##args)
+#define snd_printk(fmt, ...) \
+ __snd_printk(0, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#ifdef CONFIG_SND_DEBUG
/**
* Works like snd_printk() for debugging purposes.
* Ignored when CONFIG_SND_DEBUG is not set.
*/
-#define snd_printd(fmt, args...) \
- __snd_printk(1, __FILE__, __LINE__, fmt, ##args)
-#define _snd_printd(level, fmt, args...) \
- __snd_printk(level, __FILE__, __LINE__, fmt, ##args)
+#define snd_printd(fmt, ...) \
+ __snd_printk(1, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
+#define _snd_printd(level, fmt, ...) \
+ __snd_printk(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
/**
* snd_BUG - give a BUG warning message and stack trace
* Works like snd_printk() for debugging purposes.
* Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
*/
-#define snd_printdd(format, args...) \
- __snd_printk(2, __FILE__, __LINE__, format, ##args)
+#define snd_printdd(format, ...) \
+ __snd_printk(2, __FILE__, __LINE__, format, ##__VA_ARGS__)
#else
__printf(1, 2)
static inline void snd_printdd(const char *format, ...) {}
unsigned int internal_clock; /* 44100 or 48000 */
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
- struct task_struct *firmware_thread;
+ struct delayed_work firmware_work;
+ u32 last_reg;
};
struct snd_emu10k1 {
static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep,
__u32 speed)
{
-
- ep->speed = (__u16)speed;
+ ep->speed = (__u16)(speed & 0xFFFF);
ep->speed_hi = (__u16)(speed >> 16);
}
#define RTNH_F_OFFLOAD 8 /* offloaded route */
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
-#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN)
+#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
/* Macros to handle hexthops */
static void release_task_stack(struct task_struct *tsk)
{
+ if (WARN_ON(tsk->state != TASK_DEAD))
+ return; /* Better to leak the stack than to free prematurely */
+
account_kernel_stack(tsk, -1);
arch_release_thread_stack(tsk->stack);
free_thread_stack(tsk);
atomic_dec(&p->cred->user->processes);
exit_creds(p);
bad_fork_free:
+ p->state = TASK_DEAD;
put_task_stack(p);
free_task(p);
fork_out:
int ppid;
unsigned long state = p->state;
+ if (!try_get_task_stack(p))
+ return;
if (state)
state = __ffs(state) + 1;
printk(KERN_INFO "%-15.15s %c", p->comm,
state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?');
-#if BITS_PER_LONG == 32
- if (state == TASK_RUNNING)
- printk(KERN_CONT " running ");
- else
- printk(KERN_CONT " %08lx ", thread_saved_pc(p));
-#else
if (state == TASK_RUNNING)
printk(KERN_CONT " running task ");
- else
- printk(KERN_CONT " %016lx ", thread_saved_pc(p));
-#endif
#ifdef CONFIG_DEBUG_STACK_USAGE
free = stack_not_used(p);
#endif
print_worker_info(KERN_INFO, p);
show_stack(p, NULL);
+ put_task_stack(p);
}
void show_state_filter(unsigned long state_filter)
{ },
INTERNAL,
{ 0x34 },
- { { 1, 0xbef } },
+ { { ETH_HLEN, 0xbef } },
.fill_helper = bpf_fill_ld_abs_vlan_push_pop,
},
/*
#endif
#ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
-volatile u64 latent_entropy __latent_entropy;
+volatile unsigned long latent_entropy __latent_entropy;
EXPORT_SYMBOL(latent_entropy);
#endif
skb_gro_pull(skb, sizeof(*vhdr));
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
batadv_softif_destroy_sysfs(hard_iface->soft_iface);
}
- hard_iface->soft_iface = NULL;
batadv_hardif_put(hard_iface);
out:
BATADV_DBG_NC = BIT(5),
BATADV_DBG_MCAST = BIT(6),
BATADV_DBG_TP_METER = BIT(7),
- BATADV_DBG_ALL = 127,
+ BATADV_DBG_ALL = 255,
};
#ifdef CONFIG_BATMAN_ADV_DEBUG
if (bat_priv->algo_ops->neigh.hardif_init)
bat_priv->algo_ops->neigh.hardif_init(hardif_neigh);
- hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
+ hlist_add_head_rcu(&hardif_neigh->list, &hard_iface->neigh_list);
out:
spin_unlock_bh(&hard_iface->neigh_list_lock);
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
}
-static u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
+u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
- size_t complete_len;
size_t short_len;
- int max_len;
-
- max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
- complete_len = strlen(hdev->dev_name);
- short_len = strlen(hdev->short_name);
-
- /* no space left for name */
- if (max_len < 1)
- return ad_len;
+ size_t complete_len;
- /* no name set */
- if (!complete_len)
+ /* no space left for name (+ NULL + type + len) */
+ if ((HCI_MAX_AD_LENGTH - ad_len) < HCI_MAX_SHORT_NAME_LENGTH + 3)
return ad_len;
- /* complete name fits and is eq to max short name len or smaller */
- if (complete_len <= max_len &&
- complete_len <= HCI_MAX_SHORT_NAME_LENGTH) {
+ /* use complete name if present and fits */
+ complete_len = strlen(hdev->dev_name);
+ if (complete_len && complete_len <= HCI_MAX_SHORT_NAME_LENGTH)
return eir_append_data(ptr, ad_len, EIR_NAME_COMPLETE,
- hdev->dev_name, complete_len);
- }
+ hdev->dev_name, complete_len + 1);
- /* short name set and fits */
- if (short_len && short_len <= max_len) {
+ /* use short name if present */
+ short_len = strlen(hdev->short_name);
+ if (short_len)
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
- hdev->short_name, short_len);
- }
+ hdev->short_name, short_len + 1);
- /* no short name set so shorten complete name */
- if (!short_len) {
- return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
- hdev->dev_name, max_len);
+ /* use shortened full name if present, we already know that name
+ * is longer then HCI_MAX_SHORT_NAME_LENGTH
+ */
+ if (complete_len) {
+ u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
+
+ memcpy(name, hdev->dev_name, HCI_MAX_SHORT_NAME_LENGTH);
+ name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
+
+ return eir_append_data(ptr, ad_len, EIR_NAME_SHORT, name,
+ sizeof(name));
}
return ad_len;
void hci_request_setup(struct hci_dev *hdev);
void hci_request_cancel_all(struct hci_dev *hdev);
+u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len);
+
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type,
u8 *data, u8 data_len)
{
return err;
}
-static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
+static u8 calculate_name_len(struct hci_dev *hdev)
+{
+ u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 3];
+
+ return append_local_name(hdev, buf, 0);
+}
+
+static u8 tlv_data_max_len(struct hci_dev *hdev, u32 adv_flags,
+ bool is_adv_data)
{
u8 max_len = HCI_MAX_AD_LENGTH;
if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
max_len -= 3;
} else {
- /* at least 1 byte of name should fit in */
if (adv_flags & MGMT_ADV_FLAG_LOCAL_NAME)
- max_len -= 3;
+ max_len -= calculate_name_len(hdev);
if (adv_flags & (MGMT_ADV_FLAG_APPEARANCE))
max_len -= 4;
return adv_flags & MGMT_ADV_FLAG_APPEARANCE;
}
-static bool tlv_data_is_valid(u32 adv_flags, u8 *data, u8 len, bool is_adv_data)
+static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
+ u8 len, bool is_adv_data)
{
int i, cur_len;
u8 max_len;
- max_len = tlv_data_max_len(adv_flags, is_adv_data);
+ max_len = tlv_data_max_len(hdev, adv_flags, is_adv_data);
if (len > max_len)
return false;
goto unlock;
}
- if (!tlv_data_is_valid(flags, cp->data, cp->adv_data_len, true) ||
- !tlv_data_is_valid(flags, cp->data + cp->adv_data_len,
+ if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) ||
+ !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len,
cp->scan_rsp_len, false)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
rp.instance = cp->instance;
rp.flags = cp->flags;
- rp.max_adv_data_len = tlv_data_max_len(flags, true);
- rp.max_scan_rsp_len = tlv_data_max_len(flags, false);
+ rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true);
+ rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false);
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
mod_timer(&query->timer, jiffies);
}
-void br_multicast_enable_port(struct net_bridge_port *port)
+static void __br_multicast_enable_port(struct net_bridge_port *port)
{
struct net_bridge *br = port->br;
- spin_lock(&br->multicast_lock);
if (br->multicast_disabled || !netif_running(br->dev))
- goto out;
+ return;
br_multicast_enable(&port->ip4_own_query);
#if IS_ENABLED(CONFIG_IPV6)
if (port->multicast_router == MDB_RTR_TYPE_PERM &&
hlist_unhashed(&port->rlist))
br_multicast_add_router(br, port);
+}
-out:
+void br_multicast_enable_port(struct net_bridge_port *port)
+{
+ struct net_bridge *br = port->br;
+
+ spin_lock(&br->multicast_lock);
+ __br_multicast_enable_port(port);
spin_unlock(&br->multicast_lock);
}
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
{
- int err = 0;
struct net_bridge_mdb_htable *mdb;
+ struct net_bridge_port *port;
+ int err = 0;
spin_lock_bh(&br->multicast_lock);
if (br->multicast_disabled == !val)
goto rollback;
}
- br_multicast_start_querier(br, &br->ip4_own_query);
-#if IS_ENABLED(CONFIG_IPV6)
- br_multicast_start_querier(br, &br->ip6_own_query);
-#endif
+ br_multicast_open(br);
+ list_for_each_entry(port, &br->port_list, list)
+ __br_multicast_enable_port(port);
unlock:
spin_unlock_bh(&br->multicast_lock);
}
return head;
}
+EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
static void qdisc_pkt_len_init(struct sk_buff *skb)
{
NAPI_GRO_CB(skb)->flush = 0;
NAPI_GRO_CB(skb)->free = 0;
NAPI_GRO_CB(skb)->encap_mark = 0;
+ NAPI_GRO_CB(skb)->recursion_counter = 0;
NAPI_GRO_CB(skb)->is_fou = 0;
NAPI_GRO_CB(skb)->is_atomic = 1;
NAPI_GRO_CB(skb)->gro_remcsum_start = 0;
{
struct netdev_adjacent *lower;
- lower = list_first_or_null_rcu(&dev->all_adj_list.lower,
- struct netdev_adjacent, list);
+ lower = list_entry_rcu((*iter)->next, struct netdev_adjacent, list);
+
+ if (&lower->list == &dev->all_adj_list.lower)
+ return NULL;
+
+ *iter = &lower->list;
- return lower ? lower->dev : NULL;
+ return lower->dev;
}
EXPORT_SYMBOL(netdev_all_lower_get_next_rcu);
case htons(ETH_P_8021AD):
case htons(ETH_P_8021Q): {
const struct vlan_hdr *vlan;
+ struct vlan_hdr _vlan;
+ bool vlan_tag_present = skb && skb_vlan_tag_present(skb);
- if (skb_vlan_tag_present(skb))
+ if (vlan_tag_present)
proto = skb->protocol;
- if (!skb_vlan_tag_present(skb) ||
- proto == cpu_to_be16(ETH_P_8021Q) ||
- proto == cpu_to_be16(ETH_P_8021AD)) {
- struct vlan_hdr _vlan;
-
+ if (!vlan_tag_present || eth_type_vlan(skb->protocol)) {
vlan = __skb_header_pointer(skb, nhoff, sizeof(_vlan),
data, hlen, &_vlan);
if (!vlan)
FLOW_DISSECTOR_KEY_VLAN,
target_container);
- if (skb_vlan_tag_present(skb)) {
+ if (vlan_tag_present) {
key_vlan->vlan_id = skb_vlan_tag_get_id(skb);
key_vlan->vlan_priority =
(skb_vlan_tag_get_prio(skb) >> VLAN_PRIO_SHIFT);
*/
int peernet2id_alloc(struct net *net, struct net *peer)
{
+ unsigned long flags;
bool alloc;
int id;
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irqsave(&net->nsid_lock, flags);
alloc = atomic_read(&peer->count) == 0 ? false : true;
id = __peernet2id_alloc(net, peer, &alloc);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
if (alloc && id >= 0)
rtnl_net_notifyid(net, RTM_NEWNSID, id);
return id;
/* This function returns, if assigned, the id of a peer netns. */
int peernet2id(struct net *net, struct net *peer)
{
+ unsigned long flags;
int id;
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irqsave(&net->nsid_lock, flags);
id = __peernet2id(net, peer);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
return id;
}
EXPORT_SYMBOL(peernet2id);
struct net *get_net_ns_by_id(struct net *net, int id)
{
+ unsigned long flags;
struct net *peer;
if (id < 0)
return NULL;
rcu_read_lock();
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irqsave(&net->nsid_lock, flags);
peer = idr_find(&net->netns_ids, id);
if (peer)
get_net(peer);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
rcu_read_unlock();
return peer;
for_each_net(tmp) {
int id;
- spin_lock_bh(&tmp->nsid_lock);
+ spin_lock_irq(&tmp->nsid_lock);
id = __peernet2id(tmp, net);
if (id >= 0)
idr_remove(&tmp->netns_ids, id);
- spin_unlock_bh(&tmp->nsid_lock);
+ spin_unlock_irq(&tmp->nsid_lock);
if (id >= 0)
rtnl_net_notifyid(tmp, RTM_DELNSID, id);
}
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irq(&net->nsid_lock);
idr_destroy(&net->netns_ids);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irq(&net->nsid_lock);
}
rtnl_unlock();
{
struct net *net = sock_net(skb->sk);
struct nlattr *tb[NETNSA_MAX + 1];
+ unsigned long flags;
struct net *peer;
int nsid, err;
if (IS_ERR(peer))
return PTR_ERR(peer);
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irqsave(&net->nsid_lock, flags);
if (__peernet2id(net, peer) >= 0) {
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
err = -EEXIST;
goto out;
}
err = alloc_netid(net, peer, nsid);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
if (err >= 0) {
rtnl_net_notifyid(net, RTM_NEWNSID, err);
err = 0;
.idx = 0,
.s_idx = cb->args[0],
};
+ unsigned long flags;
- spin_lock_bh(&net->nsid_lock);
+ spin_lock_irqsave(&net->nsid_lock, flags);
idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
- spin_unlock_bh(&net->nsid_lock);
+ spin_unlock_irqrestore(&net->nsid_lock, flags);
cb->args[0] = net_cb.idx;
return skb->len;
#define M_QUEUE_XMIT 2 /* Inject packet into qdisc */
/* If lock -- protects updating of if_list */
-#define if_lock(t) spin_lock(&(t->if_lock));
-#define if_unlock(t) spin_unlock(&(t->if_lock));
+#define if_lock(t) mutex_lock(&(t->if_lock));
+#define if_unlock(t) mutex_unlock(&(t->if_lock));
/* Used to help with determining the pkts on receive */
#define PKTGEN_MAGIC 0xbe9be955
};
struct pktgen_thread {
- spinlock_t if_lock; /* for list of devices */
+ struct mutex if_lock; /* for list of devices */
struct list_head if_list; /* All device here */
struct list_head th_list;
struct task_struct *tsk;
{
struct pktgen_thread *t;
+ mutex_lock(&pktgen_thread_lock);
+
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
struct pktgen_dev *pkt_dev;
- rcu_read_lock();
- list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
+ if_lock(t);
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
if (pkt_dev->odev != dev)
continue;
dev->name);
break;
}
- rcu_read_unlock();
+ if_unlock(t);
}
+ mutex_unlock(&pktgen_thread_lock);
}
static int pktgen_device_event(struct notifier_block *unused,
return -ENOMEM;
}
- spin_lock_init(&t->if_lock);
+ mutex_init(&t->if_lock);
t->cpu = cpu;
INIT_LIST_HEAD(&t->if_list);
return 0;
}
-EXPORT_SYMBOL(reuseport_add_sock);
static void reuseport_free_rcu(struct rcu_head *head)
{
skb_gro_pull(skb, sizeof(*eh));
skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
static void check_local_dest(struct hsr_priv *hsr, struct sk_buff *skb,
struct hsr_frame_info *frame)
{
- struct net_device *master_dev;
-
- master_dev = hsr_port_get_hsr(hsr, HSR_PT_MASTER)->dev;
-
if (hsr_addr_is_self(hsr, eth_hdr(skb)->h_dest)) {
frame->is_local_exclusive = true;
skb->pkt_type = PACKET_HOST;
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
if (!ops || !ops->callbacks.gro_receive)
goto out_unlock;
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
goto out_unlock;
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
flush = 0;
out_unlock:
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
skb_gro_postpull_rcsum(skb, greh, grehlen);
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
flush = 0;
out_unlock:
#include <net/inet_hashtables.h>
#include <net/secure_seq.h>
#include <net/ip.h>
+#include <net/tcp.h>
#include <net/sock_reuseport.h>
static u32 inet_ehashfn(const struct net *net, const __be32 laddr,
static inline int compute_score(struct sock *sk, struct net *net,
const unsigned short hnum, const __be32 daddr,
- const int dif)
+ const int dif, bool exact_dif)
{
int score = -1;
struct inet_sock *inet = inet_sk(sk);
return -1;
score += 4;
}
- if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if || exact_dif) {
if (sk->sk_bound_dev_if != dif)
return -1;
score += 4;
unsigned int hash = inet_lhashfn(net, hnum);
struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
int score, hiscore = 0, matches = 0, reuseport = 0;
+ bool exact_dif = inet_exact_dif_match(net, skb);
struct sock *sk, *result = NULL;
u32 phash = 0;
sk_for_each_rcu(sk, &ilb->head) {
- score = compute_score(sk, net, hnum, daddr, dif);
+ score = compute_score(sk, net, hnum, daddr, dif, exact_dif);
if (score > hiscore) {
reuseport = sk->sk_reuseport;
if (reuseport) {
{
struct iphdr *iph;
int ptr;
- struct net_device *dev;
struct sk_buff *skb2;
unsigned int mtu, hlen, left, len, ll_rs;
int offset;
struct rtable *rt = skb_rtable(skb);
int err = 0;
- dev = rt->dst.dev;
-
/* for offloaded checksums cleanup checksum before fragmentation */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
}
static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
- int offset)
+ int tlen, int offset)
{
__wsum csum = skb->csum;
return;
if (offset != 0)
- csum = csum_sub(csum, csum_partial(skb_transport_header(skb),
- offset, 0));
+ csum = csum_sub(csum,
+ csum_partial(skb_transport_header(skb) + tlen,
+ offset, 0));
put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
}
}
void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
- int offset)
+ int tlen, int offset)
{
struct inet_sock *inet = inet_sk(skb->sk);
unsigned int flags = inet->cmsg_flags;
}
if (flags & IP_CMSG_CHECKSUM)
- ip_cmsg_recv_checksum(msg, skb, offset);
+ ip_cmsg_recv_checksum(msg, skb, tlen, offset);
}
EXPORT_SYMBOL(ip_cmsg_recv_offset);
.init = ping_init_sock,
.close = ping_close,
.connect = ip4_datagram_connect,
- .disconnect = udp_disconnect,
+ .disconnect = __udp_disconnect,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.sendmsg = ping_v4_sendmsg,
.close = raw_close,
.destroy = raw_destroy,
.connect = ip4_datagram_connect,
- .disconnect = udp_disconnect,
+ .disconnect = __udp_disconnect,
.ioctl = raw_ioctl,
.init = raw_init,
.setsockopt = raw_setsockopt,
container_of(table->data, struct net, ipv4.ping_group_range.range);
unsigned int seq;
do {
- seq = read_seqbegin(&net->ipv4.ip_local_ports.lock);
+ seq = read_seqbegin(&net->ipv4.ping_group_range.lock);
*low = data[0];
*high = data[1];
- } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq));
+ } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq));
}
/* Update system visible IP port range */
kgid_t *data = table->data;
struct net *net =
container_of(table->data, struct net, ipv4.ping_group_range.range);
- write_seqlock(&net->ipv4.ip_local_ports.lock);
+ write_seqlock(&net->ipv4.ping_group_range.lock);
data[0] = low;
data[1] = high;
- write_sequnlock(&net->ipv4.ip_local_ports.lock);
+ write_sequnlock(&net->ipv4.ping_group_range.lock);
}
/* Validate changes from /proc interface. */
int sysctl_tcp_tw_reuse __read_mostly;
int sysctl_tcp_low_latency __read_mostly;
-EXPORT_SYMBOL(sysctl_tcp_low_latency);
#ifdef CONFIG_TCP_MD5SIG
static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
struct tcp_iter_state *st = seq->private;
struct net *net = seq_file_net(seq);
struct inet_listen_hashbucket *ilb;
- struct inet_connection_sock *icsk;
struct sock *sk = cur;
if (!sk) {
continue;
if (sk->sk_family == st->family)
return sk;
- icsk = inet_csk(sk);
}
spin_unlock_bh(&ilb->lock);
st->offset = 0;
*addr_len = sizeof(*sin);
}
if (inet->cmsg_flags)
- ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr) + off);
+ ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr), off);
err = copied;
if (flags & MSG_TRUNC)
goto try_again;
}
-int udp_disconnect(struct sock *sk, int flags)
+int __udp_disconnect(struct sock *sk, int flags)
{
struct inet_sock *inet = inet_sk(sk);
/*
sk_dst_reset(sk);
return 0;
}
+EXPORT_SYMBOL(__udp_disconnect);
+
+int udp_disconnect(struct sock *sk, int flags)
+{
+ lock_sock(sk);
+ __udp_disconnect(sk, flags);
+ release_sock(sk);
+ return 0;
+}
EXPORT_SYMBOL(udp_disconnect);
void udp_lib_unhash(struct sock *sk)
sk->sk_err = err;
sk->sk_error_report(sk);
- udp_disconnect(sk, 0);
+ __udp_disconnect(sk, 0);
release_sock(sk);
skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
- pp = udp_sk(sk)->gro_receive(sk, head, skb);
+ pp = call_gro_receive_sk(udp_sk(sk)->gro_receive, sk, head, skb);
out_unlock:
rcu_read_unlock();
}
#endif
-static void __ipv6_regen_rndid(struct inet6_dev *idev);
-static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
-static void ipv6_regen_rndid(unsigned long data);
+static void ipv6_regen_rndid(struct inet6_dev *idev);
+static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
static int ipv6_count_addresses(struct inet6_dev *idev);
goto err_release;
}
- /* One reference from device. We must do this before
- * we invoke __ipv6_regen_rndid().
- */
+ /* One reference from device. */
in6_dev_hold(ndev);
if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
#endif
INIT_LIST_HEAD(&ndev->tempaddr_list);
- setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
+ ndev->desync_factor = U32_MAX;
if ((dev->flags&IFF_LOOPBACK) ||
dev->type == ARPHRD_TUNNEL ||
dev->type == ARPHRD_TUNNEL6 ||
dev->type == ARPHRD_SIT ||
dev->type == ARPHRD_NONE) {
ndev->cnf.use_tempaddr = -1;
- } else {
- in6_dev_hold(ndev);
- ipv6_regen_rndid((unsigned long) ndev);
- }
+ } else
+ ipv6_regen_rndid(ndev);
ndev->token = in6addr_any;
err = addrconf_sysctl_register(ndev);
if (err) {
ipv6_mc_destroy_dev(ndev);
- del_timer(&ndev->regen_timer);
snmp6_unregister_dev(ndev);
goto err_release;
}
int ret = 0;
u32 addr_flags;
unsigned long now = jiffies;
+ long max_desync_factor;
+ s32 cnf_temp_preferred_lft;
write_lock_bh(&idev->lock);
if (ift) {
}
in6_ifa_hold(ifp);
memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
- __ipv6_try_regen_rndid(idev, tmpaddr);
+ ipv6_try_regen_rndid(idev, tmpaddr);
memcpy(&addr.s6_addr[8], idev->rndid, 8);
age = (now - ifp->tstamp) / HZ;
+
+ regen_advance = idev->cnf.regen_max_retry *
+ idev->cnf.dad_transmits *
+ NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ;
+
+ /* recalculate max_desync_factor each time and update
+ * idev->desync_factor if it's larger
+ */
+ cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft);
+ max_desync_factor = min_t(__u32,
+ idev->cnf.max_desync_factor,
+ cnf_temp_preferred_lft - regen_advance);
+
+ if (unlikely(idev->desync_factor > max_desync_factor)) {
+ if (max_desync_factor > 0) {
+ get_random_bytes(&idev->desync_factor,
+ sizeof(idev->desync_factor));
+ idev->desync_factor %= max_desync_factor;
+ } else {
+ idev->desync_factor = 0;
+ }
+ }
+
tmp_valid_lft = min_t(__u32,
ifp->valid_lft,
idev->cnf.temp_valid_lft + age);
- tmp_prefered_lft = min_t(__u32,
- ifp->prefered_lft,
- idev->cnf.temp_prefered_lft + age -
- idev->cnf.max_desync_factor);
+ tmp_prefered_lft = cnf_temp_preferred_lft + age -
+ idev->desync_factor;
+ tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft);
tmp_plen = ifp->prefix_len;
tmp_tstamp = ifp->tstamp;
spin_unlock_bh(&ifp->lock);
- regen_advance = idev->cnf.regen_max_retry *
- idev->cnf.dad_transmits *
- NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ;
write_unlock_bh(&idev->lock);
/* A temporary address is created only if this calculated Preferred
}
/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
-static void __ipv6_regen_rndid(struct inet6_dev *idev)
+static void ipv6_regen_rndid(struct inet6_dev *idev)
{
regen:
get_random_bytes(idev->rndid, sizeof(idev->rndid));
}
}
-static void ipv6_regen_rndid(unsigned long data)
-{
- struct inet6_dev *idev = (struct inet6_dev *) data;
- unsigned long expires;
-
- rcu_read_lock_bh();
- write_lock_bh(&idev->lock);
-
- if (idev->dead)
- goto out;
-
- __ipv6_regen_rndid(idev);
-
- expires = jiffies +
- idev->cnf.temp_prefered_lft * HZ -
- idev->cnf.regen_max_retry * idev->cnf.dad_transmits *
- NEIGH_VAR(idev->nd_parms, RETRANS_TIME) -
- idev->cnf.max_desync_factor * HZ;
- if (time_before(expires, jiffies)) {
- pr_warn("%s: too short regeneration interval; timer disabled for %s\n",
- __func__, idev->dev->name);
- goto out;
- }
-
- if (!mod_timer(&idev->regen_timer, expires))
- in6_dev_hold(idev);
-
-out:
- write_unlock_bh(&idev->lock);
- rcu_read_unlock_bh();
- in6_dev_put(idev);
-}
-
-static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr)
+static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr)
{
if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)
- __ipv6_regen_rndid(idev);
+ ipv6_regen_rndid(idev);
}
/*
max_valid = 0;
max_prefered = idev->cnf.temp_prefered_lft -
- idev->cnf.max_desync_factor - age;
+ idev->desync_factor - age;
if (max_prefered < 0)
max_prefered = 0;
* lo device down, release this obsolete dst and
* reallocate a new router for ifa.
*/
- if (sp_ifa->rt->dst.obsolete > 0) {
+ if (!atomic_read(&sp_ifa->rt->rt6i_ref)) {
ip6_rt_put(sp_ifa->rt);
sp_ifa->rt = NULL;
} else {
if (!how)
idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
- if (how && del_timer(&idev->regen_timer))
- in6_dev_put(idev);
-
/* Step 3: clear tempaddr list */
while (!list_empty(&idev->tempaddr_list)) {
ifa = list_first_entry(&idev->tempaddr_list,
static inline int compute_score(struct sock *sk, struct net *net,
const unsigned short hnum,
const struct in6_addr *daddr,
- const int dif)
+ const int dif, bool exact_dif)
{
int score = -1;
return -1;
score++;
}
- if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if || exact_dif) {
if (sk->sk_bound_dev_if != dif)
return -1;
score++;
unsigned int hash = inet_lhashfn(net, hnum);
struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
int score, hiscore = 0, matches = 0, reuseport = 0;
+ bool exact_dif = inet6_exact_dif_match(net, skb);
struct sock *sk, *result = NULL;
u32 phash = 0;
sk_for_each(sk, &ilb->head) {
- score = compute_score(sk, net, hnum, daddr, dif);
+ score = compute_score(sk, net, hnum, daddr, dif, exact_dif);
if (score > hiscore) {
reuseport = sk->sk_reuseport;
if (reuseport) {
int inet6_hash(struct sock *sk)
{
+ int err = 0;
+
if (sk->sk_state != TCP_CLOSE) {
local_bh_disable();
- __inet_hash(sk, NULL, ipv6_rcv_saddr_equal);
+ err = __inet_hash(sk, NULL, ipv6_rcv_saddr_equal);
local_bh_enable();
}
- return 0;
+ return err;
}
EXPORT_SYMBOL_GPL(inet6_hash);
skb_gro_postpull_rcsum(skb, iph, nlen);
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
hash = HASH(&any, local);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ ipv6_addr_any(&t->parms.raddr) &&
(t->dev->flags & IFF_UP))
return t;
}
hash = HASH(remote, &any);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(remote, &t->parms.raddr) &&
+ ipv6_addr_any(&t->parms.laddr) &&
(t->dev->flags & IFF_UP))
return t;
}
if (err)
return err;
+ skb->protocol = htons(ETH_P_IPV6);
skb_push(skb, sizeof(struct ipv6hdr));
skb_reset_network_header(skb);
ipv6h = ipv6_hdr(skb);
static bool setsockopt_needs_rtnl(int optname)
{
switch (optname) {
+ case IPV6_ADDRFORM:
case IPV6_ADD_MEMBERSHIP:
case IPV6_DROP_MEMBERSHIP:
case IPV6_JOIN_ANYCAST:
}
fl6_free_socklist(sk);
- ipv6_sock_mc_close(sk);
+ __ipv6_sock_mc_close(sk);
/*
* Sock is moving from IPv6 to IPv4 (sk_prot), so
return idev;
}
-void ipv6_sock_mc_close(struct sock *sk)
+void __ipv6_sock_mc_close(struct sock *sk)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst;
struct net *net = sock_net(sk);
- if (!rcu_access_pointer(np->ipv6_mc_list))
- return;
+ ASSERT_RTNL();
- rtnl_lock();
while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) {
struct net_device *dev;
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
-
}
+}
+
+void ipv6_sock_mc_close(struct sock *sk)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+
+ if (!rcu_access_pointer(np->ipv6_mc_list))
+ return;
+ rtnl_lock();
+ __ipv6_sock_mc_close(sk);
rtnl_unlock();
}
.init = ping_init_sock,
.close = ping_close,
.connect = ip6_datagram_connect_v6_only,
- .disconnect = udp_disconnect,
+ .disconnect = __udp_disconnect,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.sendmsg = ping_v6_sendmsg,
.close = rawv6_close,
.destroy = raw6_destroy,
.connect = ip6_datagram_connect_v6_only,
- .disconnect = udp_disconnect,
+ .disconnect = __udp_disconnect,
.ioctl = rawv6_ioctl,
.init = rawv6_init_sk,
.setsockopt = rawv6_setsockopt,
skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
memmove(head->head + sizeof(struct frag_hdr), head->head,
(head->data - head->head) - sizeof(struct frag_hdr));
- head->mac_header += sizeof(struct frag_hdr);
+ if (skb_mac_header_was_set(head))
+ head->mac_header += sizeof(struct frag_hdr);
head->network_header += sizeof(struct frag_hdr);
skb_reset_transport_header(head);
#ifdef CONFIG_IPV6_ROUTE_INFO
static struct rt6_info *rt6_add_route_info(struct net *net,
const struct in6_addr *prefix, int prefixlen,
- const struct in6_addr *gwaddr, int ifindex,
+ const struct in6_addr *gwaddr,
+ struct net_device *dev,
unsigned int pref);
static struct rt6_info *rt6_get_route_info(struct net *net,
const struct in6_addr *prefix, int prefixlen,
- const struct in6_addr *gwaddr, int ifindex);
+ const struct in6_addr *gwaddr,
+ struct net_device *dev);
#endif
struct uncached_list {
struct net_device *dev = rt->dst.dev;
if (dev && !netif_carrier_ok(dev) &&
- idev->cnf.ignore_routes_with_linkdown)
+ idev->cnf.ignore_routes_with_linkdown &&
+ !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
goto out;
if (rt6_check_expired(rt))
rt = rt6_get_dflt_router(gwaddr, dev);
else
rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
- gwaddr, dev->ifindex);
+ gwaddr, dev);
if (rt && !lifetime) {
ip6_del_rt(rt);
}
if (!rt && lifetime)
- rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
- pref);
+ rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
+ dev, pref);
else if (rt)
rt->rt6i_flags = RTF_ROUTEINFO |
(rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
int strict = 0;
strict |= flags & RT6_LOOKUP_F_IFACE;
+ strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
if (net->ipv6.devconf_all->forwarding == 0)
strict |= RT6_LOOKUP_F_REACHABLE;
};
struct fib6_table *table;
struct rt6_info *rt;
- int flags = RT6_LOOKUP_F_IFACE;
+ int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE;
table = fib6_get_table(net, cfg->fc_table);
if (!table)
#ifdef CONFIG_IPV6_ROUTE_INFO
static struct rt6_info *rt6_get_route_info(struct net *net,
const struct in6_addr *prefix, int prefixlen,
- const struct in6_addr *gwaddr, int ifindex)
+ const struct in6_addr *gwaddr,
+ struct net_device *dev)
{
+ u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
+ int ifindex = dev->ifindex;
struct fib6_node *fn;
struct rt6_info *rt = NULL;
struct fib6_table *table;
- table = fib6_get_table(net, RT6_TABLE_INFO);
+ table = fib6_get_table(net, tb_id);
if (!table)
return NULL;
static struct rt6_info *rt6_add_route_info(struct net *net,
const struct in6_addr *prefix, int prefixlen,
- const struct in6_addr *gwaddr, int ifindex,
+ const struct in6_addr *gwaddr,
+ struct net_device *dev,
unsigned int pref)
{
struct fib6_config cfg = {
.fc_metric = IP6_RT_PRIO_USER,
- .fc_ifindex = ifindex,
+ .fc_ifindex = dev->ifindex,
.fc_dst_len = prefixlen,
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
RTF_UP | RTF_PREF(pref),
.fc_nlinfo.nl_net = net,
};
- cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
+ cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
cfg.fc_dst = *prefix;
cfg.fc_gateway = *gwaddr;
ip6_route_add(&cfg);
- return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
+ return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
}
#endif
struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
{
+ u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
struct rt6_info *rt;
struct fib6_table *table;
- table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
+ table = fib6_get_table(dev_net(dev), tb_id);
if (!table)
return NULL;
cfg.fc_gateway = *gwaddr;
- ip6_route_add(&cfg);
+ if (!ip6_route_add(&cfg)) {
+ struct fib6_table *table;
+
+ table = fib6_get_table(dev_net(dev), cfg.fc_table);
+ if (table)
+ table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
+ }
return rt6_get_dflt_router(gwaddr, dev);
}
-void rt6_purge_dflt_routers(struct net *net)
+static void __rt6_purge_dflt_routers(struct fib6_table *table)
{
struct rt6_info *rt;
- struct fib6_table *table;
-
- /* NOTE: Keep consistent with rt6_get_dflt_router */
- table = fib6_get_table(net, RT6_TABLE_DFLT);
- if (!table)
- return;
restart:
read_lock_bh(&table->tb6_lock);
}
}
read_unlock_bh(&table->tb6_lock);
+
+ table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
+}
+
+void rt6_purge_dflt_routers(struct net *net)
+{
+ struct fib6_table *table;
+ struct hlist_head *head;
+ unsigned int h;
+
+ rcu_read_lock();
+
+ for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
+ head = &net->ipv6.fib_table_hash[h];
+ hlist_for_each_entry_rcu(table, head, tb6_hlist) {
+ if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
+ __rt6_purge_dflt_routers(table);
+ }
+ }
+
+ rcu_read_unlock();
}
static void rtmsg_to_fib6_config(struct net *net,
if (is_udp4) {
if (inet->cmsg_flags)
- ip_cmsg_recv(msg, skb);
+ ip_cmsg_recv_offset(msg, skb,
+ sizeof(struct udphdr), off);
} else {
if (np->rxopt.all)
ip6_datagram_recv_specific_ctl(sk, msg, skb);
if (sock_flag(sk, SOCK_ZAPPED))
return 0;
- return udp_disconnect(sk, flags);
+ return __udp_disconnect(sk, flags);
}
static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
if (sock_flag(sk, SOCK_ZAPPED))
return 0;
- return udp_disconnect(sk, flags);
+ return __udp_disconnect(sk, flags);
}
static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
#include "key.h"
#include "aes_ccm.h"
-void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic,
- size_t mic_len)
+int ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len)
{
struct scatterlist sg[3];
+ struct aead_request *aead_req;
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ u8 *__aad;
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *) aead_req_data;
+ aead_req = kzalloc(reqsize + CCM_AAD_LEN, GFP_ATOMIC);
+ if (!aead_req)
+ return -ENOMEM;
- memset(aead_req, 0, sizeof(aead_req_data));
+ __aad = (u8 *)aead_req + reqsize;
+ memcpy(__aad, aad, CCM_AAD_LEN);
sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
sg_set_buf(&sg[1], data, data_len);
sg_set_buf(&sg[2], mic, mic_len);
aead_request_set_ad(aead_req, sg[0].length);
crypto_aead_encrypt(aead_req);
+ kzfree(aead_req);
+
+ return 0;
}
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
size_t mic_len)
{
struct scatterlist sg[3];
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *) aead_req_data;
+ struct aead_request *aead_req;
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ u8 *__aad;
+ int err;
if (data_len == 0)
return -EINVAL;
- memset(aead_req, 0, sizeof(aead_req_data));
+ aead_req = kzalloc(reqsize + CCM_AAD_LEN, GFP_ATOMIC);
+ if (!aead_req)
+ return -ENOMEM;
+
+ __aad = (u8 *)aead_req + reqsize;
+ memcpy(__aad, aad, CCM_AAD_LEN);
sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
sg_set_buf(&sg[1], data, data_len);
sg_set_buf(&sg[2], mic, mic_len);
aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0);
aead_request_set_ad(aead_req, sg[0].length);
- return crypto_aead_decrypt(aead_req);
+ err = crypto_aead_decrypt(aead_req);
+ kzfree(aead_req);
+
+ return err;
}
struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
#include <linux/crypto.h>
+#define CCM_AAD_LEN 32
+
struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
size_t key_len,
size_t mic_len);
-void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic,
- size_t mic_len);
+int ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len);
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
u8 *data, size_t data_len, u8 *mic,
size_t mic_len);
#include "key.h"
#include "aes_gcm.h"
-void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic)
+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic)
{
struct scatterlist sg[3];
+ struct aead_request *aead_req;
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ u8 *__aad;
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *)aead_req_data;
+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
+ if (!aead_req)
+ return -ENOMEM;
- memset(aead_req, 0, sizeof(aead_req_data));
+ __aad = (u8 *)aead_req + reqsize;
+ memcpy(__aad, aad, GCM_AAD_LEN);
sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
sg_set_buf(&sg[1], data, data_len);
sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
aead_request_set_ad(aead_req, sg[0].length);
crypto_aead_encrypt(aead_req);
+ kzfree(aead_req);
+ return 0;
}
int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
u8 *data, size_t data_len, u8 *mic)
{
struct scatterlist sg[3];
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *)aead_req_data;
+ struct aead_request *aead_req;
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ u8 *__aad;
+ int err;
if (data_len == 0)
return -EINVAL;
- memset(aead_req, 0, sizeof(aead_req_data));
+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
+ if (!aead_req)
+ return -ENOMEM;
+
+ __aad = (u8 *)aead_req + reqsize;
+ memcpy(__aad, aad, GCM_AAD_LEN);
sg_init_table(sg, 3);
- sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
sg_set_buf(&sg[1], data, data_len);
sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
data_len + IEEE80211_GCMP_MIC_LEN, j_0);
aead_request_set_ad(aead_req, sg[0].length);
- return crypto_aead_decrypt(aead_req);
+ err = crypto_aead_decrypt(aead_req);
+ kzfree(aead_req);
+
+ return err;
}
struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
#include <linux/crypto.h>
-void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic);
+#define GCM_AAD_LEN 32
+
+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic);
int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
u8 *data, size_t data_len, u8 *mic);
struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
#include "key.h"
#include "aes_gmac.h"
-#define GMAC_MIC_LEN 16
-#define GMAC_NONCE_LEN 12
-#define AAD_LEN 20
-
int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
const u8 *data, size_t data_len, u8 *mic)
{
struct scatterlist sg[4];
- char aead_req_data[sizeof(struct aead_request) +
- crypto_aead_reqsize(tfm)]
- __aligned(__alignof__(struct aead_request));
- struct aead_request *aead_req = (void *)aead_req_data;
- u8 zero[GMAC_MIC_LEN], iv[AES_BLOCK_SIZE];
+ u8 *zero, *__aad, iv[AES_BLOCK_SIZE];
+ struct aead_request *aead_req;
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
if (data_len < GMAC_MIC_LEN)
return -EINVAL;
- memset(aead_req, 0, sizeof(aead_req_data));
+ aead_req = kzalloc(reqsize + GMAC_MIC_LEN + GMAC_AAD_LEN, GFP_ATOMIC);
+ if (!aead_req)
+ return -ENOMEM;
+
+ zero = (u8 *)aead_req + reqsize;
+ __aad = zero + GMAC_MIC_LEN;
+ memcpy(__aad, aad, GMAC_AAD_LEN);
- memset(zero, 0, GMAC_MIC_LEN);
sg_init_table(sg, 4);
- sg_set_buf(&sg[0], aad, AAD_LEN);
+ sg_set_buf(&sg[0], __aad, GMAC_AAD_LEN);
sg_set_buf(&sg[1], data, data_len - GMAC_MIC_LEN);
sg_set_buf(&sg[2], zero, GMAC_MIC_LEN);
sg_set_buf(&sg[3], mic, GMAC_MIC_LEN);
aead_request_set_tfm(aead_req, tfm);
aead_request_set_crypt(aead_req, sg, sg, 0, iv);
- aead_request_set_ad(aead_req, AAD_LEN + data_len);
+ aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len);
crypto_aead_encrypt(aead_req);
+ kzfree(aead_req);
return 0;
}
#include <linux/crypto.h>
+#define GMAC_AAD_LEN 20
+#define GMAC_MIC_LEN 16
+#define GMAC_NONCE_LEN 12
+
struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
size_t key_len);
int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
break;
rcu_read_lock();
- sta = sta_info_get(sdata, mgmt->da);
+ sta = sta_info_get_bss(sdata, mgmt->da);
rcu_read_unlock();
if (!sta)
return -ENOLINK;
__le16 fc = hdr->frame_control;
struct sk_buff_head frame_list;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+ struct ethhdr ethhdr;
+ const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
if (unlikely(!ieee80211_is_data(fc)))
return RX_CONTINUE;
if (!(status->rx_flags & IEEE80211_RX_AMSDU))
return RX_CONTINUE;
- if (ieee80211_has_a4(hdr->frame_control) &&
- rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
- !rx->sdata->u.vlan.sta)
- return RX_DROP_UNUSABLE;
+ if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
+ switch (rx->sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ if (!rx->sdata->u.vlan.sta)
+ return RX_DROP_UNUSABLE;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!rx->sdata->u.mgd.use_4addr)
+ return RX_DROP_UNUSABLE;
+ break;
+ default:
+ return RX_DROP_UNUSABLE;
+ }
+ check_da = NULL;
+ check_sa = NULL;
+ } else switch (rx->sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ check_da = NULL;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!rx->sta ||
+ !test_sta_flag(rx->sta, WLAN_STA_TDLS_PEER))
+ check_sa = NULL;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ check_sa = NULL;
+ break;
+ default:
+ break;
+ }
- if (is_multicast_ether_addr(hdr->addr1) &&
- ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
- rx->sdata->u.vlan.sta) ||
- (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
- rx->sdata->u.mgd.use_4addr)))
+ if (is_multicast_ether_addr(hdr->addr1))
return RX_DROP_UNUSABLE;
skb->dev = dev;
__skb_queue_head_init(&frame_list);
+ if (ieee80211_data_to_8023_exthdr(skb, ðhdr,
+ rx->sdata->vif.addr,
+ rx->sdata->vif.type))
+ return RX_DROP_UNUSABLE;
+
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
- rx->local->hw.extra_tx_headroom, true);
+ rx->local->hw.extra_tx_headroom,
+ check_da, check_sa);
while (!skb_queue_empty(&frame_list)) {
rx->skb = __skb_dequeue(&frame_list);
u8 *pos;
u8 pn[6];
u64 pn64;
- u8 aad[2 * AES_BLOCK_SIZE];
+ u8 aad[CCM_AAD_LEN];
u8 b_0[AES_BLOCK_SIZE];
if (info->control.hw_key &&
pos += IEEE80211_CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, b_0, aad);
- ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
- skb_put(skb, mic_len), mic_len);
-
- return 0;
+ return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
+ skb_put(skb, mic_len), mic_len);
}
u8 *pos;
u8 pn[6];
u64 pn64;
- u8 aad[2 * AES_BLOCK_SIZE];
+ u8 aad[GCM_AAD_LEN];
u8 j_0[AES_BLOCK_SIZE];
if (info->control.hw_key &&
pos += IEEE80211_GCMP_HDR_LEN;
gcmp_special_blocks(skb, pn, j_0, aad);
- ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
- skb_put(skb, IEEE80211_GCMP_MIC_LEN));
-
- return 0;
+ return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
+ skb_put(skb, IEEE80211_GCMP_MIC_LEN));
}
ieee80211_tx_result
struct ieee80211_key *key = tx->key;
struct ieee80211_mmie_16 *mmie;
struct ieee80211_hdr *hdr;
- u8 aad[20];
+ u8 aad[GMAC_AAD_LEN];
u64 pn64;
- u8 nonce[12];
+ u8 nonce[GMAC_NONCE_LEN];
if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
return TX_DROP;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_key *key = rx->key;
struct ieee80211_mmie_16 *mmie;
- u8 aad[20], mic[16], ipn[6], nonce[12];
+ u8 aad[GMAC_AAD_LEN], mic[GMAC_MIC_LEN], ipn[6], nonce[GMAC_NONCE_LEN];
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_is_mgmt(hdr->frame_control))
ncsi_dev_state_config_gls,
ncsi_dev_state_config_done,
ncsi_dev_state_suspend_select = 0x0401,
+ ncsi_dev_state_suspend_gls,
ncsi_dev_state_suspend_dcnt,
ncsi_dev_state_suspend_dc,
ncsi_dev_state_suspend_deselect,
#endif
unsigned int package_num; /* Number of packages */
struct list_head packages; /* List of packages */
+ struct ncsi_channel *hot_channel; /* Channel was ever active */
struct ncsi_request requests[256]; /* Request table */
unsigned int request_id; /* Last used request ID */
#define NCSI_REQ_START_IDX 1
return -ENODEV;
/* If the channel is active one, we need reconfigure it */
+ spin_lock_irqsave(&nc->lock, flags);
ncm = &nc->modes[NCSI_MODE_LINK];
hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
ncm->data[3] = ntohl(hncdsc->status);
if (!list_empty(&nc->link) ||
- nc->state != NCSI_CHANNEL_ACTIVE ||
- (ncm->data[3] & 0x1))
+ nc->state != NCSI_CHANNEL_ACTIVE) {
+ spin_unlock_irqrestore(&nc->lock, flags);
return 0;
+ }
- if (ndp->flags & NCSI_DEV_HWA)
+ spin_unlock_irqrestore(&nc->lock, flags);
+ if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1))
ndp->flags |= NCSI_DEV_RESHUFFLE;
/* If this channel is the active one and the link doesn't
* work, we have to choose another channel to be active one.
* The logic here is exactly similar to what we do when link
* is down on the active channel.
+ *
+ * On the other hand, we need configure it when host driver
+ * state on the active channel becomes ready.
*/
ncsi_stop_channel_monitor(nc);
+
+ spin_lock_irqsave(&nc->lock, flags);
+ nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE :
+ NCSI_CHANNEL_ACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags);
nd->state = ncsi_dev_state_suspend_select;
/* Fall through */
case ncsi_dev_state_suspend_select:
- case ncsi_dev_state_suspend_dcnt:
- case ncsi_dev_state_suspend_dc:
- case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1;
- np = ndp->active_package;
- nc = ndp->active_channel;
+ nca.type = NCSI_PKT_CMD_SP;
nca.package = np->id;
- if (nd->state == ncsi_dev_state_suspend_select) {
- nca.type = NCSI_PKT_CMD_SP;
- nca.channel = NCSI_RESERVED_CHANNEL;
- if (ndp->flags & NCSI_DEV_HWA)
- nca.bytes[0] = 0;
- else
- nca.bytes[0] = 1;
+ nca.channel = NCSI_RESERVED_CHANNEL;
+ if (ndp->flags & NCSI_DEV_HWA)
+ nca.bytes[0] = 0;
+ else
+ nca.bytes[0] = 1;
+
+ /* To retrieve the last link states of channels in current
+ * package when current active channel needs fail over to
+ * another one. It means we will possibly select another
+ * channel as next active one. The link states of channels
+ * are most important factor of the selection. So we need
+ * accurate link states. Unfortunately, the link states on
+ * inactive channels can't be updated with LSC AEN in time.
+ */
+ if (ndp->flags & NCSI_DEV_RESHUFFLE)
+ nd->state = ncsi_dev_state_suspend_gls;
+ else
nd->state = ncsi_dev_state_suspend_dcnt;
- } else if (nd->state == ncsi_dev_state_suspend_dcnt) {
- nca.type = NCSI_PKT_CMD_DCNT;
- nca.channel = nc->id;
- nd->state = ncsi_dev_state_suspend_dc;
- } else if (nd->state == ncsi_dev_state_suspend_dc) {
- nca.type = NCSI_PKT_CMD_DC;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ break;
+ case ncsi_dev_state_suspend_gls:
+ ndp->pending_req_num = np->channel_num;
+
+ nca.type = NCSI_PKT_CMD_GLS;
+ nca.package = np->id;
+
+ nd->state = ncsi_dev_state_suspend_dcnt;
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
nca.channel = nc->id;
- nca.bytes[0] = 1;
- nd->state = ncsi_dev_state_suspend_deselect;
- } else if (nd->state == ncsi_dev_state_suspend_deselect) {
- nca.type = NCSI_PKT_CMD_DP;
- nca.channel = NCSI_RESERVED_CHANNEL;
- nd->state = ncsi_dev_state_suspend_done;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
}
+ break;
+ case ncsi_dev_state_suspend_dcnt:
+ ndp->pending_req_num = 1;
+
+ nca.type = NCSI_PKT_CMD_DCNT;
+ nca.package = np->id;
+ nca.channel = nc->id;
+
+ nd->state = ncsi_dev_state_suspend_dc;
ret = ncsi_xmit_cmd(&nca);
- if (ret) {
- nd->state = ncsi_dev_state_functional;
- return;
- }
+ if (ret)
+ goto error;
+
+ break;
+ case ncsi_dev_state_suspend_dc:
+ ndp->pending_req_num = 1;
+
+ nca.type = NCSI_PKT_CMD_DC;
+ nca.package = np->id;
+ nca.channel = nc->id;
+ nca.bytes[0] = 1;
+
+ nd->state = ncsi_dev_state_suspend_deselect;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ break;
+ case ncsi_dev_state_suspend_deselect:
+ ndp->pending_req_num = 1;
+
+ nca.type = NCSI_PKT_CMD_DP;
+ nca.package = np->id;
+ nca.channel = NCSI_RESERVED_CHANNEL;
+
+ nd->state = ncsi_dev_state_suspend_done;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
break;
case ncsi_dev_state_suspend_done:
netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
nd->state);
}
+
+ return;
+error:
+ nd->state = ncsi_dev_state_functional;
}
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
struct net_device *dev = nd->dev;
struct ncsi_package *np = ndp->active_package;
struct ncsi_channel *nc = ndp->active_channel;
+ struct ncsi_channel *hot_nc = NULL;
struct ncsi_cmd_arg nca;
unsigned char index;
unsigned long flags;
break;
case ncsi_dev_state_config_done:
spin_lock_irqsave(&nc->lock, flags);
- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1)
+ if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
+ hot_nc = nc;
nc->state = NCSI_CHANNEL_ACTIVE;
- else
+ } else {
+ hot_nc = NULL;
nc->state = NCSI_CHANNEL_INACTIVE;
+ }
spin_unlock_irqrestore(&nc->lock, flags);
+ /* Update the hot channel */
+ spin_lock_irqsave(&ndp->lock, flags);
+ ndp->hot_channel = hot_nc;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
ncsi_start_channel_monitor(nc);
ncsi_process_next_channel(ndp);
break;
static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
{
struct ncsi_package *np;
- struct ncsi_channel *nc, *found;
+ struct ncsi_channel *nc, *found, *hot_nc;
struct ncsi_channel_mode *ncm;
unsigned long flags;
+ spin_lock_irqsave(&ndp->lock, flags);
+ hot_nc = ndp->hot_channel;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
/* The search is done once an inactive channel with up
* link is found.
*/
if (!found)
found = nc;
+ if (nc == hot_nc)
+ found = nc;
+
ncm = &nc->modes[NCSI_MODE_LINK];
if (ncm->data[2] & 0x1) {
spin_unlock_irqrestore(&nc->lock, flags);
if (ret == 0)
ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
- int err;
-
- RCU_INIT_POINTER(state->hook_entries, entry);
- err = nf_queue(skb, state, verdict >> NF_VERDICT_QBITS);
- if (err < 0) {
- if (err == -ESRCH &&
- (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
- goto next_hook;
- kfree_skb(skb);
- }
+ ret = nf_queue(skb, state, &entry, verdict);
+ if (ret == 1 && entry)
+ goto next_hook;
}
return ret;
}
return;
ratio = scanned ? expired_count * 100 / scanned : 0;
- if (ratio >= 90)
+ if (ratio >= 90 || expired_count == GC_MAX_EVICTS)
next_run = 0;
gc_work->last_bucket = i;
/* nf_queue.c */
int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
- unsigned int queuenum);
+ struct nf_hook_entry **entryp, unsigned int verdict);
void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry);
int __init netfilter_queue_init(void);
rcu_read_unlock();
}
-/*
- * Any packet that leaves via this function must come back
- * through nf_reinject().
- */
-int nf_queue(struct sk_buff *skb,
- struct nf_hook_state *state,
- unsigned int queuenum)
+static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
+ unsigned int queuenum)
{
int status = -ENOENT;
struct nf_queue_entry *entry = NULL;
return status;
}
+/* Packets leaving via this function must come back through nf_reinject(). */
+int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
+ struct nf_hook_entry **entryp, unsigned int verdict)
+{
+ struct nf_hook_entry *entry = *entryp;
+ int ret;
+
+ RCU_INIT_POINTER(state->hook_entries, entry);
+ ret = __nf_queue(skb, state, verdict >> NF_VERDICT_QBITS);
+ if (ret < 0) {
+ if (ret == -ESRCH &&
+ (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) {
+ *entryp = rcu_dereference(entry->next);
+ return 1;
+ }
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
{
struct nf_hook_entry *hook_entry;
entry->state.thresh = INT_MIN;
if (verdict == NF_ACCEPT) {
- next_hook:
- verdict = nf_iterate(skb, &entry->state, &hook_entry);
+ hook_entry = rcu_dereference(hook_entry->next);
+ if (hook_entry)
+next_hook:
+ verdict = nf_iterate(skb, &entry->state, &hook_entry);
}
switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
case NF_STOP:
+okfn:
local_bh_disable();
entry->state.okfn(entry->state.net, entry->state.sk, skb);
local_bh_enable();
break;
case NF_QUEUE:
- RCU_INIT_POINTER(entry->state.hook_entries, hook_entry);
- err = nf_queue(skb, &entry->state,
- verdict >> NF_VERDICT_QBITS);
- if (err < 0) {
- if (err == -ESRCH &&
- (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
+ err = nf_queue(skb, &entry->state, &hook_entry, verdict);
+ if (err == 1) {
+ if (hook_entry)
goto next_hook;
- kfree_skb(skb);
+ goto okfn;
}
break;
case NF_STOLEN:
*/
unsigned int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest)
{
- int val;
+ u32 val;
val = ntohl(nla_get_be32(attr));
if (val > max)
if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
if (!(set->flags & NFT_SET_TIMEOUT))
return -EINVAL;
- timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
+ timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
+ tb[NFTA_DYNSET_TIMEOUT])));
}
priv->sreg_key = nft_parse_register(tb[NFTA_DYNSET_SREG_KEY]);
goto nla_put_failure;
if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
goto nla_put_failure;
- if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout),
+ if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT,
+ cpu_to_be64(jiffies_to_msecs(priv->timeout)),
NFTA_DYNSET_PAD))
goto nla_put_failure;
if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
const struct nlattr * const tb[])
{
struct nft_exthdr *priv = nft_expr_priv(expr);
- u32 offset, len, err;
+ u32 offset, len;
+ int err;
if (tb[NFTA_EXTHDR_DREG] == NULL ||
tb[NFTA_EXTHDR_TYPE] == NULL ||
[NFTA_HASH_LEN] = { .type = NLA_U32 },
[NFTA_HASH_MODULUS] = { .type = NLA_U32 },
[NFTA_HASH_SEED] = { .type = NLA_U32 },
+ [NFTA_HASH_OFFSET] = { .type = NLA_U32 },
};
static int nft_hash_init(const struct nft_ctx *ctx,
const struct nft_pktinfo *pkt)
{
const struct nft_range_expr *priv = nft_expr_priv(expr);
- bool mismatch;
int d1, d2;
d1 = memcmp(®s->data[priv->sreg], &priv->data_from, priv->len);
d2 = memcmp(®s->data[priv->sreg], &priv->data_to, priv->len);
switch (priv->op) {
case NFT_RANGE_EQ:
- mismatch = (d1 < 0 || d2 > 0);
+ if (d1 < 0 || d2 > 0)
+ regs->verdict.code = NFT_BREAK;
break;
case NFT_RANGE_NEQ:
- mismatch = (d1 >= 0 && d2 <= 0);
+ if (d1 >= 0 && d2 <= 0)
+ regs->verdict.code = NFT_BREAK;
break;
}
-
- if (mismatch)
- regs->verdict.code = NFT_BREAK;
}
static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = {
struct nft_range_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc_from, desc_to;
int err;
+ u32 op;
err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from),
&desc_from, tb[NFTA_RANGE_FROM_DATA]);
if (err < 0)
goto err2;
- priv->op = ntohl(nla_get_be32(tb[NFTA_RANGE_OP]));
+ err = nft_parse_u32_check(tb[NFTA_RANGE_OP], U8_MAX, &op);
+ if (err < 0)
+ goto err2;
+
+ switch (op) {
+ case NFT_RANGE_EQ:
+ case NFT_RANGE_NEQ:
+ break;
+ default:
+ err = -EINVAL;
+ goto err2;
+ }
+
+ priv->op = op;
priv->len = desc_from.len;
return 0;
err2:
if (!num_hooks)
return ERR_PTR(-EINVAL);
- ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
+ ops = kcalloc(num_hooks, sizeof(*ops), GFP_KERNEL);
if (ops == NULL)
return ERR_PTR(-ENOMEM);
li.u.ulog.copy_len = info->len;
li.u.ulog.group = info->group;
li.u.ulog.qthreshold = info->threshold;
+ li.u.ulog.flags = 0;
if (info->flags & XT_NFLOG_F_COPY_LEN)
li.u.ulog.flags |= NF_LOG_F_COPY_LEN;
CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32 ie.
*/
#define MAX_CPJ_v1 (0xFFFFFFFF / (HZ*60*60*24))
-#define MAX_CPJ (0xFFFFFFFFFFFFFFFF / (HZ*60*60*24))
+#define MAX_CPJ (0xFFFFFFFFFFFFFFFFULL / (HZ*60*60*24))
/* Repeated shift and or gives us all 1s, final shift and add 1 gives
* us the power of 2 below the theoretical max, so GCC simply does a
return div64_u64(user * HZ * CREDITS_PER_JIFFY_v1,
XT_HASHLIMIT_SCALE);
} else {
- if (user > 0xFFFFFFFFFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+ if (user > 0xFFFFFFFFFFFFFFFFULL / (HZ*CREDITS_PER_JIFFY))
return div64_u64(user, XT_HASHLIMIT_SCALE_v2)
* HZ * CREDITS_PER_JIFFY;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fan Du <fan.du@windriver.com>");
MODULE_DESCRIPTION("Xtables: IPv4/6 IPsec-IPComp SPI match");
+MODULE_ALIAS("ipt_ipcomp");
+MODULE_ALIAS("ip6t_ipcomp");
/* Returns 1 if the spi is matched by the range, 0 otherwise */
static inline bool
static int packet_direct_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- netdev_features_t features;
+ struct sk_buff *orig_skb = skb;
struct netdev_queue *txq;
int ret = NETDEV_TX_BUSY;
!netif_carrier_ok(dev)))
goto drop;
- features = netif_skb_features(skb);
- if (skb_needs_linearize(skb, features) &&
- __skb_linearize(skb))
+ skb = validate_xmit_skb_list(skb, dev);
+ if (skb != orig_skb)
goto drop;
txq = skb_get_tx_queue(dev, skb);
return ret;
drop:
atomic_long_inc(&dev->tx_dropped);
- kfree_skb(skb);
+ kfree_skb_list(skb);
return NET_XMIT_DROP;
}
rds_tcp-y := tcp.o tcp_connect.o tcp_listen.o tcp_recv.o \
tcp_send.o tcp_stats.o
-ccflags-$(CONFIG_RDS_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_RDS_DEBUG) := -DRDS_DEBUG
#define KERNEL_HAS_ATOMIC64
#endif
-#ifdef DEBUG
+#ifdef RDS_DEBUG
#define rdsdebug(fmt, args...) pr_debug("%s(): " fmt, __func__ , ##args)
#else
/* sigh, pr_debug() causes unused variable warnings */
goto error;
trace_rxrpc_call(call, rxrpc_call_connected, atomic_read(&call->usage),
- here, ERR_PTR(ret));
+ here, NULL);
spin_lock_bh(&call->conn->params.peer->lock);
hlist_add_head(&call->error_link,
fl6->fl6_dport = htons(7001);
fl6->fl6_sport = htons(7000);
dst = ip6_route_output(&init_net, NULL, fl6);
- if (IS_ERR(dst)) {
- _leave(" [route err %ld]", PTR_ERR(dst));
+ if (dst->error) {
+ _leave(" [route err %d]", dst->error);
return;
}
break;
if (tb[1] == NULL)
return NULL;
- if (nla_parse(tb2, TCA_ACT_MAX, nla_data(tb[1]),
- nla_len(tb[1]), NULL) < 0)
+ if (nla_parse_nested(tb2, TCA_ACT_MAX, tb[1], NULL) < 0)
return NULL;
kind = tb2[TCA_ACT_KIND];
static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets,
u64 lastuse)
{
- tcf_lastuse_update(&a->tcfa_tm);
+ struct tcf_mirred *m = to_mirred(a);
+ struct tcf_t *tm = &m->tcf_tm;
+
_bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets);
+ tm->lastuse = lastuse;
}
static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
if (err == 0) {
struct tcf_proto *next = rtnl_dereference(tp->next);
- tfilter_notify(net, skb, n, tp, fh,
+ tfilter_notify(net, skb, n, tp,
+ t->tcm_handle,
RTM_DELTFILTER, false);
if (tcf_destroy(tp, false))
RCU_INIT_POINTER(*back, next);
__u8 has_data = 0;
int gso = 0;
int pktcount = 0;
+ int auth_len = 0;
struct dst_entry *dst;
unsigned char *auth = NULL; /* pointer to auth in skb data */
list_for_each_entry(chunk, &packet->chunk_list, list) {
int padded = SCTP_PAD4(chunk->skb->len);
- if (pkt_size + padded > tp->pathmtu)
+ if (chunk == packet->auth)
+ auth_len = padded;
+ else if (auth_len + padded + packet->overhead >
+ tp->pathmtu)
+ goto nomem;
+ else if (pkt_size + padded > tp->pathmtu)
break;
pkt_size += padded;
}
return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
commands);
+ /* Report violation if chunk len overflows */
+ ch_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
+ if (ch_end > skb_tail_pointer(skb))
+ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
+ commands);
+
/* Now that we know we at least have a chunk header,
* do things that are type appropriate.
*/
}
}
- /* Report violation if chunk len overflows */
- ch_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
- if (ch_end > skb_tail_pointer(skb))
- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
- commands);
-
ch = (sctp_chunkhdr_t *) ch_end;
} while (ch_end < skb_tail_pointer(skb));
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
int __user *optlen)
{
- if (len <= 0)
+ if (len == 0)
return -EINVAL;
if (len > sizeof(struct sctp_event_subscribe))
len = sizeof(struct sctp_event_subscribe);
if (get_user(len, optlen))
return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
lock_sock(sk);
switch (optname) {
{
struct rpc_cred *cred = task->tk_rqstp->rq_cred;
struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
- __be32 seq;
+ __be32 *seq = NULL;
struct kvec iov;
struct xdr_buf verf_buf;
struct xdr_netobj mic;
goto out_bad;
if (flav != RPC_AUTH_GSS)
goto out_bad;
- seq = htonl(task->tk_rqstp->rq_seqno);
- iov.iov_base = &seq;
- iov.iov_len = sizeof(seq);
+ seq = kmalloc(4, GFP_NOFS);
+ if (!seq)
+ goto out_bad;
+ *seq = htonl(task->tk_rqstp->rq_seqno);
+ iov.iov_base = seq;
+ iov.iov_len = 4;
xdr_buf_from_iov(&iov, &verf_buf);
mic.data = (u8 *)p;
mic.len = len;
gss_put_ctx(ctx);
dprintk("RPC: %5u %s: gss_verify_mic succeeded.\n",
task->tk_pid, __func__);
+ kfree(seq);
return p + XDR_QUADLEN(len);
out_bad:
gss_put_ctx(ctx);
dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__,
PTR_ERR(ret));
+ kfree(seq);
return ret;
}
unsigned int usage, struct xdr_netobj *cksumout)
{
struct scatterlist sg[1];
- int err;
- u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
+ int err = -1;
+ u8 *checksumdata;
u8 rc4salt[4];
struct crypto_ahash *md5;
struct crypto_ahash *hmac_md5;
return GSS_S_FAILURE;
}
+ checksumdata = kmalloc(GSS_KRB5_MAX_CKSUM_LEN, GFP_NOFS);
+ if (!checksumdata)
+ return GSS_S_FAILURE;
+
md5 = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(md5))
- return GSS_S_FAILURE;
+ goto out_free_cksum;
hmac_md5 = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0,
CRYPTO_ALG_ASYNC);
- if (IS_ERR(hmac_md5)) {
- crypto_free_ahash(md5);
- return GSS_S_FAILURE;
- }
+ if (IS_ERR(hmac_md5))
+ goto out_free_md5;
req = ahash_request_alloc(md5, GFP_KERNEL);
- if (!req) {
- crypto_free_ahash(hmac_md5);
- crypto_free_ahash(md5);
- return GSS_S_FAILURE;
- }
+ if (!req)
+ goto out_free_hmac_md5;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
ahash_request_free(req);
req = ahash_request_alloc(hmac_md5, GFP_KERNEL);
- if (!req) {
- crypto_free_ahash(hmac_md5);
- crypto_free_ahash(md5);
- return GSS_S_FAILURE;
- }
+ if (!req)
+ goto out_free_hmac_md5;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
cksumout->len = kctx->gk5e->cksumlength;
out:
ahash_request_free(req);
- crypto_free_ahash(md5);
+out_free_hmac_md5:
crypto_free_ahash(hmac_md5);
+out_free_md5:
+ crypto_free_ahash(md5);
+out_free_cksum:
+ kfree(checksumdata);
return err ? GSS_S_FAILURE : 0;
}
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist sg[1];
- int err;
- u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
+ int err = -1;
+ u8 *checksumdata;
unsigned int checksumlen;
if (kctx->gk5e->ctype == CKSUMTYPE_HMAC_MD5_ARCFOUR)
return GSS_S_FAILURE;
}
+ checksumdata = kmalloc(GSS_KRB5_MAX_CKSUM_LEN, GFP_NOFS);
+ if (checksumdata == NULL)
+ return GSS_S_FAILURE;
+
tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
- return GSS_S_FAILURE;
+ goto out_free_cksum;
req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req) {
- crypto_free_ahash(tfm);
- return GSS_S_FAILURE;
- }
+ if (!req)
+ goto out_free_ahash;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
cksumout->len = kctx->gk5e->cksumlength;
out:
ahash_request_free(req);
+out_free_ahash:
crypto_free_ahash(tfm);
+out_free_cksum:
+ kfree(checksumdata);
return err ? GSS_S_FAILURE : 0;
}
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist sg[1];
- int err;
- u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
+ int err = -1;
+ u8 *checksumdata;
unsigned int checksumlen;
if (kctx->gk5e->keyed_cksum == 0) {
return GSS_S_FAILURE;
}
+ checksumdata = kmalloc(GSS_KRB5_MAX_CKSUM_LEN, GFP_NOFS);
+ if (!checksumdata)
+ return GSS_S_FAILURE;
+
tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
- return GSS_S_FAILURE;
+ goto out_free_cksum;
checksumlen = crypto_ahash_digestsize(tfm);
req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req) {
- crypto_free_ahash(tfm);
- return GSS_S_FAILURE;
- }
+ if (!req)
+ goto out_free_ahash;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
}
out:
ahash_request_free(req);
+out_free_ahash:
crypto_free_ahash(tfm);
+out_free_cksum:
+ kfree(checksumdata);
return err ? GSS_S_FAILURE : 0;
}
u32 ret;
struct scatterlist sg[1];
SKCIPHER_REQUEST_ON_STACK(req, cipher);
- u8 data[GSS_KRB5_MAX_BLOCKSIZE * 2];
+ u8 *data;
struct page **save_pages;
u32 len = buf->len - offset;
- if (len > ARRAY_SIZE(data)) {
+ if (len > GSS_KRB5_MAX_BLOCKSIZE * 2) {
WARN_ON(0);
return -ENOMEM;
}
+ data = kmalloc(GSS_KRB5_MAX_BLOCKSIZE * 2, GFP_NOFS);
+ if (!data)
+ return -ENOMEM;
/*
* For encryption, we want to read from the cleartext
ret = write_bytes_to_xdr_buf(buf, offset, data, len);
out:
+ kfree(data);
return ret;
}
static int
gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
{
- __be32 xdr_seq;
+ __be32 *xdr_seq;
u32 maj_stat;
struct xdr_buf verf_data;
struct xdr_netobj mic;
__be32 *p;
struct kvec iov;
+ int err = -1;
svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
- xdr_seq = htonl(seq);
+ xdr_seq = kmalloc(4, GFP_KERNEL);
+ if (!xdr_seq)
+ return -1;
+ *xdr_seq = htonl(seq);
- iov.iov_base = &xdr_seq;
- iov.iov_len = sizeof(xdr_seq);
+ iov.iov_base = xdr_seq;
+ iov.iov_len = 4;
xdr_buf_from_iov(&iov, &verf_data);
p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
mic.data = (u8 *)(p + 1);
maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
if (maj_stat != GSS_S_COMPLETE)
- return -1;
+ goto out;
*p++ = htonl(mic.len);
memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
p += XDR_QUADLEN(mic.len);
if (!xdr_ressize_check(rqstp, p))
- return -1;
- return 0;
+ goto out;
+ err = 0;
+out:
+ kfree(xdr_seq);
+ return err;
}
struct gss_domain {
return -EINVAL;
}
+ /* svc_rdma_sendto releases this page */
page = alloc_page(RPCRDMA_DEF_GFP);
if (!page)
return -ENOMEM;
-
rqst->rq_buffer = page_address(page);
+
+ rqst->rq_rbuffer = kmalloc(rqst->rq_rcvsize, RPCRDMA_DEF_GFP);
+ if (!rqst->rq_rbuffer) {
+ put_page(page);
+ return -ENOMEM;
+ }
return 0;
}
static void
xprt_rdma_bc_free(struct rpc_task *task)
{
- /* No-op: ctxt and page have already been freed. */
+ struct rpc_rqst *rqst = task->tk_rqstp;
+
+ kfree(rqst->rq_rbuffer);
}
static int
buf->len = PAGE_SIZE;
rqst->rq_buffer = buf->data;
+ rqst->rq_rbuffer = (char *)rqst->rq_buffer + rqst->rq_callsize;
return 0;
}
u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD;
int err;
+ if (!netif_is_bridge_port(dev))
+ return -EOPNOTSUPP;
+
err = switchdev_port_attr_get(dev, &attr);
if (err && err != -EOPNOTSUPP)
return err;
struct nlattr *afspec;
int err = 0;
+ if (!netif_is_bridge_port(dev))
+ return -EOPNOTSUPP;
+
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
IFLA_PROTINFO);
if (protinfo) {
{
struct nlattr *afspec;
+ if (!netif_is_bridge_port(dev))
+ return -EOPNOTSUPP;
+
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
IFLA_AF_SPEC);
if (afspec)
*
* RCU is locked, no other locks set
*/
-void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked)
+void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
+ struct tipc_msg *hdr)
{
struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
+ u16 acked = msg_bcast_ack(hdr);
struct sk_buff_head xmitq;
+ /* Ignore bc acks sent by peer before bcast synch point was received */
+ if (msg_bc_ack_invalid(hdr))
+ return;
+
__skb_queue_head_init(&xmitq);
tipc_bcast_lock(net);
__skb_queue_head_init(&xmitq);
tipc_bcast_lock(net);
- if (msg_type(hdr) == STATE_MSG) {
+ if (msg_type(hdr) != STATE_MSG) {
+ tipc_link_bc_init_rcv(l, hdr);
+ } else if (!msg_bc_ack_invalid(hdr)) {
tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), &xmitq);
rc = tipc_link_bc_sync_rcv(l, hdr, &xmitq);
- } else {
- tipc_link_bc_init_rcv(l, hdr);
}
tipc_bcast_unlock(net);
int tipc_bcast_get_mtu(struct net *net);
int tipc_bcast_xmit(struct net *net, struct sk_buff_head *list);
int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb);
-void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked);
+void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
+ struct tipc_msg *hdr);
int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr);
int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg);
msg_set_next_sent(hdr, l->snd_nxt);
msg_set_ack(hdr, l->rcv_nxt - 1);
msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
+ msg_set_bc_ack_invalid(hdr, !node_up);
msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
msg_set_link_tolerance(hdr, tolerance);
msg_set_linkprio(hdr, priority);
__skb_queue_head_init(&list);
if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list))
return;
+ msg_set_bc_ack_invalid(buf_msg(skb_peek(&list)), true);
tipc_link_xmit(l, &list, xmitq);
}
msg_set_bits(m, 5, 13, 0x1, s);
}
+static inline bool msg_bc_ack_invalid(struct tipc_msg *m)
+{
+ switch (msg_user(m)) {
+ case BCAST_PROTOCOL:
+ case NAME_DISTRIBUTOR:
+ case LINK_PROTOCOL:
+ return msg_bits(m, 5, 14, 0x1);
+ default:
+ return false;
+ }
+}
+
+static inline void msg_set_bc_ack_invalid(struct tipc_msg *m, bool invalid)
+{
+ msg_set_bits(m, 5, 14, 0x1, invalid);
+}
+
static inline char *msg_media_addr(struct tipc_msg *m)
{
return (char *)&m->hdr[TIPC_MEDIA_INFO_OFFSET];
pr_warn("Bulk publication failure\n");
return;
}
+ msg_set_bc_ack_invalid(buf_msg(skb), true);
item = (struct distr_item *)msg_data(buf_msg(skb));
}
if (unlikely(usr == LINK_PROTOCOL))
tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq);
else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack))
- tipc_bcast_ack_rcv(net, n->bc_entry.link, bc_ack);
+ tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr);
/* Receive packet directly if conditions permit */
tipc_node_read_lock(n);
rtnl_lock();
if (rdev->wiphy.registered) {
- if (!rdev->wiphy.wowlan_config)
+ if (!rdev->wiphy.wowlan_config) {
cfg80211_leave_all(rdev);
+ cfg80211_process_rdev_events(rdev);
+ }
if (rdev->ops->suspend)
ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
if (ret == 1) {
/* Driver refuse to configure wowlan */
cfg80211_leave_all(rdev);
+ cfg80211_process_rdev_events(rdev);
ret = rdev_suspend(rdev, NULL);
}
}
}
EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
-static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
- const u8 *addr, enum nl80211_iftype iftype)
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct {
return 0;
}
-
-int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
- enum nl80211_iftype iftype)
-{
- return __ieee80211_data_to_8023(skb, NULL, addr, iftype);
-}
-EXPORT_SYMBOL(ieee80211_data_to_8023);
+EXPORT_SYMBOL(ieee80211_data_to_8023_exthdr);
int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
enum nl80211_iftype iftype,
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype,
const unsigned int extra_headroom,
- bool has_80211_header)
+ const u8 *check_da, const u8 *check_sa)
{
unsigned int hlen = ALIGN(extra_headroom, 4);
struct sk_buff *frame = NULL;
u16 ethertype;
u8 *payload;
- int offset = 0, remaining, err;
+ int offset = 0, remaining;
struct ethhdr eth;
bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
bool reuse_skb = false;
bool last = false;
- if (has_80211_header) {
- err = __ieee80211_data_to_8023(skb, ð, addr, iftype);
- if (err)
- goto out;
- }
-
while (!last) {
unsigned int subframe_len;
int len;
goto purge;
offset += sizeof(struct ethhdr);
- /* reuse skb for the last subframe */
last = remaining <= subframe_len + padding;
+
+ /* FIXME: should we really accept multicast DA? */
+ if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
+ !ether_addr_equal(check_da, eth.h_dest)) ||
+ (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
+ offset += len + padding;
+ continue;
+ }
+
+ /* reuse skb for the last subframe */
if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
skb_pull(skb, offset);
frame = skb;
purge:
__skb_queue_purge(list);
- out:
dev_kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+#define KBUILD_MODNAME "foo"
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+#define KBUILD_MODNAME "foo"
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+#define KBUILD_MODNAME "foo"
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+#define KBUILD_MODNAME "foo"
#include <uapi/linux/if_ether.h>
#include <uapi/linux/in6.h>
#include <uapi/linux/ipv6.h>
#include "gcc-common.h"
-int plugin_is_GPL_compatible;
+__visible int plugin_is_GPL_compatible;
static struct plugin_info cyc_complexity_plugin_info = {
.version = "20160225",
#include "gcc-generate-gimple-pass.h"
-int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
{
const char * const plugin_name = plugin_info->base_name;
struct register_pass_info cyc_complexity_pass_info;
#endif
#define __unused __attribute__((__unused__))
+#define __visible __attribute__((visibility("default")))
#define DECL_NAME_POINTER(node) IDENTIFIER_POINTER(DECL_NAME(node))
#define DECL_NAME_LENGTH(node) IDENTIFIER_LENGTH(DECL_NAME(node))
#include "gcc-common.h"
-int plugin_is_GPL_compatible;
+__visible int plugin_is_GPL_compatible;
static GTY(()) tree latent_entropy_decl;
break;
}
if (rhs)
- *rhs = build_int_cstu(unsigned_intDI_type_node, random_const);
+ *rhs = build_int_cstu(long_unsigned_type_node, random_const);
return op;
}
enum tree_code op;
/* 1. create temporary copy of latent_entropy */
- temp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
+ temp = create_var(long_unsigned_type_node, "temp_latent_entropy");
/* 2. read... */
add_referenced_var(latent_entropy_decl);
gsi_insert_before(&gsi, call, GSI_NEW_STMT);
update_stmt(call);
- udi_frame_addr = fold_convert(unsigned_intDI_type_node, frame_addr);
+ udi_frame_addr = fold_convert(long_unsigned_type_node, frame_addr);
assign = gimple_build_assign(local_entropy, udi_frame_addr);
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
update_stmt(assign);
/* 3. create temporary copy of latent_entropy */
- tmp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
+ tmp = create_var(long_unsigned_type_node, "temp_latent_entropy");
/* 4. read the global entropy variable into local entropy */
add_referenced_var(latent_entropy_decl);
update_stmt(assign);
rand_cst = get_random_const();
- rand_const = build_int_cstu(unsigned_intDI_type_node, rand_cst);
+ rand_const = build_int_cstu(long_unsigned_type_node, rand_cst);
op = get_op(NULL);
assign = create_assign(op, local_entropy, local_entropy, rand_const);
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
}
/* 1. create the local entropy variable */
- local_entropy = create_var(unsigned_intDI_type_node, "local_entropy");
+ local_entropy = create_var(long_unsigned_type_node, "local_entropy");
/* 2. initialize the local entropy variable */
init_local_entropy(bb, local_entropy);
if (in_lto_p)
return;
- /* extern volatile u64 latent_entropy */
- gcc_assert(TYPE_PRECISION(long_long_unsigned_type_node) == 64);
- quals = TYPE_QUALS(long_long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
- type = build_qualified_type(long_long_unsigned_type_node, quals);
+ /* extern volatile unsigned long latent_entropy */
+ quals = TYPE_QUALS(long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
+ type = build_qualified_type(long_unsigned_type_node, quals);
id = get_identifier("latent_entropy");
latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
| TODO_update_ssa
#include "gcc-generate-gimple-pass.h"
-int plugin_init(struct plugin_name_args *plugin_info,
- struct plugin_gcc_version *version)
+__visible int plugin_init(struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
{
bool enabled = true;
const char * const plugin_name = plugin_info->base_name;
#include "gcc-common.h"
-int plugin_is_GPL_compatible;
+__visible int plugin_is_GPL_compatible;
tree sancov_fndecl;
#endif
}
-int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
{
int i;
struct register_pass_info sancov_plugin_pass_info;
return put_user(SNDRV_OSS_VERSION, p);
if (cmd == OSS_ALSAEMULVER)
return put_user(1, p);
-#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
+#if IS_REACHABLE(CONFIG_SND_MIXER_OSS)
if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
struct snd_pcm_substream *substream;
int idx;
return snd_rawmidi_free(rmidi);
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_REACHABLE(CONFIG_SND_SEQUENCER)
static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
{
struct snd_rawmidi *rmidi = device->private_data;
}
}
rmidi->proc_entry = entry;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_REACHABLE(CONFIG_SND_SEQUENCER)
if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
rmidi->seq_dev->private_data = rmidi;
opl3->hwdep = hw;
opl3->seq_dev_num = seq_device;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_REACHABLE(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) {
strcpy(opl3->seq_dev->name, hw->name);
static bool
check_audiophile_booted(struct fw_unit *unit)
{
- char name[24] = {0};
+ char name[28] = {0};
if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
return false;
- return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
+ return strncmp(name, "FW Audiophile Bootloader", 24) != 0;
}
static void
* write). The other callbacks, wait and reset, are not mandatory.
*
* The clock is set to 48000. If another clock is needed, set
- * (*rbus)->clock manually.
+ * ``(*rbus)->clock`` manually.
*
* The AC97 bus instance is registered as a low-level device, so you don't
* have to release it manually.
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
#include <linux/gameport.h>
#include <linux/export.h>
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define VORTEX_GAME_DWAIT 20 /* 20 ms */
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_GAMEPORT 1
#endif
"{C-Media,CMI8338A},"
"{C-Media,CMI8338B}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
* joystick support
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs4281_gameport_trigger(struct gameport *gameport)
{
* gameport interface
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
{
MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
"{Creative Labs,SB Audigy}}");
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_REACHABLE(CONFIG_SND_SEQUENCER)
#define ENABLE_SYNTH
#include <sound/emu10k1_synth.h>
#endif
if ((err = snd_card_register(card)) < 0)
goto error;
+ if (emu->card_capabilities->emu_model)
+ schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+
pci_set_drvdata(pci, card);
dev++;
return 0;
emu->suspend = 1;
+ cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+
snd_pcm_suspend_all(emu->pcm);
snd_pcm_suspend_all(emu->pcm_mic);
snd_pcm_suspend_all(emu->pcm_efx);
emu->suspend = 0;
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ if (emu->card_capabilities->emu_model)
+ schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+
return 0;
}
*/
#include <linux/sched.h>
-#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
return 0;
}
-static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu,
+static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
const struct firmware *fw_entry)
{
int n, i;
return 0;
}
-static int emu1010_firmware_thread(void *data)
+/* firmware file names, per model, init-fw and dock-fw (optional) */
+static const char * const firmware_names[5][2] = {
+ [EMU_MODEL_EMU1010] = {
+ HANA_FILENAME, DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1010B] = {
+ EMU1010B_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1616] = {
+ EMU1010_NOTEBOOK_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU0404] = {
+ EMU0404_FILENAME, NULL
+ },
+};
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
+ const struct firmware **fw)
{
- struct snd_emu10k1 *emu = data;
+ const char *filename;
+ int err;
+
+ if (!*fw) {
+ filename = firmware_names[emu->card_capabilities->emu_model][dock];
+ if (!filename)
+ return 0;
+ err = request_firmware(fw, filename, &emu->pci->dev);
+ if (err)
+ return err;
+ }
+
+ return snd_emu1010_load_firmware_entry(emu, *fw);
+}
+
+static void emu1010_firmware_work(struct work_struct *work)
+{
+ struct snd_emu10k1 *emu;
u32 tmp, tmp2, reg;
- u32 last_reg = 0;
int err;
- for (;;) {
- /* Delay to allow Audio Dock to settle */
- msleep_interruptible(1000);
- if (kthread_should_stop())
- break;
+ emu = container_of(work, struct snd_emu10k1,
+ emu1010.firmware_work.work);
+ if (emu->card->shutdown)
+ return;
#ifdef CONFIG_PM_SLEEP
- if (emu->suspend)
- continue;
+ if (emu->suspend)
+ return;
#endif
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */
- if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
- /* Audio Dock attached */
- /* Return to Audio Dock programming mode */
- dev_info(emu->card->dev,
- "emu1010: Loading Audio Dock Firmware\n");
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK);
-
- if (!emu->dock_fw) {
- const char *filename = NULL;
- switch (emu->card_capabilities->emu_model) {
- case EMU_MODEL_EMU1010:
- filename = DOCK_FILENAME;
- break;
- case EMU_MODEL_EMU1010B:
- filename = MICRO_DOCK_FILENAME;
- break;
- case EMU_MODEL_EMU1616:
- filename = MICRO_DOCK_FILENAME;
- break;
- }
- if (filename) {
- err = request_firmware(&emu->dock_fw,
- filename,
- &emu->pci->dev);
- if (err)
- continue;
- }
- }
-
- if (emu->dock_fw) {
- err = snd_emu1010_load_firmware(emu, emu->dock_fw);
- if (err)
- continue;
- }
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+ /* Audio Dock attached */
+ /* Return to Audio Dock programming mode */
+ dev_info(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware\n");
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+ EMU_HANA_FPGA_CONFIG_AUDIODOCK);
+ err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+ if (err < 0)
+ goto next;
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ®);
- dev_info(emu->card->dev,
- "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n",
- reg);
- /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®);
- dev_info(emu->card->dev,
- "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg);
- if ((reg & 0x1f) != 0x15) {
- /* FPGA failed to be programmed */
- dev_info(emu->card->dev,
- "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
- reg);
- continue;
- }
- dev_info(emu->card->dev,
- "emu1010: Audio Dock Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
- snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
- dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n",
- tmp, tmp2);
- /* Sync clocking between 1010 and Dock */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all. Default is muted after a firmware load */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
- } else if (!reg && last_reg) {
- /* Audio Dock removed */
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp);
+ dev_info(emu->card->dev,
+ "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp);
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+ dev_info(emu->card->dev,
+ "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+ if ((tmp & 0x1f) != 0x15) {
+ /* FPGA failed to be programmed */
dev_info(emu->card->dev,
- "emu1010: Audio Dock detached\n");
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
+ tmp);
+ goto next;
}
-
- last_reg = reg;
+ dev_info(emu->card->dev,
+ "emu1010: Audio Dock Firmware loaded\n");
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+ dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
+ /* Sync clocking between 1010 and Dock */
+ /* Allow DLL to settle */
+ msleep(10);
+ /* Unmute all. Default is muted after a firmware load */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ } else if (!reg && emu->emu1010.last_reg) {
+ /* Audio Dock removed */
+ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
+ /* Unmute all */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
}
- dev_info(emu->card->dev, "emu1010: firmware thread stopping\n");
- return 0;
+
+ next:
+ emu->emu1010.last_reg = reg;
+ if (!emu->card->shutdown)
+ schedule_delayed_work(&emu->emu1010.firmware_work,
+ msecs_to_jiffies(1000));
}
/*
}
dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
- if (!emu->firmware) {
- const char *filename;
- switch (emu->card_capabilities->emu_model) {
- case EMU_MODEL_EMU1010:
- filename = HANA_FILENAME;
- break;
- case EMU_MODEL_EMU1010B:
- filename = EMU1010B_FILENAME;
- break;
- case EMU_MODEL_EMU1616:
- filename = EMU1010_NOTEBOOK_FILENAME;
- break;
- case EMU_MODEL_EMU0404:
- filename = EMU0404_FILENAME;
- break;
- default:
- return -ENODEV;
- }
-
- err = request_firmware(&emu->firmware, filename, &emu->pci->dev);
- if (err != 0) {
- dev_info(emu->card->dev,
- "emu1010: firmware: %s not found. Err = %d\n",
- filename, err);
- return err;
- }
- dev_info(emu->card->dev,
- "emu1010: firmware file = %s, size = 0x%zx\n",
- filename, emu->firmware->size);
- }
-
- err = snd_emu1010_load_firmware(emu, emu->firmware);
- if (err != 0) {
+ err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
+ if (err < 0) {
dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
return err;
}
snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
- /* Start Micro/Audio Dock firmware loader thread */
- if (!emu->emu1010.firmware_thread) {
- emu->emu1010.firmware_thread =
- kthread_create(emu1010_firmware_thread, emu,
- "emu1010_firmware");
- if (IS_ERR(emu->emu1010.firmware_thread)) {
- err = PTR_ERR(emu->emu1010.firmware_thread);
- emu->emu1010.firmware_thread = NULL;
- dev_info(emu->card->dev,
- "emu1010: Creating thread failed\n");
- return err;
- }
-
- wake_up_process(emu->emu1010.firmware_thread);
- }
-
#if 0
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
}
- if (emu->emu1010.firmware_thread)
- kthread_stop(emu->emu1010.firmware_thread);
+ cancel_delayed_work_sync(&emu->emu1010.firmware_work);
release_firmware(emu->firmware);
release_firmware(emu->dock_fw);
if (emu->irq >= 0)
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
+ INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
"{Ectiva,EV1938}}");
#endif
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK
#endif
"{ESS,ES1969},"
"{TerraTec,128i PCI}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
"{ESS,Maestro 1},"
"{TerraTec,DMX}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
}
/* generic shutup callback;
- * just turning off EPAD and a little pause for avoiding pop-noise
+ * just turning off EAPD and a little pause for avoiding pop-noise
*/
static void alc_eapd_shutup(struct hda_codec *codec)
{
#include <sound/opl3.h>
#include <sound/initval.h>
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
* gameport interface
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static unsigned char snd_trident_gameport_read(struct gameport *gameport)
{
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */
/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK
#endif
all: ring virtio_ring_0_9 virtio_ring_poll virtio_ring_inorder ptr_ring noring
CFLAGS += -Wall
-CFLAGS += -pthread -O2 -ggdb
-LDFLAGS += -pthread -O2 -ggdb
+CFLAGS += -pthread -O2 -ggdb -flto -fwhole-program
+LDFLAGS += -pthread -O2 -ggdb -flto -fwhole-program
main.o: main.c main.h
ring.o: ring.c main.h
assert(!ret);
}
-static void run_guest(void)
+void poll_used(void)
+{
+ while (used_empty())
+ busy_wait();
+}
+
+static void __attribute__((__flatten__)) run_guest(void)
{
int completed_before;
int completed = 0;
assert(completed <= bufs);
assert(started <= bufs);
if (do_sleep) {
- if (enable_call())
+ if (used_empty() && enable_call())
wait_for_call();
} else {
poll_used();
}
}
-static void run_host(void)
+void poll_avail(void)
+{
+ while (avail_empty())
+ busy_wait();
+}
+
+static void __attribute__((__flatten__)) run_host(void)
{
int completed_before;
int completed = 0;
for (;;) {
if (do_sleep) {
- if (enable_kick())
+ if (avail_empty() && enable_kick())
wait_for_kick();
} else {
poll_avail();
int add_inbuf(unsigned, void *, void *);
void *get_buf(unsigned *, void **);
void disable_call();
+bool used_empty();
bool enable_call();
void kick_available();
-void poll_used();
/* host side */
void disable_kick();
+bool avail_empty();
bool enable_kick();
bool use_buf(unsigned *, void **);
void call_used();
-void poll_avail();
/* implemented by main */
extern bool do_sleep;
return "Buffer";
}
-void poll_used(void)
+bool used_empty()
{
+ return false;
}
void disable_call()
assert(0);
}
-void poll_avail(void)
+bool avail_empty()
{
+ return false;
}
bool use_buf(unsigned *lenp, void **bufp)
return datap;
}
-void poll_used(void)
+bool used_empty()
{
- void *b;
-
- do {
- if (tailcnt == headcnt || __ptr_ring_full(&array)) {
- b = NULL;
- barrier();
- } else {
- b = "Buffer\n";
- }
- } while (!b);
+ return (tailcnt == headcnt || __ptr_ring_full(&array));
}
void disable_call()
assert(0);
}
-void poll_avail(void)
+bool avail_empty()
{
- void *b;
-
- do {
- barrier();
- b = __ptr_ring_peek(&array);
- } while (!b);
+ return !__ptr_ring_peek(&array);
}
bool use_buf(unsigned *lenp, void **bufp)
return datap;
}
-void poll_used(void)
+bool used_empty()
{
unsigned head = (ring_size - 1) & guest.last_used_idx;
- while (ring[head].flags & DESC_HW)
- busy_wait();
+ return (ring[head].flags & DESC_HW);
}
void disable_call()
bool enable_call()
{
- unsigned head = (ring_size - 1) & guest.last_used_idx;
-
event->call_index = guest.last_used_idx;
/* Flush call index write */
/* Barrier D (for pairing) */
smp_mb();
- return ring[head].flags & DESC_HW;
+ return used_empty();
}
void kick_available(void)
bool enable_kick()
{
- unsigned head = (ring_size - 1) & host.used_idx;
-
event->kick_index = host.used_idx;
/* Barrier C (for pairing) */
smp_mb();
- return !(ring[head].flags & DESC_HW);
+ return avail_empty();
}
-void poll_avail(void)
+bool avail_empty()
{
unsigned head = (ring_size - 1) & host.used_idx;
- while (!(ring[head].flags & DESC_HW))
- busy_wait();
+ return !(ring[head].flags & DESC_HW);
}
bool use_buf(unsigned *lenp, void **bufp)
return datap;
}
-void poll_used(void)
+bool used_empty()
{
+ unsigned short last_used_idx = guest.last_used_idx;
#ifdef RING_POLL
- unsigned head = (ring_size - 1) & guest.last_used_idx;
+ unsigned short head = last_used_idx & (ring_size - 1);
+ unsigned index = ring.used->ring[head].id;
- for (;;) {
- unsigned index = ring.used->ring[head].id;
-
- if ((index ^ guest.last_used_idx ^ 0x8000) & ~(ring_size - 1))
- busy_wait();
- else
- break;
- }
+ return (index ^ last_used_idx ^ 0x8000) & ~(ring_size - 1);
#else
- unsigned head = guest.last_used_idx;
-
- while (ring.used->idx == head)
- busy_wait();
+ return ring.used->idx == last_used_idx;
#endif
}
bool enable_call()
{
- unsigned short last_used_idx;
-
- vring_used_event(&ring) = (last_used_idx = guest.last_used_idx);
+ vring_used_event(&ring) = guest.last_used_idx;
/* Flush call index write */
/* Barrier D (for pairing) */
smp_mb();
-#ifdef RING_POLL
- {
- unsigned short head = last_used_idx & (ring_size - 1);
- unsigned index = ring.used->ring[head].id;
-
- return (index ^ last_used_idx ^ 0x8000) & ~(ring_size - 1);
- }
-#else
- return ring.used->idx == last_used_idx;
-#endif
+ return used_empty();
}
void kick_available(void)
bool enable_kick()
{
- unsigned head = host.used_idx;
-
- vring_avail_event(&ring) = head;
+ vring_avail_event(&ring) = host.used_idx;
/* Barrier C (for pairing) */
smp_mb();
-#ifdef RING_POLL
- {
- unsigned index = ring.avail->ring[head & (ring_size - 1)];
-
- return (index ^ head ^ 0x8000) & ~(ring_size - 1);
- }
-#else
- return head == ring.avail->idx;
-#endif
+ return avail_empty();
}
-void poll_avail(void)
+bool avail_empty()
{
unsigned head = host.used_idx;
#ifdef RING_POLL
- for (;;) {
- unsigned index = ring.avail->ring[head & (ring_size - 1)];
- if ((index ^ head ^ 0x8000) & ~(ring_size - 1))
- busy_wait();
- else
- break;
- }
+ unsigned index = ring.avail->ring[head & (ring_size - 1)];
+
+ return ((index ^ head ^ 0x8000) & ~(ring_size - 1));
#else
- while (ring.avail->idx == head)
- busy_wait();
+ return head == ring.avail->idx;
#endif
}
#ifdef CONFIG_HAVE_KVM_IRQFD
+static struct workqueue_struct *irqfd_cleanup_wq;
static void
irqfd_inject(struct work_struct *work)
list_del_init(&irqfd->list);
- schedule_work(&irqfd->shutdown);
+ queue_work(irqfd_cleanup_wq, &irqfd->shutdown);
}
int __attribute__((weak)) kvm_arch_set_irq_inatomic(
* so that we guarantee there will not be any more interrupts on this
* gsi once this deassign function returns.
*/
- flush_work(&irqfd->shutdown);
+ flush_workqueue(irqfd_cleanup_wq);
return 0;
}
* Block until we know all outstanding shutdown jobs have completed
* since we do not take a kvm* reference.
*/
- flush_work(&irqfd->shutdown);
+ flush_workqueue(irqfd_cleanup_wq);
}
spin_unlock_irq(&kvm->irqfds.lock);
}
+/*
+ * create a host-wide workqueue for issuing deferred shutdown requests
+ * aggregated from all vm* instances. We need our own isolated
+ * queue to ease flushing work items when a VM exits.
+ */
+int kvm_irqfd_init(void)
+{
+ irqfd_cleanup_wq = alloc_workqueue("kvm-irqfd-cleanup", 0, 0);
+ if (!irqfd_cleanup_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
void kvm_irqfd_exit(void)
{
+ destroy_workqueue(irqfd_cleanup_wq);
}
#endif
* kvm_arch_init makes sure there's at most one caller
* for architectures that support multiple implementations,
* like intel and amd on x86.
+ * kvm_arch_init must be called before kvm_irqfd_init to avoid creating
+ * conflicts in case kvm is already setup for another implementation.
*/
+ r = kvm_irqfd_init();
+ if (r)
+ goto out_irqfd;
if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) {
r = -ENOMEM;
free_cpumask_var(cpus_hardware_enabled);
out_free_0:
kvm_irqfd_exit();
+out_irqfd:
kvm_arch_exit();
out_fail:
return r;