diff --git a/Documentation/ABI/testing/sysfs-bus-vfio-mdev b/Documentation/ABI/testing/sysfs-bus-vfio-mdev new file mode 100644 index 0000000000000..452dbe39270ec --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-vfio-mdev @@ -0,0 +1,111 @@ +What: /sys/...//mdev_supported_types/ +Date: October 2016 +Contact: Kirti Wankhede +Description: + This directory contains list of directories of currently + supported mediated device types and their details for + . Supported type attributes are defined by the + vendor driver who registers with Mediated device framework. + Each supported type is a directory whose name is created + by adding the device driver string as a prefix to the + string provided by the vendor driver. + +What: /sys/...//mdev_supported_types// +Date: October 2016 +Contact: Kirti Wankhede +Description: + This directory gives details of supported type, like name, + description, available_instances, device_api etc. + 'device_api' and 'available_instances' are mandatory + attributes to be provided by vendor driver. 'name', + 'description' and other vendor driver specific attributes + are optional. + +What: /sys/.../mdev_supported_types//create +Date: October 2016 +Contact: Kirti Wankhede +Description: + Writing UUID to this file will create mediated device of + type for parent device . This is a + write-only file. + For example: + # echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" > \ + /sys/devices/foo/mdev_supported_types/foo-1/create + +What: /sys/.../mdev_supported_types//devices/ +Date: October 2016 +Contact: Kirti Wankhede +Description: + This directory contains symbolic links pointing to mdev + devices sysfs entries which are created of this . + +What: /sys/.../mdev_supported_types//available_instances +Date: October 2016 +Contact: Kirti Wankhede +Description: + Reading this attribute will show the number of mediated + devices of type that can be created. This is a + readonly file. +Users: + Userspace applications interested in creating mediated + device of that type. Userspace application should check + the number of available instances could be created before + creating mediated device of this type. + +What: /sys/.../mdev_supported_types//device_api +Date: October 2016 +Contact: Kirti Wankhede +Description: + Reading this attribute will show VFIO device API supported + by this type. For example, "vfio-pci" for a PCI device, + "vfio-platform" for platform device. + +What: /sys/.../mdev_supported_types//name +Date: October 2016 +Contact: Kirti Wankhede +Description: + Reading this attribute will show human readable name of the + mediated device that will get created of type . + This is optional attribute. For example: "Grid M60-0Q" +Users: + Userspace applications interested in knowing the name of + a particular that can help in understanding the + type of mediated device. + +What: /sys/.../mdev_supported_types//description +Date: October 2016 +Contact: Kirti Wankhede +Description: + Reading this attribute will show description of the type of + mediated device that will get created of type . + This is optional attribute. For example: + "2 heads, 512M FB, 2560x1600 maximum resolution" +Users: + Userspace applications interested in knowing the details of + a particular that can help in understanding the + features provided by that type of mediated device. + +What: /sys/.../// +Date: October 2016 +Contact: Kirti Wankhede +Description: + This directory represents device directory of mediated + device. It contains all the attributes related to mediated + device. + +What: /sys/...///mdev_type +Date: October 2016 +Contact: Kirti Wankhede +Description: + This is symbolic link pointing to supported type, + directory of which this mediated device is created. + +What: /sys/...///remove +Date: October 2016 +Contact: Kirti Wankhede +Description: + Writing '1' to this file destroys the mediated device. The + vendor driver can fail the remove() callback if that device + is active and the vendor driver doesn't support hot unplug. + Example: + # echo 1 > /sys/bus/mdev/devices//remove diff --git a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl index b82deeaec314b..470def06ab0a4 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl +++ b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl @@ -1,4 +1,4 @@ -What: state +What: /sys/devices/system/ibm_rtl/state Date: Sep 2010 KernelVersion: 2.6.37 Contact: Vernon Mauery @@ -10,7 +10,7 @@ Description: The state file allows a means by which to change in and Users: The ibm-prtm userspace daemon uses this interface. -What: version +What: /sys/devices/system/ibm_rtl/version Date: Sep 2010 KernelVersion: 2.6.37 Contact: Vernon Mauery diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index fdf8232d0eeb2..72f78ae46c102 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -12,8 +12,7 @@ DOCBOOKS := z8530book.xml \ 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 diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl deleted file mode 100644 index 53f439dcc94b7..0000000000000 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - The ALSA Driver API - - - - 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. - - - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - - - 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 - - - - - - - - Management of Cards and Devices - Card Management -!Esound/core/init.c - - Device Components -!Esound/core/device.c - - Module requests and Device File Entries -!Esound/core/sound.c - - Memory Management Helpers -!Esound/core/memory.c -!Esound/core/memalloc.c - - - PCM API - PCM Core -!Esound/core/pcm.c -!Esound/core/pcm_lib.c -!Esound/core/pcm_native.c -!Iinclude/sound/pcm.h - - PCM Format Helpers -!Esound/core/pcm_misc.c - - PCM Memory Management -!Esound/core/pcm_memory.c - - PCM DMA Engine API -!Esound/core/pcm_dmaengine.c -!Iinclude/sound/dmaengine_pcm.h - - - Control/Mixer API - General Control Interface -!Esound/core/control.c - - AC97 Codec API -!Esound/pci/ac97/ac97_codec.c -!Esound/pci/ac97/ac97_pcm.c - - Virtual Master Control API -!Esound/core/vmaster.c -!Iinclude/sound/control.h - - - MIDI API - Raw MIDI API -!Esound/core/rawmidi.c - - MPU401-UART API -!Esound/drivers/mpu401/mpu401_uart.c - - - Proc Info API - Proc Info Interface -!Esound/core/info.c - - - Compress Offload - Compress Offload API -!Esound/core/compress_offload.c -!Iinclude/uapi/sound/compress_offload.h -!Iinclude/uapi/sound/compress_params.h -!Iinclude/sound/compress_driver.h - - - ASoC - ASoC Core API -!Iinclude/sound/soc.h -!Esound/soc/soc-core.c - -!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 - - ASoC DAPM API -!Esound/soc/soc-dapm.c - - ASoC DMA Engine API -!Esound/soc/soc-generic-dmaengine-pcm.c - - - Miscellaneous Functions - Hardware-Dependent Devices API -!Esound/core/hwdep.c - - Jack Abstraction Layer API -!Iinclude/sound/jack.h -!Esound/core/jack.c -!Esound/soc/soc-jack.c - - ISA DMA Helpers -!Esound/core/isadma.c - - Other Helper Macros -!Iinclude/sound/core.h - - - - diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl deleted file mode 100644 index a27ab9f53fb68..0000000000000 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ /dev/null @@ -1,6206 +0,0 @@ - - - - - - - - - Writing an ALSA Driver - - Takashi - Iwai - -
- tiwai@suse.de -
-
-
- - Oct 15, 2007 - 0.3.7 - - - - This document describes how to write an ALSA (Advanced Linux - Sound Architecture) driver. - - - - - - Copyright (c) 2002-2005 Takashi Iwai tiwai@suse.de - - - - 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. - - - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - - - 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 - - - -
- - - - - - Preface - - This document describes how to write an - - ALSA (Advanced Linux Sound Architecture) - 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. - - - ALSA File Tree Structure - - 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/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 pci_driver structure - containing the three pointers above. - create an init() function just calling - the pci_register_driver() to register the pci_driver table - defined above. - create an exit() function to call - the 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 - snd_mychip_probe() function - refer to details explained in the following section. - - - Basic Flow for PCI Drivers - Example - - - #include - #include - #include - #include - - /* 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); - } -]]> - - - -
- -
- 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. - - - -= 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). - -
- -
- 2) Create a card instance - - - -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. - - - - - - - - The details will be explained in the section PCI Resource - Management. - -
- -
- 4) Set the driver ID and name strings. - - - -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, - mixer (e.g. AC97), - MIDI (e.g. MPU-401), - and other interfaces. - Also, if you want a proc - file, define it here, too. - -
- -
- 6) Register the card instance. - - - - - - - - - - Will be explained in the section Management - of Cards and Components, too. - -
- -
- 7) Set the PCI driver data and return zero. - - - - - - - - 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: - - - - - - - - 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 - #include - #include - #include -]]> - - - - 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 mdelay() or - 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 - snd_card_new(). - - - -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 snd_card_new(). - - - - The first argument, the pointer of struct - 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 struct 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 - snd_device_new() function. - - - - - - - - - - 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 - 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 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. - - - - - - - - - - In general, there are two ways of allocating the chip record. - - -
- 1. Allocating via <function>snd_card_new()</function>. - - As mentioned above, you can pass the extra-data-length - to the 5th argument of snd_card_new(), i.e. - - - -dev, index[dev], id[dev], THIS_MODULE, - sizeof(struct mychip), &card); -]]> - - - - struct mychip is the type of the chip record. - - - - In return, the allocated record can be accessed as - - - -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 - snd_card_new() (with - 0 on the 4th arg), call - kzalloc(). - - - -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, - - - - - - - - - - Then, set the card pointer in the returned chip instance. - - - -card = card; -]]> - - - - - - Next, initialize the fields, and register this chip - record as a low-level device with a specified - ops, - - - - - - - - snd_mychip_dev_free() is the - device-destructor function, which will call the real - destructor. - - - - - -device_data); - } -]]> - - - - where snd_mychip_free() is the real destructor. - -
-
- -
- Registration and Release - - After all components are assigned, register the card instance - by calling snd_card_register(). Access - to the device files is enabled at this point. That is, before - 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 - snd_card_free(). - - - - For releasing the card instance, you can call simply - snd_card_free(). As mentioned earlier, all - components are released automatically by this call. - - - - For a device which allows hotplugging, you can use - 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. - - - PCI Resource Management Example - -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 - xxx_create() function is written for this - purpose. - - - - In the case of PCI devices, you first have to call - the 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 pci_set_master() function, - too. - - - - Suppose the 28bit mask, and the code to be added would be like: - - - - - - - -
- -
- 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 struct mychip will have the - following fields: - - - - - - - - - - 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 - kzalloc() automatically, so you - don't have to take care of resetting them. - - - - The allocation of an I/O port is done like this: - - - -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 kmalloc() by - request_region(). The pointer must be - released via kfree(), but there is a - problem with this. This issue will be explained later. - - - - The allocation of an interrupt source is done like this: - - - -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 snd_mychip_interrupt() is the - interrupt handler defined later. - Note that chip->irq should be defined - only when request_irq() succeeded. - - - - On the PCI bus, interrupts can be shared. Thus, - IRQF_SHARED is used as the interrupt flag of - request_irq(). - - - - The last argument of 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: - - - - - - - - - - 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: - - - -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 - pci_request_region() or - pci_request_regions() like in this example, - release the resource(s) using the corresponding function, - pci_release_region() or - pci_release_regions(). - - - -pci); -]]> - - - - - - When you requested manually via request_region() - or request_mem_region, you can release it via - release_resource(). Suppose that you keep - the resource pointer returned from request_region() - in chip->res_port, the release procedure looks like: - - - -res_port); -]]> - - - - - - Don't forget to call pci_disable_device() - before the end. - - - - And finally, release the chip-specific record. - - - - - - - - - - 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 - 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: - - - - - - - - and the allocation would be like below: - - - -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: - - - -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 - pci_device_id table for this - chipset. It's a table of PCI vendor/device ID number, and some - masks. - - - - For example, - - - - - - - - - - The first and second fields of - the 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 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 pci_driver record: - - - - - - - - - - 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: - - - - - - - - - - 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). - - - - - - - - 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. - - - PCM Example Code - - - .... - - /* 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; - } -]]> - - - -
- -
- Constructor - - A pcm instance is allocated by the snd_pcm_new() - function. It would be better to create a constructor for pcm, - namely, - - - -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 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 - struct snd_pcm_substream data passed to each callback - as follows: - - - -number; -]]> - - - - - - After the pcm is created, you need to set operators for each - pcm stream. - - - - - - - - - - The operators are defined typically like this: - - - - - - - - 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: - - - -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: - - - -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: - - - PCM Instance with a Destructor - -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: - - - - - - - - - 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 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 (struct snd_pcm_hardware) - contains the definitions of the fundamental hardware - configuration. Above all, you'll need to define this in - - the 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: - - -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: - - - - - - - - - - - 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 - frames_to_bytes() and - bytes_to_frames() helper functions. - - -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 - 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, - 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 struct 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 - struct 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 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. - - - -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 - snd_pcm_substream pointer. To retrieve - the chip record from the given substream instance, you can use the - following macro. - - - - - - - - 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. - - -
- open callback - - - - - - - - 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: - - - -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 - - - - - - - - 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. - - - -runtime->private_data); - .... - } -]]> - - - -
- -
- ioctl callback - - This is used for any special call to pcm ioctls. But - usually you can pass a generic ioctl callback, - snd_pcm_lib_ioctl. - -
- -
- hw_params callback - - - - - - - - - - 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 - params_xxx() macros. To allocate - buffer, you can call a helper function, - - - - - - - - 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 - - - - - - - - - - This is called to release the resources allocated via - hw_params. For example, releasing the - buffer via - snd_pcm_lib_malloc_pages() is done by - calling the following: - - - - - - - - - - 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 - - - - - - - - - - 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 - 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 - - - - - - - - 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. - - - - - - - - - - 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 - - - - - - - - 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 - 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. - -
-
- -
- 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 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 - snd_pcm_period_elapsed() at each - interrupt. - - - - 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 - snd_pcm_period_elapsed(), because - snd_pcm_period_elapsed() calls other pcm - callbacks inside. - - - - Typical code would be like: - - - Interrupt Handler Case #1 - -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 - snd_pcm_period_elapsed() and reset the - accumulator. - - - - Typical code would be like the following. - - - Interrupt Handler Case #2 - -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 <function>snd_pcm_period_elapsed()</function> - - In both cases, even if more than one period are elapsed, you - don't have to call - 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 - schedule or go to - 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 - udelay() or 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 - 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 - 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 - snd_pcm_hw_constraint_list(). - You need to call this function in the open callback. - - - Example of Hardware Constraints - -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 snd_pcm_hardware structure (or in any - other constraint_list). You can build a rule like this: - - - Example of Hardware Constraints for Channels - -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: - - - -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: - - - Example of Hardware Constraints for Formats - -min < 2) { - fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; - return snd_mask_refine(f, &fmt); - } - return 0; - } -]]> - - - - - - ...and in the open callback: - - -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 - struct snd_kcontrol_new record, such as: - - - Definition of a Control - - - - - - - - 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 - - callback functions. - -
- -
- 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 - - 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. - - -
- -
- Callbacks - -
- info callback - - The info callback is used to get - detailed information on this control. This must store the - values of the given struct snd_ctl_elem_info - object. For example, for a boolean control with a single - element: - - - Example of info callback - -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. - - - -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, - 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.) - - - - - - - - - - Some common info callbacks are available for your convenience: - snd_ctl_boolean_mono_info() and - snd_ctl_boolean_stereo_info(). - Obviously, the former is an info callback for a mono channel - boolean item, just like 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, - - - Example of get callback - -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: - - - - - - and is retrieved in callbacks like - - -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, - - - Example of put callback - -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. - -
-
- -
- Constructor - - When everything is ready, finally we can create a new - control. To create a control, there are two functions to be - called, snd_ctl_new1() and - snd_ctl_add(). - - - - In the simplest way, you can do like this: - - - - - - - - where my_control is the - struct 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. - - - - snd_ctl_new1() allocates a new - snd_kcontrol instance, - and 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 snd_ctl_notify(). For - example, - - - - - - - - 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 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 thetlv.p - field to point to this variable, and include the - SNDRV_CTL_ELEM_ACCESS_TLV_READ flag in the - access field; like this: - - - - - - - - - The 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 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 - - - Example of AC97 Interface - -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); - } - -]]> - - - -
- -
- Constructor - - To create an ac97 instance, first call snd_ac97_bus - with an ac97_bus_ops_t record with callback functions. - - - - - - - - The bus record is shared among all belonging ac97 instances. - - - - And then call snd_ac97_mixer() with an - struct snd_ac97_template - record together with the bus pointer created above. - - - -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. - -
- -
- 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. - - - -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. - - - - - - - - - - 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: - snd_ac97_write(), - snd_ac97_read(), - snd_ac97_update() and - snd_ac97_update_bits(). - - - - Both snd_ac97_write() and - snd_ac97_update() functions are used to - set a value to the given register - (AC97_XXX). The difference between them is - that snd_ac97_update() doesn't write a - value if the given value has been already set, while - snd_ac97_write() always rewrites the - value. - - - - - - - - - - snd_ac97_read() is used to read the value - of the given register. For example, - - - - - - - - - - snd_ac97_update_bits() is used to update - some bits in the given register. - - - - - - - - - - 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: - snd_ac97_set_rate(). - - - - - - - - - - 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 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. - -
- -
- Constructor - - To create a rawmidi object, call - snd_mpu401_uart_new(). - - - - - - - - - - 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 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 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). - - - - 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 - struct snd_mpu401 manually - afterward. However, snd_mpu401 pointer is not - returned explicitly by - snd_mpu401_uart_new(). You need to cast - rmidi->private_data to - snd_mpu401 explicitly, - - - -private_data; -]]> - - - - and reset the cport as you like: - - - -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. - -
- -
- Interrupt Handler - - When the interrupt is allocated in - 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 - 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 - snd_mpu401_uart_new() as the second - argument of snd_mpu401_uart_interrupt(). - - - -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>. - -
- -
- Constructor - - - To create a rawmidi device, call the - snd_rawmidi_new function: - - -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: - - - - - - - - - The operators are usually defined like this: - - - - - - These callbacks are explained in the Callbacks - section. - - - - If there are more than one substream, you should give a - unique name to each of them: - - -streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, - list { - sprintf(substream->name, "My MIDI Port %d", substream->number + 1); - } - /* same for SNDRV_RAWMIDI_STREAM_INPUT */ -]]> - - - -
- -
- 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: - - -number; -]]> - - - - -
- <function>open</function> callback - - - - - - - - - This is called when a substream is opened. - You can initialize the hardware here, but you shouldn't - start transmitting/receiving data yet. - -
- -
- <function>close</function> callback - - - - - - - - - Guess what. - - - - The open and close - callbacks of a rawmidi device are serialized with a mutex, - and can sleep. - -
- -
- <function>trigger</function> callback for output - substreams - - - - - - - - - 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 - 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 - snd_rawmidi_transmit_ack to remove the - data from the substream buffer: - - - - - - - - - If you know beforehand that the hardware will accept data, you - can use the snd_rawmidi_transmit function - which reads some data and removes them from the buffer at once: - - - - - - - - - If you know beforehand how many bytes you can accept, you can - use a buffer size greater than one with the - 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. - -
- -
- <function>trigger</function> callback for input - substreams - - - - - - - - - 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 snd_rawmidi_receive for all received - data: - - - - - - -
- -
- <function>drain</function> callback - - - - - - - - - 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. - - - - - - - - - - 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 snd_opl3_new(). - - - - - - - - - - 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 snd_opl3_new(), - call snd_opl3_init() to initialize the chip to the - proper state. Note that snd_opl3_create() always - calls it internally. - - - - If the opl3 instance is created successfully, then create a - hwdep device for this opl3. - - - - - - - - - - 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 - snd_hwdep_new(). - - - - - - - - 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. - - - -private_data = p; - hw->private_free = mydata_free; -]]> - - - - and the implementation of the destructor would be: - - - -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. - - - -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, 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 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 - snd_malloc_xxx_pages() function, where xxx - is the bus type. - - - - The allocation of pages with fallback is - 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 - 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). - - - - - - - - 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 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: - - - - - - - - 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: - - - - - - - - - - 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: - - - - - - - - - - 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). - - - - - - - - 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. - - - - - - - - - - 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: - - - - - - - - - - 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 - snd_pcm_lib_preallocate_pages() or - 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 struct 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: - - - -dma_private; -]]> - - - - - - Then call 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 - snd_pcm_sgbuf_get_addr(). - - - - When a SG-handler is used, you need to set - snd_pcm_sgbuf_ops_page as - the page callback. - (See - page callback section.) - - - - To release the data, call - snd_pcm_lib_free_pages() in the - hw_free callback as usual. - -
- -
- Vmalloc'ed Buffers - - It's possible to use a buffer allocated via - 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: - - - - - - /* 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 - snd_card_proc_new(). - - - - - - - - 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 - 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 - snd_info_set_text_ops(). - - - - - - - - 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 - - - - - - - - - - - In the read callback, use snd_iprintf() for - output strings, which works just like normal - printf(). For example, - - - -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: - - - -mode = S_IFREG | S_IRUGO | S_IWUSR; -]]> - - - - and set the write buffer size and the callback - - - -c.text.write = my_proc_write; -]]> - - - - - - For the write callback, you can use - snd_info_get_line() to get a text line, and - 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: - - - -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 - copy_from/to_user() to transfer the - data. - - - - - - - - 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 - 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 - snd_pcm_suspend_all is called, - regardless of the SNDRV_PCM_INFO_RESUME flag. - The RESUME flag affects only the behavior - of 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: - - - - - - - - - - The scheme of the real suspend job is as follows. - - - Retrieve the card and the chip data. - Call snd_power_change_state() with - SNDRV_CTL_POWER_D3hot to change the - power status. - Call snd_pcm_suspend_all() to suspend the running PCM streams. - If AC97 codecs are used, call - snd_ac97_suspend() for each codec. - Save the register values if necessary. - Stop the hardware if necessary. - Disable the PCI device by calling - pci_disable_device(). Then, call - pci_save_state() at last. - - - - - A typical code would be like: - - - -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. - - - Retrieve the card and the chip data. - Set up PCI. First, call pci_restore_state(). - Then enable the pci device again by calling pci_enable_device(). - Call pci_set_master() if necessary, too. - Re-initialize the chip. - Restore the saved registers if necessary. - Resume the mixer, e.g. calling - snd_ac97_resume(). - Restart the hardware (if any). - Call snd_power_change_state() with - SNDRV_CTL_POWER_D0 to notify the processes. - - - - - A typical code would be like: - - - -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 - snd_pcm_suspend_all() or - 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. - - - -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 - snd_card_new(), it's anyway accessible - via private_data field. - - - -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. - - - - - - - - - - - - - - - - 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: - - - - - - - - - - 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 - MODULE_PARM_DESC() macros. - - - - The typical coding would be like below: - - - - - - - - - - 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. - - - - - - - - - - - - - - - - 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 - - - - - Modify alsa-driver/pci/Makefile - - - - Suppose you have a file xyz.c. Add the following - two lines - - - - - - - - - - - Create the Kconfig entry - - - - Add the new entry of Kconfig for your xyz driver. - - - - - - - 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. - - - - - - - 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. - - - - - Add a new directory (xyz) in - alsa-driver/pci/Makefile as below - - - - - - - - - - - - Under the directory xyz, create a Makefile - - - Sample Makefile for a driver xyz - - - - - - - - - - Create the Kconfig entry - - - - This procedure is as same as in the last section. - - - - - - Run cvscompile script to re-generate the configure script and - build the whole stuff again. - - - - -
- -
- - - - - - Useful Functions - -
- <function>snd_printk()</function> and friends - - ALSA provides a verbose version of the - 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 printk() does, so it's - recommended to add this prefix, e.g. - - - - - - - - - - There are also printk()'s for - debugging. snd_printd() can be used for - general debugging purposes. If - CONFIG_SND_DEBUG is set, this function is - compiled, and works just like - snd_printk(). If the ALSA is compiled - without the debugging flag, it's ignored. - - - - 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 - option. You need to give - explicitly option instead. - -
- -
- <function>snd_BUG()</function> - - It shows the BUG? message and - stack trace as well as 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. - -
- -
- <function>snd_BUG_ON()</function> - - snd_BUG_ON() macro is similar with - WARN_ON() macro. For example, - - - - - - - - or it can be used as the condition, - - - - - - - - - - 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. - - -
diff --git a/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt b/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt index 003bc246a2706..164cbb15f04cc 100644 --- a/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt +++ b/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt @@ -16,6 +16,8 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt. - Video port 0 for RGB input - Video port 1 for VGA output +Optional properties: +- vdd-supply: Power supply for DAC Example ------- diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 4e00e859e885a..bfa461aaac99b 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt @@ -43,6 +43,9 @@ Optional properties: reset signal present internally in some host controller IC designs. See Documentation/devicetree/bindings/reset/reset.txt for details. +* reset-names: request name for using "resets" property. Must be "reset". + (It will be used together with "resets" property.) + * clocks: from common clock binding: handle to biu and ciu clocks for the bus interface unit clock and the card interface unit clock. @@ -103,6 +106,8 @@ board specific portions as listed below. interrupts = <0 75 0>; #address-cells = <1>; #size-cells = <0>; + resets = <&rst 20>; + reset-names = "reset"; }; [board specific internal DMA resources] diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt index ba67b39939c10..71aeda1ca0559 100644 --- a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt @@ -26,13 +26,16 @@ Required properties: - "sys" - "legacy" - "client" -- resets: Must contain five entries for each entry in reset-names. +- resets: Must contain seven entries for each entry in reset-names. See ../reset/reset.txt for details. - reset-names: Must include the following names - "core" - "mgmt" - "mgmt-sticky" - "pipe" + - "pm" + - "aclk" + - "pclk" - pinctrl-names : The pin control state names - pinctrl-0: The "default" pinctrl state - #interrupt-cells: specifies the number of cells needed to encode an @@ -86,8 +89,10 @@ pcie0: pcie@f8000000 { reg = <0x0 0xf8000000 0x0 0x2000000>, <0x0 0xfd000000 0x0 0x1000000>; reg-names = "axi-base", "apb-base"; resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>, - <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>; - reset-names = "core", "mgmt", "mgmt-sticky", "pipe"; + <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE> , + <&cru SRST_PCIE_PM>, <&cru SRST_P_PCIE>, <&cru SRST_A_PCIE>; + reset-names = "core", "mgmt", "mgmt-sticky", "pipe", + "pm", "pclk", "aclk"; phys = <&pcie_phy>; phy-names = "pcie-phy"; pinctrl-names = "default"; diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt index f9753c416974d..b24583aa34c3b 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt @@ -14,11 +14,6 @@ Required properies: - #size-cells : The value of this property must be 1 - ranges : defines mapping between pin controller node (parent) to gpio-bank node (children). - - interrupt-parent: phandle of the interrupt parent to which the external - GPIO interrupts are forwarded to. - - st,syscfg: Should be phandle/offset pair. The phandle to the syscon node - which includes IRQ mux selection register, and the offset of the IRQ mux - selection register. - pins-are-numbered: Specify the subnodes are using numbered pinmux to specify pins. @@ -37,6 +32,11 @@ Required properties: Optional properties: - reset: : Reference to the reset controller + - interrupt-parent: phandle of the interrupt parent to which the external + GPIO interrupts are forwarded to. + - st,syscfg: Should be phandle/offset pair. The phandle to the syscon node + which includes IRQ mux selection register, and the offset of the IRQ mux + selection register. Example: #include diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 14cdc101d165d..1b5f15653b1bb 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -447,7 +447,6 @@ prototypes: int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t start, loff_t end, int datasync); - int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index d619c8d71966e..b5039a00caafa 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -828,7 +828,6 @@ struct file_operations { int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); - int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); diff --git a/Documentation/index.rst b/Documentation/index.rst index c53d089455a42..115c551da5f53 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -18,6 +18,7 @@ Contents: media/index gpu/index 80211/index + sound/index Indices and tables ================== diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst new file mode 100644 index 0000000000000..aed6b4fb8e464 --- /dev/null +++ b/Documentation/sound/alsa-configuration.rst @@ -0,0 +1,2683 @@ +============================================================== +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=`` 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 + + [] + + name of application with (higher priority) or without path + + number of fragments or zero if auto + + size of fragment in bytes or zero if auto + + 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 diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt deleted file mode 100644 index fc53ccd9a6293..0000000000000 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ /dev/null @@ -1,2330 +0,0 @@ - - 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=" 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 " []" - - - name of application with (higher priority) or without path - - number of fragments or zero if auto - - size of fragment in bytes or zero if auto - - 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 diff --git a/Documentation/sound/alsa/ControlNames.txt b/Documentation/sound/alsa/ControlNames.txt deleted file mode 100644 index 3fc1cf50d28e1..0000000000000 --- a/Documentation/sound/alsa/ControlNames.txt +++ /dev/null @@ -1,107 +0,0 @@ -This document describes standard names of mixer controls. - -Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION - -DIRECTION: - (both directions) - Playback - Capture - Bypass Playback - Bypass Capture - -FUNCTION: - Switch (on/off switch) - Volume - Route (route control, hardware specific) - -CHANNEL: - (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) */ diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt deleted file mode 100644 index ec099d4343f20..0000000000000 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ /dev/null @@ -1,324 +0,0 @@ - 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) diff --git a/Documentation/sound/alsa/VIA82xx-mixer.txt b/Documentation/sound/alsa/VIA82xx-mixer.txt deleted file mode 100644 index 1b0ac06ba95db..0000000000000 --- a/Documentation/sound/alsa/VIA82xx-mixer.txt +++ /dev/null @@ -1,8 +0,0 @@ - - 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. diff --git a/Documentation/sound/alsa/alsa-parameters.txt b/Documentation/sound/alsa/alsa-parameters.txt deleted file mode 100644 index 0fa40679b0802..0000000000000 --- a/Documentation/sound/alsa/alsa-parameters.txt +++ /dev/null @@ -1,135 +0,0 @@ - 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] diff --git a/Documentation/sound/alsa/seq_oss.html b/Documentation/sound/alsa/seq_oss.html deleted file mode 100644 index 9663b45f6fde6..0000000000000 --- a/Documentation/sound/alsa/seq_oss.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - OSS Sequencer Emulation on ALSA - - - -
-

- -

- -
-

-OSS Sequencer Emulation on ALSA

- -
-

Copyright (c) 1998,1999 by Takashi Iwai -<iwai@ww.uni-erlangen.de> -

ver.0.1.8; Nov. 16, 1999 -

- -

- -

-1. 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.
- -

-2. 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. -

-3. 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. -

-4. 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. -

-5. 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.
- -

-6. 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. - -

-7. Interface to Synthesizer Device

- -

-7.1. 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. -

-7.2. 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). -

-7.2.1. 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: -
    -
  1. -Allocate private data record.
  2. - -
  3. -Create an ALSA sequencer port.
  4. - -
  5. -Set the new port address on arg->addr.
  6. - -
  7. -Set the private data record pointer on arg->private_data.
  8. -
-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. -

-7.2.2 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. -

-7.2.3 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. -

-7.2.4 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. -

-7.2.5 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. -

-7.3 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 eventOriginal OSS events
NOTEONSEQ_NOTEON -
MIDI_NOTEON
NOTESEQ_NOTEOFF -
MIDI_NOTEOFF
KEYPRESSMIDI_KEY_PRESSURE
CHANPRESSSEQ_AFTERTOUCH -
MIDI_CHN_PRESSURE
PGMCHANGESEQ_PGMCHANGE -
MIDI_PGM_CHANGE
PITCHBENDSEQ_CONTROLLER(CTRL_PITCH_BENDER) -
MIDI_PITCH_BEND
CONTROLLERMIDI_CTL_CHANGE -
SEQ_BALANCE (with CTL_PAN)
CONTROL14SEQ_CONTROLLER
REGPARAMSEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)
SYSEXSEQ_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. -

-8. 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. -

-9. Known Problems / TODO's

- -
    -
  • -Patch loading via ALSA instrument layer is not implemented yet.
  • -
- - - diff --git a/Documentation/sound/alsa/Audigy-mixer.txt b/Documentation/sound/cards/audigy-mixer.rst similarity index 57% rename from Documentation/sound/alsa/Audigy-mixer.txt rename to Documentation/sound/cards/audigy-mixer.rst index 7f10dc6ff28ca..86213234435fe 100644 --- a/Documentation/sound/alsa/Audigy-mixer.txt +++ b/Documentation/sound/cards/audigy-mixer.rst @@ -1,8 +1,8 @@ +============================================= +Sound Blaster Audigy mixer / default DSP code +============================================= - Sound Blaster Audigy mixer / default DSP code - =========================================== - -This is based on SB-Live-mixer.txt. +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. @@ -13,8 +13,8 @@ The ALSA driver programs this portion of chip by default code (can be altered later) which offers the following functionality: -1) Digital mixer controls -------------------------- +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 @@ -26,320 +26,343 @@ is mentioned in multiple controls, the signal is accumulated and can be wrapped 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. +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 ------------------------------- +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) +* 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 +* 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: -------------------- +* 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 ------------------------------------- - Files: - LM4545.pdf AC97 Codec +LM4545.pdf + AC97 Codec - m2049.pdf The EMU10K1 Digital Audio Processor +m2049.pdf + The EMU10K1 Digital Audio Processor - hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects +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 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) +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 +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) diff --git a/Documentation/sound/alsa/Audiophile-Usb.txt b/Documentation/sound/cards/audiophile-usb.rst similarity index 81% rename from Documentation/sound/alsa/Audiophile-Usb.txt rename to Documentation/sound/cards/audiophile-usb.rst index e7a5ed4dcae86..a7bb5648331fd 100644 --- a/Documentation/sound/alsa/Audiophile-Usb.txt +++ b/Documentation/sound/cards/audiophile-usb.rst @@ -1,32 +1,41 @@ - Guide to using M-Audio Audiophile USB with ALSA and Jack v1.5 - ======================================================== +======================================================== +Guide to using M-Audio Audiophile USB with ALSA and Jack +======================================================== - Thibault Le Meur +v1.5 + +Thibault Le Meur 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 - - Modifying document structure + + - Added Low Endianness nature of 16bits-modes + found by Hakan Lennestal + - Modifying document structure + * v1.5 - Thibault Le Meur (2007-07-12) - - Added AC3/DTS passthru info + - Added AC3/DTS passthru info -1 - Audiophile USB Specs and correct usage -========================================== +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) @@ -34,56 +43,69 @@ The device has 4 audio interfaces, and 2 MIDI ports: * 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" + 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 + 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 + 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 + 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 + 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) + synchronization error (for instance sound played at an odd sample rate) -2 - Audiophile USB MIDI support in ALSA -======================================= +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 -======================================== +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". +``device_setup``. -3.1 - Default Alsa driver mode ------------------------------- +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 @@ -101,6 +123,7 @@ 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 @@ -115,20 +138,28 @@ 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 + + * 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 + + * 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 + + * 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 + + * 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 ---------------------------- +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 @@ -137,34 +168,39 @@ 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 +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 +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 +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 +16-bit modes +~~~~~~~~~~~~ The two supported modes are: - * device_setup=0x01 + * ``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 + * ``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 @@ -173,33 +209,43 @@ The two supported modes are: 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 + +24-bit modes +~~~~~~~~~~~~ The three supported modes are: - * device_setup=0x09 + * ``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 + * ``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 + * ``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 @@ -210,29 +256,35 @@ The three supported modes are: 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 +AC3 w/ DTS passthru mode +~~~~~~~~~~~~~~~~~~~~~~~~ Thanks to Hakan Lennestal, I now have a report saying that this mode works. - * device_setup=0x03 + * ``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 ----------------------------------------------- +How to use the ``device_setup`` parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The parameter can be given: - * By manually probing the device (as root): + * 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: + (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 @@ -250,26 +302,31 @@ CAUTION when initializing the device * 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 + 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 -------------------------------------- +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 +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| @@ -278,38 +335,55 @@ The device_setup is one byte long and its structure is the following: +---+---+---+---+---+---+---+---+ Where: - * b0 is the "SET" bit + + * b0 is the ``SET`` bit + - it MUST be set if device_setup is initialized - * b1 is the "DTS" bit + + * 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 + + - 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 + + - 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 + + - 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" + + * 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 +USB implementation details for this device +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You may safely skip this section if you're not interested in driver hacking. @@ -319,46 +393,72 @@ 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 + +* 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 + +* AltSettings 2 corresponds to + - 24-bit depth, 8-48kHz sample mode - Asynch capture and playback (Ao,Ai,Do,Di) - * AltSettings 3 corresponds to + +* AltSettings 3 corresponds to + - 24-bit depth, 8-48kHz sample mode - Synch capture (Ai) and Adaptive playback (Ao,Do) - * AltSettings 4 corresponds to + +* AltSettings 4 corresponds to + - 16-bit depth, 8-48kHz sample mode - Asynch capture and playback (Ao,Ai,Do,Di) - * AltSettings 5 corresponds to + +* AltSettings 5 corresponds to + - 16-bit depth, 8-48kHz sample mode - Synch capture (Ai) and Adaptive playback (Ao,Do) - * AltSettings 6 corresponds to + +* 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: +*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 @@ -371,20 +471,21 @@ _must_know_ how the device will be used: 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 +``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 -=================================== +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 ------------------------------ +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 @@ -396,29 +497,35 @@ 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 ------------------------ +Using Alsa plughw +----------------- + If you don't have a recent Jackd installed, you can downgrade to using -the Alsa "plug" converter. +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." + 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 ------------------------------------------------------- +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) @@ -432,6 +539,7 @@ 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 diff --git a/Documentation/sound/alsa/Bt87x.txt b/Documentation/sound/cards/bt87x.rst similarity index 82% rename from Documentation/sound/alsa/Bt87x.txt rename to Documentation/sound/cards/bt87x.rst index f158cde8b065a..912732d3ef9e2 100644 --- a/Documentation/sound/alsa/Bt87x.txt +++ b/Documentation/sound/cards/bt87x.rst @@ -1,18 +1,23 @@ +================= +ALSA BT87x Driver +================= + Intro ===== You might have noticed that the bt878 grabber cards have actually -_two_ PCI functions: +*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) -[ ... ] + $ 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 +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... @@ -30,9 +35,9 @@ 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 +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 +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 diff --git a/Documentation/sound/alsa/CMIPCI.txt b/Documentation/sound/cards/cmipci.rst similarity index 86% rename from Documentation/sound/alsa/CMIPCI.txt rename to Documentation/sound/cards/cmipci.rst index 4e36e6e809cab..9ea1de6ec4ce6 100644 --- a/Documentation/sound/alsa/CMIPCI.txt +++ b/Documentation/sound/cards/cmipci.rst @@ -1,7 +1,8 @@ - Brief Notes on C-Media 8338/8738/8768/8770 Driver - ================================================= +================================================= +Brief Notes on C-Media 8338/8738/8768/8770 Driver +================================================= - Takashi Iwai +Takashi Iwai Front/Rear Multi-channel Playback @@ -30,19 +31,20 @@ 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 *** +.. 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 + +.. [#] + 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... ] + 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 @@ -73,12 +75,14 @@ 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 @@ -89,13 +93,15 @@ 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 +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 +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 @@ -111,11 +117,14 @@ 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. @@ -140,31 +149,40 @@ 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 +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) +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 +IEC958 Loop + SPDIF input data is loop back into SPDIF output (aka bypass) -"IEC958 Copyright" - Set the copyright bit. +IEC958 Copyright + Set the copyright bit. -"IEC958 5V" - Select 0.5V (coax) or 5V (optical) interface. +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 Monitor + SPDIF input is routed to DAC. -"IEC958 In Phase Inverse" - Set SPDIF input format as inverse. +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. +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. @@ -217,7 +235,7 @@ to enable MIDI support. Valid I/O ports are 0x300, 0x310, 0x320 and 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 +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 diff --git a/Documentation/sound/alsa/emu10k1-jack.txt b/Documentation/sound/cards/emu10k1-jack.rst similarity index 89% rename from Documentation/sound/alsa/emu10k1-jack.txt rename to Documentation/sound/cards/emu10k1-jack.rst index 751d45036a05a..6597f1ea83f01 100644 --- a/Documentation/sound/alsa/emu10k1-jack.txt +++ b/Documentation/sound/cards/emu10k1-jack.rst @@ -1,3 +1,7 @@ +================================================================= +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 @@ -7,8 +11,6 @@ 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 @@ -23,14 +25,15 @@ 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 + /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). +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 @@ -49,10 +52,11 @@ 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 (& 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) @@ -69,6 +73,6 @@ 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 diff --git a/Documentation/sound/alsa/hdspm.txt b/Documentation/sound/cards/hdspm.rst similarity index 56% rename from Documentation/sound/alsa/hdspm.txt rename to Documentation/sound/cards/hdspm.rst index 7ba31948dea72..5373e51ed076f 100644 --- a/Documentation/sound/alsa/hdspm.txt +++ b/Documentation/sound/cards/hdspm.rst @@ -1,21 +1,24 @@ +======================================= Software Interface ALSA-DSP MADI Driver +======================================= (translated from German, so no good English ;-), -2004 - winfried ritsch - +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. +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: +Hardware functionality +====================== - Audio transmission: +Audio transmission +------------------ - number of channels -- depends on transmission mode +* 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, @@ -23,31 +26,34 @@ Software Interface ALSA-DSP MADI Driver allocated. So also the throughput of the PCI system can be scaled. (Only important for low performance boards). - Single Speed -- 1..64 channels +* 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 +* 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 +* Quad Speed -- 1..16 channels - Note: Choosing the 56-channel mode for +.. 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) +* Format -- signed 32 Bit Little Endian (SNDRV_PCM_FMTBIT_S32_LE) - Sample Rates -- +* Sample Rates -- Single Speed -- 32000, 44100, 48000 @@ -55,14 +61,13 @@ Software Interface ALSA-DSP MADI Driver Quad Speed -- 128000, 176400, 192000 (untested) - access-mode -- MMAP (memory mapped), Not interleaved - (PCM_NON-INTERLEAVED) +* access-mode -- MMAP (memory mapped), Not interleaved (PCM_NON-INTERLEAVED) - buffer-sizes -- 64,128,256,512,1024,2048,8192 Samples +* buffer-sizes -- 64,128,256,512,1024,2048,8192 Samples - fragments -- 2 +* fragments -- 2 - Hardware-pointer -- 2 Modi +* Hardware-pointer -- 2 Modi The Card supports the readout of the actual Buffer-pointer, @@ -74,53 +79,54 @@ Software Interface ALSA-DSP MADI Driver 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 + * Precise Pointer -- off interrupt used for pointer-calculation - - Precise Pointer -- on + + * Precise Pointer -- on hardware pointer used. - Controller: - +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. +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 !!! +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" +* System Clock -- suspended !!!! - Access -- Read Write - - Values -- "Master" "Slave" + * 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 @@ -128,106 +134,107 @@ Software Interface ALSA-DSP MADI Driver a studio should have working synchronisations setup. So use Clock-source-controller instead !!!! - Clock Source +* Clock Source - Name -- "Sample Clock Source" + * Name -- "Sample Clock Source" - Access -- Read Write + * 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" + * 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 +* Preferred Sync Ref - Name -- "Preferred Sync Reference" + * Name -- "Preferred Sync Reference" - Access -- Read Write + * Access -- Read Write - Values -- "Word" "MADI" + * 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 --- +* TX 64 channel - Name -- "TX 64 channels mode" + * Name -- "TX 64 channels mode" - Access -- Read Write + * Access -- Read Write - Values -- 0 1 + * 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 --- +* Clear TMS - Name -- "Clear Track Marker" + * Name -- "Clear Track Marker" - Access -- Read Write + * Access -- Read Write - Values -- 0 1 + * Values -- 0 1 Don't use to lower 5 Audio-bits on AES as additional Bits. - Safe Mode oder Auto Input --- +* Safe Mode oder Auto Input - Name -- "Safe Mode" + * Name -- "Safe Mode" - Access -- Read Write + * Access -- Read Write - Values -- 0 1 - - (default on) + * 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 --- +* Input - Name -- "Input Select" + * Name -- "Input Select" - Access -- Read Write + * Access -- Read Write - Values -- optical coaxial + * Values -- optical coaxial Choosing the Input, optical or coaxial. If Safe-mode is active, this is the preferred Input. --------------- Mixer ---------------------- +Mixer +----- - Mixer +* Mixer - Name -- "Mixer" + * Name -- "Mixer" - Access -- Read Write + * Access -- Read Write - Values - + * Values - Here as a first value the channel-index is taken to get/set the @@ -235,40 +242,41 @@ Software Interface ALSA-DSP MADI Driver fader and 64-127 the playback to outputs fader. Value 0 is channel muted 0 and 32768 an amplification of 1. - Chn 1-64 +* Chn 1-64 fast mixer for the ALSA-mixer utils. The diagonal of the mixer-matrix is implemented from playback to output. - Line Out +* Line Out - Name -- "Line Out" + * Name -- "Line Out" - Access -- Read Write + * Access -- Read Write - Values -- 0 1 + * 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): +Information (only read access) +------------------------------ - Sample Rate +* Sample Rate - Name -- "System Sample Rate" + * Name -- "System Sample Rate" - Access -- Read-only + * Access -- Read-only getting the sample rate. - External Rate measured +* External Rate measured - Name -- "External Rate" + * Name -- "External Rate" - Access -- Read only + * Access -- Read only Should be "Autosync Rate", but Name used is @@ -276,79 +284,86 @@ Software Interface ALSA-DSP MADI Driver reported. - MADI Sync Status +* MADI Sync Status - Name -- "MADI Sync Lock Status" + * Name -- "MADI Sync Lock Status" - Access -- Read + * Access -- Read - Values -- 0,1,2 + * Values -- 0,1,2 MADI-Input is 0=Unlocked, 1=Locked, or 2=Synced. - Word Clock Sync Status +* Word Clock Sync Status - Name -- "Word Clock Lock Status" + * Name -- "Word Clock Lock Status" - Access -- Read + * Access -- Read - Values -- 0,1,2 + * Values -- 0,1,2 Word Clock Input is 0=Unlocked, 1=Locked, or 2=Synced. - AutoSync +* AutoSync - Name -- "AutoSync Reference" + * Name -- "AutoSync Reference" - Access -- Read + * Access -- Read - Values -- "WordClock", "MADI", "None" + * Values -- "WordClock", "MADI", "None" Sync-Reference is either "WordClock", "MADI" or none. - RX 64ch --- noch nicht implementiert +* RX 64ch --- noch nicht implementiert MADI-Receiver is in 64 channel mode oder 56 channel mode. - AB_inp --- not tested +* AB_inp --- not tested Used input for Auto-Input. - actual Buffer Position --- not implemented +* actual Buffer Position --- not implemented !!! this is a ALSA internal function, so no control is used !!! -Calling Parameter: +Calling Parameter +================= + +* index int array (min = 1, max = 8) - index int array (min = 1, max = 8), - "Index value for RME HDSPM interface." card-index within ALSA + 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." +* 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." +* 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." +* 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." +* 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 @@ -356,7 +371,9 @@ Calling Parameter: - enable_monitor int array (min = 1, max = 8), - "Enable Analog Out on Channel 63/64 by default." +* 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). diff --git a/Documentation/sound/alsa/img,spdif-in.txt b/Documentation/sound/cards/img-spdif-in.rst similarity index 68% rename from Documentation/sound/alsa/img,spdif-in.txt rename to Documentation/sound/cards/img-spdif-in.rst index 8b7505785fa67..7df9f5ae26097 100644 --- a/Documentation/sound/alsa/img,spdif-in.txt +++ b/Documentation/sound/cards/img-spdif-in.rst @@ -1,21 +1,25 @@ +================================================ +Imagination Technologies SPDIF Input Controllers +================================================ + The Imagination Technologies SPDIF Input controller contains the following controls: -name='IEC958 Capture Mask',index=0 +* 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 +* 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 +* 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' @@ -29,21 +33,21 @@ 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 +* 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 +* 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 +* 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 +* name='SPDIF In Lock Release Threshold',index=0 This control is used to change the threshold at which a lock is released. diff --git a/Documentation/sound/cards/index.rst b/Documentation/sound/cards/index.rst new file mode 100644 index 0000000000000..c016f8c3b88bf --- /dev/null +++ b/Documentation/sound/cards/index.rst @@ -0,0 +1,19 @@ +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 diff --git a/Documentation/sound/alsa/Joystick.txt b/Documentation/sound/cards/joystick.rst similarity index 56% rename from Documentation/sound/alsa/Joystick.txt rename to Documentation/sound/cards/joystick.rst index ccda41b10f8a3..a6e468c81d02f 100644 --- a/Documentation/sound/alsa/Joystick.txt +++ b/Documentation/sound/cards/joystick.rst @@ -1,7 +1,10 @@ +======================================= Analog Joystick Support on ALSA Drivers ======================================= - Oct. 14, 2003 - Takashi Iwai + +Oct. 14, 2003 + +Takashi Iwai General ------- @@ -34,44 +37,46 @@ 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 +============== ============= ============================================ +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 - ----------------------------- +======= ================= +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. +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" diff --git a/Documentation/sound/alsa/README.maya44 b/Documentation/sound/cards/maya44.rst similarity index 65% rename from Documentation/sound/alsa/README.maya44 rename to Documentation/sound/cards/maya44.rst index 67b2ea1cc31dc..bf09a584b4431 100644 --- a/Documentation/sound/alsa/README.maya44 +++ b/Documentation/sound/cards/maya44.rst @@ -1,10 +1,18 @@ -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 +================================= +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 -STATE OF DEVELOPMENT: +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). @@ -44,16 +52,17 @@ Things that do not seem to work: - 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: +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 +* 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. @@ -62,25 +71,26 @@ This is done in maya44.c, mainly because some of the WM8776 controls are used in 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 +* 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: +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. @@ -98,66 +108,79 @@ I propose some additional code for limiting the sampling rate when setting on a The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712). -SOUND DEVICES: +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) +* 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: +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 +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. -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). +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. -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 -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. -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: - +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 + 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. -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 +PCM out + DAC output of the corresponding channel (default setting) +Input 1 ... Input 4 + direct routing from ADC output of the selected input channel diff --git a/Documentation/sound/alsa/MIXART.txt b/Documentation/sound/cards/mixart.rst similarity index 83% rename from Documentation/sound/alsa/MIXART.txt rename to Documentation/sound/cards/mixart.rst index 4ee35b4fbe4a1..48aba98b088fa 100644 --- a/Documentation/sound/alsa/MIXART.txt +++ b/Documentation/sound/cards/mixart.rst @@ -1,5 +1,8 @@ - Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards - Digigram +============================================================== +Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards +============================================================== + +Digigram GENERAL @@ -48,11 +51,15 @@ formats are supported. Mixer ----- - and : analog volume control of playback and capture PCM. - and : digital volume control of each analog substream. - and : digital volume control of each AES/EBU substream. - : Loopback from 'pcm0c' to 'pcm0p' with digital volume -and mute control. + and + analog volume control of playback and capture PCM. + and + digital volume control of each analog substream. + and + digital volume control of each AES/EBU substream. + + 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 @@ -79,11 +86,14 @@ FIRMWARE 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.) +/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 diff --git a/Documentation/sound/alsa/SB-Live-mixer.txt b/Documentation/sound/cards/sb-live-mixer.rst similarity index 54% rename from Documentation/sound/alsa/SB-Live-mixer.txt rename to Documentation/sound/cards/sb-live-mixer.rst index f4b5988f450cf..bcb62fc99bbb7 100644 --- a/Documentation/sound/alsa/SB-Live-mixer.txt +++ b/Documentation/sound/cards/sb-live-mixer.rst @@ -1,6 +1,6 @@ - - Sound Blaster Live mixer / default DSP code - =========================================== +=========================================== +Sound Blaster Live mixer / default DSP code +=========================================== The EMU10K1 chips have a DSP part which can be programmed to support @@ -12,8 +12,8 @@ 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 --------------------------- +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 @@ -27,8 +27,8 @@ at the time. Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details. -2) Digital mixer controls -------------------------- +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 @@ -40,317 +40,334 @@ is mentioned in multiple controls, the signal is accumulated and can be wrapped 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 - +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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 +.. 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 +.. 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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``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 - +``name='Headphone Playback Volume',index=1`` +-------------------------------------------- This control attenuates the samples for the headphone output. -name='Headphone Center Playback Switch',index=1 - +``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 - +``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 +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 +* 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 +* 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 - +``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) +* 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: -------------------- +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 +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 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) +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 +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) diff --git a/Documentation/sound/alsa/serial-u16550.txt b/Documentation/sound/cards/serial-u16550.rst similarity index 92% rename from Documentation/sound/alsa/serial-u16550.txt rename to Documentation/sound/cards/serial-u16550.rst index c1919559d5091..197aeacea3da8 100644 --- a/Documentation/sound/alsa/serial-u16550.txt +++ b/Documentation/sound/cards/serial-u16550.rst @@ -1,14 +1,14 @@ - - Serial UART 16450/16550 MIDI driver - =================================== +=================================== +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) +* 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. @@ -22,11 +22,13 @@ 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 @@ -37,6 +39,7 @@ 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 \ @@ -47,6 +50,7 @@ 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 @@ -63,6 +67,7 @@ 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 diff --git a/Documentation/sound/cards/via82xx-mixer.rst b/Documentation/sound/cards/via82xx-mixer.rst new file mode 100644 index 0000000000000..6ee993d4535be --- /dev/null +++ b/Documentation/sound/cards/via82xx-mixer.rst @@ -0,0 +1,8 @@ +============= +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. diff --git a/Documentation/sound/alsa/Channel-Mapping-API.txt b/Documentation/sound/designs/channel-mapping-api.rst similarity index 75% rename from Documentation/sound/alsa/Channel-Mapping-API.txt rename to Documentation/sound/designs/channel-mapping-api.rst index 3c43d1a4ca0ea..58e6312a43c00 100644 --- a/Documentation/sound/alsa/Channel-Mapping-API.txt +++ b/Documentation/sound/designs/channel-mapping-api.rst @@ -1,9 +1,11 @@ +============================ ALSA PCM channel-mapping API ============================ - Takashi Iwai -GENERAL -------- +Takashi Iwai + +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 @@ -11,9 +13,9 @@ 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 } +``{ front_left, front_right }`` while a 4.0 surround PCM stream has a channel map of - { front left, front right, rear left, rear right }. +``{ 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 @@ -29,8 +31,8 @@ specification. These are the main motivations for the new channel mapping API. -DESIGN ------- +Design +====== Actually, "the channel mapping API" doesn't introduce anything new in the kernel/user-space ABI perspective. It uses only the existing @@ -39,10 +41,11 @@ 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 + +* 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. @@ -50,32 +53,35 @@ 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 +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... +``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 +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 +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. +These new TLV types are defined in ``sound/tlv.h``. -The available channel position values are defined in sound/asound.h, +The available channel position values are defined in ``sound/asound.h``, here is a cut: -/* channel positions */ -enum { +:: + + /* channel positions */ + enum { SNDRV_CHMAP_UNKNOWN = 0, SNDRV_CHMAP_NA, /* N/A, silent */ SNDRV_CHMAP_MONO, /* mono stream */ @@ -107,11 +113,13 @@ enum { 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 @@ -120,19 +128,21 @@ to be returned will contain such as: 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) + #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, +``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 +When ``SNDRV_CHMAP_DRIVER_SPEC`` is set, all the channel position values don't follow the standard definition above but driver-specific. -* READ OPERATION +Read Operation +-------------- The control read operation is for providing the current channel map of the given stream. The control element returns an integer array @@ -140,9 +150,10 @@ 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. +``UNKNOWN``. -* WRITE OPERATION +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 diff --git a/Documentation/sound/alsa/compress_offload.txt b/Documentation/sound/designs/compress-offload.rst similarity index 73% rename from Documentation/sound/alsa/compress_offload.txt rename to Documentation/sound/designs/compress-offload.rst index 8ba556a131c37..ad4bfbdacc835 100644 --- a/Documentation/sound/alsa/compress_offload.txt +++ b/Documentation/sound/designs/compress-offload.rst @@ -1,10 +1,14 @@ - compress_offload.txt - ===================== - Pierre-Louis.Bossart - Vinod Koul +========================= +ALSA Compress-Offload API +========================= + +Pierre-Louis.Bossart + +Vinod Koul -Overview +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 @@ -27,8 +31,9 @@ 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 +Requirements +============ The main requirements are: - separation between byte counts and time. Compressed formats may have @@ -63,7 +68,7 @@ The main requirements are: 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 +- 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 @@ -72,7 +77,7 @@ The main requirements are: 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. @@ -95,43 +100,44 @@ 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. +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. @@ -145,6 +151,7 @@ Modifications include: - 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 @@ -162,19 +169,19 @@ 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_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 +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 +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 @@ -189,10 +196,12 @@ Sequence flow for gapless would be: - 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: +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. @@ -225,7 +234,9 @@ Not supported: rendered output in time, this does not deal with underrun/overrun and maybe dealt in user-library -Credits: + +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 diff --git a/Documentation/sound/designs/control-names.rst b/Documentation/sound/designs/control-names.rst new file mode 100644 index 0000000000000..7fedd0f33cd97 --- /dev/null +++ b/Documentation/sound/designs/control-names.rst @@ -0,0 +1,142 @@ +=========================== +Standard ALSA Control Names +=========================== + +This document describes standard names of mixer controls. + +Standard Syntax +--------------- +Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION + + +DIRECTION +~~~~~~~~~ +================ =============== + 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 +~~~~~~~ +============ ================================================== + 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) +============================================ ====================================== diff --git a/Documentation/sound/designs/index.rst b/Documentation/sound/designs/index.rst new file mode 100644 index 0000000000000..04dcdae3e4f2a --- /dev/null +++ b/Documentation/sound/designs/index.rst @@ -0,0 +1,15 @@ +Designs and Implementations +=========================== + +.. toctree:: + :maxdepth: 2 + + control-names + channel-mapping-api + compress-offload + timestamping + jack-controls + procfile + powersave + oss-emulation + seq-oss diff --git a/Documentation/sound/alsa/Jack-Controls.txt b/Documentation/sound/designs/jack-controls.rst similarity index 86% rename from Documentation/sound/alsa/Jack-Controls.txt rename to Documentation/sound/designs/jack-controls.rst index fe1c5e0c8555e..ae25b1531bb0c 100644 --- a/Documentation/sound/alsa/Jack-Controls.txt +++ b/Documentation/sound/designs/jack-controls.rst @@ -1,3 +1,7 @@ +================== +ALSA Jack Controls +================== + Why we need Jack kcontrols ========================== @@ -29,11 +33,12 @@ How to use jack kcontrols ========================= In order to keep compatibility, snd_jack_new() has been modified by -adding two params :- +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. +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 diff --git a/Documentation/sound/alsa/OSS-Emulation.txt b/Documentation/sound/designs/oss-emulation.rst similarity index 70% rename from Documentation/sound/alsa/OSS-Emulation.txt rename to Documentation/sound/designs/oss-emulation.rst index 152ca2a3f1bd9..e8dcb9633e7b7 100644 --- a/Documentation/sound/alsa/OSS-Emulation.txt +++ b/Documentation/sound/designs/oss-emulation.rst @@ -1,7 +1,8 @@ - NOTES ON KERNEL OSS-EMULATION - ============================= +============================= +Notes on Kernel OSS-Emulation +============================= - Jan. 22, 2004 Takashi Iwai +Jan. 22, 2004 Takashi Iwai Modules @@ -14,18 +15,18 @@ 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 +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 +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. +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 +``alias sound-slot-0 snd-card-0`` doesn't work any more like the old modutils). The currently available OSS configuration is shown in @@ -42,6 +43,7 @@ Device Mapping ============== ALSA supports the following OSS device files: +:: PCM: /dev/dspX @@ -61,48 +63,55 @@ ALSA supports the following OSS device files: 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.) +/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 +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) +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, +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) +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 @@ -118,43 +127,52 @@ 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 +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 +``app_name`` is the name of application with (higher priority) or without path. -fragments specifies the number of fragments or zero if no specific +``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 +``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 +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 +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 @@ -171,10 +189,11 @@ 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. +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 @@ -183,15 +202,18 @@ 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 +Also, for clearing all the current configuration, send ``erase`` command as below: +:: % echo "erase" > /proc/asound/card0/pcm0p/oss @@ -211,40 +233,43 @@ 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 +==================== ===================== ===== +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. +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 @@ -261,6 +286,7 @@ 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 @@ -284,12 +310,18 @@ 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. @@ -302,4 +334,3 @@ 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. - diff --git a/Documentation/sound/alsa/powersave.txt b/Documentation/sound/designs/powersave.rst similarity index 76% rename from Documentation/sound/alsa/powersave.txt rename to Documentation/sound/designs/powersave.rst index 9657e8099228d..138157452eb92 100644 --- a/Documentation/sound/alsa/powersave.txt +++ b/Documentation/sound/designs/powersave.rst @@ -1,9 +1,10 @@ +========================== 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. +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 @@ -11,20 +12,21 @@ 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 +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 +``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 +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): +``/sys/modules/snd_ac97_codec/parameters/power_save`` (usually as root): +:: # echo 10 > /sys/modules/snd_ac97_codec/parameters/power_save diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/designs/procfile.rst similarity index 71% rename from Documentation/sound/alsa/Procfile.txt rename to Documentation/sound/designs/procfile.rst index 7f8a0d3259057..29a466851fd2e 100644 --- a/Documentation/sound/alsa/Procfile.txt +++ b/Documentation/sound/designs/procfile.rst @@ -1,20 +1,22 @@ - Proc Files of ALSA Drivers - ========================== - Takashi Iwai +========================== +Proc Files of ALSA Drivers +========================== + +Takashi Iwai 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. +card-specific files are stored in the ``card*`` subdirectories. Global Information ------------------- +================== cards Shows the list of currently configured ALSA drivers, @@ -31,15 +33,15 @@ devices meminfo Shows the status of allocated pages via ALSA drivers. - Appears only when CONFIG_SND_DEBUG=y. + Appears only when ``CONFIG_SND_DEBUG=y``. hwdep Lists the currently available hwdep devices in format of - -: + ``-: `` pcm Lists the currently available PCM devices in format of - -: : : + ``-: : : `` timer Lists the currently available timer devices @@ -54,23 +56,23 @@ oss/sndstat Card Specific Files -------------------- +=================== -The card-specific files are found in /proc/asound/card* directories. +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 +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 +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 +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#* +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), @@ -81,26 +83,27 @@ details. PCM Proc Files --------------- +============== -card*/pcm*/info +``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. +``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 + 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 + + * 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 @@ -117,72 +120,74 @@ card*/pcm*/xrun_debug buggy) hardware that doesn't give smooth pointer updates. This feature is enabled via the bit 2. -card*/pcm*/sub*/info +``card*/pcm*/sub*/info`` The general information of this PCM sub-stream. -card*/pcm*/sub*/status +``card*/pcm*/sub*/status`` The current status of this PCM sub-stream, elapsed time, H/W position, etc. -card*/pcm*/sub*/hw_params +``card*/pcm*/sub*/hw_params`` The hardware parameters set for this sub-stream. -card*/pcm*/sub*/sw_params +``card*/pcm*/sub*/sw_params`` The soft parameters set for this sub-stream. -card*/pcm*/sub*/prealloc +``card*/pcm*/sub*/prealloc`` The buffer pre-allocation information. -card*/pcm*/sub*/xrun_injection +``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#?-? +``card*/codec97#*/ac97#?-?`` Shows the general information of this AC97 codec chip, such as name, capabilities, set up. -card*/codec97#0/ac97#?-?+regs +``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* +``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#* +``card*/codec#*`` Shows the general codec information and the attribute of each widget node. -card*/eld#* +``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#*`. + 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. @@ -203,7 +208,7 @@ seq/oss 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 @@ -211,24 +216,23 @@ 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) +- 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 +- 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 +- AC97 proc files, ``codec97#*/*`` files for USB audio/midi, - - output of lsusb -v - - stream* files in card directory +- output of ``lsusb -v`` +- ``stream*`` files in card directory The ALSA bug-tracking system is found at: - - https://bugtrack.alsa-project.org/alsa-bug/ +https://bugtrack.alsa-project.org/alsa-bug/ diff --git a/Documentation/sound/designs/seq-oss.rst b/Documentation/sound/designs/seq-oss.rst new file mode 100644 index 0000000000000..e82ffe0e7f436 --- /dev/null +++ b/Documentation/sound/designs/seq-oss.rst @@ -0,0 +1,371 @@ +=============================== +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. + diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/designs/timestamping.rst similarity index 56% rename from Documentation/sound/alsa/timestamping.txt rename to Documentation/sound/designs/timestamping.rst index 9d579aefbffd4..2b0fff5034151 100644 --- a/Documentation/sound/alsa/timestamping.txt +++ b/Documentation/sound/designs/timestamping.rst @@ -1,18 +1,22 @@ +===================== +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. + 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. + 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 @@ -22,15 +26,15 @@ 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. +- ``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 +``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 @@ -38,17 +42,18 @@ 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-->||<---avail->| + |<----------------- delay---------------------->| | + |<----ring buffer length---->| ---------------------------------------------------------------> time - ^ ^ ^ ^ ^ - | | | | | - analog link dma app FullBuffer - time time time time time - | | | | | - |< codec delay >|<--hw delay-->||<---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 @@ -113,11 +118,11 @@ 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 +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 +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. +``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 @@ -135,36 +140,42 @@ 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 +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 +:: + + $ ./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 +:: + + $ ./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 +:: + + $ ./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 @@ -181,20 +192,24 @@ 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 +:: + + $ ./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 +:: + + $ ./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 diff --git a/Documentation/sound/alsa/HD-Audio-Controls.txt b/Documentation/sound/hd-audio/controls.rst similarity index 92% rename from Documentation/sound/alsa/HD-Audio-Controls.txt rename to Documentation/sound/hd-audio/controls.rst index e9621e349e173..f2ebc4f79b44d 100644 --- a/Documentation/sound/alsa/HD-Audio-Controls.txt +++ b/Documentation/sound/hd-audio/controls.rst @@ -1,16 +1,21 @@ +====================================== +HD-Audio Codec-Specific Mixer Controls +====================================== + + This file explains the codec-specific mixer controls. Realtek codecs -------------- -* Channel Mode +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 +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 @@ -30,24 +35,24 @@ Realtek codecs IDT/Sigmatel codecs ------------------- -* Analog Loopback +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 +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 +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 +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" @@ -57,19 +62,19 @@ IDT/Sigmatel codecs VIA codecs ---------- -* Smart 5.1 +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 +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 +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 @@ -78,7 +83,7 @@ VIA codecs headphones and speakers because there is only one DAC connected to a mixer widget. -* Dynamic Power-Control +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 @@ -86,7 +91,7 @@ VIA codecs doesn't provide a proper jack-detection, this won't work; in such a case, turn this control OFF. -* Jack Detect +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 @@ -96,21 +101,21 @@ VIA codecs Conexant codecs --------------- -* Auto-Mute Mode +Auto-Mute Mode See Reatek codecs. Analog codecs -------------- -* Channel Mode +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 +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. diff --git a/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt b/Documentation/sound/hd-audio/dp-mst.rst similarity index 69% rename from Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt rename to Documentation/sound/hd-audio/dp-mst.rst index 82744ac3513d8..58b72437e6c3e 100644 --- a/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt +++ b/Documentation/sound/hd-audio/dp-mst.rst @@ -1,3 +1,7 @@ +======================= +HD-Audio DP-MST Support +======================= + To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin and dynamic pcm assignment. @@ -44,10 +48,12 @@ Build Jack ---------- - dyn_pcm_assign -Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly. + + 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. + + Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically. Unsolicited Event Enabling @@ -58,16 +64,20 @@ 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 + + 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. + + 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 diff --git a/Documentation/sound/hd-audio/index.rst b/Documentation/sound/hd-audio/index.rst new file mode 100644 index 0000000000000..f8a72ffffe66f --- /dev/null +++ b/Documentation/sound/hd-audio/index.rst @@ -0,0 +1,10 @@ +HD-Audio +======== + +.. toctree:: + :maxdepth: 2 + + notes + models + controls + dp-mst diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst new file mode 100644 index 0000000000000..5338673c88d95 --- /dev/null +++ b/Documentation/sound/hd-audio/models.rst @@ -0,0 +1,518 @@ +============================== +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) diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/hd-audio/notes.rst similarity index 61% rename from Documentation/sound/alsa/HD-Audio.txt rename to Documentation/sound/hd-audio/notes.rst index d4510ebf2e8c5..168d0cfab1cec 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/hd-audio/notes.rst @@ -1,10 +1,12 @@ -MORE NOTES ON HD-AUDIO DRIVER ============================= - Takashi Iwai +More Notes on HD-Audio Driver +============================= +Takashi Iwai -GENERAL -------- + +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 @@ -40,28 +42,28 @@ 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/ +* http://www.intel.com/standards/hdaudio/ -HD-AUDIO CONTROLLER -------------------- +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. +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 +``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, +``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 @@ -74,9 +76,9 @@ 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. +via ``bdl_pos_adj`` option. -When `bdl_pos_adj` is a negative value (as default), it's assigned to +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 @@ -84,19 +86,19 @@ 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 ------------------------------------------------------------------------- +:: + + 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 @@ -108,24 +110,24 @@ 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 +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. +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. +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 +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 @@ -134,17 +136,17 @@ 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 +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 --------------- +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 @@ -153,13 +155,15 @@ 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 @@ -182,32 +186,33 @@ 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 +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. +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, +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 +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 +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 + +* 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 +* 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 @@ -236,23 +241,23 @@ 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 +* 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 +* 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 +* 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 +* 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 @@ -284,7 +289,7 @@ 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 @@ -293,45 +298,45 @@ below. You'd need to enable hwdep for using these tools. See "Kernel Configuration" section. -OTHER ISSUES ------------- +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. +``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 +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_*` +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. +``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 +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 +``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. +``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. @@ -351,161 +356,178 @@ 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. +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 ------------------------------------------------------------------------- +:: + + # 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 +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. +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 +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. +-------------- +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 +:: + + [codec] + 0x12345678 0xabcd1234 2 - [model] - auto + [model] + auto - [pincfg] - 0x12 0x411111f0 + [pincfg] + 0x12 0x411111f0 - [verb] - 0x20 0x500 0x03 - 0x20 0x400 0xff + [verb] + 0x20 0x500 0x03 + 0x20 0x400 0xff - [hint] - jack_detect = no ------------------------------------------------------------------------- + [hint] + jack_detect = no -The file needs to have a line `[codec]`. The next line should contain + +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 @@ -514,66 +536,68 @@ 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. +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. +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` +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 +:: + + [codec] + 0x12345678 0xabcd1234 2 + + [vendor_id] + 0xdeadbeef - [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]``, 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 - [subsystem_id] - 0xffff1111 + [revision_id] + 0x10 - [revision_id] - 0x10 + [chip_name] + My-own NEWS-0002 - [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 +``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 +``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 @@ -592,63 +616,65 @@ 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 +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` +``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 +``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 ------------------------------------------------------------------------- +:: + + # 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 +:: + + # 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 ------------------------------------------------------------------------- +:: + + % 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 +* 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 @@ -657,14 +683,14 @@ 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 +* 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 @@ -673,60 +699,60 @@ 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 +kernel-bugzilla + https://bugzilla.kernel.org/ +alsa-devel ML + alsa-devel@alsa-project.org -DEBUG TOOLS ------------ +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 +--------- +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 +* 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 +* 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. +run with ``--no-upload`` option, and attach the generated file. -There are some other useful options. See `--help` option output for +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 +``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 +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 +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. +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 @@ -739,19 +765,20 @@ 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 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 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 - % 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 @@ -760,22 +787,22 @@ 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 +* 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/ +* 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 +* 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 @@ -784,14 +811,14 @@ proc-compatible output. The hda-analyzer: -- http://git.alsa-project.org/?p=alsa.git;a=tree;f=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 +* 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 @@ -800,11 +827,11 @@ program. The tarball and GIT trees are found in the web page at: -- http://helllabs.org/codecgraph/ +* 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 @@ -817,13 +844,14 @@ 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 - .... ------------------------------------------------------------------------- +:: + + % 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 @@ -832,14 +860,14 @@ 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 +* 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 @@ -849,5 +877,4 @@ firmware patch file (see "Early Patching" section). The program is included in alsa-tools now: -- git://git.alsa-project.org/alsa-tools.git - +* git://git.alsa-project.org/alsa-tools.git diff --git a/Documentation/sound/index.rst b/Documentation/sound/index.rst new file mode 100644 index 0000000000000..47b89f014e694 --- /dev/null +++ b/Documentation/sound/index.rst @@ -0,0 +1,20 @@ +=================================== +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` diff --git a/Documentation/sound/kernel-api/alsa-driver-api.rst b/Documentation/sound/kernel-api/alsa-driver-api.rst new file mode 100644 index 0000000000000..14cd138989e35 --- /dev/null +++ b/Documentation/sound/kernel-api/alsa-driver-api.rst @@ -0,0 +1,134 @@ +=================== +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 diff --git a/Documentation/sound/kernel-api/index.rst b/Documentation/sound/kernel-api/index.rst new file mode 100644 index 0000000000000..d0e6df35b4b4d --- /dev/null +++ b/Documentation/sound/kernel-api/index.rst @@ -0,0 +1,8 @@ +ALSA Kernel API Documentation +============================= + +.. toctree:: + :maxdepth: 2 + + alsa-driver-api + writing-an-alsa-driver diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst new file mode 100644 index 0000000000000..95c5443eff38b --- /dev/null +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -0,0 +1,4219 @@ +====================== +Writing an ALSA Driver +====================== + +:Author: Takashi Iwai +:Date: Oct 15, 2007 +:Edition: 0.3.7 + +Preface +======= + +This document describes how to write an `ALSA (Advanced Linux Sound +Architecture) `__ 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 ` 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 + #include + #include + #include + #include + + /* 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 + #include + #include + #include + #include + +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 ```` for +interrupt handling, and ```` for I/O access. If you use the +:c:func:`mdelay()` or :c:func:`udelay()` functions, you'll need +to include ```` too. + +The ALSA interfaces like the PCM and control APIs are defined in other +```` header files. They have to be included after +````. + +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 +`, 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 ` 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 ` 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 ` 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 ` 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 +` 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 ` 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 ` +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 ```` +first. In addition, ```` 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 + .... + + /* 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 +` 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 ````, 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 ````. 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 +`) 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 ```` 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 +` 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 +` 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 ` 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 ````. 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 +` 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 +` 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 ````. 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 ` 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 ` 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 +` 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 ` 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 ` 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 ```` 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 ````. + +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 ` 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 +````. + +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 ` manually afterward. +However, :c:type:`struct 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 +` 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 ````. + +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 ````. + +FM registers can be directly accessed through the direct-FM API, defined +in ````. 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 +````. 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 +````. + +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 +````. + +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 ` 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 + + /* 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 ````. + +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. diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/soc/clocking.rst similarity index 94% rename from Documentation/sound/alsa/soc/clocking.txt rename to Documentation/sound/soc/clocking.rst index b1300162e01ce..32122d6877a3d 100644 --- a/Documentation/sound/alsa/soc/clocking.txt +++ b/Documentation/sound/soc/clocking.rst @@ -1,3 +1,4 @@ +============== Audio Clocking ============== @@ -30,15 +31,9 @@ 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 +- 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 diff --git a/Documentation/sound/soc/codec-to-codec.rst b/Documentation/sound/soc/codec-to-codec.rst new file mode 100644 index 0000000000000..810109d7500df --- /dev/null +++ b/Documentation/sound/soc/codec-to-codec.rst @@ -0,0 +1,108 @@ +============================================== +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. diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/soc/codec.rst similarity index 84% rename from Documentation/sound/alsa/soc/codec.txt rename to Documentation/sound/soc/codec.rst index db5f9c9ae1495..f87612b948120 100644 --- a/Documentation/sound/alsa/soc/codec.txt +++ b/Documentation/sound/soc/codec.rst @@ -1,3 +1,4 @@ +======================= ASoC Codec Class Driver ======================= @@ -9,16 +10,16 @@ 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. +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. +7. DAC Digital mute control. Its probably best to use this guide in conjunction with the existing codec driver code in sound/soc/codecs/ @@ -26,24 +27,25 @@ driver code in sound/soc/codecs/ ASoC Codec driver breakdown =========================== -1 - Codec DAI and PCM configuration ------------------------------------ +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 = { + 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 = { + }; + + struct snd_soc_dai_driver wm8731_dai = { .name = "wm8731-hifi", .playback = { .stream_name = "Playback", @@ -59,25 +61,27 @@ struct snd_soc_dai_driver wm8731_dai = { .formats = WM8731_FORMATS,}, .ops = &wm8731_dai_ops, .symmetric_rates = 1, -}; + }; -2 - Codec control IO --------------------- +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 ------------------------------ +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 @@ -86,18 +90,22 @@ Defines a single control as follows:- 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 @@ -109,25 +117,26 @@ Defines an single enumerated control as follows:- Defines a stereo enumerated control -4 - Codec Audio Operations --------------------------- +Codec Audio Operations +---------------------- The codec driver also supports the following ALSA PCM operations:- +:: -/* SoC audio ops */ -struct snd_soc_ops { + /* 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. ---------------------- +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. @@ -135,13 +144,14 @@ Please read dapm.txt for details of building the description. Please also see the examples in other codec drivers. -6 - DAPM event handler ----------------------- +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 */ @@ -155,8 +165,8 @@ Power states:- SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */ -7 - Codec DAC digital mute control ----------------------------------- +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. @@ -165,9 +175,10 @@ 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) -{ + 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; @@ -176,4 +187,4 @@ static int wm8974_mute(struct snd_soc_dai *dai, int mute) else snd_soc_write(codec, WM8974_DAC, mute_reg); return 0; -} + } diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/soc/dai.rst similarity index 71% rename from Documentation/sound/alsa/soc/DAI.txt rename to Documentation/sound/soc/dai.rst index c9679264c5598..55820e51708fd 100644 --- a/Documentation/sound/alsa/soc/DAI.txt +++ b/Documentation/sound/soc/dai.rst @@ -1,3 +1,7 @@ +================================== +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. @@ -5,21 +9,21 @@ 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 +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 :- +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 +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 @@ -30,13 +34,15 @@ 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. +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. +Left Justified + MSB is transmitted on transition of LRC. - o Right Justified - MSB is transmitted sample size BCLKs before LRC - transition. +Right Justified + MSB is transmitted sample size BCLKs before LRC transition. PCM === @@ -51,6 +57,8 @@ 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. +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. +Mode B + MSB is transmitted on rising edge of FRAME/SYNC. diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/soc/dapm.rst similarity index 62% rename from Documentation/sound/alsa/soc/dapm.txt rename to Documentation/sound/soc/dapm.rst index c45bd79f291ea..a27f42befa4dc 100644 --- a/Documentation/sound/alsa/soc/dapm.txt +++ b/Documentation/sound/soc/dapm.rst @@ -1,8 +1,9 @@ +=================================================== Dynamic Audio Power Management for Portable Devices =================================================== -1. Description -============== +Description +=========== Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices to use the minimum amount of power within the audio @@ -21,20 +22,28 @@ level power systems. There are 4 power domains within DAPM - 1. Codec bias domain - VREF, VMID (core codec and audio power) +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 +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 +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. +Stream domain + DACs and ADCs. + Enabled and disabled when stream playback/capture is started and stopped respectively. e.g. aplay, arecord. @@ -45,34 +54,57 @@ internal codec components). All audio components that effect power are called widgets hereafter. -2. DAPM Widgets -=============== +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) +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) @@ -84,52 +116,57 @@ 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 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) + 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), + 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), + 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 +------------------- 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) + 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), -}; + /* 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, + 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, @@ -137,48 +174,49 @@ 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 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 +* 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:- +when the Mic is inserted:-:: -static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event) -{ + 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), + SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), -2.4 Codec (BIAS) Domain ------------------------ +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 -------------------- +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), + 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. @@ -186,8 +224,8 @@ 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 -==================================== +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 @@ -201,13 +239,14 @@ 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 +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"}, @@ -216,22 +255,17 @@ connect the destination widget (wrt audio signal) with its source widgets. 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". +* 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); + 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 @@ -239,12 +273,13 @@ 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 +------------------------------- 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"}, @@ -254,52 +289,54 @@ 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 -=================== +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 +* 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. -5 DAPM Widget Events -==================== +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) -{ + /* 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 = + /* 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 ---------------- +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 */ +:: + + /* 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 */ diff --git a/Documentation/sound/alsa/soc/DPCM.txt b/Documentation/sound/soc/dpcm.rst similarity index 57% rename from Documentation/sound/alsa/soc/DPCM.txt rename to Documentation/sound/soc/dpcm.rst index 0110180b7ac63..395e5a5162820 100644 --- a/Documentation/sound/alsa/soc/DPCM.txt +++ b/Documentation/sound/soc/dpcm.rst @@ -1,8 +1,9 @@ +=========== Dynamic PCM =========== -1. Description -============== +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 @@ -23,22 +24,23 @@ 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 - ************* +:: + + | 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 @@ -55,50 +57,52 @@ 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 - ************* +:: + + ************* + 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 - ************* +:: + + ************* + 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. +1. Machine driver receives Jack removal event. - 2) Machine driver OR audio HAL disables the Headset path. +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. +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. +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. +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 @@ -112,36 +116,38 @@ 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. +1. Define the FE and BE DAI links. - 2) Define any FE/BE PCM operations. +2. Define any FE/BE PCM operations. - 3) Define widget graph connections. +3. Define widget graph connections. -1 FE and BE DAI links ---------------------- +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 - ************* + | 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[] = { + static struct snd_soc_dai_link machine_dais[] = { { .name = "PCM0 System", .stream_name = "System Playback", @@ -154,11 +160,11 @@ static struct snd_soc_dai_link machine_dais[] = { .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" +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 @@ -168,8 +174,9 @@ 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[] = { + static struct snd_soc_dai_link machine_dais[] = { .....< FE DAI links here > { .name = "Codec Headset", @@ -186,29 +193,30 @@ static struct snd_soc_dai_link machine_dais[] = { .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 ``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 - ************* +:: + + ************* + 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. @@ -220,10 +228,10 @@ 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 ----------------------- +FE/BE PCM operations +-------------------- -The BE above also exports some PCM operations and a "fixup" callback. The fixup +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. @@ -231,10 +239,11 @@ e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo fo 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, + 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, @@ -249,21 +258,22 @@ static int dai0_fixup(struct snd_soc_pcm_runtime *rtd, 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 --------------------------- +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"}, + /* 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 @@ -273,24 +283,25 @@ 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. +1. Front End PCM DAIs - i.e. struct snd_soc_dai_driver. - 2) DAPM graph showing DSP audio routing from FE DAIs to BEs. +2. DAPM graph showing DSP audio routing from FE DAIs to BEs. - 3) DAPM widgets from DSP graph. +3. DAPM widgets from DSP graph. - 4) Mixers for gains, routing, etc. +4. Mixers for gains, routing, etc. - 5) DMA configuration. +5. DMA configuration. - 6) BE AIF widgets. +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), + 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). @@ -301,33 +312,33 @@ 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 - ************* +:: + + ************* + 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 + 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 + 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 @@ -339,16 +350,17 @@ 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 = { + 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[] = { + static struct snd_soc_dai_link dais[] = { < ... more DAI links above ... > { .name = "MODEM", diff --git a/Documentation/sound/soc/index.rst b/Documentation/sound/soc/index.rst new file mode 100644 index 0000000000000..e57df2dab2fdf --- /dev/null +++ b/Documentation/sound/soc/index.rst @@ -0,0 +1,20 @@ +============== +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 diff --git a/Documentation/sound/alsa/soc/jack.txt b/Documentation/sound/soc/jack.rst similarity index 99% rename from Documentation/sound/alsa/soc/jack.txt rename to Documentation/sound/soc/jack.rst index fcf82a4172935..644b99ecba35e 100644 --- a/Documentation/sound/alsa/soc/jack.txt +++ b/Documentation/sound/soc/jack.rst @@ -1,3 +1,4 @@ +=================== ASoC jack detection =================== diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/soc/machine.rst similarity index 90% rename from Documentation/sound/alsa/soc/machine.txt rename to Documentation/sound/soc/machine.rst index 6bf2d2063b524..515c9444deafc 100644 --- a/Documentation/sound/alsa/soc/machine.txt +++ b/Documentation/sound/soc/machine.rst @@ -1,3 +1,4 @@ +=================== ASoC Machine Driver =================== @@ -9,9 +10,10 @@ 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 { + /* SoC machine */ + struct snd_soc_card { char *name; ... @@ -33,7 +35,7 @@ struct snd_soc_card { int num_links; ... -}; + }; probe()/remove() ---------------- @@ -55,9 +57,10 @@ 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 = { + /* 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", @@ -66,16 +69,17 @@ static struct snd_soc_dai_link corgi_dai = { .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 = { + /* corgi audio machine driver */ + static struct snd_soc_card snd_soc_corgi = { .name = "Corgi", .dai_link = &corgi_dai, .num_links = 1, -}; + }; Machine Power Map diff --git a/Documentation/sound/alsa/soc/overview.txt b/Documentation/sound/soc/overview.rst similarity index 83% rename from Documentation/sound/alsa/soc/overview.txt rename to Documentation/sound/soc/overview.rst index f3f28b7ae2420..dc8370bbfff66 100644 --- a/Documentation/sound/alsa/soc/overview.txt +++ b/Documentation/sound/soc/overview.rst @@ -1,5 +1,6 @@ -ALSA SoC Layer -============== +======================= +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. @@ -66,30 +67,3 @@ multiple re-usable component drivers :- 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. diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/soc/platform.rst similarity index 81% rename from Documentation/sound/alsa/soc/platform.txt rename to Documentation/sound/soc/platform.rst index 3a08a2c9150c9..d5574904d981d 100644 --- a/Documentation/sound/alsa/soc/platform.txt +++ b/Documentation/sound/soc/platform.rst @@ -1,3 +1,4 @@ +==================== ASoC Platform Driver ==================== @@ -9,21 +10,23 @@ Audio DMA ========= The platform DMA driver optionally supports the following ALSA operations:- +:: -/* SoC audio ops */ -struct snd_soc_ops { + /* 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 { + struct snd_soc_platform_driver { char *name; int (*probe)(struct platform_device *pdev); @@ -44,7 +47,7 @@ struct snd_soc_platform_driver { /* 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/ @@ -57,11 +60,11 @@ 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) +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. @@ -71,9 +74,9 @@ 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. +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. diff --git a/Documentation/sound/alsa/soc/pops_clicks.txt b/Documentation/sound/soc/pops-clicks.rst similarity index 87% rename from Documentation/sound/alsa/soc/pops_clicks.txt rename to Documentation/sound/soc/pops-clicks.rst index e1e74daa44972..de7eb2a6604ae 100644 --- a/Documentation/sound/alsa/soc/pops_clicks.txt +++ b/Documentation/sound/soc/pops-clicks.rst @@ -1,3 +1,4 @@ +===================== Audio Pops and Clicks ===================== @@ -20,10 +21,11 @@ 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 + 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. @@ -36,10 +38,11 @@ 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 + Startup Order - Input PGA --> Mixers --> ADC + + Shutdown Order - ADC --> Mixers --> Input PGA Zipper Noise diff --git a/Documentation/vfio-mediated-device.txt b/Documentation/vfio-mediated-device.txt new file mode 100644 index 0000000000000..b38afec35edc2 --- /dev/null +++ b/Documentation/vfio-mediated-device.txt @@ -0,0 +1,398 @@ +/* + * VFIO Mediated devices + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +Virtual Function I/O (VFIO) Mediated devices[1] +=============================================== + +The number of use cases for virtualizing DMA devices that do not have built-in +SR_IOV capability is increasing. Previously, to virtualize such devices, +developers had to create their own management interfaces and APIs, and then +integrate them with user space software. To simplify integration with user space +software, we have identified common requirements and a unified management +interface for such devices. + +The VFIO driver framework provides unified APIs for direct device access. It is +an IOMMU/device-agnostic framework for exposing direct device access to user +space in a secure, IOMMU-protected environment. This framework is used for +multiple devices, such as GPUs, network adapters, and compute accelerators. With +direct device access, virtual machines or user space applications have direct +access to the physical device. This framework is reused for mediated devices. + +The mediated core driver provides a common interface for mediated device +management that can be used by drivers of different devices. This module +provides a generic interface to perform these operations: + +* Create and destroy a mediated device +* Add a mediated device to and remove it from a mediated bus driver +* Add a mediated device to and remove it from an IOMMU group + +The mediated core driver also provides an interface to register a bus driver. +For example, the mediated VFIO mdev driver is designed for mediated devices and +supports VFIO APIs. The mediated bus driver adds a mediated device to and +removes it from a VFIO group. + +The following high-level block diagram shows the main components and interfaces +in the VFIO mediated driver framework. The diagram shows NVIDIA, Intel, and IBM +devices as examples, as these devices are the first devices to use this module. + + +---------------+ + | | + | +-----------+ | mdev_register_driver() +--------------+ + | | | +<------------------------+ | + | | mdev | | | | + | | bus | +------------------------>+ vfio_mdev.ko |<-> VFIO user + | | driver | | probe()/remove() | | APIs + | | | | +--------------+ + | +-----------+ | + | | + | MDEV CORE | + | MODULE | + | mdev.ko | + | +-----------+ | mdev_register_device() +--------------+ + | | | +<------------------------+ | + | | | | | nvidia.ko |<-> physical + | | | +------------------------>+ | device + | | | | callbacks +--------------+ + | | Physical | | + | | device | | mdev_register_device() +--------------+ + | | interface | |<------------------------+ | + | | | | | i915.ko |<-> physical + | | | +------------------------>+ | device + | | | | callbacks +--------------+ + | | | | + | | | | mdev_register_device() +--------------+ + | | | +<------------------------+ | + | | | | | ccw_device.ko|<-> physical + | | | +------------------------>+ | device + | | | | callbacks +--------------+ + | +-----------+ | + +---------------+ + + +Registration Interfaces +======================= + +The mediated core driver provides the following types of registration +interfaces: + +* Registration interface for a mediated bus driver +* Physical device driver interface + +Registration Interface for a Mediated Bus Driver +------------------------------------------------ + +The registration interface for a mediated bus driver provides the following +structure to represent a mediated device's driver: + + /* + * struct mdev_driver [2] - Mediated device's driver + * @name: driver name + * @probe: called when new device created + * @remove: called when device removed + * @driver: device driver structure + */ + struct mdev_driver { + const char *name; + int (*probe) (struct device *dev); + void (*remove) (struct device *dev); + struct device_driver driver; + }; + +A mediated bus driver for mdev should use this structure in the function calls +to register and unregister itself with the core driver: + +* Register: + + extern int mdev_register_driver(struct mdev_driver *drv, + struct module *owner); + +* Unregister: + + extern void mdev_unregister_driver(struct mdev_driver *drv); + +The mediated bus driver is responsible for adding mediated devices to the VFIO +group when devices are bound to the driver and removing mediated devices from +the VFIO when devices are unbound from the driver. + + +Physical Device Driver Interface +-------------------------------- + +The physical device driver interface provides the parent_ops[3] structure to +define the APIs to manage work in the mediated core driver that is related to +the physical device. + +The structures in the parent_ops structure are as follows: + +* dev_attr_groups: attributes of the parent device +* mdev_attr_groups: attributes of the mediated device +* supported_config: attributes to define supported configurations + +The functions in the parent_ops structure are as follows: + +* create: allocate basic resources in a driver for a mediated device +* remove: free resources in a driver when a mediated device is destroyed + +The callbacks in the parent_ops structure are as follows: + +* open: open callback of mediated device +* close: close callback of mediated device +* ioctl: ioctl callback of mediated device +* read : read emulation callback +* write: write emulation callback +* mmap: mmap emulation callback + +A driver should use the parent_ops structure in the function call to register +itself with the mdev core driver: + +extern int mdev_register_device(struct device *dev, + const struct parent_ops *ops); + +However, the parent_ops structure is not required in the function call that a +driver should use to unregister itself with the mdev core driver: + +extern void mdev_unregister_device(struct device *dev); + + +Mediated Device Management Interface Through sysfs +================================================== + +The management interface through sysfs enables user space software, such as +libvirt, to query and configure mediated devices in a hardware-agnostic fashion. +This management interface provides flexibility to the underlying physical +device's driver to support features such as: + +* Mediated device hot plug +* Multiple mediated devices in a single virtual machine +* Multiple mediated devices from different physical devices + +Links in the mdev_bus Class Directory +------------------------------------- +The /sys/class/mdev_bus/ directory contains links to devices that are registered +with the mdev core driver. + +Directories and files under the sysfs for Each Physical Device +-------------------------------------------------------------- + +|- [parent physical device] +|--- Vendor-specific-attributes [optional] +|--- [mdev_supported_types] +| |--- [] +| | |--- create +| | |--- name +| | |--- available_instances +| | |--- device_api +| | |--- description +| | |--- [devices] +| |--- [] +| | |--- create +| | |--- name +| | |--- available_instances +| | |--- device_api +| | |--- description +| | |--- [devices] +| |--- [] +| |--- create +| |--- name +| |--- available_instances +| |--- device_api +| |--- description +| |--- [devices] + +* [mdev_supported_types] + + The list of currently supported mediated device types and their details. + + [], device_api, and available_instances are mandatory attributes + that should be provided by vendor driver. + +* [] + + The [] name is created by adding the the device driver string as a + prefix to the string provided by the vendor driver. This format of this name + is as follows: + + sprintf(buf, "%s-%s", dev_driver_string(parent->dev), group->name); + +* device_api + + This attribute should show which device API is being created, for example, + "vfio-pci" for a PCI device. + +* available_instances + + This attribute should show the number of devices of type that can be + created. + +* [device] + + This directory contains links to the devices of type that have been +created. + +* name + + This attribute should show human readable name. This is optional attribute. + +* description + + This attribute should show brief features/description of the type. This is + optional attribute. + +Directories and Files Under the sysfs for Each mdev Device +---------------------------------------------------------- + +|- [parent phy device] +|--- [$MDEV_UUID] + |--- remove + |--- mdev_type {link to its type} + |--- vendor-specific-attributes [optional] + +* remove (write only) +Writing '1' to the 'remove' file destroys the mdev device. The vendor driver can +fail the remove() callback if that device is active and the vendor driver +doesn't support hot unplug. + +Example: + # echo 1 > /sys/bus/mdev/devices/$mdev_UUID/remove + +Mediated device Hot plug: +------------------------ + +Mediated devices can be created and assigned at runtime. The procedure to hot +plug a mediated device is the same as the procedure to hot plug a PCI device. + +Translation APIs for Mediated Devices +===================================== + +The following APIs are provided for translating user pfn to host pfn in a VFIO +driver: + +extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, + int npage, int prot, unsigned long *phys_pfn); + +extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, + int npage); + +These functions call back into the back-end IOMMU module by using the pin_pages +and unpin_pages callbacks of the struct vfio_iommu_driver_ops[4]. Currently +these callbacks are supported in the TYPE1 IOMMU module. To enable them for +other IOMMU backend modules, such as PPC64 sPAPR module, they need to provide +these two callback functions. + +Using the Sample Code +===================== + +mtty.c in samples/vfio-mdev/ directory is a sample driver program to +demonstrate how to use the mediated device framework. + +The sample driver creates an mdev device that simulates a serial port over a PCI +card. + +1. Build and load the mtty.ko module. + + This step creates a dummy device, /sys/devices/virtual/mtty/mtty/ + + Files in this device directory in sysfs are similar to the following: + + # tree /sys/devices/virtual/mtty/mtty/ + /sys/devices/virtual/mtty/mtty/ + |-- mdev_supported_types + | |-- mtty-1 + | | |-- available_instances + | | |-- create + | | |-- device_api + | | |-- devices + | | `-- name + | `-- mtty-2 + | |-- available_instances + | |-- create + | |-- device_api + | |-- devices + | `-- name + |-- mtty_dev + | `-- sample_mtty_dev + |-- power + | |-- autosuspend_delay_ms + | |-- control + | |-- runtime_active_time + | |-- runtime_status + | `-- runtime_suspended_time + |-- subsystem -> ../../../../class/mtty + `-- uevent + +2. Create a mediated device by using the dummy device that you created in the + previous step. + + # echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" > \ + /sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create + +3. Add parameters to qemu-kvm. + + -device vfio-pci,\ + sysfsdev=/sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001 + +4. Boot the VM. + + In the Linux guest VM, with no hardware on the host, the device appears + as follows: + + # lspci -s 00:05.0 -xxvv + 00:05.0 Serial controller: Device 4348:3253 (rev 10) (prog-if 02 [16550]) + Subsystem: Device 4348:3253 + Physical Slot: 5 + Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- + Stepping- SERR- FastB2B- DisINTx- + Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- + SERR- Link[LNKA] -> GSI 10 (level, high) -> IRQ +10 + 0000:00:05.0: ttyS1 at I/O 0xc150 (irq = 10) is a 16550A + 0000:00:05.0: ttyS2 at I/O 0xc158 (irq = 10) is a 16550A + + +5. In the Linux guest VM, check the serial ports. + + # setserial -g /dev/ttyS* + /dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4 + /dev/ttyS1, UART: 16550A, Port: 0xc150, IRQ: 10 + /dev/ttyS2, UART: 16550A, Port: 0xc158, IRQ: 10 + +6. Using a minicom or any terminal enulation program, open port /dev/ttyS1 or + /dev/ttyS2 with hardware flow control disabled. + +7. Type data on the minicom terminal or send data to the terminal emulation + program and read the data. + + Data is loop backed from hosts mtty driver. + +8. Destroy the mediated device that you created. + + # echo 1 > /sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001/remove + +References +========== + +[1] See Documentation/vfio.txt for more information on VFIO. +[2] struct mdev_driver in include/linux/mdev.h +[3] struct parent_ops in include/linux/mdev.h +[4] struct vfio_iommu_driver_ops in include/linux/vfio.h diff --git a/MAINTAINERS b/MAINTAINERS index 02f20dba70553..4297e7231ffe7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3911,7 +3911,7 @@ F: include/linux/dma-buf* F: include/linux/reservation.h F: include/linux/*fence.h F: Documentation/dma-buf-sharing.txt -T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git +T: git git://anongit.freedesktop.org/drm/drm-misc SYNC FILE FRAMEWORK M: Sumit Semwal @@ -3924,7 +3924,7 @@ F: drivers/dma-buf/sw_sync.c F: include/linux/sync_file.h F: include/uapi/linux/sync_file.h F: Documentation/sync_file.txt -T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git +T: git git://anongit.freedesktop.org/drm/drm-misc DMA GENERIC OFFLOAD ENGINE SUBSYSTEM M: Vinod Koul @@ -4022,11 +4022,29 @@ F: Documentation/gpu/ F: include/drm/ F: include/uapi/drm/ +DRM DRIVERS AND MISC GPU PATCHES +M: Daniel Vetter +M: Jani Nikula +M: Sean Paul +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/gpu/ +F: drivers/gpu/vga/ +F: drivers/gpu/drm/* +F: include/drm/drm* +F: include/uapi/drm/drm* + DRM DRIVER FOR AST SERVER GRAPHICS CHIPS M: Dave Airlie S: Odd Fixes F: drivers/gpu/drm/ast/ +DRM DRIVERS FOR BRIDGE CHIPS +M: Archit Taneja +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/bridge/ + DRM DRIVER FOR BOCHS VIRTUAL GPU M: Gerd Hoffmann S: Odd Fixes @@ -9353,7 +9371,7 @@ PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD) M: Keith Busch L: linux-pci@vger.kernel.org S: Supported -F: arch/x86/pci/vmd.c +F: drivers/pci/host/vmd.c PCIE DRIVER FOR ST SPEAR13XX M: Pratyush Anand @@ -12772,6 +12790,15 @@ F: drivers/vfio/ F: include/linux/vfio.h F: include/uapi/linux/vfio.h +VFIO MEDIATED DEVICE DRIVERS +M: Kirti Wankhede +L: kvm@vger.kernel.org +S: Maintained +F: Documentation/vfio-mediated-device.txt +F: drivers/vfio/mdev/ +F: include/linux/mdev.h +F: samples/vfio-mdev/ + VFIO PLATFORM DRIVER M: Baptiste Reynal L: kvm@vger.kernel.org diff --git a/Makefile b/Makefile index f97f786de58dd..c0738cd081641 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 4 PATCHLEVEL = 9 SUBLEVEL = 0 -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc5 NAME = Psychotic Stoned Sheep # *DOCUMENTATION* @@ -370,7 +370,7 @@ LDFLAGS_MODULE = CFLAGS_KERNEL = AFLAGS_KERNEL = LDFLAGS_vmlinux = -CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -fno-tree-loop-im +CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -fno-tree-loop-im -Wno-maybe-uninitialized CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) @@ -620,7 +620,6 @@ ARCH_CFLAGS := include arch/$(SRCARCH)/Makefile KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,) -KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,) KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,) ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION @@ -629,18 +628,26 @@ KBUILD_CFLAGS += $(call cc-option,-fdata-sections,) endif ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE -KBUILD_CFLAGS += -Os +KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,) else ifdef CONFIG_PROFILE_ALL_BRANCHES -KBUILD_CFLAGS += -O2 +KBUILD_CFLAGS += -O2 $(call cc-disable-warning,maybe-uninitialized,) else KBUILD_CFLAGS += -O2 endif endif +KBUILD_CFLAGS += $(call cc-ifversion, -lt, 0409, \ + $(call cc-disable-warning,maybe-uninitialized,)) + # Tell gcc to never replace conditional load with a non-conditional one KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0) +# force no-pie for distro compilers that enable pie by default +KBUILD_CFLAGS += $(call cc-option, -fno-pie) +KBUILD_CFLAGS += $(call cc-option, -no-pie) +KBUILD_AFLAGS += $(call cc-option, -fno-pie) + include scripts/Makefile.gcc-plugins ifdef CONFIG_READABLE_ASM diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 864adad522803..19cce226d1a83 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -50,6 +50,9 @@ atleast_gcc44 := $(call cc-ifversion, -ge, 0404, y) cflags-$(atleast_gcc44) += -fsection-anchors +cflags-$(CONFIG_ARC_HAS_LLSC) += -mlock +cflags-$(CONFIG_ARC_HAS_SWAPE) += -mswape + ifdef CONFIG_ISA_ARCV2 ifndef CONFIG_ARC_HAS_LL64 @@ -68,7 +71,9 @@ cflags-$(CONFIG_ARC_DW2_UNWIND) += -fasynchronous-unwind-tables $(cfi) ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE # Generic build system uses -O2, we want -O3 # Note: No need to add to cflags-y as that happens anyways -ARCH_CFLAGS += -O3 +# +# Disable the false maybe-uninitialized warings gcc spits out at -O3 +ARCH_CFLAGS += -O3 $(call cc-disable-warning,maybe-uninitialized,) endif # small data is default for elf32 tool-chain. If not usable, disable it diff --git a/arch/arc/boot/dts/axc001.dtsi b/arch/arc/boot/dts/axc001.dtsi index 6ae2c476ad825..53ce226f77a59 100644 --- a/arch/arc/boot/dts/axc001.dtsi +++ b/arch/arc/boot/dts/axc001.dtsi @@ -71,7 +71,7 @@ reg-io-width = <4>; }; - arcpmu0: pmu { + arcpct0: pct { compatible = "snps,arc700-pct"; }; }; diff --git a/arch/arc/boot/dts/nsim_700.dts b/arch/arc/boot/dts/nsim_700.dts index ce0ccd20b5bfc..5ee96b067c085 100644 --- a/arch/arc/boot/dts/nsim_700.dts +++ b/arch/arc/boot/dts/nsim_700.dts @@ -69,7 +69,7 @@ }; }; - arcpmu0: pmu { + arcpct0: pct { compatible = "snps,arc700-pct"; }; }; diff --git a/arch/arc/boot/dts/nsimosci.dts b/arch/arc/boot/dts/nsimosci.dts index bcf603142a33c..3c391ba565ed0 100644 --- a/arch/arc/boot/dts/nsimosci.dts +++ b/arch/arc/boot/dts/nsimosci.dts @@ -83,5 +83,9 @@ reg = <0xf0003000 0x44>; interrupts = <7>; }; + + arcpct0: pct { + compatible = "snps,arc700-pct"; + }; }; }; diff --git a/arch/arc/configs/nsim_700_defconfig b/arch/arc/configs/nsim_700_defconfig index 7314f538847bd..b0066a749d4c4 100644 --- a/arch/arc/configs/nsim_700_defconfig +++ b/arch/arc/configs/nsim_700_defconfig @@ -14,6 +14,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs/" CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/nsim_hs_defconfig b/arch/arc/configs/nsim_hs_defconfig index 65ab9fbf83f25..ebe9ebb929333 100644 --- a/arch/arc/configs/nsim_hs_defconfig +++ b/arch/arc/configs/nsim_hs_defconfig @@ -14,6 +14,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../../arc_initramfs_hs/" CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/nsim_hs_smp_defconfig b/arch/arc/configs/nsim_hs_smp_defconfig index 3b3990cddbe10..4bde43278be67 100644 --- a/arch/arc/configs/nsim_hs_smp_defconfig +++ b/arch/arc/configs/nsim_hs_smp_defconfig @@ -12,6 +12,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs_hs/" CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/nsimosci_defconfig b/arch/arc/configs/nsimosci_defconfig index 98cf20933bbb3..f6fb3d26557eb 100644 --- a/arch/arc/configs/nsimosci_defconfig +++ b/arch/arc/configs/nsimosci_defconfig @@ -14,6 +14,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs/" CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/nsimosci_hs_defconfig b/arch/arc/configs/nsimosci_hs_defconfig index ddf8b96d494e9..b9f0fe00044b6 100644 --- a/arch/arc/configs/nsimosci_hs_defconfig +++ b/arch/arc/configs/nsimosci_hs_defconfig @@ -14,6 +14,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs_hs/" CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/nsimosci_hs_smp_defconfig b/arch/arc/configs/nsimosci_hs_smp_defconfig index ceb90745326e5..6da71ba253a93 100644 --- a/arch/arc/configs/nsimosci_hs_smp_defconfig +++ b/arch/arc/configs/nsimosci_hs_smp_defconfig @@ -10,6 +10,7 @@ CONFIG_IKCONFIG_PROC=y # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs_hs/" +CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y CONFIG_MODULES=y @@ -34,7 +35,6 @@ CONFIG_INET=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set # CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set # CONFIG_IPV6 is not set # CONFIG_WIRELESS is not set CONFIG_DEVTMPFS=y @@ -72,7 +72,6 @@ CONFIG_SERIAL_OF_PLATFORM=y # CONFIG_HWMON is not set CONFIG_DRM=y CONFIG_DRM_ARCPGU=y -CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 7f3f9f63708cf..1bd24ec3e3502 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -43,12 +43,14 @@ #define STATUS_AE_BIT 5 /* Exception active */ #define STATUS_DE_BIT 6 /* PC is in delay slot */ #define STATUS_U_BIT 7 /* User/Kernel mode */ +#define STATUS_Z_BIT 11 #define STATUS_L_BIT 12 /* Loop inhibit */ /* These masks correspond to the status word(STATUS_32) bits */ #define STATUS_AE_MASK (1<hwirq, cpumask_bits(&online)[0]); - idu_set_mode(data->hwirq, IDU_M_TRIG_LEVEL, IDU_M_DISTRI_RR); + destination_bits = cpumask_bits(&online)[0]; + idu_set_dest(data->hwirq, destination_bits); + + if (ffs(destination_bits) == fls(destination_bits)) + distribution_mode = IDU_M_DISTRI_DEST; + else + distribution_mode = IDU_M_DISTRI_RR; + + idu_set_mode(data->hwirq, IDU_M_TRIG_LEVEL, distribution_mode); raw_spin_unlock_irqrestore(&mcip_lock, flags); @@ -207,16 +216,15 @@ static struct irq_chip idu_irq_chip = { }; -static int idu_first_irq; +static irq_hw_number_t idu_first_hwirq; static void idu_cascade_isr(struct irq_desc *desc) { - struct irq_domain *domain = irq_desc_get_handler_data(desc); - unsigned int core_irq = irq_desc_get_irq(desc); - unsigned int idu_irq; + struct irq_domain *idu_domain = irq_desc_get_handler_data(desc); + irq_hw_number_t core_hwirq = irqd_to_hwirq(irq_desc_get_irq_data(desc)); + irq_hw_number_t idu_hwirq = core_hwirq - idu_first_hwirq; - idu_irq = core_irq - idu_first_irq; - generic_handle_irq(irq_find_mapping(domain, idu_irq)); + generic_handle_irq(irq_find_mapping(idu_domain, idu_hwirq)); } static int idu_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hwirq) @@ -282,7 +290,7 @@ idu_of_init(struct device_node *intc, struct device_node *parent) struct irq_domain *domain; /* Read IDU BCR to confirm nr_irqs */ int nr_irqs = of_irq_count(intc); - int i, irq; + int i, virq; struct mcip_bcr mp; READ_BCR(ARC_REG_MCIP_BCR, mp); @@ -303,11 +311,11 @@ idu_of_init(struct device_node *intc, struct device_node *parent) * however we need it to get the parent virq and set IDU handler * as first level isr */ - irq = irq_of_parse_and_map(intc, i); + virq = irq_of_parse_and_map(intc, i); if (!i) - idu_first_irq = irq; + idu_first_hwirq = irqd_to_hwirq(irq_get_irq_data(virq)); - irq_set_chained_handler_and_data(irq, idu_cascade_isr, domain); + irq_set_chained_handler_and_data(virq, idu_cascade_isr, domain); } __mcip_cmd(CMD_IDU_ENABLE, 0); diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 59aa43cb146e7..a41a79a4f4fea 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -43,8 +43,8 @@ SYSCALL_DEFINE0(arc_gettls) SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new) { - int uval; - int ret; + struct pt_regs *regs = current_pt_regs(); + int uval = -EFAULT; /* * This is only for old cores lacking LLOCK/SCOND, which by defintion @@ -54,24 +54,26 @@ SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new) */ WARN_ON_ONCE(IS_ENABLED(CONFIG_SMP)); + /* Z indicates to userspace if operation succeded */ + regs->status32 &= ~STATUS_Z_MASK; + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; preempt_disable(); - ret = __get_user(uval, uaddr); - if (ret) + if (__get_user(uval, uaddr)) goto done; - if (uval != expected) - ret = -EAGAIN; - else - ret = __put_user(new, uaddr); + if (uval == expected) { + if (!__put_user(new, uaddr)) + regs->status32 |= STATUS_Z_MASK; + } done: preempt_enable(); - return ret; + return uval; } void arch_cpu_idle(void) diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index f183cc648851e..88674d972c9d0 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -67,11 +68,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus) int i; /* - * Initialise the present map, which describes the set of CPUs - * actually populated at the present time. + * if platform didn't set the present map already, do it now + * boot cpu is set to present already by init/main.c */ - for (i = 0; i < max_cpus; i++) - set_cpu_present(i, true); + if (num_present_cpus() <= 1) { + for (i = 0; i < max_cpus; i++) + set_cpu_present(i, true); + } } void __init smp_cpus_done(unsigned int max_cpus) @@ -351,20 +354,24 @@ irqreturn_t do_IPI(int irq, void *dev_id) */ static DEFINE_PER_CPU(int, ipi_dev); -int smp_ipi_irq_setup(int cpu, int irq) +int smp_ipi_irq_setup(int cpu, irq_hw_number_t hwirq) { int *dev = per_cpu_ptr(&ipi_dev, cpu); + unsigned int virq = irq_find_mapping(NULL, hwirq); + + if (!virq) + panic("Cannot find virq for root domain and hwirq=%lu", hwirq); /* Boot cpu calls request, all call enable */ if (!cpu) { int rc; - rc = request_percpu_irq(irq, do_IPI, "IPI Interrupt", dev); + rc = request_percpu_irq(virq, do_IPI, "IPI Interrupt", dev); if (rc) - panic("Percpu IRQ request failed for %d\n", irq); + panic("Percpu IRQ request failed for %u\n", virq); } - enable_percpu_irq(irq, 0); + enable_percpu_irq(virq, 0); return 0; } diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index f927b8dc6eddf..c10390d1ddb6b 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -152,14 +152,17 @@ static cycle_t arc_read_rtc(struct clocksource *cs) cycle_t full; } stamp; - - __asm__ __volatile( - "1: \n" - " lr %0, [AUX_RTC_LOW] \n" - " lr %1, [AUX_RTC_HIGH] \n" - " lr %2, [AUX_RTC_CTRL] \n" - " bbit0.nt %2, 31, 1b \n" - : "=r" (stamp.low), "=r" (stamp.high), "=r" (status)); + /* + * hardware has an internal state machine which tracks readout of + * low/high and updates the CTRL.status if + * - interrupt/exception taken between the two reads + * - high increments after low has been read + */ + do { + stamp.low = read_aux_reg(AUX_RTC_LOW); + stamp.high = read_aux_reg(AUX_RTC_HIGH); + status = read_aux_reg(AUX_RTC_CTRL); + } while (!(status & _BITUL(31))); return stamp.full; } diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c index 60aab5a7522b5..cd8aad8226dd5 100644 --- a/arch/arc/mm/dma.c +++ b/arch/arc/mm/dma.c @@ -105,6 +105,31 @@ static void arc_dma_free(struct device *dev, size_t size, void *vaddr, __free_pages(page, get_order(size)); } +static int arc_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs) +{ + unsigned long user_count = vma_pages(vma); + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; + unsigned long pfn = __phys_to_pfn(plat_dma_to_phys(dev, dma_addr)); + unsigned long off = vma->vm_pgoff; + int ret = -ENXIO; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) + return ret; + + if (off < count && user_count <= (count - off)) { + ret = remap_pfn_range(vma, vma->vm_start, + pfn + off, + user_count << PAGE_SHIFT, + vma->vm_page_prot); + } + + return ret; +} + /* * streaming DMA Mapping API... * CPU accesses page via normal paddr, thus needs to explicitly made @@ -193,6 +218,7 @@ static int arc_dma_supported(struct device *dev, u64 dma_mask) struct dma_map_ops arc_dma_ops = { .alloc = arc_dma_alloc, .free = arc_dma_free, + .mmap = arc_dma_mmap, .map_page = arc_dma_map_page, .map_sg = arc_dma_map_sg, .sync_single_for_device = arc_dma_sync_single_for_device, diff --git a/arch/arc/plat-eznps/smp.c b/arch/arc/plat-eznps/smp.c index 5e901f86e4bd0..56a4c8522f111 100644 --- a/arch/arc/plat-eznps/smp.c +++ b/arch/arc/plat-eznps/smp.c @@ -140,16 +140,10 @@ static void eznps_init_per_cpu(int cpu) mtm_enable_core(cpu); } -static void eznps_ipi_clear(int irq) -{ - write_aux_reg(CTOP_AUX_IACK, 1 << irq); -} - struct plat_smp_ops plat_smp_ops = { .info = smp_cpuinfo_buf, .init_early_smp = eznps_init_cpumasks, .cpu_kick = eznps_smp_wakeup_cpu, .ipi_send = eznps_ipi_send, .init_per_cpu = eznps_init_per_cpu, - .ipi_clear = eznps_ipi_clear, }; diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h index d7ea6bcb29bf6..8ef05381984b1 100644 --- a/arch/arm/include/asm/kvm_asm.h +++ b/arch/arm/include/asm/kvm_asm.h @@ -66,6 +66,7 @@ extern char __kvm_hyp_vector[]; extern void __kvm_flush_vm_context(void); extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); extern void __kvm_tlb_flush_vmid(struct kvm *kvm); +extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu); extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 2d19e02d03fd6..d5423ab15ed5b 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -57,6 +57,9 @@ struct kvm_arch { /* VTTBR value associated with below pgd and vmid */ u64 vttbr; + /* The last vcpu id that ran on each physical CPU */ + int __percpu *last_vcpu_ran; + /* Timer */ struct arch_timer_kvm timer; diff --git a/arch/arm/include/asm/kvm_hyp.h b/arch/arm/include/asm/kvm_hyp.h index 343135ede5fa0..58508900c4bb2 100644 --- a/arch/arm/include/asm/kvm_hyp.h +++ b/arch/arm/include/asm/kvm_hyp.h @@ -71,6 +71,7 @@ #define ICIALLUIS __ACCESS_CP15(c7, 0, c1, 0) #define ATS1CPR __ACCESS_CP15(c7, 0, c8, 0) #define TLBIALLIS __ACCESS_CP15(c8, 0, c3, 0) +#define TLBIALL __ACCESS_CP15(c8, 0, c7, 0) #define TLBIALLNSNHIS __ACCESS_CP15(c8, 4, c3, 4) #define PRRR __ACCESS_CP15(c10, 0, c2, 0) #define NMRR __ACCESS_CP15(c10, 0, c2, 1) diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 08bb84f2ad58e..19b5f5c1c0ff3 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -114,11 +114,18 @@ void kvm_arch_check_processor_compat(void *rtn) */ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { - int ret = 0; + int ret, cpu; if (type) return -EINVAL; + kvm->arch.last_vcpu_ran = alloc_percpu(typeof(*kvm->arch.last_vcpu_ran)); + if (!kvm->arch.last_vcpu_ran) + return -ENOMEM; + + for_each_possible_cpu(cpu) + *per_cpu_ptr(kvm->arch.last_vcpu_ran, cpu) = -1; + ret = kvm_alloc_stage2_pgd(kvm); if (ret) goto out_fail_alloc; @@ -141,6 +148,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) out_free_stage2_pgd: kvm_free_stage2_pgd(kvm); out_fail_alloc: + free_percpu(kvm->arch.last_vcpu_ran); + kvm->arch.last_vcpu_ran = NULL; return ret; } @@ -168,6 +177,9 @@ void kvm_arch_destroy_vm(struct kvm *kvm) { int i; + free_percpu(kvm->arch.last_vcpu_ran); + kvm->arch.last_vcpu_ran = NULL; + for (i = 0; i < KVM_MAX_VCPUS; ++i) { if (kvm->vcpus[i]) { kvm_arch_vcpu_free(kvm->vcpus[i]); @@ -312,6 +324,19 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + int *last_ran; + + last_ran = this_cpu_ptr(vcpu->kvm->arch.last_vcpu_ran); + + /* + * We might get preempted before the vCPU actually runs, but + * over-invalidation doesn't affect correctness. + */ + if (*last_ran != vcpu->vcpu_id) { + kvm_call_hyp(__kvm_tlb_flush_local_vmid, vcpu); + *last_ran = vcpu->vcpu_id; + } + vcpu->cpu = cpu; vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state); diff --git a/arch/arm/kvm/hyp/tlb.c b/arch/arm/kvm/hyp/tlb.c index 729652854f909..6d810af2d9fd7 100644 --- a/arch/arm/kvm/hyp/tlb.c +++ b/arch/arm/kvm/hyp/tlb.c @@ -55,6 +55,21 @@ void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa) __kvm_tlb_flush_vmid(kvm); } +void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm = kern_hyp_va(kern_hyp_va(vcpu)->kvm); + + /* Switch to requested VMID */ + write_sysreg(kvm->arch.vttbr, VTTBR); + isb(); + + write_sysreg(0, TLBIALL); + dsb(nsh); + isb(); + + write_sysreg(0, VTTBR); +} + void __hyp_text __kvm_flush_vm_context(void) { write_sysreg(0, TLBIALLNSNHIS); diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index b65c193dc64ef..7afbfb0f96a3c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -300,8 +300,11 @@ ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000 0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>; resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>, - <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>; - reset-names = "core", "mgmt", "mgmt-sticky", "pipe"; + <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>, + <&cru SRST_PCIE_PM>, <&cru SRST_P_PCIE>, + <&cru SRST_A_PCIE>; + reset-names = "core", "mgmt", "mgmt-sticky", "pipe", + "pm", "pclk", "aclk"; status = "disabled"; pcie0_intc: interrupt-controller { diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index 39feb85a69310..6e1cb8c5af4d6 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -1,7 +1,7 @@ #ifndef __ASM_ALTERNATIVE_H #define __ASM_ALTERNATIVE_H -#include +#include #include #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h new file mode 100644 index 0000000000000..87b4465351855 --- /dev/null +++ b/arch/arm64/include/asm/cpucaps.h @@ -0,0 +1,40 @@ +/* + * arch/arm64/include/asm/cpucaps.h + * + * Copyright (C) 2016 ARM Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __ASM_CPUCAPS_H +#define __ASM_CPUCAPS_H + +#define ARM64_WORKAROUND_CLEAN_CACHE 0 +#define ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE 1 +#define ARM64_WORKAROUND_845719 2 +#define ARM64_HAS_SYSREG_GIC_CPUIF 3 +#define ARM64_HAS_PAN 4 +#define ARM64_HAS_LSE_ATOMICS 5 +#define ARM64_WORKAROUND_CAVIUM_23154 6 +#define ARM64_WORKAROUND_834220 7 +#define ARM64_HAS_NO_HW_PREFETCH 8 +#define ARM64_HAS_UAO 9 +#define ARM64_ALT_PAN_NOT_UAO 10 +#define ARM64_HAS_VIRT_HOST_EXTN 11 +#define ARM64_WORKAROUND_CAVIUM_27456 12 +#define ARM64_HAS_32BIT_EL0 13 +#define ARM64_HYP_OFFSET_LOW 14 +#define ARM64_MISMATCHED_CACHE_LINE_SIZE 15 + +#define ARM64_NCAPS 16 + +#endif /* __ASM_CPUCAPS_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index a27c3245ba218..0bc0b1de90c45 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -11,6 +11,7 @@ #include +#include #include #include @@ -24,25 +25,6 @@ #define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap)) #define cpu_feature(x) ilog2(HWCAP_ ## x) -#define ARM64_WORKAROUND_CLEAN_CACHE 0 -#define ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE 1 -#define ARM64_WORKAROUND_845719 2 -#define ARM64_HAS_SYSREG_GIC_CPUIF 3 -#define ARM64_HAS_PAN 4 -#define ARM64_HAS_LSE_ATOMICS 5 -#define ARM64_WORKAROUND_CAVIUM_23154 6 -#define ARM64_WORKAROUND_834220 7 -#define ARM64_HAS_NO_HW_PREFETCH 8 -#define ARM64_HAS_UAO 9 -#define ARM64_ALT_PAN_NOT_UAO 10 -#define ARM64_HAS_VIRT_HOST_EXTN 11 -#define ARM64_WORKAROUND_CAVIUM_27456 12 -#define ARM64_HAS_32BIT_EL0 13 -#define ARM64_HYP_OFFSET_LOW 14 -#define ARM64_MISMATCHED_CACHE_LINE_SIZE 15 - -#define ARM64_NCAPS 16 - #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 18f746551bf63..ec3553eb93490 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -54,6 +54,7 @@ extern char __kvm_hyp_vector[]; extern void __kvm_flush_vm_context(void); extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); extern void __kvm_tlb_flush_vmid(struct kvm *kvm); +extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu); extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index bd94e67667599..e5050388e0622 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -62,6 +62,9 @@ struct kvm_arch { /* VTTBR value associated with above pgd and vmid */ u64 vttbr; + /* The last vcpu id that ran on each physical CPU */ + int __percpu *last_vcpu_ran; + /* The maximum number of vCPUs depends on the used GIC model */ int max_vcpus; diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index a79b969c26fca..6f72fe8b0e3ee 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -128,7 +128,7 @@ static inline unsigned long __kern_hyp_va(unsigned long v) return v; } -#define kern_hyp_va(v) (typeof(v))(__kern_hyp_va((unsigned long)(v))) +#define kern_hyp_va(v) ((typeof(v))(__kern_hyp_va((unsigned long)(v)))) /* * We currently only support a 40bit IPA. diff --git a/arch/arm64/include/asm/lse.h b/arch/arm64/include/asm/lse.h index 23acc00be32d0..fc756e22c84cd 100644 --- a/arch/arm64/include/asm/lse.h +++ b/arch/arm64/include/asm/lse.h @@ -5,7 +5,6 @@ #include #include -#include #ifdef __ASSEMBLER__ diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c index 9cc0ea784ae60..88e2f2b938f07 100644 --- a/arch/arm64/kvm/hyp/tlb.c +++ b/arch/arm64/kvm/hyp/tlb.c @@ -64,6 +64,21 @@ void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm) write_sysreg(0, vttbr_el2); } +void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm = kern_hyp_va(kern_hyp_va(vcpu)->kvm); + + /* Switch to requested VMID */ + write_sysreg(kvm->arch.vttbr, vttbr_el2); + isb(); + + asm volatile("tlbi vmalle1" : : ); + dsb(nsh); + isb(); + + write_sysreg(0, vttbr_el2); +} + void __hyp_text __kvm_flush_vm_context(void) { dsb(ishst); diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index d9563ddb337ea..746bf5caaffc7 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -324,6 +324,7 @@ static int __init nios2_time_init(struct device_node *timer) ret = nios2_clocksource_init(timer); break; default: + ret = 0; break; } diff --git a/arch/openrisc/include/asm/cache.h b/arch/openrisc/include/asm/cache.h index 4ce7a01a252dc..5f55da9cbfd5c 100644 --- a/arch/openrisc/include/asm/cache.h +++ b/arch/openrisc/include/asm/cache.h @@ -23,6 +23,8 @@ * they shouldn't be hard-coded! */ +#define __ro_after_init __read_mostly + #define L1_CACHE_BYTES 16 #define L1_CACHE_SHIFT 4 diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c index 28f03ca60100a..794bebb43d23d 100644 --- a/arch/s390/hypfs/hypfs_diag.c +++ b/arch/s390/hypfs/hypfs_diag.c @@ -363,11 +363,11 @@ static void *diag204_store(void) static int diag224_get_name_table(void) { /* memory must be below 2GB */ - diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); + diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); if (!diag224_cpu_names) return -ENOMEM; if (diag224(diag224_cpu_names)) { - kfree(diag224_cpu_names); + free_page((unsigned long) diag224_cpu_names); return -EOPNOTSUPP; } EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); @@ -376,7 +376,7 @@ static int diag224_get_name_table(void) static void diag224_delete_name_table(void) { - kfree(diag224_cpu_names); + free_page((unsigned long) diag224_cpu_names); } static int diag224_idx2name(int index, char *name) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 000e6e91f6a06..3667d20e997f3 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -62,9 +62,11 @@ SECTIONS . = ALIGN(PAGE_SIZE); __start_ro_after_init = .; + __start_data_ro_after_init = .; .data..ro_after_init : { *(.data..ro_after_init) } + __end_data_ro_after_init = .; EXCEPTION_TABLE(16) . = ALIGN(PAGE_SIZE); __end_ro_after_init = .; diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c index 7350c8bc13a29..6b2f72f523b91 100644 --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -423,7 +423,7 @@ static int __s390_dma_map_sg(struct device *dev, struct scatterlist *sg, dma_addr_t dma_addr_base, dma_addr; int flags = ZPCI_PTE_VALID; struct scatterlist *s; - unsigned long pa; + unsigned long pa = 0; int ret; size = PAGE_ALIGN(size); diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 2d449337a3605..7ee0422b97d34 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -27,6 +27,7 @@ REALMODE_CFLAGS := $(M16_CFLAGS) -g -Os -D__KERNEL__ \ -mno-mmx -mno-sse \ $(call cc-option, -ffreestanding) \ $(call cc-option, -fno-stack-protector) \ + $(call cc-option, -fno-pie) \ $(call cc-option, -mpreferred-stack-boundary=2) export REALMODE_CFLAGS diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index 0ab5ee1c26af0..aa8b0672f87a4 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -888,7 +888,7 @@ static int helper_rfc4106_encrypt(struct aead_request *req) unsigned long auth_tag_len = crypto_aead_authsize(tfm); u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN))); struct scatter_walk src_sg_walk; - struct scatter_walk dst_sg_walk; + struct scatter_walk dst_sg_walk = {}; unsigned int i; /* Assuming we are supporting rfc4106 64-bit extended */ @@ -968,7 +968,7 @@ static int helper_rfc4106_decrypt(struct aead_request *req) u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN))); u8 authTag[16]; struct scatter_walk src_sg_walk; - struct scatter_walk dst_sg_walk; + struct scatter_walk dst_sg_walk = {}; unsigned int i; if (unlikely(req->assoclen != 16 && req->assoclen != 20)) diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index d5409660f5de6..d9a46a8e845d9 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -73,6 +73,7 @@ $(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so $(obj)/vdso2c FORCE CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \ $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \ -fno-omit-frame-pointer -foptimize-sibling-calls \ + $(call cc-option, -fno-pie) \ -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO $(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) @@ -147,6 +148,7 @@ KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -mfentry,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 += -m32 -msoft-float -mregparm=0 -fpic +KBUILD_CFLAGS_32 += $(call cc-option, -no-pie) KBUILD_CFLAGS_32 += $(call cc-option, -fno-stack-protector) KBUILD_CFLAGS_32 += $(call cc-option, -foptimize-sibling-calls) KBUILD_CFLAGS_32 += -fno-omit-frame-pointer diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c index c5047b8f777b2..1c1b9fe705c84 100644 --- a/arch/x86/events/intel/pt.c +++ b/arch/x86/events/intel/pt.c @@ -36,13 +36,6 @@ static DEFINE_PER_CPU(struct pt, pt_ctx); static struct pt_pmu pt_pmu; -enum cpuid_regs { - CR_EAX = 0, - CR_ECX, - CR_EDX, - CR_EBX -}; - /* * Capabilities of Intel PT hardware, such as number of address bits or * supported output schemes, are cached and exported to userspace as "caps" @@ -64,21 +57,21 @@ static struct pt_cap_desc { u8 reg; u32 mask; } pt_caps[] = { - PT_CAP(max_subleaf, 0, CR_EAX, 0xffffffff), - PT_CAP(cr3_filtering, 0, CR_EBX, BIT(0)), - PT_CAP(psb_cyc, 0, CR_EBX, BIT(1)), - PT_CAP(ip_filtering, 0, CR_EBX, BIT(2)), - PT_CAP(mtc, 0, CR_EBX, BIT(3)), - PT_CAP(ptwrite, 0, CR_EBX, BIT(4)), - PT_CAP(power_event_trace, 0, CR_EBX, BIT(5)), - PT_CAP(topa_output, 0, CR_ECX, BIT(0)), - PT_CAP(topa_multiple_entries, 0, CR_ECX, BIT(1)), - PT_CAP(single_range_output, 0, CR_ECX, BIT(2)), - PT_CAP(payloads_lip, 0, CR_ECX, BIT(31)), - PT_CAP(num_address_ranges, 1, CR_EAX, 0x3), - PT_CAP(mtc_periods, 1, CR_EAX, 0xffff0000), - PT_CAP(cycle_thresholds, 1, CR_EBX, 0xffff), - PT_CAP(psb_periods, 1, CR_EBX, 0xffff0000), + PT_CAP(max_subleaf, 0, CPUID_EAX, 0xffffffff), + PT_CAP(cr3_filtering, 0, CPUID_EBX, BIT(0)), + PT_CAP(psb_cyc, 0, CPUID_EBX, BIT(1)), + PT_CAP(ip_filtering, 0, CPUID_EBX, BIT(2)), + PT_CAP(mtc, 0, CPUID_EBX, BIT(3)), + PT_CAP(ptwrite, 0, CPUID_EBX, BIT(4)), + PT_CAP(power_event_trace, 0, CPUID_EBX, BIT(5)), + PT_CAP(topa_output, 0, CPUID_ECX, BIT(0)), + PT_CAP(topa_multiple_entries, 0, CPUID_ECX, BIT(1)), + PT_CAP(single_range_output, 0, CPUID_ECX, BIT(2)), + PT_CAP(payloads_lip, 0, CPUID_ECX, BIT(31)), + PT_CAP(num_address_ranges, 1, CPUID_EAX, 0x3), + PT_CAP(mtc_periods, 1, CPUID_EAX, 0xffff0000), + PT_CAP(cycle_thresholds, 1, CPUID_EBX, 0xffff), + PT_CAP(psb_periods, 1, CPUID_EBX, 0xffff0000), }; static u32 pt_cap_get(enum pt_capabilities cap) @@ -213,10 +206,10 @@ static int __init pt_pmu_hw_init(void) for (i = 0; i < PT_CPUID_LEAVES; i++) { cpuid_count(20, i, - &pt_pmu.caps[CR_EAX + i*PT_CPUID_REGS_NUM], - &pt_pmu.caps[CR_EBX + i*PT_CPUID_REGS_NUM], - &pt_pmu.caps[CR_ECX + i*PT_CPUID_REGS_NUM], - &pt_pmu.caps[CR_EDX + i*PT_CPUID_REGS_NUM]); + &pt_pmu.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM], + &pt_pmu.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM], + &pt_pmu.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM], + &pt_pmu.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM]); } ret = -ENOMEM; diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 984a7bf17f6a7..e7f8c62701d4a 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -137,6 +137,17 @@ struct cpuinfo_x86 { u32 microcode; }; +struct cpuid_regs { + u32 eax, ebx, ecx, edx; +}; + +enum cpuid_regs_idx { + CPUID_EAX = 0, + CPUID_EBX, + CPUID_ECX, + CPUID_EDX, +}; + #define X86_VENDOR_INTEL 0 #define X86_VENDOR_CYRIX 1 #define X86_VENDOR_AMD 2 @@ -178,6 +189,9 @@ extern void identify_secondary_cpu(struct cpuinfo_x86 *); extern void print_cpu_info(struct cpuinfo_x86 *); void print_cpu_msr(struct cpuinfo_x86 *); extern void init_scattered_cpuid_features(struct cpuinfo_x86 *c); +extern u32 get_scattered_cpuid_leaf(unsigned int level, + unsigned int sub_leaf, + enum cpuid_regs_idx reg); extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); extern void init_amd_cacheinfo(struct cpuinfo_x86 *c); diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index a002b07a7099c..5ef9848f743fe 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -60,6 +60,7 @@ */ #define SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES 0x00000001 #define SECONDARY_EXEC_ENABLE_EPT 0x00000002 +#define SECONDARY_EXEC_DESC 0x00000004 #define SECONDARY_EXEC_RDTSCP 0x00000008 #define SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE 0x00000010 #define SECONDARY_EXEC_ENABLE_VPID 0x00000020 diff --git a/arch/x86/include/uapi/asm/vmx.h b/arch/x86/include/uapi/asm/vmx.h index 37fee272618f1..f9dea4fd41072 100644 --- a/arch/x86/include/uapi/asm/vmx.h +++ b/arch/x86/include/uapi/asm/vmx.h @@ -65,6 +65,8 @@ #define EXIT_REASON_TPR_BELOW_THRESHOLD 43 #define EXIT_REASON_APIC_ACCESS 44 #define EXIT_REASON_EOI_INDUCED 45 +#define EXIT_REASON_GDTR_IDTR 46 +#define EXIT_REASON_LDTR_TR 47 #define EXIT_REASON_EPT_VIOLATION 48 #define EXIT_REASON_EPT_MISCONFIG 49 #define EXIT_REASON_INVEPT 50 @@ -113,6 +115,8 @@ { EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \ { EXIT_REASON_TPR_BELOW_THRESHOLD, "TPR_BELOW_THRESHOLD" }, \ { EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \ + { EXIT_REASON_GDTR_IDTR, "GDTR_IDTR" }, \ + { EXIT_REASON_LDTR_TR, "LDTR_TR" }, \ { EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \ { EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \ { EXIT_REASON_INVEPT, "INVEPT" }, \ diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index c7364bd633e1d..51287cd90bf65 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -1042,8 +1042,11 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) if (apm_info.get_power_status_broken) return APM_32_UNSUPPORTED; - if (apm_bios_call(&call)) + if (apm_bios_call(&call)) { + if (!call.err) + return APM_NO_ERROR; return call.err; + } *status = call.ebx; *bat = call.ecx; if (apm_info.get_power_status_swabinminutes) { diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c index 1db8dc490b665..d1316f9c83298 100644 --- a/arch/x86/kernel/cpu/scattered.c +++ b/arch/x86/kernel/cpu/scattered.c @@ -17,11 +17,17 @@ struct cpuid_bit { u32 sub_leaf; }; -enum cpuid_regs { - CR_EAX = 0, - CR_ECX, - CR_EDX, - CR_EBX +/* Please keep the leaf sorted by cpuid_bit.level for faster search. */ +static const struct cpuid_bit cpuid_bits[] = { + { X86_FEATURE_APERFMPERF, CPUID_ECX, 0, 0x00000006, 0 }, + { X86_FEATURE_EPB, CPUID_ECX, 3, 0x00000006, 0 }, + { X86_FEATURE_INTEL_PT, CPUID_EBX, 25, 0x00000007, 0 }, + { X86_FEATURE_AVX512_4VNNIW, CPUID_EDX, 2, 0x00000007, 0 }, + { X86_FEATURE_AVX512_4FMAPS, CPUID_EDX, 3, 0x00000007, 0 }, + { X86_FEATURE_HW_PSTATE, CPUID_EDX, 7, 0x80000007, 0 }, + { X86_FEATURE_CPB, CPUID_EDX, 9, 0x80000007, 0 }, + { X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 }, + { 0, 0, 0, 0, 0 } }; void init_scattered_cpuid_features(struct cpuinfo_x86 *c) @@ -30,18 +36,6 @@ void init_scattered_cpuid_features(struct cpuinfo_x86 *c) u32 regs[4]; const struct cpuid_bit *cb; - static const struct cpuid_bit cpuid_bits[] = { - { X86_FEATURE_INTEL_PT, CR_EBX,25, 0x00000007, 0 }, - { X86_FEATURE_AVX512_4VNNIW, CR_EDX, 2, 0x00000007, 0 }, - { X86_FEATURE_AVX512_4FMAPS, CR_EDX, 3, 0x00000007, 0 }, - { X86_FEATURE_APERFMPERF, CR_ECX, 0, 0x00000006, 0 }, - { X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 }, - { X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 }, - { X86_FEATURE_CPB, CR_EDX, 9, 0x80000007, 0 }, - { X86_FEATURE_PROC_FEEDBACK, CR_EDX,11, 0x80000007, 0 }, - { 0, 0, 0, 0, 0 } - }; - for (cb = cpuid_bits; cb->feature; cb++) { /* Verify that the level is valid */ @@ -50,10 +44,35 @@ void init_scattered_cpuid_features(struct cpuinfo_x86 *c) max_level > (cb->level | 0xffff)) continue; - cpuid_count(cb->level, cb->sub_leaf, ®s[CR_EAX], - ®s[CR_EBX], ®s[CR_ECX], ®s[CR_EDX]); + cpuid_count(cb->level, cb->sub_leaf, ®s[CPUID_EAX], + ®s[CPUID_EBX], ®s[CPUID_ECX], + ®s[CPUID_EDX]); if (regs[cb->reg] & (1 << cb->bit)) set_cpu_cap(c, cb->feature); } } + +u32 get_scattered_cpuid_leaf(unsigned int level, unsigned int sub_leaf, + enum cpuid_regs_idx reg) +{ + const struct cpuid_bit *cb; + u32 cpuid_val = 0; + + for (cb = cpuid_bits; cb->feature; cb++) { + + if (level > cb->level) + continue; + + if (level < cb->level) + break; + + if (reg == cb->reg && sub_leaf == cb->sub_leaf) { + if (cpu_has(&boot_cpu_data, cb->feature)) + cpuid_val |= BIT(cb->bit); + } + } + + return cpuid_val; +} +EXPORT_SYMBOL_GPL(get_scattered_cpuid_leaf); diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 2836de390f95c..9095c80723d6b 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -46,10 +46,6 @@ static struct class *cpuid_class; -struct cpuid_regs { - u32 eax, ebx, ecx, edx; -}; - static void cpuid_smp_cpuid(void *cmd_block) { struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block; diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index afa7bbb596cd7..a982fd80bcebd 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* For use_eager_fpu. Ugh! */ #include #include @@ -65,6 +66,11 @@ u64 kvm_supported_xcr0(void) #define F(x) bit(X86_FEATURE_##x) +/* These are scattered features in cpufeatures.h. */ +#define KVM_CPUID_BIT_AVX512_4VNNIW 2 +#define KVM_CPUID_BIT_AVX512_4FMAPS 3 +#define KF(x) bit(KVM_CPUID_BIT_##x) + int kvm_update_cpuid(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; @@ -376,6 +382,10 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 7.0.ecx*/ const u32 kvm_cpuid_7_0_ecx_x86_features = F(PKU) | 0 /*OSPKE*/; + /* cpuid 7.0.edx*/ + const u32 kvm_cpuid_7_0_edx_x86_features = + KF(AVX512_4VNNIW) | KF(AVX512_4FMAPS); + /* all calls to cpuid_count() should be made on the same cpu */ get_cpu(); @@ -458,12 +468,14 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* PKU is not yet implemented for shadow paging. */ if (!tdp_enabled) entry->ecx &= ~F(PKU); + entry->edx &= kvm_cpuid_7_0_edx_x86_features; + entry->edx &= get_scattered_cpuid_leaf(7, 0, CPUID_EDX); } else { entry->ebx = 0; entry->ecx = 0; + entry->edx = 0; } entry->eax = 0; - entry->edx = 0; break; } case 9: @@ -865,9 +877,9 @@ EXPORT_SYMBOL_GPL(kvm_cpuid); void kvm_emulate_cpuid(struct kvm_vcpu *vcpu) { - u32 function, eax, ebx, ecx, edx; + u32 eax, ebx, ecx, edx; - function = eax = kvm_register_read(vcpu, VCPU_REGS_RAX); + eax = kvm_register_read(vcpu, VCPU_REGS_RAX); ecx = kvm_register_read(vcpu, VCPU_REGS_RCX); kvm_cpuid(vcpu, &eax, &ebx, &ecx, &edx); kvm_register_write(vcpu, VCPU_REGS_RAX, eax); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index cbd7b92585bbb..7d4f9b7f06ee2 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -158,9 +158,11 @@ #define Src2GS (OpGS << Src2Shift) #define Src2Mask (OpMask << Src2Shift) #define Mmx ((u64)1 << 40) /* MMX Vector instruction */ +#define AlignMask ((u64)7 << 41) #define Aligned ((u64)1 << 41) /* Explicitly aligned (e.g. MOVDQA) */ -#define Unaligned ((u64)1 << 42) /* Explicitly unaligned (e.g. MOVDQU) */ -#define Avx ((u64)1 << 43) /* Advanced Vector Extensions */ +#define Unaligned ((u64)2 << 41) /* Explicitly unaligned (e.g. MOVDQU) */ +#define Avx ((u64)3 << 41) /* Advanced Vector Extensions */ +#define Aligned16 ((u64)4 << 41) /* Aligned to 16 byte boundary (e.g. FXSAVE) */ #define Fastop ((u64)1 << 44) /* Use opcode::u.fastop */ #define NoWrite ((u64)1 << 45) /* No writeback */ #define SrcWrite ((u64)1 << 46) /* Write back src operand */ @@ -446,6 +448,26 @@ FOP_END; FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET FOP_END; +/* + * XXX: inoutclob user must know where the argument is being expanded. + * Relying on CC_HAVE_ASM_GOTO would allow us to remove _fault. + */ +#define asm_safe(insn, inoutclob...) \ +({ \ + int _fault = 0; \ + \ + asm volatile("1:" insn "\n" \ + "2:\n" \ + ".pushsection .fixup, \"ax\"\n" \ + "3: movl $1, %[_fault]\n" \ + " jmp 2b\n" \ + ".popsection\n" \ + _ASM_EXTABLE(1b, 3b) \ + : [_fault] "+qm"(_fault) inoutclob ); \ + \ + _fault ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; \ +}) + static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt, enum x86_intercept intercept, enum x86_intercept_stage stage) @@ -632,21 +654,26 @@ static void set_segment_selector(struct x86_emulate_ctxt *ctxt, u16 selector, * depending on whether they're AVX encoded or not. * * Also included is CMPXCHG16B which is not a vector instruction, yet it is - * subject to the same check. + * subject to the same check. FXSAVE and FXRSTOR are checked here too as their + * 512 bytes of data must be aligned to a 16 byte boundary. */ -static bool insn_aligned(struct x86_emulate_ctxt *ctxt, unsigned size) +static unsigned insn_alignment(struct x86_emulate_ctxt *ctxt, unsigned size) { - if (likely(size < 16)) - return false; + u64 alignment = ctxt->d & AlignMask; - if (ctxt->d & Aligned) - return true; - else if (ctxt->d & Unaligned) - return false; - else if (ctxt->d & Avx) - return false; - else - return true; + if (likely(size < 16)) + return 1; + + switch (alignment) { + case Unaligned: + case Avx: + return 1; + case Aligned16: + return 16; + case Aligned: + default: + return size; + } } static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt, @@ -704,7 +731,7 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt, } break; } - if (insn_aligned(ctxt, size) && ((la & (size - 1)) != 0)) + if (la & (insn_alignment(ctxt, size) - 1)) return emulate_gp(ctxt, 0); return X86EMUL_CONTINUE; bad: @@ -3856,6 +3883,131 @@ static int em_movsxd(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } +static int check_fxsr(struct x86_emulate_ctxt *ctxt) +{ + u32 eax = 1, ebx, ecx = 0, edx; + + ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx); + if (!(edx & FFL(FXSR))) + return emulate_ud(ctxt); + + if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) + return emulate_nm(ctxt); + + /* + * Don't emulate a case that should never be hit, instead of working + * around a lack of fxsave64/fxrstor64 on old compilers. + */ + if (ctxt->mode >= X86EMUL_MODE_PROT64) + return X86EMUL_UNHANDLEABLE; + + return X86EMUL_CONTINUE; +} + +/* + * FXSAVE and FXRSTOR have 4 different formats depending on execution mode, + * 1) 16 bit mode + * 2) 32 bit mode + * - like (1), but FIP and FDP (foo) are only 16 bit. At least Intel CPUs + * preserve whole 32 bit values, though, so (1) and (2) are the same wrt. + * save and restore + * 3) 64-bit mode with REX.W prefix + * - like (2), but XMM 8-15 are being saved and restored + * 4) 64-bit mode without REX.W prefix + * - like (3), but FIP and FDP are 64 bit + * + * Emulation uses (3) for (1) and (2) and preserves XMM 8-15 to reach the + * desired result. (4) is not emulated. + * + * Note: Guest and host CPUID.(EAX=07H,ECX=0H):EBX[bit 13] (deprecate FPU CS + * and FPU DS) should match. + */ +static int em_fxsave(struct x86_emulate_ctxt *ctxt) +{ + struct fxregs_state fx_state; + size_t size; + int rc; + + rc = check_fxsr(ctxt); + if (rc != X86EMUL_CONTINUE) + return rc; + + ctxt->ops->get_fpu(ctxt); + + rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state)); + + ctxt->ops->put_fpu(ctxt); + + if (rc != X86EMUL_CONTINUE) + return rc; + + if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR) + size = offsetof(struct fxregs_state, xmm_space[8 * 16/4]); + else + size = offsetof(struct fxregs_state, xmm_space[0]); + + return segmented_write(ctxt, ctxt->memop.addr.mem, &fx_state, size); +} + +static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt, + struct fxregs_state *new) +{ + int rc = X86EMUL_CONTINUE; + struct fxregs_state old; + + rc = asm_safe("fxsave %[fx]", , [fx] "+m"(old)); + if (rc != X86EMUL_CONTINUE) + return rc; + + /* + * 64 bit host will restore XMM 8-15, which is not correct on non-64 + * bit guests. Load the current values in order to preserve 64 bit + * XMMs after fxrstor. + */ +#ifdef CONFIG_X86_64 + /* XXX: accessing XMM 8-15 very awkwardly */ + memcpy(&new->xmm_space[8 * 16/4], &old.xmm_space[8 * 16/4], 8 * 16); +#endif + + /* + * Hardware doesn't save and restore XMM 0-7 without CR4.OSFXSR, but + * does save and restore MXCSR. + */ + if (!(ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR)) + memcpy(new->xmm_space, old.xmm_space, 8 * 16); + + return rc; +} + +static int em_fxrstor(struct x86_emulate_ctxt *ctxt) +{ + struct fxregs_state fx_state; + int rc; + + rc = check_fxsr(ctxt); + if (rc != X86EMUL_CONTINUE) + return rc; + + rc = segmented_read(ctxt, ctxt->memop.addr.mem, &fx_state, 512); + if (rc != X86EMUL_CONTINUE) + return rc; + + if (fx_state.mxcsr >> 16) + return emulate_gp(ctxt, 0); + + ctxt->ops->get_fpu(ctxt); + + if (ctxt->mode < X86EMUL_MODE_PROT64) + rc = fxrstor_fixup(ctxt, &fx_state); + + if (rc == X86EMUL_CONTINUE) + rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state)); + + ctxt->ops->put_fpu(ctxt); + + return rc; +} + static bool valid_cr(int nr) { switch (nr) { @@ -4208,7 +4360,9 @@ static const struct gprefix pfx_0f_ae_7 = { }; static const struct group_dual group15 = { { - N, N, N, N, N, N, N, GP(0, &pfx_0f_ae_7), + I(ModRM | Aligned16, em_fxsave), + I(ModRM | Aligned16, em_fxrstor), + N, N, N, N, N, GP(0, &pfx_0f_ae_7), }, { N, N, N, N, N, N, N, N, } }; @@ -5080,21 +5234,13 @@ static bool string_insn_completed(struct x86_emulate_ctxt *ctxt) static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt) { - bool fault = false; + int rc; ctxt->ops->get_fpu(ctxt); - asm volatile("1: fwait \n\t" - "2: \n\t" - ".pushsection .fixup,\"ax\" \n\t" - "3: \n\t" - "movb $1, %[fault] \n\t" - "jmp 2b \n\t" - ".popsection \n\t" - _ASM_EXTABLE(1b, 3b) - : [fault]"+qm"(fault)); + rc = asm_safe("fwait"); ctxt->ops->put_fpu(ctxt); - if (unlikely(fault)) + if (unlikely(rc != X86EMUL_CONTINUE)) return emulate_exception(ctxt, MF_VECTOR, 0, false); return X86EMUL_CONTINUE; diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 42b1c83741c8f..99cde5220e079 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -291,7 +291,7 @@ static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata) return ret; } -int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint) +static int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint) { struct kvm_vcpu *vcpu = synic_to_vcpu(synic); struct kvm_lapic_irq irq; diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 23b99f3053825..890f218ddd7a2 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -342,9 +342,11 @@ void __kvm_apic_update_irr(u32 *pir, void *regs) u32 i, pir_val; for (i = 0; i <= 7; i++) { - pir_val = xchg(&pir[i], 0); - if (pir_val) + pir_val = READ_ONCE(pir[i]); + if (pir_val) { + pir_val = xchg(&pir[i], 0); *((u32 *)(regs + APIC_IRR + i * 0x10)) |= pir_val; + } } } EXPORT_SYMBOL_GPL(__kvm_apic_update_irr); @@ -1090,7 +1092,7 @@ static void apic_send_ipi(struct kvm_lapic *apic) static u32 apic_get_tmcct(struct kvm_lapic *apic) { - ktime_t remaining; + ktime_t remaining, now; s64 ns; u32 tmcct; @@ -1101,7 +1103,8 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic) apic->lapic_timer.period == 0) return 0; - remaining = hrtimer_get_remaining(&apic->lapic_timer.timer); + now = ktime_get(); + remaining = ktime_sub(apic->lapic_timer.target_expiration, now); if (ktime_to_ns(remaining) < 0) remaining = ktime_set(0, 0); @@ -1332,7 +1335,7 @@ static void start_sw_tscdeadline(struct kvm_lapic *apic) local_irq_save(flags); - now = apic->lapic_timer.timer.base->get_time(); + now = ktime_get(); guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); if (likely(tscdeadline > guest_tsc)) { ns = (tscdeadline - guest_tsc) * 1000000ULL; @@ -1347,6 +1350,79 @@ static void start_sw_tscdeadline(struct kvm_lapic *apic) local_irq_restore(flags); } +static void start_sw_period(struct kvm_lapic *apic) +{ + if (!apic->lapic_timer.period) + return; + + if (apic_lvtt_oneshot(apic) && + ktime_after(ktime_get(), + apic->lapic_timer.target_expiration)) { + apic_timer_expired(apic); + return; + } + + hrtimer_start(&apic->lapic_timer.timer, + apic->lapic_timer.target_expiration, + HRTIMER_MODE_ABS_PINNED); +} + +static bool set_target_expiration(struct kvm_lapic *apic) +{ + ktime_t now; + u64 tscl = rdtsc(); + + now = ktime_get(); + apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT) + * APIC_BUS_CYCLE_NS * apic->divide_count; + + if (!apic->lapic_timer.period) + return false; + + /* + * Do not allow the guest to program periodic timers with small + * interval, since the hrtimers are not throttled by the host + * scheduler. + */ + if (apic_lvtt_period(apic)) { + s64 min_period = min_timer_period_us * 1000LL; + + if (apic->lapic_timer.period < min_period) { + pr_info_ratelimited( + "kvm: vcpu %i: requested %lld ns " + "lapic timer period limited to %lld ns\n", + apic->vcpu->vcpu_id, + apic->lapic_timer.period, min_period); + apic->lapic_timer.period = min_period; + } + } + + apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" + PRIx64 ", " + "timer initial count 0x%x, period %lldns, " + "expire @ 0x%016" PRIx64 ".\n", __func__, + APIC_BUS_CYCLE_NS, ktime_to_ns(now), + kvm_lapic_get_reg(apic, APIC_TMICT), + apic->lapic_timer.period, + ktime_to_ns(ktime_add_ns(now, + apic->lapic_timer.period))); + + apic->lapic_timer.tscdeadline = kvm_read_l1_tsc(apic->vcpu, tscl) + + nsec_to_cycles(apic->vcpu, apic->lapic_timer.period); + apic->lapic_timer.target_expiration = ktime_add_ns(now, apic->lapic_timer.period); + + return true; +} + +static void advance_periodic_target_expiration(struct kvm_lapic *apic) +{ + apic->lapic_timer.tscdeadline += + nsec_to_cycles(apic->vcpu, apic->lapic_timer.period); + apic->lapic_timer.target_expiration = + ktime_add_ns(apic->lapic_timer.target_expiration, + apic->lapic_timer.period); +} + bool kvm_lapic_hv_timer_in_use(struct kvm_vcpu *vcpu) { if (!lapic_in_kernel(vcpu)) @@ -1356,52 +1432,59 @@ bool kvm_lapic_hv_timer_in_use(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_lapic_hv_timer_in_use); -static void cancel_hv_tscdeadline(struct kvm_lapic *apic) +static void cancel_hv_timer(struct kvm_lapic *apic) { kvm_x86_ops->cancel_hv_timer(apic->vcpu); apic->lapic_timer.hv_timer_in_use = false; } -void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu) -{ - struct kvm_lapic *apic = vcpu->arch.apic; - - WARN_ON(!apic->lapic_timer.hv_timer_in_use); - WARN_ON(swait_active(&vcpu->wq)); - cancel_hv_tscdeadline(apic); - apic_timer_expired(apic); -} -EXPORT_SYMBOL_GPL(kvm_lapic_expired_hv_timer); - -static bool start_hv_tscdeadline(struct kvm_lapic *apic) +static bool start_hv_timer(struct kvm_lapic *apic) { u64 tscdeadline = apic->lapic_timer.tscdeadline; - if (atomic_read(&apic->lapic_timer.pending) || + if ((atomic_read(&apic->lapic_timer.pending) && + !apic_lvtt_period(apic)) || kvm_x86_ops->set_hv_timer(apic->vcpu, tscdeadline)) { if (apic->lapic_timer.hv_timer_in_use) - cancel_hv_tscdeadline(apic); + cancel_hv_timer(apic); } else { apic->lapic_timer.hv_timer_in_use = true; hrtimer_cancel(&apic->lapic_timer.timer); /* In case the sw timer triggered in the window */ - if (atomic_read(&apic->lapic_timer.pending)) - cancel_hv_tscdeadline(apic); + if (atomic_read(&apic->lapic_timer.pending) && + !apic_lvtt_period(apic)) + cancel_hv_timer(apic); } trace_kvm_hv_timer_state(apic->vcpu->vcpu_id, apic->lapic_timer.hv_timer_in_use); return apic->lapic_timer.hv_timer_in_use; } +void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu) +{ + struct kvm_lapic *apic = vcpu->arch.apic; + + WARN_ON(!apic->lapic_timer.hv_timer_in_use); + WARN_ON(swait_active(&vcpu->wq)); + cancel_hv_timer(apic); + apic_timer_expired(apic); + + if (apic_lvtt_period(apic) && apic->lapic_timer.period) { + advance_periodic_target_expiration(apic); + if (!start_hv_timer(apic)) + start_sw_period(apic); + } +} +EXPORT_SYMBOL_GPL(kvm_lapic_expired_hv_timer); + void kvm_lapic_switch_to_hv_timer(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; WARN_ON(apic->lapic_timer.hv_timer_in_use); - if (apic_lvtt_tscdeadline(apic)) - start_hv_tscdeadline(apic); + start_hv_timer(apic); } EXPORT_SYMBOL_GPL(kvm_lapic_switch_to_hv_timer); @@ -1413,62 +1496,28 @@ void kvm_lapic_switch_to_sw_timer(struct kvm_vcpu *vcpu) if (!apic->lapic_timer.hv_timer_in_use) return; - cancel_hv_tscdeadline(apic); + cancel_hv_timer(apic); if (atomic_read(&apic->lapic_timer.pending)) return; - start_sw_tscdeadline(apic); + if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic)) + start_sw_period(apic); + else if (apic_lvtt_tscdeadline(apic)) + start_sw_tscdeadline(apic); } EXPORT_SYMBOL_GPL(kvm_lapic_switch_to_sw_timer); static void start_apic_timer(struct kvm_lapic *apic) { - ktime_t now; - atomic_set(&apic->lapic_timer.pending, 0); if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic)) { - /* lapic timer in oneshot or periodic mode */ - now = apic->lapic_timer.timer.base->get_time(); - apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT) - * APIC_BUS_CYCLE_NS * apic->divide_count; - - if (!apic->lapic_timer.period) - return; - /* - * Do not allow the guest to program periodic timers with small - * interval, since the hrtimers are not throttled by the host - * scheduler. - */ - if (apic_lvtt_period(apic)) { - s64 min_period = min_timer_period_us * 1000LL; - - if (apic->lapic_timer.period < min_period) { - pr_info_ratelimited( - "kvm: vcpu %i: requested %lld ns " - "lapic timer period limited to %lld ns\n", - apic->vcpu->vcpu_id, - apic->lapic_timer.period, min_period); - apic->lapic_timer.period = min_period; - } - } - - hrtimer_start(&apic->lapic_timer.timer, - ktime_add_ns(now, apic->lapic_timer.period), - HRTIMER_MODE_ABS_PINNED); - - apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" - PRIx64 ", " - "timer initial count 0x%x, period %lldns, " - "expire @ 0x%016" PRIx64 ".\n", __func__, - APIC_BUS_CYCLE_NS, ktime_to_ns(now), - kvm_lapic_get_reg(apic, APIC_TMICT), - apic->lapic_timer.period, - ktime_to_ns(ktime_add_ns(now, - apic->lapic_timer.period))); + if (set_target_expiration(apic) && + !(kvm_x86_ops->set_hv_timer && start_hv_timer(apic))) + start_sw_period(apic); } else if (apic_lvtt_tscdeadline(apic)) { - if (!(kvm_x86_ops->set_hv_timer && start_hv_tscdeadline(apic))) + if (!(kvm_x86_ops->set_hv_timer && start_hv_timer(apic))) start_sw_tscdeadline(apic); } } @@ -1701,13 +1750,22 @@ void kvm_free_lapic(struct kvm_vcpu *vcpu) * LAPIC interface *---------------------------------------------------------------------- */ +u64 kvm_get_lapic_target_expiration_tsc(struct kvm_vcpu *vcpu) +{ + struct kvm_lapic *apic = vcpu->arch.apic; + + if (!lapic_in_kernel(vcpu)) + return 0; + + return apic->lapic_timer.tscdeadline; +} u64 kvm_get_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; - if (!lapic_in_kernel(vcpu) || apic_lvtt_oneshot(apic) || - apic_lvtt_period(apic)) + if (!lapic_in_kernel(vcpu) || + !apic_lvtt_tscdeadline(apic)) return 0; return apic->lapic_timer.tscdeadline; @@ -1909,6 +1967,7 @@ static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) apic_timer_expired(apic); if (lapic_is_periodic(apic)) { + advance_periodic_target_expiration(apic); hrtimer_add_expires_ns(&ktimer->timer, ktimer->period); return HRTIMER_RESTART; } else @@ -1993,6 +2052,10 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) kvm_apic_local_deliver(apic, APIC_LVTT); if (apic_lvtt_tscdeadline(apic)) apic->lapic_timer.tscdeadline = 0; + if (apic_lvtt_oneshot(apic)) { + apic->lapic_timer.tscdeadline = 0; + apic->lapic_timer.target_expiration = ktime_set(0, 0); + } atomic_set(&apic->lapic_timer.pending, 0); } } diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index f60d01c29d510..e0c80233b3e17 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -15,6 +15,7 @@ struct kvm_timer { struct hrtimer timer; s64 period; /* unit: ns */ + ktime_t target_expiration; u32 timer_mode; u32 timer_mode_mask; u64 tscdeadline; @@ -85,6 +86,7 @@ int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); +u64 kvm_get_lapic_target_expiration_tsc(struct kvm_vcpu *vcpu); u64 kvm_get_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu); void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 87c5880ba3b7d..1d2f350dcf6c1 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1660,17 +1660,9 @@ int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) * This has some overhead, but not as much as the cost of swapping * out actively used pages or breaking up actively used hugepages. */ - if (!shadow_accessed_mask) { - /* - * We are holding the kvm->mmu_lock, and we are blowing up - * shadow PTEs. MMU notifier consumers need to be kept at bay. - * This is correct as long as we don't decouple the mmu_lock - * protected regions (like invalidate_range_start|end does). - */ - kvm->mmu_notifier_seq++; + if (!shadow_accessed_mask) return kvm_handle_hva_range(kvm, start, end, 0, kvm_unmap_rmapp); - } return kvm_handle_hva_range(kvm, start, end, 0, kvm_age_rmapp); } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 5382b82462fcb..f81a8e44ae523 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -520,6 +520,12 @@ static inline void pi_set_sn(struct pi_desc *pi_desc) (unsigned long *)&pi_desc->control); } +static inline void pi_clear_on(struct pi_desc *pi_desc) +{ + clear_bit(POSTED_INTR_ON, + (unsigned long *)&pi_desc->control); +} + static inline int pi_test_on(struct pi_desc *pi_desc) { return test_bit(POSTED_INTR_ON, @@ -920,16 +926,32 @@ static DEFINE_PER_CPU(struct desc_ptr, host_gdt); static DEFINE_PER_CPU(struct list_head, blocked_vcpu_on_cpu); static DEFINE_PER_CPU(spinlock_t, blocked_vcpu_on_cpu_lock); -static unsigned long *vmx_io_bitmap_a; -static unsigned long *vmx_io_bitmap_b; -static unsigned long *vmx_msr_bitmap_legacy; -static unsigned long *vmx_msr_bitmap_longmode; -static unsigned long *vmx_msr_bitmap_legacy_x2apic; -static unsigned long *vmx_msr_bitmap_longmode_x2apic; -static unsigned long *vmx_msr_bitmap_legacy_x2apic_apicv_inactive; -static unsigned long *vmx_msr_bitmap_longmode_x2apic_apicv_inactive; -static unsigned long *vmx_vmread_bitmap; -static unsigned long *vmx_vmwrite_bitmap; +enum { + VMX_IO_BITMAP_A, + VMX_IO_BITMAP_B, + VMX_MSR_BITMAP_LEGACY, + VMX_MSR_BITMAP_LONGMODE, + VMX_MSR_BITMAP_LEGACY_X2APIC_APICV, + VMX_MSR_BITMAP_LONGMODE_X2APIC_APICV, + VMX_MSR_BITMAP_LEGACY_X2APIC, + VMX_MSR_BITMAP_LONGMODE_X2APIC, + VMX_VMREAD_BITMAP, + VMX_VMWRITE_BITMAP, + VMX_BITMAP_NR +}; + +static unsigned long *vmx_bitmap[VMX_BITMAP_NR]; + +#define vmx_io_bitmap_a (vmx_bitmap[VMX_IO_BITMAP_A]) +#define vmx_io_bitmap_b (vmx_bitmap[VMX_IO_BITMAP_B]) +#define vmx_msr_bitmap_legacy (vmx_bitmap[VMX_MSR_BITMAP_LEGACY]) +#define vmx_msr_bitmap_longmode (vmx_bitmap[VMX_MSR_BITMAP_LONGMODE]) +#define vmx_msr_bitmap_legacy_x2apic_apicv (vmx_bitmap[VMX_MSR_BITMAP_LEGACY_X2APIC_APICV]) +#define vmx_msr_bitmap_longmode_x2apic_apicv (vmx_bitmap[VMX_MSR_BITMAP_LONGMODE_X2APIC_APICV]) +#define vmx_msr_bitmap_legacy_x2apic (vmx_bitmap[VMX_MSR_BITMAP_LEGACY_X2APIC]) +#define vmx_msr_bitmap_longmode_x2apic (vmx_bitmap[VMX_MSR_BITMAP_LONGMODE_X2APIC]) +#define vmx_vmread_bitmap (vmx_bitmap[VMX_VMREAD_BITMAP]) +#define vmx_vmwrite_bitmap (vmx_bitmap[VMX_VMWRITE_BITMAP]) static bool cpu_has_load_ia32_efer; static bool cpu_has_load_perf_global_ctrl; @@ -2529,14 +2551,14 @@ static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu) SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) { if (enable_apicv && kvm_vcpu_apicv_active(vcpu)) { if (is_long_mode(vcpu)) - msr_bitmap = vmx_msr_bitmap_longmode_x2apic; + msr_bitmap = vmx_msr_bitmap_longmode_x2apic_apicv; else - msr_bitmap = vmx_msr_bitmap_legacy_x2apic; + msr_bitmap = vmx_msr_bitmap_legacy_x2apic_apicv; } else { if (is_long_mode(vcpu)) - msr_bitmap = vmx_msr_bitmap_longmode_x2apic_apicv_inactive; + msr_bitmap = vmx_msr_bitmap_longmode_x2apic; else - msr_bitmap = vmx_msr_bitmap_legacy_x2apic_apicv_inactive; + msr_bitmap = vmx_msr_bitmap_legacy_x2apic; } } else { if (is_long_mode(vcpu)) @@ -2780,6 +2802,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) vmx->nested.nested_vmx_secondary_ctls_high &= SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | SECONDARY_EXEC_RDTSCP | + SECONDARY_EXEC_DESC | SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | SECONDARY_EXEC_ENABLE_VPID | SECONDARY_EXEC_APIC_REGISTER_VIRT | @@ -4575,41 +4598,6 @@ static void __vmx_disable_intercept_for_msr(unsigned long *msr_bitmap, } } -static void __vmx_enable_intercept_for_msr(unsigned long *msr_bitmap, - u32 msr, int type) -{ - int f = sizeof(unsigned long); - - if (!cpu_has_vmx_msr_bitmap()) - return; - - /* - * See Intel PRM Vol. 3, 20.6.9 (MSR-Bitmap Address). Early manuals - * have the write-low and read-high bitmap offsets the wrong way round. - * We can control MSRs 0x00000000-0x00001fff and 0xc0000000-0xc0001fff. - */ - if (msr <= 0x1fff) { - if (type & MSR_TYPE_R) - /* read-low */ - __set_bit(msr, msr_bitmap + 0x000 / f); - - if (type & MSR_TYPE_W) - /* write-low */ - __set_bit(msr, msr_bitmap + 0x800 / f); - - } else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) { - msr &= 0x1fff; - if (type & MSR_TYPE_R) - /* read-high */ - __set_bit(msr, msr_bitmap + 0x400 / f); - - if (type & MSR_TYPE_W) - /* write-high */ - __set_bit(msr, msr_bitmap + 0xc00 / f); - - } -} - /* * If a msr is allowed by L0, we should check whether it is allowed by L1. * The corresponding bit will be cleared unless both of L0 and L1 allow it. @@ -4665,48 +4653,18 @@ static void vmx_disable_intercept_for_msr(u32 msr, bool longmode_only) msr, MSR_TYPE_R | MSR_TYPE_W); } -static void vmx_enable_intercept_msr_read_x2apic(u32 msr, bool apicv_active) +static void vmx_disable_intercept_msr_x2apic(u32 msr, int type, bool apicv_active) { if (apicv_active) { - __vmx_enable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic, - msr, MSR_TYPE_R); - __vmx_enable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic, - msr, MSR_TYPE_R); + __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv, + msr, type); + __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv, + msr, type); } else { - __vmx_enable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive, - msr, MSR_TYPE_R); - __vmx_enable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive, - msr, MSR_TYPE_R); - } -} - -static void vmx_disable_intercept_msr_read_x2apic(u32 msr, bool apicv_active) -{ - if (apicv_active) { __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic, - msr, MSR_TYPE_R); + msr, type); __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic, - msr, MSR_TYPE_R); - } else { - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive, - msr, MSR_TYPE_R); - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive, - msr, MSR_TYPE_R); - } -} - -static void vmx_disable_intercept_msr_write_x2apic(u32 msr, bool apicv_active) -{ - if (apicv_active) { - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic, - msr, MSR_TYPE_W); - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic, - msr, MSR_TYPE_W); - } else { - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive, - msr, MSR_TYPE_W); - __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive, - msr, MSR_TYPE_W); + msr, type); } } @@ -4828,9 +4786,15 @@ static void vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); - if (!pi_test_and_clear_on(&vmx->pi_desc)) + if (!pi_test_on(&vmx->pi_desc)) return; + pi_clear_on(&vmx->pi_desc); + /* + * IOMMU can write to PIR.ON, so the barrier matters even on UP. + * But on x86 this is just a compiler barrier anyway. + */ + smp_mb__after_atomic(); kvm_apic_update_irr(vcpu, vmx->pi_desc.pir); } @@ -6352,50 +6316,13 @@ static __init int hardware_setup(void) for (i = 0; i < ARRAY_SIZE(vmx_msr_index); ++i) kvm_define_shared_msr(i, vmx_msr_index[i]); - vmx_io_bitmap_a = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_io_bitmap_a) - return r; + for (i = 0; i < VMX_BITMAP_NR; i++) { + vmx_bitmap[i] = (unsigned long *)__get_free_page(GFP_KERNEL); + if (!vmx_bitmap[i]) + goto out; + } vmx_io_bitmap_b = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_io_bitmap_b) - goto out; - - vmx_msr_bitmap_legacy = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_legacy) - goto out1; - - vmx_msr_bitmap_legacy_x2apic = - (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_legacy_x2apic) - goto out2; - - vmx_msr_bitmap_legacy_x2apic_apicv_inactive = - (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_legacy_x2apic_apicv_inactive) - goto out3; - - vmx_msr_bitmap_longmode = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_longmode) - goto out4; - - vmx_msr_bitmap_longmode_x2apic = - (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_longmode_x2apic) - goto out5; - - vmx_msr_bitmap_longmode_x2apic_apicv_inactive = - (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_msr_bitmap_longmode_x2apic_apicv_inactive) - goto out6; - - vmx_vmread_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_vmread_bitmap) - goto out7; - - vmx_vmwrite_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL); - if (!vmx_vmwrite_bitmap) - goto out8; - memset(vmx_vmread_bitmap, 0xff, PAGE_SIZE); memset(vmx_vmwrite_bitmap, 0xff, PAGE_SIZE); @@ -6413,7 +6340,7 @@ static __init int hardware_setup(void) if (setup_vmcs_config(&vmcs_config) < 0) { r = -EIO; - goto out9; + goto out; } if (boot_cpu_has(X86_FEATURE_NX)) @@ -6476,39 +6403,34 @@ static __init int hardware_setup(void) vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false); vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true); - memcpy(vmx_msr_bitmap_legacy_x2apic, + memcpy(vmx_msr_bitmap_legacy_x2apic_apicv, vmx_msr_bitmap_legacy, PAGE_SIZE); - memcpy(vmx_msr_bitmap_longmode_x2apic, + memcpy(vmx_msr_bitmap_longmode_x2apic_apicv, vmx_msr_bitmap_longmode, PAGE_SIZE); - memcpy(vmx_msr_bitmap_legacy_x2apic_apicv_inactive, + memcpy(vmx_msr_bitmap_legacy_x2apic, vmx_msr_bitmap_legacy, PAGE_SIZE); - memcpy(vmx_msr_bitmap_longmode_x2apic_apicv_inactive, + memcpy(vmx_msr_bitmap_longmode_x2apic, vmx_msr_bitmap_longmode, PAGE_SIZE); set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */ + for (msr = 0x800; msr <= 0x8ff; msr++) { + if (msr == 0x839 /* TMCCT */) + continue; + vmx_disable_intercept_msr_x2apic(msr, MSR_TYPE_R, true); + } + /* - * enable_apicv && kvm_vcpu_apicv_active() + * TPR reads and writes can be virtualized even if virtual interrupt + * delivery is not in use. */ - for (msr = 0x800; msr <= 0x8ff; msr++) - vmx_disable_intercept_msr_read_x2apic(msr, true); + vmx_disable_intercept_msr_x2apic(0x808, MSR_TYPE_W, true); + vmx_disable_intercept_msr_x2apic(0x808, MSR_TYPE_R | MSR_TYPE_W, false); - /* TMCCT */ - vmx_enable_intercept_msr_read_x2apic(0x839, true); - /* TPR */ - vmx_disable_intercept_msr_write_x2apic(0x808, true); /* EOI */ - vmx_disable_intercept_msr_write_x2apic(0x80b, true); + vmx_disable_intercept_msr_x2apic(0x80b, MSR_TYPE_W, true); /* SELF-IPI */ - vmx_disable_intercept_msr_write_x2apic(0x83f, true); - - /* - * (enable_apicv && !kvm_vcpu_apicv_active()) || - * !enable_apicv - */ - /* TPR */ - vmx_disable_intercept_msr_read_x2apic(0x808, false); - vmx_disable_intercept_msr_write_x2apic(0x808, false); + vmx_disable_intercept_msr_x2apic(0x83f, MSR_TYPE_W, true); if (enable_ept) { kvm_mmu_set_mask_ptes(VMX_EPT_READABLE_MASK, @@ -6555,42 +6477,19 @@ static __init int hardware_setup(void) return alloc_kvm_area(); -out9: - free_page((unsigned long)vmx_vmwrite_bitmap); -out8: - free_page((unsigned long)vmx_vmread_bitmap); -out7: - free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic_apicv_inactive); -out6: - free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic); -out5: - free_page((unsigned long)vmx_msr_bitmap_longmode); -out4: - free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic_apicv_inactive); -out3: - free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic); -out2: - free_page((unsigned long)vmx_msr_bitmap_legacy); -out1: - free_page((unsigned long)vmx_io_bitmap_b); out: - free_page((unsigned long)vmx_io_bitmap_a); + for (i = 0; i < VMX_BITMAP_NR; i++) + free_page((unsigned long)vmx_bitmap[i]); return r; } static __exit void hardware_unsetup(void) { - free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic); - free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic_apicv_inactive); - free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic); - free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic_apicv_inactive); - free_page((unsigned long)vmx_msr_bitmap_legacy); - free_page((unsigned long)vmx_msr_bitmap_longmode); - free_page((unsigned long)vmx_io_bitmap_b); - free_page((unsigned long)vmx_io_bitmap_a); - free_page((unsigned long)vmx_vmwrite_bitmap); - free_page((unsigned long)vmx_vmread_bitmap); + int i; + + for (i = 0; i < VMX_BITMAP_NR; i++) + free_page((unsigned long)vmx_bitmap[i]); free_kvm_area(); } @@ -8075,6 +7974,8 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu) return nested_cpu_has(vmcs12, CPU_BASED_MOV_DR_EXITING); case EXIT_REASON_IO_INSTRUCTION: return nested_vmx_exit_handled_io(vcpu, vmcs12); + case EXIT_REASON_GDTR_IDTR: case EXIT_REASON_LDTR_TR: + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_DESC); case EXIT_REASON_MSR_READ: case EXIT_REASON_MSR_WRITE: return nested_vmx_exit_handled_msr(vcpu, vmcs12, exit_reason); @@ -8624,11 +8525,6 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu) u32 exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO); register void *__sp asm(_ASM_SP); - /* - * If external interrupt exists, IF bit is set in rflags/eflags on the - * interrupt stack frame, and interrupt will be enabled on a return - * from interrupt handler. - */ if ((exit_intr_info & (INTR_INFO_VALID_MASK | INTR_INFO_INTR_TYPE_MASK)) == (INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR)) { unsigned int vector; @@ -8813,7 +8709,7 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) msrs[i].host); } -void vmx_arm_hv_timer(struct kvm_vcpu *vcpu) +static void vmx_arm_hv_timer(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); u64 tscl; @@ -9967,6 +9863,15 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) */ vmx_set_constant_host_state(vmx); + /* + * Set the MSR load/store lists to match L0's settings. + */ + vmcs_write32(VM_EXIT_MSR_STORE_COUNT, 0); + vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.nr); + vmcs_write64(VM_EXIT_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.host)); + vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.nr); + vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest)); + /* * HOST_RSP is normally set correctly in vmx_vcpu_run() just before * entry, but only if the current (host) sp changed from the value @@ -10755,6 +10660,7 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, { struct vcpu_vmx *vmx = to_vmx(vcpu); struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + u32 vm_inst_error = 0; /* trying to cancel vmlaunch/vmresume is a bug */ WARN_ON_ONCE(vmx->nested.nested_run_pending); @@ -10767,6 +10673,9 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, vmcs12->vm_exit_msr_store_count)) nested_vmx_abort(vcpu, VMX_ABORT_SAVE_GUEST_MSR_FAIL); + if (unlikely(vmx->fail)) + vm_inst_error = vmcs_read32(VM_INSTRUCTION_ERROR); + vmx_load_vmcs01(vcpu); if ((exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT) @@ -10795,6 +10704,8 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, load_vmcs12_host_state(vcpu, vmcs12); /* Update any VMCS fields that might have changed while L2 ran */ + vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.nr); + vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.nr); vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset); if (vmx->hv_deadline_tsc == -1) vmcs_clear_bits(PIN_BASED_VM_EXEC_CONTROL, @@ -10843,7 +10754,7 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, */ if (unlikely(vmx->fail)) { vmx->fail = 0; - nested_vmx_failValid(vcpu, vmcs_read32(VM_INSTRUCTION_ERROR)); + nested_vmx_failValid(vcpu, vm_inst_error); } else nested_vmx_succeed(vcpu); if (enable_shadow_vmcs) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7e30c720d0c59..792980f6e1237 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2162,7 +2162,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_KVM_SYSTEM_TIME_NEW: case MSR_KVM_SYSTEM_TIME: { - u64 gpa_offset; struct kvm_arch *ka = &vcpu->kvm->arch; kvmclock_reset(vcpu); @@ -2184,8 +2183,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (!(data & 1)) break; - gpa_offset = data & ~(PAGE_MASK | 1); - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_time, data & ~1ULL, sizeof(struct pvclock_vcpu_time_info))) @@ -2794,7 +2791,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) } if (kvm_lapic_hv_timer_in_use(vcpu) && kvm_x86_ops->set_hv_timer(vcpu, - kvm_get_lapic_tscdeadline_msr(vcpu))) + kvm_get_lapic_target_expiration_tsc(vcpu))) kvm_lapic_switch_to_sw_timer(vcpu); /* * On a host with synchronized TSC, there is no need to update @@ -4795,7 +4792,7 @@ static void emulator_invlpg(struct x86_emulate_ctxt *ctxt, ulong address) kvm_mmu_invlpg(emul_to_vcpu(ctxt), address); } -int kvm_emulate_wbinvd_noskip(struct kvm_vcpu *vcpu) +static int kvm_emulate_wbinvd_noskip(struct kvm_vcpu *vcpu) { if (!need_emulate_wbinvd(vcpu)) return X86EMUL_CONTINUE; diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index d58fbf7f04e6c..7dd70927991e7 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -122,7 +122,7 @@ static int acpi_apd_create_device(struct acpi_device *adev, int ret; if (!dev_desc) { - pdev = acpi_create_platform_device(adev); + pdev = acpi_create_platform_device(adev, NULL); return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; } @@ -139,14 +139,8 @@ static int acpi_apd_create_device(struct acpi_device *adev, goto err_out; } - if (dev_desc->properties) { - ret = device_add_properties(&adev->dev, dev_desc->properties); - if (ret) - goto err_out; - } - adev->driver_data = pdata; - pdev = acpi_create_platform_device(adev); + pdev = acpi_create_platform_device(adev, dev_desc->properties); if (!IS_ERR_OR_NULL(pdev)) return 1; diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 5520102881357..373657f7e35a9 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -395,7 +395,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, dev_desc = (const struct lpss_device_desc *)id->driver_data; if (!dev_desc) { - pdev = acpi_create_platform_device(adev); + pdev = acpi_create_platform_device(adev, NULL); return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; } pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); @@ -451,14 +451,8 @@ static int acpi_lpss_create_device(struct acpi_device *adev, goto err_out; } - if (dev_desc->properties) { - ret = device_add_properties(&adev->dev, dev_desc->properties); - if (ret) - goto err_out; - } - adev->driver_data = pdata; - pdev = acpi_create_platform_device(adev); + pdev = acpi_create_platform_device(adev, dev_desc->properties); if (!IS_ERR_OR_NULL(pdev)) { return 1; } diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index b200ae1f3c6fb..b4c1a6a51da48 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -50,6 +50,7 @@ static void acpi_platform_fill_resource(struct acpi_device *adev, /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. + * @properties: Optional collection of build-in properties. * * Check if the given @adev can be represented as a platform device and, if * that's the case, create and register a platform device, populate its common @@ -57,7 +58,8 @@ static void acpi_platform_fill_resource(struct acpi_device *adev, * * Name of the platform device will be the same as @adev's. */ -struct platform_device *acpi_create_platform_device(struct acpi_device *adev) +struct platform_device *acpi_create_platform_device(struct acpi_device *adev, + struct property_entry *properties) { struct platform_device *pdev = NULL; struct platform_device_info pdevinfo; @@ -106,6 +108,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) pdevinfo.res = resources; pdevinfo.num_res = count; pdevinfo.fwnode = acpi_fwnode_handle(adev); + pdevinfo.properties = properties; if (acpi_dma_supported(adev)) pdevinfo.dma_mask = DMA_BIT_MASK(32); diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c index 33505c651f627..86364097e236e 100644 --- a/drivers/acpi/dptf/int340x_thermal.c +++ b/drivers/acpi/dptf/int340x_thermal.c @@ -34,11 +34,11 @@ static int int340x_thermal_handler_attach(struct acpi_device *adev, const struct acpi_device_id *id) { if (IS_ENABLED(CONFIG_INT340X_THERMAL)) - acpi_create_platform_device(adev); + acpi_create_platform_device(adev, NULL); /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) && id->driver_data == INT3401_DEVICE) - acpi_create_platform_device(adev); + acpi_create_platform_device(adev, NULL); return 1; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 035ac646d8db5..3d1856f1f4d03 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1734,7 +1734,7 @@ static void acpi_default_enumeration(struct acpi_device *device) &is_spi_i2c_slave); acpi_dev_free_resource_list(&resource_list); if (!is_spi_i2c_slave) { - acpi_create_platform_device(device); + acpi_create_platform_device(device, NULL); acpi_device_set_enumerated(device); } else { blocking_notifier_call_chain(&acpi_reconfig_chain, diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d22a7260f42b2..d76cd97a98b6b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) { int ret = -EPROBE_DEFER; int local_trigger_count = atomic_read(&deferred_trigger_count); - bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE); + bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && + !drv->suppress_bind_attrs; if (defer_all_probes) { /* @@ -383,7 +384,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) if (test_remove) { test_remove = false; - if (dev->bus && dev->bus->remove) + if (dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e44944f4be77d..2932a5bd892f7 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1027,6 +1027,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a TRACE_DEVICE(dev); TRACE_SUSPEND(0); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1038,8 +1040,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -1174,6 +1174,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as __pm_runtime_disable(dev, false); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1185,8 +1187,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "late power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index ab19adb07a126..3c606c09fd5ac 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -853,45 +853,6 @@ rqbiocnt(struct request *r) return n; } -/* This can be removed if we are certain that no users of the block - * layer will ever use zero-count pages in bios. Otherwise we have to - * protect against the put_page sometimes done by the network layer. - * - * See http://oss.sgi.com/archives/xfs/2007-01/msg00594.html for - * discussion. - * - * We cannot use get_page in the workaround, because it insists on a - * positive page count as a precondition. So we use _refcount directly. - */ -static void -bio_pageinc(struct bio *bio) -{ - struct bio_vec bv; - struct page *page; - struct bvec_iter iter; - - bio_for_each_segment(bv, bio, iter) { - /* Non-zero page count for non-head members of - * compound pages is no longer allowed by the kernel. - */ - page = compound_head(bv.bv_page); - page_ref_inc(page); - } -} - -static void -bio_pagedec(struct bio *bio) -{ - struct page *page; - struct bio_vec bv; - struct bvec_iter iter; - - bio_for_each_segment(bv, bio, iter) { - page = compound_head(bv.bv_page); - page_ref_dec(page); - } -} - static void bufinit(struct buf *buf, struct request *rq, struct bio *bio) { @@ -899,7 +860,6 @@ bufinit(struct buf *buf, struct request *rq, struct bio *bio) buf->rq = rq; buf->bio = bio; buf->iter = bio->bi_iter; - bio_pageinc(bio); } static struct buf * @@ -1127,7 +1087,6 @@ aoe_end_buf(struct aoedev *d, struct buf *buf) if (buf == d->ip.buf) d->ip.buf = NULL; rq = buf->rq; - bio_pagedec(buf->bio); mempool_free(buf, d->bufpool); n = (unsigned long) rq->special; rq->special = (void *) --n; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 100be556e6137..83482721bc012 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -1871,7 +1871,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock, drbd_update_congested(connection); } do { - rv = kernel_sendmsg(sock, &msg, &iov, 1, size); + rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len); if (rv == -EAGAIN) { if (we_should_drop_the_connection(connection, sock)) break; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 19a16b2dbb917..7a10487559140 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -599,7 +599,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, return -EINVAL; sreq = blk_mq_alloc_request(bdev_get_queue(bdev), WRITE, 0); - if (!sreq) + if (IS_ERR(sreq)) return -ENOMEM; mutex_unlock(&nbd->tx_lock); diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index d23368874710f..6af1ce04b3dac 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -748,10 +748,7 @@ static int pp_release(struct inode *inode, struct file *file) } if (pp->pdev) { - const char *name = pp->pdev->name; - parport_unregister_device(pp->pdev); - kfree(name); pp->pdev = NULL; pr_debug(CHRDEV "%x: unregistered pardevice\n", minor); } diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 20b105584f82f..80ae2a51452d7 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -700,6 +700,7 @@ static struct clk * __init create_mux_common(struct clockgen *cg, struct mux_hwclock *hwc, const struct clk_ops *ops, unsigned long min_rate, + unsigned long max_rate, unsigned long pct80_rate, const char *fmt, int idx) { @@ -728,6 +729,8 @@ static struct clk * __init create_mux_common(struct clockgen *cg, continue; if (rate < min_rate) continue; + if (rate > max_rate) + continue; parent_names[j] = div->name; hwc->parent_to_clksel[j] = i; @@ -759,7 +762,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) struct mux_hwclock *hwc; const struct clockgen_pll_div *div; unsigned long plat_rate, min_rate; - u64 pct80_rate; + u64 max_rate, pct80_rate; u32 clksel; hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); @@ -787,8 +790,8 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) return NULL; } - pct80_rate = clk_get_rate(div->clk); - pct80_rate *= 8; + max_rate = clk_get_rate(div->clk); + pct80_rate = max_rate * 8; do_div(pct80_rate, 10); plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk); @@ -798,7 +801,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) else min_rate = plat_rate / 2; - return create_mux_common(cg, hwc, &cmux_ops, min_rate, + return create_mux_common(cg, hwc, &cmux_ops, min_rate, max_rate, pct80_rate, "cg-cmux%d", idx); } @@ -813,7 +816,7 @@ static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx) hwc->reg = cg->regs + 0x20 * idx + 0x10; hwc->info = cg->info.hwaccel[idx]; - return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0, + return create_mux_common(cg, hwc, &hwaccel_ops, 0, ULONG_MAX, 0, "cg-hwaccel%d", idx); } diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 5daddf5ecc4b2..bc37030e38ba6 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -463,22 +463,20 @@ static int xgene_clk_enable(struct clk_hw *hw) struct xgene_clk *pclk = to_xgene_clk(hw); unsigned long flags = 0; u32 data; - phys_addr_t reg; if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); if (pclk->param.csr_reg != NULL) { pr_debug("%s clock enabled\n", clk_hw_get_name(hw)); - reg = __pa(pclk->param.csr_reg); /* First enable the clock */ data = xgene_clk_read(pclk->param.csr_reg + pclk->param.reg_clk_offset); data |= pclk->param.reg_clk_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_clk_offset); - pr_debug("%s clock PADDR base %pa clk offset 0x%08X mask 0x%08X value 0x%08X\n", - clk_hw_get_name(hw), ®, + pr_debug("%s clk offset 0x%08X mask 0x%08X value 0x%08X\n", + clk_hw_get_name(hw), pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, data); @@ -488,8 +486,8 @@ static int xgene_clk_enable(struct clk_hw *hw) data &= ~pclk->param.reg_csr_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_csr_offset); - pr_debug("%s CSR RESET PADDR base %pa csr offset 0x%08X mask 0x%08X value 0x%08X\n", - clk_hw_get_name(hw), ®, + pr_debug("%s csr offset 0x%08X mask 0x%08X value 0x%08X\n", + clk_hw_get_name(hw), pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, data); } diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index 19f9b622981a5..7a6acc3e4a927 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -223,7 +223,7 @@ static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw, temp64 *= mfn; do_div(temp64, mfd); - return (parent_rate * div) + (u32)temp64; + return parent_rate * div + (unsigned long)temp64; } static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, @@ -247,7 +247,11 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, do_div(temp64, parent_rate); mfn = temp64; - return parent_rate * div + parent_rate * mfn / mfd; + temp64 = (u64)parent_rate; + temp64 *= mfn; + do_div(temp64, mfd); + + return parent_rate * div + (unsigned long)temp64; } static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 3a51fff1b0e76..9adaf48aea231 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -313,7 +313,7 @@ static void __init mmp2_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 87f2317b2a005..f110c02e83cb6 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -262,7 +262,7 @@ static void __init pxa168_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index e22a67f76d932..64d1ef49caebe 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -282,7 +282,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } @@ -294,7 +294,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apbcp_base = of_iomap(np, 3); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apbcp_base) { pr_err("failed to map apbcp registers\n"); return; } diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c index 8feba93672c5e..e8075359366b0 100644 --- a/drivers/clk/rockchip/clk-ddr.c +++ b/drivers/clk/rockchip/clk-ddr.c @@ -144,11 +144,8 @@ struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, ddrclk->ddr_flag = ddr_flag; clk = clk_register(NULL, &ddrclk->hw); - if (IS_ERR(clk)) { - pr_err("%s: could not register ddrclk %s\n", __func__, name); + if (IS_ERR(clk)) kfree(ddrclk); - return NULL; - } return clk; } diff --git a/drivers/clk/samsung/clk-exynos-clkout.c b/drivers/clk/samsung/clk-exynos-clkout.c index 96fab6cfb2027..6c6afb87b4ce3 100644 --- a/drivers/clk/samsung/clk-exynos-clkout.c +++ b/drivers/clk/samsung/clk-exynos-clkout.c @@ -132,28 +132,34 @@ static void __init exynos_clkout_init(struct device_node *node, u32 mux_mask) pr_err("%s: failed to register clkout clock\n", __func__); } +/* + * We use CLK_OF_DECLARE_DRIVER initialization method to avoid setting + * the OF_POPULATED flag on the pmu device tree node, so later the + * Exynos PMU platform device can be properly probed with PMU driver. + */ + static void __init exynos4_clkout_init(struct device_node *node) { exynos_clkout_init(node, EXYNOS4_CLKOUT_MUX_MASK); } -CLK_OF_DECLARE(exynos4210_clkout, "samsung,exynos4210-pmu", +CLK_OF_DECLARE_DRIVER(exynos4210_clkout, "samsung,exynos4210-pmu", exynos4_clkout_init); -CLK_OF_DECLARE(exynos4212_clkout, "samsung,exynos4212-pmu", +CLK_OF_DECLARE_DRIVER(exynos4212_clkout, "samsung,exynos4212-pmu", exynos4_clkout_init); -CLK_OF_DECLARE(exynos4412_clkout, "samsung,exynos4412-pmu", +CLK_OF_DECLARE_DRIVER(exynos4412_clkout, "samsung,exynos4412-pmu", exynos4_clkout_init); -CLK_OF_DECLARE(exynos3250_clkout, "samsung,exynos3250-pmu", +CLK_OF_DECLARE_DRIVER(exynos3250_clkout, "samsung,exynos3250-pmu", exynos4_clkout_init); static void __init exynos5_clkout_init(struct device_node *node) { exynos_clkout_init(node, EXYNOS5_CLKOUT_MUX_MASK); } -CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", +CLK_OF_DECLARE_DRIVER(exynos5250_clkout, "samsung,exynos5250-pmu", exynos5_clkout_init); -CLK_OF_DECLARE(exynos5410_clkout, "samsung,exynos5410-pmu", +CLK_OF_DECLARE_DRIVER(exynos5410_clkout, "samsung,exynos5410-pmu", exynos5_clkout_init); -CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", +CLK_OF_DECLARE_DRIVER(exynos5420_clkout, "samsung,exynos5420-pmu", exynos5_clkout_init); -CLK_OF_DECLARE(exynos5433_clkout, "samsung,exynos5433-pmu", +CLK_OF_DECLARE_DRIVER(exynos5433_clkout, "samsung,exynos5433-pmu", exynos5_clkout_init); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 2f9f96cc9f65b..06879d1dcabd3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -395,9 +395,12 @@ static int acp_hw_fini(void *handle) { int i, ret; struct device *dev; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; + /* return early if no ACP */ + if (!adev->acp.acp_genpd) + return 0; + for (i = 0; i < ACP_DEVS ; i++) { dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); ret = pm_genpd_remove_device(&adev->acp.acp_genpd->gpd, dev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index 017556ca22e6c..7ded61e6dd81f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -809,10 +809,19 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device, if (!adev->pm.fw) { switch (adev->asic_type) { case CHIP_TOPAZ: - strcpy(fw_name, "amdgpu/topaz_smc.bin"); + if (((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x81)) || + ((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x83)) || + ((adev->pdev->device == 0x6907) && (adev->pdev->revision == 0x87))) + strcpy(fw_name, "amdgpu/topaz_k_smc.bin"); + else + strcpy(fw_name, "amdgpu/topaz_smc.bin"); break; case CHIP_TONGA: - strcpy(fw_name, "amdgpu/tonga_smc.bin"); + if (((adev->pdev->device == 0x6939) && (adev->pdev->revision == 0xf1)) || + ((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) + strcpy(fw_name, "amdgpu/tonga_k_smc.bin"); + else + strcpy(fw_name, "amdgpu/tonga_smc.bin"); break; case CHIP_FIJI: strcpy(fw_name, "amdgpu/fiji_smc.bin"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index 3af8ffb45b64d..8d1cf2d3e663a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -769,7 +769,7 @@ static void amdgpu_connector_unregister(struct drm_connector *connector) { struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); - if (amdgpu_connector->ddc_bus->has_aux) { + if (amdgpu_connector->ddc_bus && amdgpu_connector->ddc_bus->has_aux) { drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux); amdgpu_connector->ddc_bus->has_aux = false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 6bb4d9e9afe4e..42da6163b893a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -742,8 +742,20 @@ static struct pci_driver amdgpu_kms_pci_driver = { static int __init amdgpu_init(void) { - amdgpu_sync_init(); - amdgpu_fence_slab_init(); + int r; + + r = amdgpu_sync_init(); + if (r) + goto error_sync; + + r = amdgpu_fence_slab_init(); + if (r) + goto error_fence; + + r = amd_sched_fence_slab_init(); + if (r) + goto error_sched; + if (vgacon_text_force()) { DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; @@ -755,6 +767,15 @@ static int __init amdgpu_init(void) amdgpu_register_atpx_handler(); /* let modprobe override vga console setting */ return drm_pci_init(driver, pdriver); + +error_sched: + amdgpu_fence_slab_fini(); + +error_fence: + amdgpu_sync_fini(); + +error_sync: + return r; } static void __exit amdgpu_exit(void) @@ -763,6 +784,7 @@ static void __exit amdgpu_exit(void) drm_pci_exit(driver, pdriver); amdgpu_unregister_atpx_handler(); amdgpu_sync_fini(); + amd_sched_fence_slab_fini(); amdgpu_fence_slab_fini(); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index ad908612aff99..d1cf9ac0dff1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -99,6 +99,8 @@ int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags) if ((amdgpu_runtime_pm != 0) && amdgpu_has_atpx() && + (amdgpu_is_atpx_hybrid() || + amdgpu_has_atpx_dgpu_power_cntl()) && ((flags & AMD_IS_APU) == 0)) flags |= AMD_IS_PX; diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index 52d0a83e6ad10..0b21e7beda913 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -80,7 +80,9 @@ #include "dce_virtual.h" MODULE_FIRMWARE("amdgpu/topaz_smc.bin"); +MODULE_FIRMWARE("amdgpu/topaz_k_smc.bin"); MODULE_FIRMWARE("amdgpu/tonga_smc.bin"); +MODULE_FIRMWARE("amdgpu/tonga_k_smc.bin"); MODULE_FIRMWARE("amdgpu/fiji_smc.bin"); MODULE_FIRMWARE("amdgpu/polaris10_smc.bin"); MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin"); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c index 14f8c1f4da3d7..0723758ed0650 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c @@ -272,7 +272,7 @@ bool phm_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hw PHM_FUNC_CHECK(hwmgr); if (hwmgr->hwmgr_func->check_smc_update_required_for_display_configuration == NULL) - return -EINVAL; + return false; return hwmgr->hwmgr_func->check_smc_update_required_for_display_configuration(hwmgr); } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c index 2ba7937d25457..e03dcb6ea9c17 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c @@ -710,8 +710,10 @@ int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint32_t vol; int ret = 0; - if (hwmgr->chip_id < CHIP_POLARIS10) { - atomctrl_get_voltage_evv_on_sclk(hwmgr, voltage_type, sclk, id, voltage); + if (hwmgr->chip_id < CHIP_TONGA) { + ret = atomctrl_get_voltage_evv(hwmgr, id, voltage); + } else if (hwmgr->chip_id < CHIP_POLARIS10) { + ret = atomctrl_get_voltage_evv_on_sclk(hwmgr, voltage_type, sclk, id, voltage); if (*voltage >= 2000 || *voltage == 0) *voltage = 1150; } else { diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c index 9e49f2777143a..28e748d688e23 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c @@ -1474,19 +1474,19 @@ static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr) struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table = NULL; - if (table_info == NULL) - return -EINVAL; - - sclk_table = table_info->vdd_dep_on_sclk; - for (i = 0; i < SMU7_MAX_LEAKAGE_COUNT; i++) { vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i; if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) { - if (0 == phm_get_sclk_for_voltage_evv(hwmgr, + if ((hwmgr->pp_table_version == PP_TABLE_V1) + && !phm_get_sclk_for_voltage_evv(hwmgr, table_info->vddgfx_lookup_table, vv_id, &sclk)) { if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ClockStretcher)) { + if (table_info == NULL) + return -EINVAL; + sclk_table = table_info->vdd_dep_on_sclk; + for (j = 1; j < sclk_table->count; j++) { if (sclk_table->entries[j].clk == sclk && sclk_table->entries[j].cks_enable == 0) { @@ -1512,12 +1512,15 @@ static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr) } } } else { - if ((hwmgr->pp_table_version == PP_TABLE_V0) || !phm_get_sclk_for_voltage_evv(hwmgr, table_info->vddc_lookup_table, vv_id, &sclk)) { if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ClockStretcher)) { + if (table_info == NULL) + return -EINVAL; + sclk_table = table_info->vdd_dep_on_sclk; + for (j = 1; j < sclk_table->count; j++) { if (sclk_table->entries[j].clk == sclk && sclk_table->entries[j].cks_enable == 0) { @@ -2147,9 +2150,11 @@ static int smu7_patch_limits_vddc(struct pp_hwmgr *hwmgr, struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); if (tab) { + vddc = tab->vddc; smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddc, &data->vddc_leakage); tab->vddc = vddc; + vddci = tab->vddci; smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddci, &data->vddci_leakage); tab->vddci = vddci; @@ -4247,18 +4252,26 @@ static int smu7_get_sclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks) { struct phm_ppt_v1_information *table_info = (struct phm_ppt_v1_information *)hwmgr->pptable; - struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table; + struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table = NULL; + struct phm_clock_voltage_dependency_table *sclk_table; int i; - if (table_info == NULL) - return -EINVAL; - - dep_sclk_table = table_info->vdd_dep_on_sclk; - - for (i = 0; i < dep_sclk_table->count; i++) { - clocks->clock[i] = dep_sclk_table->entries[i].clk; - clocks->count++; + if (hwmgr->pp_table_version == PP_TABLE_V1) { + if (table_info == NULL || table_info->vdd_dep_on_sclk == NULL) + return -EINVAL; + dep_sclk_table = table_info->vdd_dep_on_sclk; + for (i = 0; i < dep_sclk_table->count; i++) { + clocks->clock[i] = dep_sclk_table->entries[i].clk; + clocks->count++; + } + } else if (hwmgr->pp_table_version == PP_TABLE_V0) { + sclk_table = hwmgr->dyn_state.vddc_dependency_on_sclk; + for (i = 0; i < sclk_table->count; i++) { + clocks->clock[i] = sclk_table->entries[i].clk; + clocks->count++; + } } + return 0; } @@ -4280,17 +4293,24 @@ static int smu7_get_mclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks) (struct phm_ppt_v1_information *)hwmgr->pptable; struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table; int i; + struct phm_clock_voltage_dependency_table *mclk_table; - if (table_info == NULL) - return -EINVAL; - - dep_mclk_table = table_info->vdd_dep_on_mclk; - - for (i = 0; i < dep_mclk_table->count; i++) { - clocks->clock[i] = dep_mclk_table->entries[i].clk; - clocks->latency[i] = smu7_get_mem_latency(hwmgr, + if (hwmgr->pp_table_version == PP_TABLE_V1) { + if (table_info == NULL) + return -EINVAL; + dep_mclk_table = table_info->vdd_dep_on_mclk; + for (i = 0; i < dep_mclk_table->count; i++) { + clocks->clock[i] = dep_mclk_table->entries[i].clk; + clocks->latency[i] = smu7_get_mem_latency(hwmgr, dep_mclk_table->entries[i].clk); - clocks->count++; + clocks->count++; + } + } else if (hwmgr->pp_table_version == PP_TABLE_V0) { + mclk_table = hwmgr->dyn_state.vddc_dependency_on_mclk; + for (i = 0; i < mclk_table->count; i++) { + clocks->clock[i] = mclk_table->entries[i].clk; + clocks->count++; + } } return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c index fb6c6f6106d5f..29d0319b22e6c 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c @@ -30,7 +30,7 @@ int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info) { if (hwmgr->thermal_controller.fanInfo.bNoFan) - return 0; + return -ENODEV; fan_speed_info->supports_percent_read = true; fan_speed_info->supports_percent_write = true; @@ -60,7 +60,7 @@ int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint64_t tmp64; if (hwmgr->thermal_controller.fanInfo.bNoFan) - return 0; + return -ENODEV; duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100); @@ -89,7 +89,7 @@ int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) if (hwmgr->thermal_controller.fanInfo.bNoFan || (hwmgr->thermal_controller.fanInfo. ucTachometerPulsesPerRevolution == 0)) - return 0; + return -ENODEV; tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_TACH_STATUS, TACH_PERIOD); diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c index 09b2cf6ccfa40..1bf83ed113b3c 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c @@ -34,9 +34,6 @@ static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity); static void amd_sched_wakeup(struct amd_gpu_scheduler *sched); static void amd_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb); -struct kmem_cache *sched_fence_slab; -atomic_t sched_fence_slab_ref = ATOMIC_INIT(0); - /* Initialize a given run queue struct */ static void amd_sched_rq_init(struct amd_sched_rq *rq) { @@ -619,13 +616,6 @@ int amd_sched_init(struct amd_gpu_scheduler *sched, INIT_LIST_HEAD(&sched->ring_mirror_list); spin_lock_init(&sched->job_list_lock); atomic_set(&sched->hw_rq_count, 0); - if (atomic_inc_return(&sched_fence_slab_ref) == 1) { - sched_fence_slab = kmem_cache_create( - "amd_sched_fence", sizeof(struct amd_sched_fence), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!sched_fence_slab) - return -ENOMEM; - } /* Each scheduler will run on a seperate kernel thread */ sched->thread = kthread_run(amd_sched_main, sched, sched->name); @@ -646,7 +636,4 @@ void amd_sched_fini(struct amd_gpu_scheduler *sched) { if (sched->thread) kthread_stop(sched->thread); - rcu_barrier(); - if (atomic_dec_and_test(&sched_fence_slab_ref)) - kmem_cache_destroy(sched_fence_slab); } diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h index 876aa43b57df3..d8dc681bcda68 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h @@ -30,9 +30,6 @@ struct amd_gpu_scheduler; struct amd_sched_rq; -extern struct kmem_cache *sched_fence_slab; -extern atomic_t sched_fence_slab_ref; - /** * A scheduler entity is a wrapper around a job queue or a group * of other entities. Entities take turns emitting jobs from their @@ -145,6 +142,9 @@ void amd_sched_entity_fini(struct amd_gpu_scheduler *sched, struct amd_sched_entity *entity); void amd_sched_entity_push_job(struct amd_sched_job *sched_job); +int amd_sched_fence_slab_init(void); +void amd_sched_fence_slab_fini(void); + struct amd_sched_fence *amd_sched_fence_create( struct amd_sched_entity *s_entity, void *owner); void amd_sched_fence_scheduled(struct amd_sched_fence *fence); diff --git a/drivers/gpu/drm/amd/scheduler/sched_fence.c b/drivers/gpu/drm/amd/scheduler/sched_fence.c index 91530e25aafff..33f54d0a5c4fc 100644 --- a/drivers/gpu/drm/amd/scheduler/sched_fence.c +++ b/drivers/gpu/drm/amd/scheduler/sched_fence.c @@ -27,6 +27,25 @@ #include #include "gpu_scheduler.h" +static struct kmem_cache *sched_fence_slab; + +int amd_sched_fence_slab_init(void) +{ + sched_fence_slab = kmem_cache_create( + "amd_sched_fence", sizeof(struct amd_sched_fence), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!sched_fence_slab) + return -ENOMEM; + + return 0; +} + +void amd_sched_fence_slab_fini(void) +{ + rcu_barrier(); + kmem_cache_destroy(sched_fence_slab); +} + struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *entity, void *owner) { diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c index b7a8b2ac4055b..b69c66b4897e4 100644 --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c @@ -14,170 +14,45 @@ * */ -#include +#include #include -#include #include "arcpgu.h" -struct arcpgu_drm_connector { - struct drm_connector connector; - struct drm_encoder_slave *encoder_slave; -}; - -static int arcpgu_drm_connector_get_modes(struct drm_connector *connector) -{ - const struct drm_encoder_slave_funcs *sfuncs; - struct drm_encoder_slave *slave; - struct arcpgu_drm_connector *con = - container_of(connector, struct arcpgu_drm_connector, connector); - - slave = con->encoder_slave; - if (slave == NULL) { - dev_err(connector->dev->dev, - "connector_get_modes: cannot find slave encoder for connector\n"); - return 0; - } - - sfuncs = slave->slave_funcs; - if (sfuncs->get_modes == NULL) - return 0; - - return sfuncs->get_modes(&slave->base, connector); -} - -static enum drm_connector_status -arcpgu_drm_connector_detect(struct drm_connector *connector, bool force) -{ - enum drm_connector_status status = connector_status_unknown; - const struct drm_encoder_slave_funcs *sfuncs; - struct drm_encoder_slave *slave; - - struct arcpgu_drm_connector *con = - container_of(connector, struct arcpgu_drm_connector, connector); - - slave = con->encoder_slave; - if (slave == NULL) { - dev_err(connector->dev->dev, - "connector_detect: cannot find slave encoder for connector\n"); - return status; - } - - sfuncs = slave->slave_funcs; - if (sfuncs && sfuncs->detect) - return sfuncs->detect(&slave->base, connector); - - dev_err(connector->dev->dev, "connector_detect: could not detect slave funcs\n"); - return status; -} - -static void arcpgu_drm_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - -static const struct drm_connector_helper_funcs -arcpgu_drm_connector_helper_funcs = { - .get_modes = arcpgu_drm_connector_get_modes, -}; - -static const struct drm_connector_funcs arcpgu_drm_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .reset = drm_atomic_helper_connector_reset, - .detect = arcpgu_drm_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = arcpgu_drm_connector_destroy, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static struct drm_encoder_helper_funcs arcpgu_drm_encoder_helper_funcs = { - .dpms = drm_i2c_encoder_dpms, - .mode_fixup = drm_i2c_encoder_mode_fixup, - .mode_set = drm_i2c_encoder_mode_set, - .prepare = drm_i2c_encoder_prepare, - .commit = drm_i2c_encoder_commit, - .detect = drm_i2c_encoder_detect, -}; - static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = { .destroy = drm_encoder_cleanup, }; int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np) { - struct arcpgu_drm_connector *arcpgu_connector; - struct drm_i2c_encoder_driver *driver; - struct drm_encoder_slave *encoder; - struct drm_connector *connector; - struct i2c_client *i2c_slave; - int ret; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + + int ret = 0; encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - i2c_slave = of_find_i2c_device_by_node(np); - if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) { - dev_err(drm->dev, "failed to find i2c slave encoder\n"); - return -EPROBE_DEFER; - } - - if (i2c_slave->dev.driver == NULL) { - dev_err(drm->dev, "failed to find i2c slave driver\n"); + /* Locate drm bridge from the hdmi encoder DT node */ + bridge = of_drm_find_bridge(np); + if (!bridge) return -EPROBE_DEFER; - } - driver = - to_drm_i2c_encoder_driver(to_i2c_driver(i2c_slave->dev.driver)); - ret = driver->encoder_init(i2c_slave, drm, encoder); - if (ret) { - dev_err(drm->dev, "failed to initialize i2c encoder slave\n"); - return ret; - } - - encoder->base.possible_crtcs = 1; - encoder->base.possible_clones = 0; - ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs, + encoder->possible_crtcs = 1; + encoder->possible_clones = 0; + ret = drm_encoder_init(drm, encoder, &arcpgu_drm_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) return ret; - drm_encoder_helper_add(&encoder->base, - &arcpgu_drm_encoder_helper_funcs); - - arcpgu_connector = devm_kzalloc(drm->dev, sizeof(*arcpgu_connector), - GFP_KERNEL); - if (!arcpgu_connector) { - ret = -ENOMEM; - goto error_encoder_cleanup; - } - - connector = &arcpgu_connector->connector; - drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs); - ret = drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret < 0) { - dev_err(drm->dev, "failed to initialize drm connector\n"); - goto error_encoder_cleanup; - } + /* Link drm_bridge to encoder */ + bridge->encoder = encoder; + encoder->bridge = bridge; - ret = drm_mode_connector_attach_encoder(connector, &encoder->base); - if (ret < 0) { - dev_err(drm->dev, "could not attach connector to encoder\n"); - drm_connector_unregister(connector); - goto error_connector_cleanup; - } - - arcpgu_connector->encoder_slave = encoder; - - return 0; - -error_connector_cleanup: - drm_connector_cleanup(connector); + ret = drm_bridge_attach(drm, bridge); + if (ret) + drm_encoder_cleanup(encoder); -error_encoder_cleanup: - drm_encoder_cleanup(&encoder->base); return ret; } diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index afec232185a76..e5706981c9346 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -23,6 +24,7 @@ struct dumb_vga { struct drm_connector connector; struct i2c_adapter *ddc; + struct regulator *vdd; }; static inline struct dumb_vga * @@ -124,8 +126,30 @@ static int dumb_vga_attach(struct drm_bridge *bridge) return 0; } +static void dumb_vga_enable(struct drm_bridge *bridge) +{ + struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + int ret = 0; + + if (vga->vdd) + ret = regulator_enable(vga->vdd); + + if (ret) + DRM_ERROR("Failed to enable vdd regulator: %d\n", ret); +} + +static void dumb_vga_disable(struct drm_bridge *bridge) +{ + struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + + if (vga->vdd) + regulator_disable(vga->vdd); +} + static const struct drm_bridge_funcs dumb_vga_bridge_funcs = { .attach = dumb_vga_attach, + .enable = dumb_vga_enable, + .disable = dumb_vga_disable, }; static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev) @@ -169,6 +193,15 @@ static int dumb_vga_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, vga); + vga->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); + if (IS_ERR(vga->vdd)) { + ret = PTR_ERR(vga->vdd); + if (ret == -EPROBE_DEFER) + return -EPROBE_DEFER; + vga->vdd = NULL; + dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret); + } + vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev); if (IS_ERR(vga->ddc)) { if (PTR_ERR(vga->ddc) == -ENODEV) { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index b476ec5855472..85466cc67819a 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -965,12 +965,12 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, drm_printf(p, "\t\tformat=%s\n", drm_get_format_name(fb->pixel_format, &format_name)); + drm_printf(p, "\t\t\tmodifier=0x%llx\n", fb->modifier); drm_printf(p, "\t\tsize=%dx%d\n", fb->width, fb->height); drm_printf(p, "\t\tlayers:\n"); for (i = 0; i < n; i++) { drm_printf(p, "\t\t\tpitch[%d]=%u\n", i, fb->pitches[i]); drm_printf(p, "\t\t\toffset[%d]=%u\n", i, fb->offsets[i]); - drm_printf(p, "\t\t\tmodifier[%d]=0x%llx\n", i, fb->modifier[i]); } } drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 6dbb986497955..cc6c2530764b5 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -507,12 +507,6 @@ int drm_dev_init(struct drm_device *dev, goto err_free; } - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL); - if (ret) - goto err_minors; - } - if (drm_core_check_feature(dev, DRIVER_RENDER)) { ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); if (ret) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 06ad3d1350c49..cbf0c893f426d 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -177,6 +177,13 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) return -EINVAL; } + if (r->flags & DRM_MODE_FB_MODIFIERS && + r->modifier[i] != r->modifier[0]) { + DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", + r->modifier[i], i); + return -EINVAL; + } + /* modifier specific checks: */ switch (r->modifier[i]) { case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index 2f452b3dd40e1..cc232ac6c9503 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -38,7 +38,7 @@ * Some userspace presumes that the first connected connector is the main * display, where it's supposed to display e.g. the login screen. For * laptops, this should be the main panel. Use this function to sort all - * (eDP/LVDS) panels to the front of the connector list, instead of + * (eDP/LVDS/DSI) panels to the front of the connector list, instead of * painstakingly trying to initialize them in the right order. */ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) @@ -51,7 +51,8 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) + connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_DSI) list_move_tail(&connector->head, &panel_list); } @@ -93,8 +94,8 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, for (i = 0; i < 4; i++) { fb->pitches[i] = mode_cmd->pitches[i]; fb->offsets[i] = mode_cmd->offsets[i]; - fb->modifier[i] = mode_cmd->modifier[i]; } + fb->modifier = mode_cmd->modifier[0]; fb->pixel_format = mode_cmd->pixel_format; fb->flags = mode_cmd->flags; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index b2d5e188b1b88..deb57435cc897 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -25,8 +25,13 @@ static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + struct drm_device *dev = crtc->dev; + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; struct drm_pending_vblank_event *event = crtc->state->event; + regmap_write(fsl_dev->regmap, + DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); + if (event) { crtc->state->event = NULL; @@ -39,11 +44,15 @@ static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, } } -static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) +static void fsl_dcu_drm_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + /* always disable planes on the CRTC */ + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true); + drm_crtc_vblank_off(crtc); regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, @@ -122,8 +131,8 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) } static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { + .atomic_disable = fsl_dcu_drm_crtc_atomic_disable, .atomic_flush = fsl_dcu_drm_crtc_atomic_flush, - .disable = fsl_dcu_drm_disable_crtc, .enable = fsl_dcu_drm_crtc_enable, .mode_set_nofb = fsl_dcu_drm_crtc_mode_set_nofb, }; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 0b0d1cb116415..320e4728c9b96 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -59,8 +59,6 @@ static int fsl_dcu_drm_irq_init(struct drm_device *dev) regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0); regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0); - regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); return ret; } @@ -139,8 +137,6 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) drm_handle_vblank(dev, 0); regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status); - regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index 9e6f7d8112b32..a99f488474205 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -160,11 +160,6 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, DCU_LAYER_POST_SKIP(0) | DCU_LAYER_PRE_SKIP(0)); } - regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); - regmap_write(fsl_dev->regmap, - DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); return; } diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 5ddde7349fbde..183f5dc1c3f22 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -116,6 +116,7 @@ config DRM_I915_GVT_KVMGT tristate "Enable KVM/VFIO support for Intel GVT-g" depends on DRM_I915_GVT depends on KVM + depends on VFIO_MDEV && VFIO_MDEV_DEVICE default n help Choose this option if you want to enable KVMGT support for diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 3d4223e8ebe3a..b9d4a8db077be 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -164,15 +164,15 @@ struct intel_vgpu { #if IS_ENABLED(CONFIG_DRM_I915_GVT_KVMGT) struct { - struct device *mdev; + struct mdev_device *mdev; struct vfio_region *region; int num_regions; struct eventfd_ctx *intx_trigger; struct eventfd_ctx *msi_trigger; struct rb_root cache; struct mutex cache_lock; - void *vfio_group; - struct notifier_block iommu_notifier; + struct kvm *kvm; + struct work_struct release_work; } vdev; #endif }; diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index dc03650331574..82f5314e5114c 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -39,24 +39,13 @@ #include #include #include +#include #include "i915_drv.h" #include "gvt.h" -static inline long kvmgt_pin_pages(struct device *dev, unsigned long *user_pfn, - long npage, int prot, unsigned long *phys_pfn) -{ - return 0; -} -static inline long kvmgt_unpin_pages(struct device *dev, unsigned long *pfn, - long npage) -{ - return 0; -} - static const struct intel_gvt_ops *intel_gvt_ops; - /* helper macros copied from vfio-pci */ #define VFIO_PCI_OFFSET_SHIFT 40 #define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT) @@ -90,6 +79,15 @@ struct gvt_dma { kvm_pfn_t pfn; }; +static inline bool handle_valid(unsigned long handle) +{ + return !!(handle & ~0xff); +} + +static int kvmgt_guest_init(struct mdev_device *mdev); +static void intel_vgpu_release_work(struct work_struct *work); +static bool kvmgt_guest_exit(struct kvmgt_guest_info *info); + static struct gvt_dma *__gvt_cache_find(struct intel_vgpu *vgpu, gfn_t gfn) { struct rb_node *node = vgpu->vdev.cache.rb_node; @@ -167,9 +165,9 @@ static void __gvt_cache_remove_entry(struct intel_vgpu *vgpu, static void gvt_cache_remove(struct intel_vgpu *vgpu, gfn_t gfn) { - struct device *dev = vgpu->vdev.mdev; + struct device *dev = &vgpu->vdev.mdev->dev; struct gvt_dma *this; - unsigned long pfn; + unsigned long g1; mutex_lock(&vgpu->vdev.cache_lock); this = __gvt_cache_find(vgpu, gfn); @@ -178,8 +176,8 @@ static void gvt_cache_remove(struct intel_vgpu *vgpu, gfn_t gfn) return; } - pfn = this->pfn; - WARN_ON((kvmgt_unpin_pages(dev, &pfn, 1) != 1)); + g1 = gfn; + WARN_ON((vfio_unpin_pages(dev, &g1, 1) != 1)); __gvt_cache_remove_entry(vgpu, this); mutex_unlock(&vgpu->vdev.cache_lock); } @@ -194,15 +192,15 @@ static void gvt_cache_destroy(struct intel_vgpu *vgpu) { struct gvt_dma *dma; struct rb_node *node = NULL; - struct device *dev = vgpu->vdev.mdev; - unsigned long pfn; + struct device *dev = &vgpu->vdev.mdev->dev; + unsigned long g1; mutex_lock(&vgpu->vdev.cache_lock); while ((node = rb_first(&vgpu->vdev.cache))) { dma = rb_entry(node, struct gvt_dma, node); - pfn = dma->pfn; + g1 = dma->gfn; - kvmgt_unpin_pages(dev, &pfn, 1); + vfio_unpin_pages(dev, &g1, 1); __gvt_cache_remove_entry(vgpu, dma); } mutex_unlock(&vgpu->vdev.cache_lock); @@ -226,7 +224,53 @@ static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt, return NULL; } +static ssize_t available_instance_show(struct kobject *kobj, struct device *dev, + char *buf) +{ + struct intel_vgpu_type *type; + unsigned int num = 0; + void *gvt = kdev_to_i915(dev)->gvt; + + type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj)); + if (!type) + num = 0; + else + num = type->avail_instance; + + return sprintf(buf, "%u\n", num); +} + +static ssize_t device_api_show(struct kobject *kobj, struct device *dev, + char *buf) +{ + return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); +} + +static ssize_t description_show(struct kobject *kobj, struct device *dev, + char *buf) +{ + struct intel_vgpu_type *type; + void *gvt = kdev_to_i915(dev)->gvt; + + type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj)); + if (!type) + return 0; + + return sprintf(buf, "low_gm_size: %dMB\nhigh_gm_size: %dMB\n" + "fence: %d\n", + BYTES_TO_MB(type->low_gm_size), + BYTES_TO_MB(type->high_gm_size), + type->fence); +} + +static MDEV_TYPE_ATTR_RO(available_instance); +static MDEV_TYPE_ATTR_RO(device_api); +static MDEV_TYPE_ATTR_RO(description); + static struct attribute *type_attrs[] = { + &mdev_type_attr_available_instance.attr, + &mdev_type_attr_device_api.attr, + &mdev_type_attr_description.attr, NULL, }; @@ -322,7 +366,7 @@ static void kvmgt_protect_table_add(struct kvmgt_guest_info *info, gfn_t gfn) if (kvmgt_gfn_is_write_protected(info, gfn)) return; - p = kmalloc(sizeof(struct kvmgt_pgfn), GFP_ATOMIC); + p = kzalloc(sizeof(struct kvmgt_pgfn), GFP_ATOMIC); if (WARN(!p, "gfn: 0x%llx\n", gfn)) return; @@ -342,6 +386,646 @@ static void kvmgt_protect_table_del(struct kvmgt_guest_info *info, } } +static int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev) +{ + struct intel_vgpu *vgpu; + struct intel_vgpu_type *type; + struct device *pdev; + void *gvt; + + pdev = mdev->parent->dev; + gvt = kdev_to_i915(pdev)->gvt; + + type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj)); + if (!type) { + gvt_err("failed to find type %s to create\n", + kobject_name(kobj)); + return -EINVAL; + } + + vgpu = intel_gvt_ops->vgpu_create(gvt, type); + if (IS_ERR_OR_NULL(vgpu)) { + gvt_err("create intel vgpu failed\n"); + return -EINVAL; + } + + INIT_WORK(&vgpu->vdev.release_work, intel_vgpu_release_work); + + vgpu->vdev.mdev = mdev; + mdev_set_drvdata(mdev, vgpu); + + gvt_dbg_core("intel_vgpu_create succeeded for mdev: %s\n", + dev_name(&mdev->dev)); + return 0; +} + +static int intel_vgpu_remove(struct mdev_device *mdev) +{ + struct intel_vgpu *vgpu = mdev_get_drvdata(mdev); + + if (handle_valid(vgpu->handle)) + return -EBUSY; + + intel_gvt_ops->vgpu_destroy(vgpu); + return 0; +} + +static int intel_vgpu_open(struct mdev_device *mdev) +{ + return kvmgt_guest_init(mdev); +} + +static void __intel_vgpu_release(struct intel_vgpu *vgpu) +{ + struct kvmgt_guest_info *info; + + if (!handle_valid(vgpu->handle)) + return; + + info = (struct kvmgt_guest_info *)vgpu->handle; + kvmgt_guest_exit(info); + vgpu->handle = 0; +} + +static void intel_vgpu_release(struct mdev_device *mdev) +{ + struct intel_vgpu *vgpu = mdev_get_drvdata(mdev); + + __intel_vgpu_release(vgpu); +} + +static void intel_vgpu_release_work(struct work_struct *work) +{ + struct intel_vgpu *vgpu = container_of(work, struct intel_vgpu, + vdev.release_work); + __intel_vgpu_release(vgpu); +} + +static uint64_t intel_vgpu_get_bar0_addr(struct intel_vgpu *vgpu) +{ + u32 start_lo, start_hi; + u32 mem_type; + int pos = PCI_BASE_ADDRESS_0; + + start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) & + PCI_BASE_ADDRESS_MEM_MASK; + mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) & + PCI_BASE_ADDRESS_MEM_TYPE_MASK; + + switch (mem_type) { + case PCI_BASE_ADDRESS_MEM_TYPE_64: + start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + + pos + 4)); + break; + case PCI_BASE_ADDRESS_MEM_TYPE_32: + case PCI_BASE_ADDRESS_MEM_TYPE_1M: + /* 1M mem BAR treated as 32-bit BAR */ + default: + /* mem unknown type treated as 32-bit BAR */ + start_hi = 0; + break; + } + + return ((u64)start_hi << 32) | start_lo; +} + +static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf, + size_t count, loff_t *ppos, bool is_write) +{ + struct intel_vgpu *vgpu = mdev_get_drvdata(mdev); + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + uint64_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int ret = -EINVAL; + + + if (index >= VFIO_PCI_NUM_REGIONS) { + gvt_err("invalid index: %u\n", index); + return -EINVAL; + } + + switch (index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + if (is_write) + ret = intel_gvt_ops->emulate_cfg_write(vgpu, pos, + buf, count); + else + ret = intel_gvt_ops->emulate_cfg_read(vgpu, pos, + buf, count); + break; + case VFIO_PCI_BAR0_REGION_INDEX: + case VFIO_PCI_BAR1_REGION_INDEX: + if (is_write) { + uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu); + + ret = intel_gvt_ops->emulate_mmio_write(vgpu, + bar0_start + pos, buf, count); + } else { + uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu); + + ret = intel_gvt_ops->emulate_mmio_read(vgpu, + bar0_start + pos, buf, count); + } + break; + case VFIO_PCI_BAR2_REGION_INDEX: + case VFIO_PCI_BAR3_REGION_INDEX: + case VFIO_PCI_BAR4_REGION_INDEX: + case VFIO_PCI_BAR5_REGION_INDEX: + case VFIO_PCI_VGA_REGION_INDEX: + case VFIO_PCI_ROM_REGION_INDEX: + default: + gvt_err("unsupported region: %u\n", index); + } + + return ret == 0 ? count : ret; +} + +static ssize_t intel_vgpu_read(struct mdev_device *mdev, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val), + ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val), + ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 2; + } else { + u8 val; + + ret = intel_vgpu_rw(mdev, &val, sizeof(val), ppos, + false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 1; + } + + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; + +read_err: + return -EFAULT; +} + +static ssize_t intel_vgpu_write(struct mdev_device *mdev, + const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val), + ppos, true); + if (ret <= 0) + goto write_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = intel_vgpu_rw(mdev, (char *)&val, + sizeof(val), ppos, true); + if (ret <= 0) + goto write_err; + + filled = 2; + } else { + u8 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = intel_vgpu_rw(mdev, &val, sizeof(val), + ppos, true); + if (ret <= 0) + goto write_err; + + filled = 1; + } + + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; +write_err: + return -EFAULT; +} + +static int intel_vgpu_mmap(struct mdev_device *mdev, struct vm_area_struct *vma) +{ + unsigned int index; + u64 virtaddr; + unsigned long req_size, pgoff = 0; + pgprot_t pg_prot; + struct intel_vgpu *vgpu = mdev_get_drvdata(mdev); + + index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT); + if (index >= VFIO_PCI_ROM_REGION_INDEX) + return -EINVAL; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + if (index != VFIO_PCI_BAR2_REGION_INDEX) + return -EINVAL; + + pg_prot = vma->vm_page_prot; + virtaddr = vma->vm_start; + req_size = vma->vm_end - vma->vm_start; + pgoff = vgpu_aperture_pa_base(vgpu) >> PAGE_SHIFT; + + return remap_pfn_range(vma, virtaddr, pgoff, req_size, pg_prot); +} + +static int intel_vgpu_get_irq_count(struct intel_vgpu *vgpu, int type) +{ + if (type == VFIO_PCI_INTX_IRQ_INDEX || type == VFIO_PCI_MSI_IRQ_INDEX) + return 1; + + return 0; +} + +static int intel_vgpu_set_intx_mask(struct intel_vgpu *vgpu, unsigned index, + unsigned start, unsigned count, uint32_t flags, + void *data) +{ + return 0; +} + +static int intel_vgpu_set_intx_unmask(struct intel_vgpu *vgpu, + unsigned int index, unsigned int start, unsigned int count, + uint32_t flags, void *data) +{ + return 0; +} + +static int intel_vgpu_set_intx_trigger(struct intel_vgpu *vgpu, + unsigned int index, unsigned int start, unsigned int count, + uint32_t flags, void *data) +{ + return 0; +} + +static int intel_vgpu_set_msi_trigger(struct intel_vgpu *vgpu, + unsigned int index, unsigned int start, unsigned int count, + uint32_t flags, void *data) +{ + struct eventfd_ctx *trigger; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int fd = *(int *)data; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + gvt_err("eventfd_ctx_fdget failed\n"); + return PTR_ERR(trigger); + } + vgpu->vdev.msi_trigger = trigger; + } + + return 0; +} + +static int intel_vgpu_set_irqs(struct intel_vgpu *vgpu, uint32_t flags, + unsigned int index, unsigned int start, unsigned int count, + void *data) +{ + int (*func)(struct intel_vgpu *vgpu, unsigned index, + unsigned start, unsigned count, uint32_t flags, + void *data) = NULL; + + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + func = intel_vgpu_set_intx_mask; + break; + case VFIO_IRQ_SET_ACTION_UNMASK: + func = intel_vgpu_set_intx_unmask; + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = intel_vgpu_set_intx_trigger; + break; + } + break; + case VFIO_PCI_MSI_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + /* XXX Need masking support exported */ + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = intel_vgpu_set_msi_trigger; + break; + } + break; + } + + if (!func) + return -ENOTTY; + + return func(vgpu, index, start, count, flags, data); +} + +static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg) +{ + struct intel_vgpu *vgpu = mdev_get_drvdata(mdev); + unsigned long minsz; + + gvt_dbg_core("vgpu%d ioctl, cmd: %d\n", vgpu->id, cmd); + + if (cmd == VFIO_DEVICE_GET_INFO) { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = VFIO_DEVICE_FLAGS_PCI; + info.flags |= VFIO_DEVICE_FLAGS_RESET; + info.num_regions = VFIO_PCI_NUM_REGIONS; + info.num_irqs = VFIO_PCI_NUM_IRQS; + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; + + } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { + struct vfio_region_info info; + struct vfio_info_cap caps = { .buf = NULL, .size = 0 }; + int i, ret; + struct vfio_region_info_cap_sparse_mmap *sparse = NULL; + size_t size; + int nr_areas = 1; + int cap_type_id; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = INTEL_GVT_MAX_CFG_SPACE_SZ; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + break; + case VFIO_PCI_BAR0_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = vgpu->cfg_space.bar[info.index].size; + if (!info.size) { + info.flags = 0; + break; + } + + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + break; + case VFIO_PCI_BAR1_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = 0; + info.flags = 0; + break; + case VFIO_PCI_BAR2_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.flags = VFIO_REGION_INFO_FLAG_CAPS | + VFIO_REGION_INFO_FLAG_MMAP | + VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + info.size = gvt_aperture_sz(vgpu->gvt); + + size = sizeof(*sparse) + + (nr_areas * sizeof(*sparse->areas)); + sparse = kzalloc(size, GFP_KERNEL); + if (!sparse) + return -ENOMEM; + + sparse->nr_areas = nr_areas; + cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP; + sparse->areas[0].offset = + PAGE_ALIGN(vgpu_aperture_offset(vgpu)); + sparse->areas[0].size = vgpu_aperture_sz(vgpu); + if (!caps.buf) { + kfree(caps.buf); + caps.buf = NULL; + caps.size = 0; + } + break; + + case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = 0; + + info.flags = 0; + gvt_dbg_core("get region info bar:%d\n", info.index); + break; + + case VFIO_PCI_ROM_REGION_INDEX: + case VFIO_PCI_VGA_REGION_INDEX: + gvt_dbg_core("get region info index:%d\n", info.index); + break; + default: + { + struct vfio_region_info_cap_type cap_type; + + if (info.index >= VFIO_PCI_NUM_REGIONS + + vgpu->vdev.num_regions) + return -EINVAL; + + i = info.index - VFIO_PCI_NUM_REGIONS; + + info.offset = + VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = vgpu->vdev.region[i].size; + info.flags = vgpu->vdev.region[i].flags; + + cap_type.type = vgpu->vdev.region[i].type; + cap_type.subtype = vgpu->vdev.region[i].subtype; + + ret = vfio_info_add_capability(&caps, + VFIO_REGION_INFO_CAP_TYPE, + &cap_type); + if (ret) + return ret; + } + } + + if ((info.flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) { + switch (cap_type_id) { + case VFIO_REGION_INFO_CAP_SPARSE_MMAP: + ret = vfio_info_add_capability(&caps, + VFIO_REGION_INFO_CAP_SPARSE_MMAP, + sparse); + kfree(sparse); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + } + + if (caps.size) { + if (info.argsz < sizeof(info) + caps.size) { + info.argsz = sizeof(info) + caps.size; + info.cap_offset = 0; + } else { + vfio_info_cap_shift(&caps, sizeof(info)); + if (copy_to_user((void __user *)arg + + sizeof(info), caps.buf, + caps.size)) { + kfree(caps.buf); + return -EFAULT; + } + info.cap_offset = sizeof(info); + } + + kfree(caps.buf); + } + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; + } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_INTX_IRQ_INDEX: + case VFIO_PCI_MSI_IRQ_INDEX: + break; + default: + return -EINVAL; + } + + info.flags = VFIO_IRQ_INFO_EVENTFD; + + info.count = intel_vgpu_get_irq_count(vgpu, info.index); + + if (info.index == VFIO_PCI_INTX_IRQ_INDEX) + info.flags |= (VFIO_IRQ_INFO_MASKABLE | + VFIO_IRQ_INFO_AUTOMASKED); + else + info.flags |= VFIO_IRQ_INFO_NORESIZE; + + return copy_to_user((void __user *)arg, &info, minsz) ? + -EFAULT : 0; + } else if (cmd == VFIO_DEVICE_SET_IRQS) { + struct vfio_irq_set hdr; + u8 *data = NULL; + int ret = 0; + size_t data_size = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { + int max = intel_vgpu_get_irq_count(vgpu, hdr.index); + + ret = vfio_set_irqs_validate_and_prepare(&hdr, max, + VFIO_PCI_NUM_IRQS, &data_size); + if (ret) { + gvt_err("intel:vfio_set_irqs_validate_and_prepare failed\n"); + return -EINVAL; + } + if (data_size) { + data = memdup_user((void __user *)(arg + minsz), + data_size); + if (IS_ERR(data)) + return PTR_ERR(data); + } + } + + ret = intel_vgpu_set_irqs(vgpu, hdr.flags, hdr.index, + hdr.start, hdr.count, data); + kfree(data); + + return ret; + } else if (cmd == VFIO_DEVICE_RESET) { + intel_gvt_ops->vgpu_reset(vgpu); + return 0; + } + + return 0; +} + +static const struct parent_ops intel_vgpu_ops = { + .supported_type_groups = intel_vgpu_type_groups, + .create = intel_vgpu_create, + .remove = intel_vgpu_remove, + + .open = intel_vgpu_open, + .release = intel_vgpu_release, + + .read = intel_vgpu_read, + .write = intel_vgpu_write, + .mmap = intel_vgpu_mmap, + .ioctl = intel_vgpu_ioctl, +}; + static int kvmgt_host_init(struct device *dev, void *gvt, const void *ops) { if (!intel_gvt_init_vgpu_type_groups(gvt)) @@ -349,22 +1033,28 @@ static int kvmgt_host_init(struct device *dev, void *gvt, const void *ops) intel_gvt_ops = ops; - /* MDEV is not yet available */ - return -ENODEV; + return mdev_register_device(dev, &intel_vgpu_ops); } static void kvmgt_host_exit(struct device *dev, void *gvt) { intel_gvt_cleanup_vgpu_type_groups(gvt); + mdev_unregister_device(dev); } static int kvmgt_write_protect_add(unsigned long handle, u64 gfn) { - struct kvmgt_guest_info *info = (struct kvmgt_guest_info *)handle; - struct kvm *kvm = info->kvm; + struct kvmgt_guest_info *info; + struct kvm *kvm; struct kvm_memory_slot *slot; int idx; + if (!handle_valid(handle)) + return -ESRCH; + + info = (struct kvmgt_guest_info *)handle; + kvm = info->kvm; + idx = srcu_read_lock(&kvm->srcu); slot = gfn_to_memslot(kvm, gfn); @@ -384,11 +1074,17 @@ static int kvmgt_write_protect_add(unsigned long handle, u64 gfn) static int kvmgt_write_protect_remove(unsigned long handle, u64 gfn) { - struct kvmgt_guest_info *info = (struct kvmgt_guest_info *)handle; - struct kvm *kvm = info->kvm; + struct kvmgt_guest_info *info; + struct kvm *kvm; struct kvm_memory_slot *slot; int idx; + if (!handle_valid(handle)) + return 0; + + info = (struct kvmgt_guest_info *)handle; + kvm = info->kvm; + idx = srcu_read_lock(&kvm->srcu); slot = gfn_to_memslot(kvm, gfn); @@ -476,6 +1172,85 @@ static int kvmgt_detect_host(void) return kvmgt_check_guest() ? -ENODEV : 0; } +static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu, struct kvm *kvm) +{ + struct intel_vgpu *itr; + struct kvmgt_guest_info *info; + int id; + bool ret = false; + + mutex_lock(&vgpu->gvt->lock); + for_each_active_vgpu(vgpu->gvt, itr, id) { + if (!handle_valid(itr->handle)) + continue; + + info = (struct kvmgt_guest_info *)itr->handle; + if (kvm && kvm == info->kvm) { + ret = true; + goto out; + } + } +out: + mutex_unlock(&vgpu->gvt->lock); + return ret; +} + +static int kvmgt_guest_init(struct mdev_device *mdev) +{ + struct kvmgt_guest_info *info; + struct intel_vgpu *vgpu; + struct kvm *kvm; + + vgpu = mdev_get_drvdata(mdev); + if (handle_valid(vgpu->handle)) + return -EEXIST; + + kvm = vgpu->vdev.kvm; + if (!kvm || kvm->mm != current->mm) { + gvt_err("KVM is required to use Intel vGPU\n"); + return -ESRCH; + } + + if (__kvmgt_vgpu_exist(vgpu, kvm)) + return -EEXIST; + + info = vzalloc(sizeof(struct kvmgt_guest_info)); + if (!info) + return -ENOMEM; + + vgpu->handle = (unsigned long)info; + info->vgpu = vgpu; + info->kvm = kvm; + + kvmgt_protect_table_init(info); + gvt_cache_init(vgpu); + + info->track_node.track_write = kvmgt_page_track_write; + info->track_node.track_flush_slot = kvmgt_page_track_flush_slot; + kvm_page_track_register_notifier(kvm, &info->track_node); + + return 0; +} + +static bool kvmgt_guest_exit(struct kvmgt_guest_info *info) +{ + struct intel_vgpu *vgpu; + + if (!info) { + gvt_err("kvmgt_guest_info invalid\n"); + return false; + } + + vgpu = info->vgpu; + + kvm_page_track_unregister_notifier(info->kvm, &info->track_node); + kvmgt_protect_table_destroy(info); + gvt_cache_destroy(vgpu); + vfree(info); + + return true; +} + static int kvmgt_attach_vgpu(void *vgpu, unsigned long *handle) { /* nothing to do here */ @@ -489,30 +1264,40 @@ static void kvmgt_detach_vgpu(unsigned long handle) static int kvmgt_inject_msi(unsigned long handle, u32 addr, u16 data) { - struct kvmgt_guest_info *info = (struct kvmgt_guest_info *)handle; - struct intel_vgpu *vgpu = info->vgpu; + struct kvmgt_guest_info *info; + struct intel_vgpu *vgpu; - if (vgpu->vdev.msi_trigger) - return eventfd_signal(vgpu->vdev.msi_trigger, 1) == 1; + if (!handle_valid(handle)) + return -ESRCH; - return false; + info = (struct kvmgt_guest_info *)handle; + vgpu = info->vgpu; + + if (eventfd_signal(vgpu->vdev.msi_trigger, 1) == 1) + return 0; + + return -EFAULT; } static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn) { unsigned long pfn; - struct kvmgt_guest_info *info = (struct kvmgt_guest_info *)handle; + struct kvmgt_guest_info *info; int rc; + if (!handle_valid(handle)) + return INTEL_GVT_INVALID_ADDR; + + info = (struct kvmgt_guest_info *)handle; pfn = gvt_cache_find(info->vgpu, gfn); if (pfn != 0) return pfn; - rc = kvmgt_pin_pages(info->vgpu->vdev.mdev, &gfn, 1, - IOMMU_READ | IOMMU_WRITE, &pfn); + rc = vfio_pin_pages(&info->vgpu->vdev.mdev->dev, &gfn, 1, + IOMMU_READ | IOMMU_WRITE, &pfn); if (rc != 1) { gvt_err("vfio_pin_pages failed for gfn: 0x%lx\n", gfn); - return 0; + return INTEL_GVT_INVALID_ADDR; } gvt_cache_add(info->vgpu, gfn, pfn); @@ -525,7 +1310,7 @@ static void *kvmgt_gpa_to_hva(unsigned long handle, unsigned long gpa) gfn_t gfn = gpa_to_gfn(gpa); pfn = kvmgt_gfn_to_pfn(handle, gfn); - if (!pfn) + if (pfn == INTEL_GVT_INVALID_ADDR) return NULL; return (char *)pfn_to_kaddr(pfn) + offset_in_page(gpa); @@ -536,6 +1321,9 @@ static int kvmgt_rw_gpa(unsigned long handle, unsigned long gpa, { void *hva = NULL; + if (!handle_valid(handle)) + return -ESRCH; + hva = kvmgt_gpa_to_hva(handle, gpa); if (!hva) return -EFAULT; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 96407f684f7f5..b7f42c448a441 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1896,7 +1896,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) fbdev_fb->base.height, fbdev_fb->base.depth, fbdev_fb->base.bits_per_pixel, - fbdev_fb->base.modifier[0], + fbdev_fb->base.modifier, drm_framebuffer_read_refcount(&fbdev_fb->base)); describe_obj(m, fbdev_fb->obj); seq_putc(m, '\n'); @@ -1914,7 +1914,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) fb->base.height, fb->base.depth, fb->base.bits_per_pixel, - fb->base.modifier[0], + fb->base.modifier, drm_framebuffer_read_refcount(&fb->base)); describe_obj(m, fb->obj); seq_putc(m, '\n'); diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index ff821649486e0..dbe9fb41ae535 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -144,8 +144,8 @@ static int intel_plane_atomic_check(struct drm_plane *plane, if (state->fb && drm_rotation_90_or_270(state->rotation)) { struct drm_format_name_buf format_name; - if (!(state->fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || - state->fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED)) { + if (state->fb->modifier != I915_FORMAT_MOD_Y_TILED && + state->fb->modifier != I915_FORMAT_MOD_Yf_TILED) { DRM_DEBUG_KMS("Y/Yf tiling required for 90/270!\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 58fc78702e2b2..bd2c99e2e523e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2189,7 +2189,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation) WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - alignment = intel_surf_alignment(dev_priv, fb->modifier[0]); + alignment = intel_surf_alignment(dev_priv, fb->modifier); intel_fill_fb_ggtt_view(&view, fb, rotation); @@ -2350,13 +2350,13 @@ static u32 intel_adjust_tile_offset(int *x, int *y, WARN_ON(new_offset > old_offset); - if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) { + if (fb->modifier != DRM_FORMAT_MOD_NONE) { unsigned int tile_size, tile_width, tile_height; unsigned int pitch_tiles; tile_size = intel_tile_size(dev_priv); intel_tile_dims(dev_priv, &tile_width, &tile_height, - fb->modifier[plane], cpp); + fb->modifier, cpp); if (drm_rotation_90_or_270(rotation)) { pitch_tiles = pitch / tile_height; @@ -2399,7 +2399,7 @@ static u32 _intel_compute_tile_offset(const struct drm_i915_private *dev_priv, unsigned int rotation, u32 alignment) { - uint64_t fb_modifier = fb->modifier[plane]; + uint64_t fb_modifier = fb->modifier; unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); u32 offset, offset_aligned; @@ -2458,7 +2458,7 @@ u32 intel_compute_tile_offset(int *x, int *y, if (fb->pixel_format == DRM_FORMAT_NV12 && plane == 1) alignment = 4096; else - alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]); + alignment = intel_surf_alignment(dev_priv, fb->modifier); return _intel_compute_tile_offset(dev_priv, x, y, fb, plane, pitch, rotation, alignment); @@ -2540,13 +2540,13 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv, DRM_ROTATE_0, tile_size); offset /= tile_size; - if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) { + if (fb->modifier != DRM_FORMAT_MOD_NONE) { unsigned int tile_width, tile_height; unsigned int pitch_tiles; struct drm_rect r; intel_tile_dims(dev_priv, &tile_width, &tile_height, - fb->modifier[i], cpp); + fb->modifier, cpp); rot_info->plane[i].offset = offset; rot_info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width * cpp); @@ -2705,7 +2705,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, mode_cmd.width = fb->width; mode_cmd.height = fb->height; mode_cmd.pitches[0] = fb->pitches[0]; - mode_cmd.modifier[0] = fb->modifier[0]; + mode_cmd.modifier[0] = fb->modifier; mode_cmd.flags = DRM_MODE_FB_MODIFIERS; if (intel_framebuffer_init(dev, to_intel_framebuffer(fb), @@ -2835,7 +2835,7 @@ static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane, { int cpp = drm_format_plane_cpp(fb->pixel_format, plane); - switch (fb->modifier[plane]) { + switch (fb->modifier) { case DRM_FORMAT_MOD_NONE: case I915_FORMAT_MOD_X_TILED: switch (cpp) { @@ -2866,7 +2866,7 @@ static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane, } break; default: - MISSING_CASE(fb->modifier[plane]); + MISSING_CASE(fb->modifier); } return 2048; @@ -2894,7 +2894,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state) intel_add_fb_offsets(&x, &y, plane_state, 0); offset = intel_compute_tile_offset(&x, &y, plane_state, 0); - alignment = intel_surf_alignment(dev_priv, fb->modifier[0]); + alignment = intel_surf_alignment(dev_priv, fb->modifier); /* * AUX surface offset is specified as the distance from the @@ -2911,7 +2911,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state) * * TODO: linear and Y-tiled seem fine, Yf untested, */ - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) { + if (fb->modifier == I915_FORMAT_MOD_X_TILED) { int cpp = drm_format_plane_cpp(fb->pixel_format, 0); while ((x + w) * cpp > fb->pitches[0]) { @@ -3059,7 +3059,7 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, } if (INTEL_GEN(dev_priv) >= 4 && - fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + fb->modifier == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; if (rotation & DRM_ROTATE_180) @@ -3170,7 +3170,7 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, BUG(); } - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; if (rotation & DRM_ROTATE_180) @@ -3280,9 +3280,9 @@ u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane, if (drm_rotation_90_or_270(rotation)) { int cpp = drm_format_plane_cpp(fb->pixel_format, plane); - stride /= intel_tile_height(dev_priv, fb->modifier[0], cpp); + stride /= intel_tile_height(dev_priv, fb->modifier, cpp); } else { - stride /= intel_fb_stride_alignment(dev_priv, fb->modifier[0], + stride /= intel_fb_stride_alignment(dev_priv, fb->modifier, fb->pixel_format); } @@ -3398,7 +3398,7 @@ static void skylake_update_primary_plane(struct drm_plane *plane, PLANE_CTL_PIPE_CSC_ENABLE; plane_ctl |= skl_plane_ctl_format(fb->pixel_format); - plane_ctl |= skl_plane_ctl_tiling(fb->modifier[0]); + plane_ctl |= skl_plane_ctl_tiling(fb->modifier); plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE; plane_ctl |= skl_plane_ctl_rotation(rotation); @@ -8707,7 +8707,7 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, if (INTEL_GEN(dev_priv) >= 4) { if (val & DISPPLANE_TILED) { plane_config->tiling = I915_TILING_X; - fb->modifier[0] = I915_FORMAT_MOD_X_TILED; + fb->modifier = I915_FORMAT_MOD_X_TILED; } } @@ -8736,7 +8736,7 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, aligned_height = intel_fb_align_height(dev, fb->height, fb->pixel_format, - fb->modifier[0]); + fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; @@ -9748,17 +9748,17 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, tiling = val & PLANE_CTL_TILED_MASK; switch (tiling) { case PLANE_CTL_TILED_LINEAR: - fb->modifier[0] = DRM_FORMAT_MOD_NONE; + fb->modifier = DRM_FORMAT_MOD_NONE; break; case PLANE_CTL_TILED_X: plane_config->tiling = I915_TILING_X; - fb->modifier[0] = I915_FORMAT_MOD_X_TILED; + fb->modifier = I915_FORMAT_MOD_X_TILED; break; case PLANE_CTL_TILED_Y: - fb->modifier[0] = I915_FORMAT_MOD_Y_TILED; + fb->modifier = I915_FORMAT_MOD_Y_TILED; break; case PLANE_CTL_TILED_YF: - fb->modifier[0] = I915_FORMAT_MOD_Yf_TILED; + fb->modifier = I915_FORMAT_MOD_Yf_TILED; break; default: MISSING_CASE(tiling); @@ -9775,13 +9775,13 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, fb->width = ((val >> 0) & 0x1fff) + 1; val = I915_READ(PLANE_STRIDE(pipe, 0)); - stride_mult = intel_fb_stride_alignment(dev_priv, fb->modifier[0], + stride_mult = intel_fb_stride_alignment(dev_priv, fb->modifier, fb->pixel_format); fb->pitches[0] = (val & 0x3ff) * stride_mult; aligned_height = intel_fb_align_height(dev, fb->height, fb->pixel_format, - fb->modifier[0]); + fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; @@ -9849,7 +9849,7 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, if (INTEL_GEN(dev_priv) >= 4) { if (val & DISPPLANE_TILED) { plane_config->tiling = I915_TILING_X; - fb->modifier[0] = I915_FORMAT_MOD_X_TILED; + fb->modifier = I915_FORMAT_MOD_X_TILED; } } @@ -9878,7 +9878,7 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, aligned_height = intel_fb_align_height(dev, fb->height, fb->pixel_format, - fb->modifier[0]); + fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; @@ -11784,7 +11784,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev, MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); intel_ring_emit(ring, fb->pitches[0]); intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset | - intel_fb_modifier_to_tiling(fb->modifier[0])); + intel_fb_modifier_to_tiling(fb->modifier)); /* XXX Enabling the panel-fitter across page-flip is so far * untested on non-native modes, so ignore it for now. @@ -11817,7 +11817,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev, intel_ring_emit(ring, MI_DISPLAY_FLIP | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); intel_ring_emit(ring, fb->pitches[0] | - intel_fb_modifier_to_tiling(fb->modifier[0])); + intel_fb_modifier_to_tiling(fb->modifier)); intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); /* Contrary to the suggestions in the documentation, @@ -11923,7 +11923,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev, intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit); intel_ring_emit(ring, fb->pitches[0] | - intel_fb_modifier_to_tiling(fb->modifier[0])); + intel_fb_modifier_to_tiling(fb->modifier)); intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); intel_ring_emit(ring, (MI_NOOP)); @@ -11969,7 +11969,7 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, ctl = I915_READ(PLANE_CTL(pipe, 0)); ctl &= ~PLANE_CTL_TILED_MASK; - switch (fb->modifier[0]) { + switch (fb->modifier) { case DRM_FORMAT_MOD_NONE: break; case I915_FORMAT_MOD_X_TILED: @@ -11982,7 +11982,7 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, ctl |= PLANE_CTL_TILED_YF; break; default: - MISSING_CASE(fb->modifier[0]); + MISSING_CASE(fb->modifier); } /* @@ -12007,7 +12007,7 @@ static void ilk_do_mmio_flip(struct intel_crtc *intel_crtc, dspcntr = I915_READ(reg); - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; else dspcntr &= ~DISPPLANE_TILED; @@ -12223,7 +12223,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { engine = dev_priv->engine[BCS]; - if (fb->modifier[0] != old_fb->modifier[0]) + if (fb->modifier != old_fb->modifier) /* vlv: DISPLAY_FLIP fails to change tiling */ engine = NULL; } else if (IS_IVYBRIDGE(dev_priv) || IS_HASWELL(dev_priv)) { @@ -12379,7 +12379,7 @@ static bool intel_wm_need_update(struct drm_plane *plane, if (!cur->base.fb || !new->base.fb) return false; - if (cur->base.fb->modifier[0] != new->base.fb->modifier[0] || + if (cur->base.fb->modifier != new->base.fb->modifier || cur->base.rotation != new->base.rotation || drm_rect_width(&new->base.src) != drm_rect_width(&cur->base.src) || drm_rect_height(&new->base.src) != drm_rect_height(&cur->base.src) || @@ -15114,7 +15114,7 @@ intel_check_cursor_plane(struct drm_plane *plane, return -ENOMEM; } - if (fb->modifier[0] != DRM_FORMAT_MOD_NONE) { + if (fb->modifier != DRM_FORMAT_MOD_NONE) { DRM_DEBUG_KMS("cursor cannot be tiled\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fc958d5ed0dc8..beb08982dc0b5 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -633,7 +633,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, cur_size = intel_crtc->config->base.adjusted_mode.crtc_vdisplay; cur_size = intel_fb_align_height(dev, cur_size, fb->base.pixel_format, - fb->base.modifier[0]); + fb->base.modifier); cur_size *= fb->base.pitches[0]; DRM_DEBUG_KMS("pipe %c area: %dx%d, bpp: %d, size: %d\n", pipe_name(intel_crtc->pipe), diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e207dc69e8b39..bbb1eaf1e6db0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3067,7 +3067,7 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) latency = dev_priv->wm.skl_latency[level]; if (skl_needs_memory_bw_wa(intel_state) && - plane->base.state->fb->modifier[0] == + plane->base.state->fb->modifier == I915_FORMAT_MOD_X_TILED) latency += 15; @@ -3327,8 +3327,8 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate, return 0; /* For Non Y-tile return 8-blocks */ - if (fb->modifier[0] != I915_FORMAT_MOD_Y_TILED && - fb->modifier[0] != I915_FORMAT_MOD_Yf_TILED) + if (fb->modifier != I915_FORMAT_MOD_Y_TILED && + fb->modifier != I915_FORMAT_MOD_Yf_TILED) return 8; src_w = drm_rect_width(&intel_pstate->base.src) >> 16; @@ -3597,7 +3597,7 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, return 0; } - if (apply_memory_bw_wa && fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (apply_memory_bw_wa && fb->modifier == I915_FORMAT_MOD_X_TILED) latency += 15; width = drm_rect_width(&intel_pstate->base.src) >> 16; @@ -3636,12 +3636,12 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, y_min_scanlines *= 2; plane_bytes_per_line = width * cpp; - if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || - fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) { + if (fb->modifier == I915_FORMAT_MOD_Y_TILED || + fb->modifier == I915_FORMAT_MOD_Yf_TILED) { plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line * y_min_scanlines, 512); plane_blocks_per_line /= y_min_scanlines; - } else if (fb->modifier[0] == DRM_FORMAT_MOD_NONE) { + } else if (fb->modifier == DRM_FORMAT_MOD_NONE) { plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512) + 1; } else { @@ -3656,8 +3656,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, y_tile_minimum = plane_blocks_per_line * y_min_scanlines; - if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || - fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) { + if (fb->modifier == I915_FORMAT_MOD_Y_TILED || + fb->modifier == I915_FORMAT_MOD_Yf_TILED) { selected_result = max(method2, y_tile_minimum); } else { if ((cpp * cstate->base.adjusted_mode.crtc_htotal / 512 < 1) && @@ -3673,8 +3673,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, res_lines = DIV_ROUND_UP(selected_result, plane_blocks_per_line); if (level >= 1 && level <= 7) { - if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || - fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) { + if (fb->modifier == I915_FORMAT_MOD_Y_TILED || + fb->modifier == I915_FORMAT_MOD_Yf_TILED) { res_blocks += y_tile_minimum; res_lines += y_min_scanlines; } else { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c2b629c922e7f..8f131a08d440c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -224,7 +224,7 @@ skl_update_plane(struct drm_plane *drm_plane, PLANE_CTL_PIPE_CSC_ENABLE; plane_ctl |= skl_plane_ctl_format(fb->pixel_format); - plane_ctl |= skl_plane_ctl_tiling(fb->modifier[0]); + plane_ctl |= skl_plane_ctl_tiling(fb->modifier); plane_ctl |= skl_plane_ctl_rotation(rotation); @@ -406,7 +406,7 @@ vlv_update_plane(struct drm_plane *dplane, */ sprctl |= SP_GAMMA_ENABLE; - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) sprctl |= SP_TILED; if (rotation & DRM_ROTATE_180) @@ -448,7 +448,7 @@ vlv_update_plane(struct drm_plane *dplane, I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x); else I915_WRITE(SPLINOFF(pipe, plane), linear_offset); @@ -531,7 +531,7 @@ ivb_update_plane(struct drm_plane *plane, */ sprctl |= SPRITE_GAMMA_ENABLE; - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) sprctl |= SPRITE_TILED; if (rotation & DRM_ROTATE_180) @@ -584,7 +584,7 @@ ivb_update_plane(struct drm_plane *plane, * register */ if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) I915_WRITE(SPROFFSET(pipe), (y << 16) | x); - else if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + else if (fb->modifier == I915_FORMAT_MOD_X_TILED) I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); else I915_WRITE(SPRLINOFF(pipe), linear_offset); @@ -669,7 +669,7 @@ ilk_update_plane(struct drm_plane *plane, */ dvscntr |= DVS_GAMMA_ENABLE; - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) dvscntr |= DVS_TILED; if (rotation & DRM_ROTATE_180) @@ -712,7 +712,7 @@ ilk_update_plane(struct drm_plane *plane, I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); - if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) + if (fb->modifier == I915_FORMAT_MOD_X_TILED) I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); else I915_WRITE(DVSLINOFF(pipe), linear_offset); diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 4e1ae3fc462dc..6be515a9fb694 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -68,6 +68,12 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, ipu_dc_disable_channel(ipu_crtc->dc); ipu_di_disable(ipu_crtc->di); + /* + * Planes must be disabled before DC clock is removed, as otherwise the + * attached IDMACs will be left in undefined state, possibly hanging + * the IPU or even system. + */ + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); ipu_dc_disable(ipu); spin_lock_irq(&crtc->dev->event_lock); @@ -77,9 +83,6 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, } spin_unlock_irq(&crtc->dev->event_lock); - /* always disable planes on the CRTC */ - drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true); - drm_crtc_vblank_off(crtc); } diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index 019b7ca392d7a..f75c5b5a536c7 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -80,6 +80,7 @@ static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp, ddp_comp); priv->crtc = crtc; + writel(0x0, comp->regs + DISP_REG_OVL_INTSTA); writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN); } diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 0186e500d2a54..90fb831ef031b 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -432,11 +432,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, unsigned long pll_rate; unsigned int factor; + /* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */ pix_rate = 1000UL * mode->clock; - if (mode->clock <= 74000) + if (mode->clock <= 27000) + factor = 16 * 3; + else if (mode->clock <= 84000) factor = 8 * 3; - else + else if (mode->clock <= 167000) factor = 4 * 3; + else + factor = 2 * 3; pll_rate = pix_rate * factor; dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n", diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c index df33b3ca6ffd5..aa5f20fabd10f 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -123,7 +123,7 @@ static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w, unsigned int bpc) { writel(w << 16 | h, comp->regs + DISP_OD_SIZE); - writel(OD_RELAYMODE, comp->regs + OD_RELAYMODE); + writel(OD_RELAYMODE, comp->regs + OD_CFG); mtk_dither_set(comp, bpc, DISP_OD_CFG); } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 4b7fe7eaec014..bc176abd1069c 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -219,6 +219,7 @@ static int mtk_drm_kms_init(struct drm_device *drm) if (ret < 0) goto err_component_unbind; + drm->vblank_disable_allowed = true; drm_kms_helper_poll_init(drm); drm_mode_config_reset(drm); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 71227deef21b1..0e8c4d9af3406 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1133,12 +1133,6 @@ static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, phy_power_on(hdmi->phy); mtk_hdmi_aud_output_config(hdmi, mode); - mtk_hdmi_setup_audio_infoframe(hdmi); - mtk_hdmi_setup_avi_infoframe(hdmi, mode); - mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); - if (mode->flags & DRM_MODE_FLAG_3D_MASK) - mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); - mtk_hdmi_hw_vid_black(hdmi, false); mtk_hdmi_hw_aud_unmute(hdmi); mtk_hdmi_hw_send_av_unmute(hdmi); @@ -1401,6 +1395,16 @@ static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge) hdmi->powered = true; } +static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi, + struct drm_display_mode *mode) +{ + mtk_hdmi_setup_audio_infoframe(hdmi); + mtk_hdmi_setup_avi_infoframe(hdmi, mode); + mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI"); + if (mode->flags & DRM_MODE_FLAG_3D_MASK) + mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode); +} + static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) { struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); @@ -1409,6 +1413,7 @@ static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge) clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]); clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]); phy_power_on(hdmi->phy); + mtk_hdmi_send_infoframe(hdmi, &hdmi->mode); hdmi->enabled = true; } diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c index 8a24754b440f0..51cb9cfb6646e 100644 --- a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c +++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c @@ -265,6 +265,9 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); unsigned int pre_div; unsigned int div; + unsigned int pre_ibias; + unsigned int hdmi_ibias; + unsigned int imp_en; dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__, rate, parent_rate); @@ -298,18 +301,31 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, (0x1 << PLL_BR_SHIFT), RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC | RG_HDMITX_PLL_BR); - mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_PRD_IMP_EN); + if (rate < 165000000) { + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, + RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x3; + imp_en = 0x0; + hdmi_ibias = hdmi_phy->ibias; + } else { + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON3, + RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x6; + imp_en = 0xf; + hdmi_ibias = hdmi_phy->ibias_up; + } mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4, - (0x3 << PRD_IBIAS_CLK_SHIFT) | - (0x3 << PRD_IBIAS_D2_SHIFT) | - (0x3 << PRD_IBIAS_D1_SHIFT) | - (0x3 << PRD_IBIAS_D0_SHIFT), + (pre_ibias << PRD_IBIAS_CLK_SHIFT) | + (pre_ibias << PRD_IBIAS_D2_SHIFT) | + (pre_ibias << PRD_IBIAS_D1_SHIFT) | + (pre_ibias << PRD_IBIAS_D0_SHIFT), RG_HDMITX_PRD_IBIAS_CLK | RG_HDMITX_PRD_IBIAS_D2 | RG_HDMITX_PRD_IBIAS_D1 | RG_HDMITX_PRD_IBIAS_D0); mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, - (0x0 << DRV_IMP_EN_SHIFT), RG_HDMITX_DRV_IMP_EN); + (imp_en << DRV_IMP_EN_SHIFT), + RG_HDMITX_DRV_IMP_EN); mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) | (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) | @@ -318,12 +334,14 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 | RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0); mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5, - (hdmi_phy->ibias << DRV_IBIAS_CLK_SHIFT) | - (hdmi_phy->ibias << DRV_IBIAS_D2_SHIFT) | - (hdmi_phy->ibias << DRV_IBIAS_D1_SHIFT) | - (hdmi_phy->ibias << DRV_IBIAS_D0_SHIFT), - RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 | - RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0); + (hdmi_ibias << DRV_IBIAS_CLK_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D2_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D1_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D0_SHIFT), + RG_HDMITX_DRV_IBIAS_CLK | + RG_HDMITX_DRV_IBIAS_D2 | + RG_HDMITX_DRV_IBIAS_D1 | + RG_HDMITX_DRV_IBIAS_D0); return 0; } diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index f05ed0e1f3d65..6f240021705b0 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -139,6 +139,7 @@ struct msm_dsi_host { u32 err_work_state; struct work_struct err_work; + struct work_struct hpd_work; struct workqueue_struct *workqueue; /* DSI 6G TX buffer*/ @@ -1294,6 +1295,14 @@ static void dsi_sw_reset_restore(struct msm_dsi_host *msm_host) wmb(); /* make sure dsi controller enabled again */ } +static void dsi_hpd_worker(struct work_struct *work) +{ + struct msm_dsi_host *msm_host = + container_of(work, struct msm_dsi_host, hpd_work); + + drm_helper_hpd_irq_event(msm_host->dev); +} + static void dsi_err_worker(struct work_struct *work) { struct msm_dsi_host *msm_host = @@ -1480,7 +1489,7 @@ static int dsi_host_attach(struct mipi_dsi_host *host, DBG("id=%d", msm_host->id); if (msm_host->dev) - drm_helper_hpd_irq_event(msm_host->dev); + queue_work(msm_host->workqueue, &msm_host->hpd_work); return 0; } @@ -1494,7 +1503,7 @@ static int dsi_host_detach(struct mipi_dsi_host *host, DBG("id=%d", msm_host->id); if (msm_host->dev) - drm_helper_hpd_irq_event(msm_host->dev); + queue_work(msm_host->workqueue, &msm_host->hpd_work); return 0; } @@ -1748,6 +1757,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) /* setup workqueue */ msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); INIT_WORK(&msm_host->err_work, dsi_err_worker); + INIT_WORK(&msm_host->hpd_work, dsi_hpd_worker); msm_dsi->host = &msm_host->base; msm_dsi->id = msm_host->id; diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c index 598fdaff0a41a..26e3a01a99c2b 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c @@ -521,6 +521,7 @@ static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm) .parent_names = (const char *[]){ "xo" }, .num_parents = 1, .name = vco_name, + .flags = CLK_IGNORE_UNUSED, .ops = &clk_ops_dsi_pll_28nm_vco, }; struct device *dev = &pll_28nm->pdev->dev; diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c index 38c90e1eb0028..49008451085b8 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c @@ -412,6 +412,7 @@ static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm) struct clk_init_data vco_init = { .parent_names = (const char *[]){ "pxo" }, .num_parents = 1, + .flags = CLK_IGNORE_UNUSED, .ops = &clk_ops_dsi_pll_28nm_vco, }; struct device *dev = &pll_28nm->pdev->dev; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c index aa94a553794f5..143eab46ba687 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c @@ -702,6 +702,7 @@ static struct clk_init_data pll_init = { .ops = &hdmi_8996_pll_ops, .parent_names = hdmi_pll_parents, .num_parents = ARRAY_SIZE(hdmi_pll_parents), + .flags = CLK_IGNORE_UNUSED, }; int msm_hdmi_pll_8996_init(struct platform_device *pdev) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c index 92da69aa6187e..99590758c68b7 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c @@ -424,6 +424,7 @@ static struct clk_init_data pll_init = { .ops = &hdmi_pll_ops, .parent_names = hdmi_pll_parents, .num_parents = ARRAY_SIZE(hdmi_pll_parents), + .flags = CLK_IGNORE_UNUSED, }; int msm_hdmi_pll_8960_init(struct platform_device *pdev) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 3903dbcda763a..911e4690d36af 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -40,7 +40,7 @@ enum mdp4_frame_format mdp4_get_frame_format(struct drm_framebuffer *fb) { bool is_tile = false; - if (fb->modifier[1] == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE) + if (fb->modifier == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE) is_tile = true; if (fb->pixel_format == DRM_FORMAT_NV12 && is_tile) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index ac9e4cde13804..8b4e3004f4518 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -272,7 +272,7 @@ const struct mdp5_cfg_hw msm8x16_config = { .count = 2, .base = { 0x14000, 0x16000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | - MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, + MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 1, @@ -282,7 +282,7 @@ const struct mdp5_cfg_hw msm8x16_config = { .lm = { .count = 2, /* LM0 and LM3 */ .base = { 0x44000, 0x47000 }, - .nb_stages = 5, + .nb_stages = 8, .max_width = 2048, .max_height = 0xFFFF, }, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index fa2be7ce94687..c205c360e16dc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -223,12 +223,7 @@ static void blend_setup(struct drm_crtc *crtc) plane_cnt++; } - /* - * If there is no base layer, enable border color. - * Although it's not possbile in current blend logic, - * put it here as a reminder. - */ - if (!pstates[STAGE_BASE] && plane_cnt) { + if (!pstates[STAGE_BASE]) { ctl_blend_flags |= MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT; DBG("Border Color is enabled"); } @@ -365,6 +360,15 @@ static int pstate_cmp(const void *a, const void *b) return pa->state->zpos - pb->state->zpos; } +/* is there a helper for this? */ +static bool is_fullscreen(struct drm_crtc_state *cstate, + struct drm_plane_state *pstate) +{ + return (pstate->crtc_x <= 0) && (pstate->crtc_y <= 0) && + ((pstate->crtc_x + pstate->crtc_w) >= cstate->mode.hdisplay) && + ((pstate->crtc_y + pstate->crtc_h) >= cstate->mode.vdisplay); +} + static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -375,21 +379,11 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct plane_state pstates[STAGE_MAX + 1]; const struct mdp5_cfg_hw *hw_cfg; const struct drm_plane_state *pstate; - int cnt = 0, i; + int cnt = 0, base = 0, i; DBG("%s: check", mdp5_crtc->name); - /* verify that there are not too many planes attached to crtc - * and that we don't have conflicting mixer stages: - */ - hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { - if (cnt >= (hw_cfg->lm.nb_stages)) { - dev_err(dev->dev, "too many planes!\n"); - return -EINVAL; - } - - pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate); @@ -399,8 +393,24 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, /* assign a stage based on sorted zpos property */ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); + /* if the bottom-most layer is not fullscreen, we need to use + * it for solid-color: + */ + if ((cnt > 0) && !is_fullscreen(state, &pstates[0].state->base)) + base++; + + /* verify that there are not too many planes attached to crtc + * and that we don't have conflicting mixer stages: + */ + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); + + if ((cnt + base) >= hw_cfg->lm.nb_stages) { + dev_err(dev->dev, "too many planes!\n"); + return -EINVAL; + } + for (i = 0; i < cnt; i++) { - pstates[i].state->stage = STAGE_BASE + i; + pstates[i].state->stage = STAGE_BASE + i + base; DBG("%s: assign pipe %s on stage=%d", mdp5_crtc->name, pipe2name(mdp5_plane_pipe(pstates[i].plane)), pstates[i].state->stage); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 8bf55e3450c58..81c0562ab489c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -307,8 +307,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, format = to_mdp_format(msm_framebuffer_format(state->fb)); if (MDP_FORMAT_IS_YUV(format) && !pipe_supports_yuv(mdp5_plane->caps)) { - dev_err(plane->dev->dev, - "Pipe doesn't support YUV\n"); + DBG("Pipe doesn't support YUV\n"); return -EINVAL; } @@ -316,8 +315,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, if (!(mdp5_plane->caps & MDP_PIPE_CAP_SCALE) && (((state->src_w >> 16) != state->crtc_w) || ((state->src_h >> 16) != state->crtc_h))) { - dev_err(plane->dev->dev, - "Pipe doesn't support scaling (%dx%d -> %dx%d)\n", + DBG("Pipe doesn't support scaling (%dx%d -> %dx%d)\n", state->src_w >> 16, state->src_h >> 16, state->crtc_w, state->crtc_h); @@ -333,8 +331,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) || (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) { - dev_err(plane->dev->dev, - "Pipe doesn't support flip\n"); + DBG("Pipe doesn't support flip\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 8d21fb27a4013..440c00ff84096 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -234,7 +234,7 @@ static int msm_drm_uninit(struct device *dev) flush_workqueue(priv->atomic_wq); destroy_workqueue(priv->atomic_wq); - if (kms) + if (kms && kms->funcs) kms->funcs->destroy(kms); if (gpu) { diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index 283d2841ba581..192b2d3a79cb2 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -163,6 +163,9 @@ void msm_gem_shrinker_init(struct drm_device *dev) void msm_gem_shrinker_cleanup(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; - WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier)); - unregister_shrinker(&priv->shrinker); + + if (priv->shrinker.nr_deferred) { + WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier)); + unregister_shrinker(&priv->shrinker); + } } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index e18839d52e3e9..27affbde058c6 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -931,7 +931,7 @@ static void radeon_connector_unregister(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); - if (radeon_connector->ddc_bus->has_aux) { + if (radeon_connector->ddc_bus && radeon_connector->ddc_bus->has_aux) { drm_dp_aux_unregister(&radeon_connector->ddc_bus->aux); radeon_connector->ddc_bus->has_aux = false; } diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 0be8d5cd78265..60a8920fa0b9c 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -104,6 +104,14 @@ static const char radeon_family_name[][16] = { "LAST", }; +#if defined(CONFIG_VGA_SWITCHEROO) +bool radeon_has_atpx_dgpu_power_cntl(void); +bool radeon_is_atpx_hybrid(void); +#else +static inline bool radeon_has_atpx_dgpu_power_cntl(void) { return false; } +static inline bool radeon_is_atpx_hybrid(void) { return false; } +#endif + #define RADEON_PX_QUIRK_DISABLE_PX (1 << 0) #define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1) @@ -160,6 +168,11 @@ static void radeon_device_handle_px_quirks(struct radeon_device *rdev) if (rdev->px_quirk_flags & RADEON_PX_QUIRK_DISABLE_PX) rdev->flags &= ~RADEON_IS_PX; + + /* disable PX is the system doesn't support dGPU power control or hybrid gfx */ + if (!radeon_is_atpx_hybrid() && + !radeon_has_atpx_dgpu_power_cntl()) + rdev->flags &= ~RADEON_IS_PX; } /** diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 2891aa914cfab..4ce665349f6b6 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -141,9 +141,9 @@ static int sun4i_drv_bind(struct device *dev) /* Create our layers */ drv->layers = sun4i_layers_init(drm); - if (!drv->layers) { + if (IS_ERR(drv->layers)) { dev_err(drm->dev, "Couldn't create the planes\n"); - ret = -EINVAL; + ret = PTR_ERR(drv->layers); goto free_drm; } diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index c3ff10f559cc4..d198ad7e53234 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -152,15 +152,13 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Enabling RGB output\n"); - if (!IS_ERR(tcon->panel)) { + if (!IS_ERR(tcon->panel)) drm_panel_prepare(tcon->panel); - drm_panel_enable(tcon->panel); - } - - /* encoder->bridge can be NULL; drm_bridge_enable checks for it */ - drm_bridge_enable(encoder->bridge); sun4i_tcon_channel_enable(tcon, 0); + + if (!IS_ERR(tcon->panel)) + drm_panel_enable(tcon->panel); } static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) @@ -171,15 +169,13 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Disabling RGB output\n"); - sun4i_tcon_channel_disable(tcon, 0); + if (!IS_ERR(tcon->panel)) + drm_panel_disable(tcon->panel); - /* encoder->bridge can be NULL; drm_bridge_disable checks for it */ - drm_bridge_disable(encoder->bridge); + sun4i_tcon_channel_disable(tcon, 0); - if (!IS_ERR(tcon->panel)) { - drm_panel_disable(tcon->panel); + if (!IS_ERR(tcon->panel)) drm_panel_unprepare(tcon->panel); - } } static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 29f0207fa6770..873f010d9616f 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -98,17 +98,23 @@ static int udl_parse_vendor_descriptor(struct drm_device *dev, static int udl_select_std_channel(struct udl_device *udl) { int ret; - u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7, - 0x1C, 0x88, 0x5E, 0x15, - 0x60, 0xFE, 0xC6, 0x97, - 0x16, 0x3D, 0x47, 0xF2}; + static const u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7, + 0x1C, 0x88, 0x5E, 0x15, + 0x60, 0xFE, 0xC6, 0x97, + 0x16, 0x3D, 0x47, 0xF2}; + void *sendbuf; + + sendbuf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL); + if (!sendbuf) + return -ENOMEM; ret = usb_control_msg(udl->udev, usb_sndctrlpipe(udl->udev, 0), NR_USB_REQUEST_CHANNEL, (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, - set_def_chn, sizeof(set_def_chn), + sendbuf, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); + kfree(sendbuf); return ret < 0 ? ret : 0; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6cfb5cacc2533..575aa65436d18 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -179,6 +179,7 @@ #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 #define USB_DEVICE_ID_ATEN_CS682 0x2213 +#define USB_DEVICE_ID_ATEN_CS692 0x8021 #define USB_VENDOR_ID_ATMEL 0x03eb #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index 5614fee82347f..3a84aaf1418b4 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -292,11 +292,11 @@ static ssize_t show_value(struct device *dev, struct device_attribute *attr, bool input = false; int value = 0; - if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage, + if (sscanf(attr->attr.name, "feature-%x-%x-%s", &index, &usage, name) == 3) { feature = true; field_index = index + sensor_inst->input_field_count; - } else if (sscanf(attr->attr.name, "input-%d-%x-%s", &index, &usage, + } else if (sscanf(attr->attr.name, "input-%x-%x-%s", &index, &usage, name) == 3) { input = true; field_index = index; @@ -398,7 +398,7 @@ static ssize_t store_value(struct device *dev, struct device_attribute *attr, char name[HID_CUSTOM_NAME_LENGTH]; int value; - if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage, + if (sscanf(attr->attr.name, "feature-%x-%x-%s", &index, &usage, name) == 3) { field_index = index + sensor_inst->input_field_count; } else diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 658a607dc6d9e..c5c3d61117299 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -251,6 +251,9 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); int report_size; int ret = 0; + u8 *val_ptr; + int buffer_index = 0; + int i; mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); @@ -271,7 +274,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, goto done_proc; } ret = min(report_size, buffer_size); - memcpy(buffer, report->field[field_index]->value, ret); + + val_ptr = (u8 *)report->field[field_index]->value; + for (i = 0; i < report->field[field_index]->report_count; ++i) { + if (buffer_index >= ret) + break; + + memcpy(&((u8 *)buffer)[buffer_index], val_ptr, + report->field[field_index]->report_size / 8); + val_ptr += sizeof(__s32); + buffer_index += (report->field[field_index]->report_size / 8); + } done_proc: mutex_unlock(&data->mutex); diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index e2517c11e0ee0..0c9ac4d5d8500 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -637,6 +637,58 @@ irqreturn_t ish_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * ish_disable_dma() - disable dma communication between host and ISHFW + * @dev: ishtp device pointer + * + * Clear the dma enable bit and wait for dma inactive. + * + * Return: 0 for success else error code. + */ +static int ish_disable_dma(struct ishtp_device *dev) +{ + unsigned int dma_delay; + + /* Clear the dma enable bit */ + ish_reg_write(dev, IPC_REG_ISH_RMP2, 0); + + /* wait for dma inactive */ + for (dma_delay = 0; dma_delay < MAX_DMA_DELAY && + _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA); + dma_delay += 5) + mdelay(5); + + if (dma_delay >= MAX_DMA_DELAY) { + dev_err(dev->devc, + "Wait for DMA inactive timeout\n"); + return -EBUSY; + } + + return 0; +} + +/** + * ish_wakeup() - wakeup ishfw from waiting-for-host state + * @dev: ishtp device pointer + * + * Set the dma enable bit and send a void message to FW, + * it wil wakeup FW from waiting-for-host state. + */ +static void ish_wakeup(struct ishtp_device *dev) +{ + /* Set dma enable bit */ + ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); + + /* + * Send 0 IPC message so that ISH FW wakes up if it was already + * asleep. + */ + ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); + + /* Flush writes to doorbell and REMAP2 */ + ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); +} + /** * _ish_hw_reset() - HW reset * @dev: ishtp device pointer @@ -649,7 +701,6 @@ static int _ish_hw_reset(struct ishtp_device *dev) { struct pci_dev *pdev = dev->pdev; int rv; - unsigned int dma_delay; uint16_t csr; if (!pdev) @@ -664,15 +715,8 @@ static int _ish_hw_reset(struct ishtp_device *dev) return -EINVAL; } - /* Now trigger reset to FW */ - ish_reg_write(dev, IPC_REG_ISH_RMP2, 0); - - for (dma_delay = 0; dma_delay < MAX_DMA_DELAY && - _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA); - dma_delay += 5) - mdelay(5); - - if (dma_delay >= MAX_DMA_DELAY) { + /* Disable dma communication between FW and host */ + if (ish_disable_dma(dev)) { dev_err(&pdev->dev, "Can't reset - stuck with DMA in-progress\n"); return -EBUSY; @@ -690,16 +734,8 @@ static int _ish_hw_reset(struct ishtp_device *dev) csr |= PCI_D0; pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); - ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); - - /* - * Send 0 IPC message so that ISH FW wakes up if it was already - * asleep - */ - ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); - - /* Flush writes to doorbell and REMAP2 */ - ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); + /* Now we can enable ISH DMA operation and wakeup ISHFW */ + ish_wakeup(dev); return 0; } @@ -758,16 +794,9 @@ static int _ish_ipc_reset(struct ishtp_device *dev) int ish_hw_start(struct ishtp_device *dev) { ish_set_host_rdy(dev); - /* After that we can enable ISH DMA operation */ - ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); - /* - * Send 0 IPC message so that ISH FW wakes up if it was already - * asleep - */ - ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); - /* Flush write to doorbell */ - ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); + /* After that we can enable ISH DMA operation and wakeup ISHFW */ + ish_wakeup(dev); set_host_ready(dev); @@ -876,6 +905,21 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev) */ void ish_device_disable(struct ishtp_device *dev) { + struct pci_dev *pdev = dev->pdev; + + if (!pdev) + return; + + /* Disable dma communication between FW and host */ + if (ish_disable_dma(dev)) { + dev_err(&pdev->dev, + "Can't reset - stuck with DMA in-progress\n"); + return; + } + + /* Put ISH to D3hot state for power saving */ + pci_set_power_state(pdev, PCI_D3hot); + dev->dev_state = ISHTP_DEV_DISABLED; ish_clr_host_rdy(dev); } diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 42f0beeb09fd4..20d647d2dd2cb 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -146,7 +146,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; /* request and enable interrupt */ - ret = request_irq(pdev->irq, ish_irq_handler, IRQF_NO_SUSPEND, + ret = request_irq(pdev->irq, ish_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) { dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n", @@ -202,6 +202,7 @@ static void ish_remove(struct pci_dev *pdev) kfree(ishtp_dev); } +#ifdef CONFIG_PM static struct device *ish_resume_device; /** @@ -293,7 +294,6 @@ static int ish_resume(struct device *device) return 0; } -#ifdef CONFIG_PM static const struct dev_pm_ops ish_pm_ops = { .suspend = ish_suspend, .resume = ish_resume, @@ -301,7 +301,7 @@ static const struct dev_pm_ops ish_pm_ops = { #define ISHTP_ISH_PM_OPS (&ish_pm_ops) #else #define ISHTP_ISH_PM_OPS NULL -#endif +#endif /* CONFIG_PM */ static struct pci_driver ish_driver = { .name = KBUILD_MODNAME, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 354d49ea36dd9..e6cfd323babc6 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -63,6 +63,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET }, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a259e18d22d5b..0276d2ef06ee5 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -961,7 +961,7 @@ int vmbus_device_register(struct hv_device *child_device_obj) { int ret = 0; - dev_set_name(&child_device_obj->device, "vmbus-%pUl", + dev_set_name(&child_device_obj->device, "%pUl", child_device_obj->channel->offermsg.offer.if_instance.b); child_device_obj->device.bus = &hv_bus; diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index adae6848ffb23..a74c075a30ec4 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -536,8 +536,10 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups), GFP_KERNEL); - if (!hwdev->groups) - return ERR_PTR(-ENOMEM); + if (!hwdev->groups) { + err = -ENOMEM; + goto free_hwmon; + } attrs = __hwmon_create_attrs(dev, drvdata, chip); if (IS_ERR(attrs)) { diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index da3fb069ec5c0..ce69048c88e98 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -743,8 +743,8 @@ static int st_accel_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = adata->current_fullscale->gain; + *val = adata->current_fullscale->gain / 1000000; + *val2 = adata->current_fullscale->gain % 1000000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: *val = adata->odr; @@ -763,9 +763,13 @@ static int st_accel_write_raw(struct iio_dev *indio_dev, int err; switch (mask) { - case IIO_CHAN_INFO_SCALE: - err = st_sensors_set_fullscale_by_gain(indio_dev, val2); + case IIO_CHAN_INFO_SCALE: { + int gain; + + gain = val * 1000000 + val2; + err = st_sensors_set_fullscale_by_gain(indio_dev, gain); break; + } case IIO_CHAN_INFO_SAMP_FREQ: if (val2) return -EINVAL; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index dc33c1dd5191a..b5beea53d6f65 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -30,26 +30,26 @@ static struct { u32 usage_id; int unit; /* 0 for default others from HID sensor spec */ int scale_val0; /* scale, whole number */ - int scale_val1; /* scale, fraction in micros */ + int scale_val1; /* scale, fraction in nanos */ } unit_conversion[] = { - {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650}, + {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000}, {HID_USAGE_SENSOR_ACCEL_3D, HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, {HID_USAGE_SENSOR_ACCEL_3D, - HID_USAGE_SENSOR_UNITS_G, 9, 806650}, + HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, - {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293}, {HID_USAGE_SENSOR_GYRO_3D, HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, {HID_USAGE_SENSOR_GYRO_3D, - HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453}, + HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293}, - {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000}, + {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000}, {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0}, - {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293}, {HID_USAGE_SENSOR_INCLINOMETER_3D, - HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453}, + HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293}, {HID_USAGE_SENSOR_INCLINOMETER_3D, HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0}, @@ -57,7 +57,7 @@ static struct { {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0}, - {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000}, + {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000}, }; static int pow_10(unsigned power) @@ -266,15 +266,15 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); /* * This fuction applies the unit exponent to the scale. * For example: - * 9.806650 ->exp:2-> val0[980]val1[665000] - * 9.000806 ->exp:2-> val0[900]val1[80600] - * 0.174535 ->exp:2-> val0[17]val1[453500] - * 1.001745 ->exp:0-> val0[1]val1[1745] - * 1.001745 ->exp:2-> val0[100]val1[174500] - * 1.001745 ->exp:4-> val0[10017]val1[450000] - * 9.806650 ->exp:-2-> val0[0]val1[98066] + * 9.806650000 ->exp:2-> val0[980]val1[665000000] + * 9.000806000 ->exp:2-> val0[900]val1[80600000] + * 0.174535293 ->exp:2-> val0[17]val1[453529300] + * 1.001745329 ->exp:0-> val0[1]val1[1745329] + * 1.001745329 ->exp:2-> val0[100]val1[174532900] + * 1.001745329 ->exp:4-> val0[10017]val1[453290000] + * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_micro(int *val0, int *val1, int scale0, +static void adjust_exponent_nano(int *val0, int *val1, int scale0, int scale1, int exp) { int i; @@ -285,32 +285,32 @@ static void adjust_exponent_micro(int *val0, int *val1, int scale0, if (exp > 0) { *val0 = scale0 * pow_10(exp); res = 0; - if (exp > 6) { + if (exp > 9) { *val1 = 0; return; } for (i = 0; i < exp; ++i) { - x = scale1 / pow_10(5 - i); + x = scale1 / pow_10(8 - i); res += (pow_10(exp - 1 - i) * x); - scale1 = scale1 % pow_10(5 - i); + scale1 = scale1 % pow_10(8 - i); } *val0 += res; *val1 = scale1 * pow_10(exp); } else if (exp < 0) { exp = abs(exp); - if (exp > 6) { + if (exp > 9) { *val0 = *val1 = 0; return; } *val0 = scale0 / pow_10(exp); rem = scale0 % pow_10(exp); res = 0; - for (i = 0; i < (6 - exp); ++i) { - x = scale1 / pow_10(5 - i); - res += (pow_10(5 - exp - i) * x); - scale1 = scale1 % pow_10(5 - i); + for (i = 0; i < (9 - exp); ++i) { + x = scale1 / pow_10(8 - i); + res += (pow_10(8 - exp - i) * x); + scale1 = scale1 % pow_10(8 - i); } - *val1 = rem * pow_10(6 - exp) + res; + *val1 = rem * pow_10(9 - exp) + res; } else { *val0 = scale0; *val1 = scale1; @@ -332,14 +332,14 @@ int hid_sensor_format_scale(u32 usage_id, unit_conversion[i].unit == attr_info->units) { exp = hid_sensor_convert_exponent( attr_info->unit_expo); - adjust_exponent_micro(val0, val1, + adjust_exponent_nano(val0, val1, unit_conversion[i].scale_val0, unit_conversion[i].scale_val1, exp); break; } } - return IIO_VAL_INT_PLUS_MICRO; + return IIO_VAL_INT_PLUS_NANO; } EXPORT_SYMBOL(hid_sensor_format_scale); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 285a64a589d71..975a1f19f7476 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -612,7 +612,7 @@ EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail); ssize_t st_sensors_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { - int i, len = 0; + int i, len = 0, q, r; struct iio_dev *indio_dev = dev_get_drvdata(dev); struct st_sensor_data *sdata = iio_priv(indio_dev); @@ -621,8 +621,10 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev, if (sdata->sensor_settings->fs.fs_avl[i].num == 0) break; - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", - sdata->sensor_settings->fs.fs_avl[i].gain); + q = sdata->sensor_settings->fs.fs_avl[i].gain / 1000000; + r = sdata->sensor_settings->fs.fs_avl[i].gain % 1000000; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", q, r); } mutex_unlock(&indio_dev->mlock); buf[len - 1] = '\n'; diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index b98b9d94d184c..a97e802ca5231 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -335,6 +335,7 @@ static struct platform_driver hid_dev_rot_platform_driver = { .id_table = hid_dev_rot_ids, .driver = { .name = KBUILD_MODNAME, + .pm = &hid_sensor_pm_ops, }, .probe = hid_dev_rot_probe, .remove = hid_dev_rot_remove, diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 066161a4bccd9..f962f31a5eb22 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -136,6 +136,8 @@ static int maxim_thermocouple_read(struct maxim_thermocouple_data *data, ret = spi_read(data->spi, (void *)&buf32, storage_bytes); *val = be32_to_cpu(buf32); break; + default: + ret = -EINVAL; } if (ret) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 36bf50ebb187e..89a6b05468045 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1094,47 +1094,47 @@ static void cma_save_ib_info(struct sockaddr *src_addr, } } -static void cma_save_ip4_info(struct sockaddr *src_addr, - struct sockaddr *dst_addr, +static void cma_save_ip4_info(struct sockaddr_in *src_addr, + struct sockaddr_in *dst_addr, struct cma_hdr *hdr, __be16 local_port) { - struct sockaddr_in *ip4; - if (src_addr) { - ip4 = (struct sockaddr_in *)src_addr; - ip4->sin_family = AF_INET; - ip4->sin_addr.s_addr = hdr->dst_addr.ip4.addr; - ip4->sin_port = local_port; + *src_addr = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr.s_addr = hdr->dst_addr.ip4.addr, + .sin_port = local_port, + }; } if (dst_addr) { - ip4 = (struct sockaddr_in *)dst_addr; - ip4->sin_family = AF_INET; - ip4->sin_addr.s_addr = hdr->src_addr.ip4.addr; - ip4->sin_port = hdr->port; + *dst_addr = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr.s_addr = hdr->src_addr.ip4.addr, + .sin_port = hdr->port, + }; } } -static void cma_save_ip6_info(struct sockaddr *src_addr, - struct sockaddr *dst_addr, +static void cma_save_ip6_info(struct sockaddr_in6 *src_addr, + struct sockaddr_in6 *dst_addr, struct cma_hdr *hdr, __be16 local_port) { - struct sockaddr_in6 *ip6; - if (src_addr) { - ip6 = (struct sockaddr_in6 *)src_addr; - ip6->sin6_family = AF_INET6; - ip6->sin6_addr = hdr->dst_addr.ip6; - ip6->sin6_port = local_port; + *src_addr = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_addr = hdr->dst_addr.ip6, + .sin6_port = local_port, + }; } if (dst_addr) { - ip6 = (struct sockaddr_in6 *)dst_addr; - ip6->sin6_family = AF_INET6; - ip6->sin6_addr = hdr->src_addr.ip6; - ip6->sin6_port = hdr->port; + *dst_addr = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_addr = hdr->src_addr.ip6, + .sin6_port = hdr->port, + }; } } @@ -1159,10 +1159,12 @@ static int cma_save_ip_info(struct sockaddr *src_addr, switch (cma_get_ip_ver(hdr)) { case 4: - cma_save_ip4_info(src_addr, dst_addr, hdr, port); + cma_save_ip4_info((struct sockaddr_in *)src_addr, + (struct sockaddr_in *)dst_addr, hdr, port); break; case 6: - cma_save_ip6_info(src_addr, dst_addr, hdr, port); + cma_save_ip6_info((struct sockaddr_in6 *)src_addr, + (struct sockaddr_in6 *)dst_addr, hdr, port); break; default: return -EAFNOSUPPORT; diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 15c01c3cd540b..e6f9b2d745ca0 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -2636,17 +2636,26 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) /* And we're up. Go go go! */ of_iommu_set_ops(dev->of_node, &arm_smmu_ops); #ifdef CONFIG_PCI - pci_request_acs(); - ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops); - if (ret) - return ret; + if (pci_bus_type.iommu_ops != &arm_smmu_ops) { + pci_request_acs(); + ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops); + if (ret) + return ret; + } #endif #ifdef CONFIG_ARM_AMBA - ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops); - if (ret) - return ret; + if (amba_bustype.iommu_ops != &arm_smmu_ops) { + ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops); + if (ret) + return ret; + } #endif - return bus_set_iommu(&platform_bus_type, &arm_smmu_ops); + if (platform_bus_type.iommu_ops != &arm_smmu_ops) { + ret = bus_set_iommu(&platform_bus_type, &arm_smmu_ops); + if (ret) + return ret; + } + return 0; } static int arm_smmu_device_remove(struct platform_device *pdev) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index c841eb7a1a741..8f7281444551d 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -324,8 +324,10 @@ struct arm_smmu_master_cfg { #define INVALID_SMENDX -1 #define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv) #define fwspec_smmu(fw) (__fwspec_cfg(fw)->smmu) +#define fwspec_smendx(fw, i) \ + (i >= fw->num_ids ? INVALID_SMENDX : __fwspec_cfg(fw)->smendx[i]) #define for_each_cfg_sme(fw, i, idx) \ - for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i) + for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i) struct arm_smmu_device { struct device *dev; @@ -1228,6 +1230,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) return -ENXIO; } + /* + * FIXME: The arch/arm DMA API code tries to attach devices to its own + * domains between of_xlate() and add_device() - we have no way to cope + * with that, so until ARM gets converted to rely on groups and default + * domains, just say no (but more politely than by dereferencing NULL). + * This should be at least a WARN_ON once that's sorted. + */ + if (!fwspec->iommu_priv) + return -ENODEV; + smmu = fwspec_smmu(fwspec); /* Ensure that the domain is finalised */ ret = arm_smmu_init_domain_context(domain, smmu); @@ -1390,7 +1402,7 @@ static int arm_smmu_add_device(struct device *dev) fwspec = dev->iommu_fwspec; if (ret) goto out_free; - } else if (fwspec) { + } else if (fwspec && fwspec->ops == &arm_smmu_ops) { smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode)); } else { return -ENODEV; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a4407eabf0e64..3965e73db51ce 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1711,6 +1711,7 @@ static void disable_dmar_iommu(struct intel_iommu *iommu) if (!iommu->domains || !iommu->domain_ids) return; +again: spin_lock_irqsave(&device_domain_lock, flags); list_for_each_entry_safe(info, tmp, &device_domain_list, global) { struct dmar_domain *domain; @@ -1723,10 +1724,19 @@ static void disable_dmar_iommu(struct intel_iommu *iommu) domain = info->domain; - dmar_remove_one_dev_info(domain, info->dev); + __dmar_remove_one_dev_info(info); - if (!domain_type_is_vm_or_si(domain)) + if (!domain_type_is_vm_or_si(domain)) { + /* + * The domain_exit() function can't be called under + * device_domain_lock, as it takes this lock itself. + * So release the lock here and re-run the loop + * afterwards. + */ + spin_unlock_irqrestore(&device_domain_lock, flags); domain_exit(domain); + goto again; + } } spin_unlock_irqrestore(&device_domain_lock, flags); diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 012225587c258..b71b747ee0baa 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -513,6 +513,11 @@ config DVB_AS102_FE depends on DVB_CORE default DVB_AS102 +config DVB_GP8PSK_FE + tristate + depends on DVB_CORE + default DVB_USB_GP8PSK + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index e90165ad361bb..93921a4eaa275 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o diff --git a/drivers/media/usb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c similarity index 72% rename from drivers/media/usb/dvb-usb/gp8psk-fe.c rename to drivers/media/dvb-frontends/gp8psk-fe.c index db6eb79cde073..be19afeed7a96 100644 --- a/drivers/media/usb/dvb-usb/gp8psk-fe.c +++ b/drivers/media/dvb-frontends/gp8psk-fe.c @@ -14,11 +14,27 @@ * * see Documentation/dvb/README.dvb-usb for more information */ -#include "gp8psk.h" + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gp8psk-fe.h" +#include "dvb_frontend.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) struct gp8psk_fe_state { struct dvb_frontend fe; - struct dvb_usb_device *d; + void *priv; + const struct gp8psk_fe_ops *ops; + bool is_rev1; u8 lock; u16 snr; unsigned long next_status_check; @@ -29,22 +45,24 @@ static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) { struct gp8psk_fe_state *st = fe->demodulator_priv; u8 status; - gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); + + st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1); return status & bmDCtuned; } static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) { - struct gp8psk_fe_state *state = fe->demodulator_priv; - return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0); } static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) { u8 buf[6]; if (time_after(jiffies,st->next_status_check)) { - gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); - gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); + st->ops->in(st->priv, GET_SIGNAL_LOCK, 0, 0, &st->lock, 1); + st->ops->in(st->priv, GET_SIGNAL_STRENGTH, 0, 0, buf, 6); st->snr = (buf[1]) << 8 | buf[0]; st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; } @@ -116,13 +134,12 @@ static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_front static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) { - struct gp8psk_fe_state *state = fe->demodulator_priv; + struct gp8psk_fe_state *st = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; u8 cmd[10]; u32 freq = c->frequency * 1000; - int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); - deb_fe("%s()\n", __func__); + dprintk("%s()\n", __func__); cmd[4] = freq & 0xff; cmd[5] = (freq >> 8) & 0xff; @@ -136,21 +153,21 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_DVBS: if (c->modulation != QPSK) { - deb_fe("%s: unsupported modulation selected (%d)\n", + dprintk("%s: unsupported modulation selected (%d)\n", __func__, c->modulation); return -EOPNOTSUPP; } c->fec_inner = FEC_AUTO; break; case SYS_DVBS2: /* kept for backwards compatibility */ - deb_fe("%s: DVB-S2 delivery system selected\n", __func__); + dprintk("%s: DVB-S2 delivery system selected\n", __func__); break; case SYS_TURBO: - deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); + dprintk("%s: Turbo-FEC delivery system selected\n", __func__); break; default: - deb_fe("%s: unsupported delivery system selected (%d)\n", + dprintk("%s: unsupported delivery system selected (%d)\n", __func__, c->delivery_system); return -EOPNOTSUPP; } @@ -161,9 +178,9 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) cmd[3] = (c->symbol_rate >> 24) & 0xff; switch (c->modulation) { case QPSK: - if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (st->is_rev1) if (gp8psk_tuned_to_DCII(fe)) - gp8psk_bcm4500_reload(state->d); + st->ops->reload(st->priv); switch (c->fec_inner) { case FEC_1_2: cmd[9] = 0; break; @@ -207,18 +224,18 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) cmd[9] = 0; break; default: /* Unknown modulation */ - deb_fe("%s: unsupported modulation selected (%d)\n", + dprintk("%s: unsupported modulation selected (%d)\n", __func__, c->modulation); return -EOPNOTSUPP; } - if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (st->is_rev1) gp8psk_set_tuner_mode(fe, 0); - gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); + st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10); - state->lock = 0; - state->next_status_check = jiffies; - state->status_check_interval = 200; + st->lock = 0; + st->next_status_check = jiffies; + st->status_check_interval = 200; return 0; } @@ -228,9 +245,9 @@ static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, { struct gp8psk_fe_state *st = fe->demodulator_priv; - deb_fe("%s\n",__func__); + dprintk("%s\n", __func__); - if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0, m->msg, m->msg_len)) { return -EINVAL; } @@ -243,12 +260,12 @@ static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, struct gp8psk_fe_state *st = fe->demodulator_priv; u8 cmd; - deb_fe("%s\n",__func__); + dprintk("%s\n", __func__); /* These commands are certainly wrong */ cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; - if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0, &cmd, 0)) { return -EINVAL; } @@ -258,10 +275,10 @@ static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, static int gp8psk_fe_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { - struct gp8psk_fe_state* state = fe->demodulator_priv; + struct gp8psk_fe_state *st = fe->demodulator_priv; - if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, - (tone == SEC_TONE_ON), 0, NULL, 0)) { + if (st->ops->out(st->priv, SET_22KHZ_TONE, + (tone == SEC_TONE_ON), 0, NULL, 0)) { return -EINVAL; } return 0; @@ -270,9 +287,9 @@ static int gp8psk_fe_set_tone(struct dvb_frontend *fe, static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { - struct gp8psk_fe_state* state = fe->demodulator_priv; + struct gp8psk_fe_state *st = fe->demodulator_priv; - if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { return -EINVAL; } @@ -281,52 +298,60 @@ static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) { - struct gp8psk_fe_state* state = fe->demodulator_priv; - return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0); } static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) { - struct gp8psk_fe_state* state = fe->demodulator_priv; + struct gp8psk_fe_state *st = fe->demodulator_priv; u8 cmd = sw_cmd & 0x7f; - if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, - NULL, 0)) { + if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0)) return -EINVAL; - } - if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), - 0, NULL, 0)) { + + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), + 0, NULL, 0)) return -EINVAL; - } return 0; } static void gp8psk_fe_release(struct dvb_frontend* fe) { - struct gp8psk_fe_state *state = fe->demodulator_priv; - kfree(state); + struct gp8psk_fe_state *st = fe->demodulator_priv; + + kfree(st); } static struct dvb_frontend_ops gp8psk_fe_ops; -struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1) { - struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); - if (s == NULL) - goto error; - - s->d = d; - memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); - s->fe.demodulator_priv = s; - - goto success; -error: - return NULL; -success: - return &s->fe; -} + struct gp8psk_fe_state *st; + if (!ops || !ops->in || !ops->out || !ops->reload) { + pr_err("Error! gp8psk-fe ops not defined.\n"); + return NULL; + } + + st = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); + if (!st) + return NULL; + + memcpy(&st->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); + st->fe.demodulator_priv = st; + st->ops = ops; + st->priv = priv; + st->is_rev1 = is_rev1; + + pr_info("Frontend %sattached\n", is_rev1 ? "revision 1 " : ""); + + return &st->fe; +} +EXPORT_SYMBOL_GPL(gp8psk_fe_attach); static struct dvb_frontend_ops gp8psk_fe_ops = { .delsys = { SYS_DVBS }, diff --git a/drivers/media/dvb-frontends/gp8psk-fe.h b/drivers/media/dvb-frontends/gp8psk-fe.h new file mode 100644 index 0000000000000..6c7944b1ecd60 --- /dev/null +++ b/drivers/media/dvb-frontends/gp8psk-fe.h @@ -0,0 +1,82 @@ +/* + * gp8psk_fe driver + * + * This program is free software; 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef GP8PSK_FE_H +#define GP8PSK_FE_H + +#include + +/* gp8psk commands */ + +#define GET_8PSK_CONFIG 0x80 /* in */ +#define SET_8PSK_CONFIG 0x81 +#define I2C_WRITE 0x83 +#define I2C_READ 0x84 +#define ARM_TRANSFER 0x85 +#define TUNE_8PSK 0x86 +#define GET_SIGNAL_STRENGTH 0x87 /* in */ +#define LOAD_BCM4500 0x88 +#define BOOT_8PSK 0x89 /* in */ +#define START_INTERSIL 0x8A /* in */ +#define SET_LNB_VOLTAGE 0x8B +#define SET_22KHZ_TONE 0x8C +#define SEND_DISEQC_COMMAND 0x8D +#define SET_DVB_MODE 0x8E +#define SET_DN_SWITCH 0x8F +#define GET_SIGNAL_LOCK 0x90 /* in */ +#define GET_FW_VERS 0x92 +#define GET_SERIAL_NUMBER 0x93 /* in */ +#define USE_EXTRA_VOLT 0x94 +#define GET_FPGA_VERS 0x95 +#define CW3K_INIT 0x9d + +/* PSK_configuration bits */ +#define bm8pskStarted 0x01 +#define bm8pskFW_Loaded 0x02 +#define bmIntersilOn 0x04 +#define bmDVBmode 0x08 +#define bm22kHz 0x10 +#define bmSEL18V 0x20 +#define bmDCtuned 0x40 +#define bmArmed 0x80 + +/* Satellite modulation modes */ +#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ +#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ +#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ +#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ + +#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ +#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ +#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ +#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ +#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ +#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ + +/* firmware revision id's */ +#define GP8PSK_FW_REV1 0x020604 +#define GP8PSK_FW_REV2 0x020704 +#define GP8PSK_FW_VERS(_fw_vers) \ + ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) + +struct gp8psk_fe_ops { + int (*in)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*out)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*reload)(void *priv); +}; + +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1); + +#endif diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index f95a6bc839d58..cede3975d04bd 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -118,7 +118,7 @@ static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol, *protocol = RC_TYPE_RC6_MCE; dev &= 0x7f; dprintk(1, "ir hauppauge (rc6-mce): t%d vendor=%d dev=%d code=%d\n", - toggle, vendor, dev, code); + *ptoggle, vendor, dev, code); } else { *ptoggle = 0; *protocol = RC_TYPE_RC6_6A_32; diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 2a7b5a963acfd..3b3f32b426d19 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o dvb-usb-vp702x-objs := vp702x.o vp702x-fe.o obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o -dvb-usb-gp8psk-objs := gp8psk.o gp8psk-fe.o +dvb-usb-gp8psk-objs := gp8psk.o obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o dvb-usb-dtt200u-objs := dtt200u.o dtt200u-fe.o diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index b257780fb380a..7853261906b1a 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -53,7 +53,6 @@ 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, @@ -72,7 +71,7 @@ static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, return -EINVAL; } - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = 14; /* rest of buffer length low */ st->data[1] = 0; /* rest of buffer length high */ @@ -140,7 +139,7 @@ static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, values[i] = st->data[8 + i]; ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -481,7 +480,7 @@ int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, } packet_len = wlen + 5; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = (u8) (packet_len & 0xff); st->data[1] = (u8) ((packet_len & 0xff00) >> 8); @@ -512,7 +511,7 @@ int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, rbuf[i] = st->data[i + 7]; } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -523,7 +522,7 @@ int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, u8 seq; int ret, i; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); memset(st->data, 0, sizeof(st->data)); @@ -559,7 +558,7 @@ int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, for (i = 0; i < len; i++) values[i] = st->data[6 + i]; } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -847,7 +846,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) return 0; } - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); /* deb_info("rc_query\n"); */ st->data[0] = 3; /* rest of packet length low */ @@ -890,7 +889,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) } ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -1004,20 +1003,8 @@ static struct dvb_usb_device_properties af9005_properties; static int af9005_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - 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; + return dvb_usb_device_init(intf, &af9005_properties, + THIS_MODULE, NULL, adapter_nr); } enum af9005_usb_table_entry { diff --git a/drivers/media/usb/dvb-usb/cinergyT2-core.c b/drivers/media/usb/dvb-usb/cinergyT2-core.c index 8ac825413d5ac..290275bc7fdee 100644 --- a/drivers/media/usb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/usb/dvb-usb/cinergyT2-core.c @@ -42,7 +42,6 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct cinergyt2_state { u8 rc_counter; unsigned char data[64]; - struct mutex data_mutex; }; /* We are missing a release hook with usb_device data */ @@ -56,12 +55,12 @@ static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable) struct cinergyt2_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->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); + mutex_unlock(&d->data_mutex); return ret; } @@ -71,12 +70,12 @@ static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable) struct cinergyt2_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->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); + mutex_unlock(&d->data_mutex); return ret; } @@ -89,7 +88,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_GET_FIRMWARE_VERSION; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 3, 0); @@ -97,7 +96,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep " "state info\n"); } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); /* Copy this pointer as we are gonna need it in the release phase */ cinergyt2_usb_device = adap->dev; @@ -166,7 +165,7 @@ static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_GET_RC_EVENTS; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0); @@ -202,29 +201,17 @@ static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int cinergyt2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - 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; + return dvb_usb_device_init(intf, &cinergyt2_properties, + THIS_MODULE, NULL, adapter_nr); } - static struct usb_device_id cinergyt2_usb_table[] = { { USB_DEVICE(USB_VID_TERRATEC, 0x0038) }, { 0 } diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 39772812269dc..243403081fa53 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -68,7 +68,7 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d, wo = (rbuf == NULL || rlen == 0); /* write-only */ - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = cmd; memcpy(&st->data[1], wbuf, wlen); if (wo) @@ -77,7 +77,7 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d, ret = dvb_usb_generic_rw(d, st->data, 1 + wlen, rbuf, rlen, 0); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -1461,43 +1461,36 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties; 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, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_needsfirmware_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_rev2_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, - THIS_MODULE, &d, adapter_nr) || - 0) { - st = d->priv; - mutex_init(&st->data_mutex); - + THIS_MODULE, NULL, adapter_nr) || + 0) return 0; - } return -EINVAL; } diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 9f3ee0e47d5c6..18acda19527a6 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -37,7 +37,6 @@ struct cxusb_state { struct i2c_client *i2c_client_tuner; unsigned char data[MAX_XFER_SIZE]; - struct mutex data_mutex; }; #endif diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 92d5408684ac1..47ce9d5de4c67 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -704,7 +704,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) struct dvb_usb_device *d = purb->context; struct dib0700_rc_response *poll_reply; enum rc_type protocol; - u32 uninitialized_var(keycode); + u32 keycode; u8 toggle; deb_info("%s()\n", __func__); @@ -745,7 +745,8 @@ static void dib0700_rc_urb_completion(struct urb *purb) poll_reply->nec.data == 0x00 && poll_reply->nec.not_data == 0xff) { poll_reply->data_state = 2; - break; + rc_repeat(d->rc_dev); + goto resubmit; } if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) { diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c index f88572c7ae7cb..fcbff7fb0c4e1 100644 --- a/drivers/media/usb/dvb-usb/dtt200u.c +++ b/drivers/media/usb/dvb-usb/dtt200u.c @@ -22,7 +22,6 @@ 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) @@ -30,23 +29,24 @@ static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) struct dtt200u_state *st = d->priv; int ret = 0; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_INIT; if (onoff) ret = dvb_usb_generic_write(d, st->data, 2); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { - struct dtt200u_state *st = adap->dev->priv; + struct dvb_usb_device *d = adap->dev; + struct dtt200u_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_STREAMING; st->data[1] = onoff; @@ -61,26 +61,27 @@ static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) ret = dvb_usb_generic_write(adap->dev, st->data, 1); ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) { - struct dtt200u_state *st = adap->dev->priv; + struct dvb_usb_device *d = adap->dev; + struct dtt200u_state *st = d->priv; int ret; pid = onoff ? pid : 0; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_PID_FILTER; st->data[1] = index; st->data[2] = pid & 0xff; st->data[3] = (pid >> 8) & 0x1f; ret = dvb_usb_generic_write(adap->dev, st->data, 4); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -91,7 +92,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d) u32 scancode; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = GET_RC_CODE; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0); @@ -126,7 +127,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d) deb_info("st->data: %*ph\n", 5, st->data); ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -145,24 +146,17 @@ static struct dvb_usb_device_properties wt220u_miglia_properties; 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, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_fc_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties, - THIS_MODULE, &d, adapter_nr)) { - st = d->priv; - mutex_init(&st->data_mutex); - + THIS_MODULE, NULL, adapter_nr)) return 0; - } return -ENODEV; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 3896ba9a41796..84308569e7dc1 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -142,6 +142,7 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) { int ret = 0; + mutex_init(&d->data_mutex); mutex_init(&d->usb_mutex); mutex_init(&d->i2c_mutex); diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h index 639c4678c65b9..107255b08b2b1 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb.h +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -404,8 +404,12 @@ struct dvb_usb_adapter { * Powered is in/decremented for each call to modify the state. * @udev: pointer to the device's struct usb_device. * - * @usb_mutex: semaphore of USB control messages (reading needs two messages) - * @i2c_mutex: semaphore for i2c-transfers + * @data_mutex: mutex to protect the data structure used to store URB data + * @usb_mutex: mutex of USB control messages (reading needs two messages). + * Please notice that this mutex is used internally at the generic + * URB control functions. So, drivers using dvb_usb_generic_rw() and + * derivated functions should not lock it internally. + * @i2c_mutex: mutex for i2c-transfers * * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB * @@ -433,6 +437,7 @@ struct dvb_usb_device { int powered; /* locking */ + struct mutex data_mutex; struct mutex usb_mutex; /* i2c */ diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index adfd764914517..993bb7a72985f 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -15,6 +15,7 @@ * see Documentation/dvb/README.dvb-usb for more information */ #include "gp8psk.h" +#include "gp8psk-fe.h" /* debug */ static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw"; @@ -28,34 +29,8 @@ 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)); -} - -static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) -{ - return (gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1)); -} - -static void gp8psk_info(struct dvb_usb_device *d) -{ - u8 fpga_vers, fw_vers[6]; - - if (!gp8psk_get_fw_version(d, fw_vers)) - info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", - fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), - 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); - else - info("failed to get FW version"); - - if (!gp8psk_get_fpga_version(d, &fpga_vers)) - info("FPGA Version = %i", fpga_vers); - else - info("failed to get FPGA version"); -} - -int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +static 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; @@ -67,7 +42,6 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 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, @@ -81,8 +55,10 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 if (ret < 0 || ret != blen) { warn("usb in %d operation failed.", req); ret = -EIO; - } else + } else { ret = 0; + memcpy(b, st->data, blen); + } deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index); debug_dump(b,blen,deb_xfer); @@ -92,7 +68,7 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 return ret; } -int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, +static 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; @@ -123,6 +99,34 @@ int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, return ret; } + +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); +} + +static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) +{ + return gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1); +} + +static void gp8psk_info(struct dvb_usb_device *d) +{ + u8 fpga_vers, fw_vers[6]; + + if (!gp8psk_get_fw_version(d, fw_vers)) + info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", + fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), + 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); + else + info("failed to get FW version"); + + if (!gp8psk_get_fpga_version(d, &fpga_vers)) + info("FPGA Version = %i", fpga_vers); + else + info("failed to get FPGA version"); +} + static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) { int ret; @@ -225,10 +229,13 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) return 0; } -int gp8psk_bcm4500_reload(struct dvb_usb_device *d) +static int gp8psk_bcm4500_reload(struct dvb_usb_device *d) { u8 buf; int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); + + deb_xfer("reloading firmware\n"); + /* Turn off 8psk power */ if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) return -EINVAL; @@ -247,9 +254,47 @@ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return gp8psk_usb_out_op(adap->dev, ARM_TRANSFER, onoff, 0 , NULL, 0); } +/* Callbacks for gp8psk-fe.c */ + +static int gp8psk_fe_in(void *priv, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_usb_in_op(d, req, value, index, b, blen); +} + +static int gp8psk_fe_out(void *priv, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_usb_out_op(d, req, value, index, b, blen); +} + +static int gp8psk_fe_reload(void *priv) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_bcm4500_reload(d); +} + +const struct gp8psk_fe_ops gp8psk_fe_ops = { + .in = gp8psk_fe_in, + .out = gp8psk_fe_out, + .reload = gp8psk_fe_reload, +}; + static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev); + struct dvb_usb_device *d = adap->dev; + int id = le16_to_cpu(d->udev->descriptor.idProduct); + int is_rev1; + + is_rev1 = (id == USB_PID_GENPIX_8PSK_REV_1_WARM) ? true : false; + + adap->fe_adap[0].fe = dvb_attach(gp8psk_fe_attach, + &gp8psk_fe_ops, d, is_rev1); return 0; } diff --git a/drivers/media/usb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h index ed32b9da48436..d8975b866deec 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.h +++ b/drivers/media/usb/dvb-usb/gp8psk.h @@ -24,58 +24,6 @@ extern int dvb_usb_gp8psk_debug; #define deb_info(args...) dprintk(dvb_usb_gp8psk_debug,0x01,args) #define deb_xfer(args...) dprintk(dvb_usb_gp8psk_debug,0x02,args) #define deb_rc(args...) dprintk(dvb_usb_gp8psk_debug,0x04,args) -#define deb_fe(args...) dprintk(dvb_usb_gp8psk_debug,0x08,args) - -/* Twinhan Vendor requests */ -#define TH_COMMAND_IN 0xC0 -#define TH_COMMAND_OUT 0xC1 - -/* gp8psk commands */ - -#define GET_8PSK_CONFIG 0x80 /* in */ -#define SET_8PSK_CONFIG 0x81 -#define I2C_WRITE 0x83 -#define I2C_READ 0x84 -#define ARM_TRANSFER 0x85 -#define TUNE_8PSK 0x86 -#define GET_SIGNAL_STRENGTH 0x87 /* in */ -#define LOAD_BCM4500 0x88 -#define BOOT_8PSK 0x89 /* in */ -#define START_INTERSIL 0x8A /* in */ -#define SET_LNB_VOLTAGE 0x8B -#define SET_22KHZ_TONE 0x8C -#define SEND_DISEQC_COMMAND 0x8D -#define SET_DVB_MODE 0x8E -#define SET_DN_SWITCH 0x8F -#define GET_SIGNAL_LOCK 0x90 /* in */ -#define GET_FW_VERS 0x92 -#define GET_SERIAL_NUMBER 0x93 /* in */ -#define USE_EXTRA_VOLT 0x94 -#define GET_FPGA_VERS 0x95 -#define CW3K_INIT 0x9d - -/* PSK_configuration bits */ -#define bm8pskStarted 0x01 -#define bm8pskFW_Loaded 0x02 -#define bmIntersilOn 0x04 -#define bmDVBmode 0x08 -#define bm22kHz 0x10 -#define bmSEL18V 0x20 -#define bmDCtuned 0x40 -#define bmArmed 0x80 - -/* Satellite modulation modes */ -#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ -#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ -#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ -#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ - -#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ -#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ -#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ -#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ -#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ -#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ #define GET_USB_SPEED 0x07 @@ -86,15 +34,4 @@ extern int dvb_usb_gp8psk_debug; #define PRODUCT_STRING_READ 0x0D #define FW_BCD_VERSION_READ 0x14 -/* firmware revision id's */ -#define GP8PSK_FW_REV1 0x020604 -#define GP8PSK_FW_REV2 0x020704 -#define GP8PSK_FW_VERS(_fw_vers) ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) - -extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); -extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); -extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, - u16 index, u8 *b, int blen); -extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); - #endif diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index e9e6ea3ab73cf..75b9d4ac8b1e3 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -178,7 +178,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, ret = 0; bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + if (bytes_recv < if_version_length) { dev_err(bus->dev, "Could not read IF version\n"); ret = -EIO; goto err; diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 5a8dc5a76e0df..3678220964fe6 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -2347,7 +2347,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test, struct mmc_test_req *rq = mmc_test_req_alloc(); struct mmc_host *host = test->card->host; struct mmc_test_area *t = &test->area; - struct mmc_async_req areq; + struct mmc_test_async_req test_areq = { .test = test }; struct mmc_request *mrq; unsigned long timeout; bool expired = false; @@ -2363,8 +2363,8 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test, mrq->sbc = &rq->sbc; mrq->cap_cmd_during_tfr = true; - areq.mrq = mrq; - areq.err_check = mmc_test_check_result_async; + test_areq.areq.mrq = mrq; + test_areq.areq.err_check = mmc_test_check_result_async; mmc_test_prepare_mrq(test, mrq, t->sg, t->sg_len, dev_addr, t->blocks, 512, write); @@ -2378,7 +2378,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test, /* Start ongoing data request */ if (use_areq) { - mmc_start_req(host, &areq, &ret); + mmc_start_req(host, &test_areq.areq, &ret); if (ret) goto out_free; } else { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 39fc5b2b96c59..df19777068a62 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -26,6 +26,8 @@ #include "mmc_ops.h" #include "sd_ops.h" +#define DEFAULT_CMD6_TIMEOUT_MS 500 + static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 @@ -571,6 +573,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->erased_byte = 0x0; /* eMMC v4.5 or later */ + card->ext_csd.generic_cmd6_time = DEFAULT_CMD6_TIMEOUT_MS; if (card->ext_csd.rev >= 6) { card->ext_csd.feature_support |= MMC_DISCARD_FEATURE; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4fcbc4012ed03..50a674be66558 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2940,7 +2940,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(-ENOMEM); /* find reset controller when exist */ - pdata->rstc = devm_reset_control_get_optional(dev, NULL); + pdata->rstc = devm_reset_control_get_optional(dev, "reset"); if (IS_ERR(pdata->rstc)) { if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER) return ERR_PTR(-EPROBE_DEFER); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index d839147e591d2..44ecebd1ea8c1 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mmc); + spin_lock_init(&host->lock); + ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, dev_name(&pdev->dev), host); if (ret) goto out_free_dma; - spin_lock_init(&host->lock); - ret = mmc_add_host(mmc); if (ret) goto out_free_dma; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 71654b90227f6..42ef3ebb1d8cf 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2086,6 +2086,10 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (!host->tuning_done) { pr_info(DRIVER_NAME ": Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock\n"); + + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl &= ~SDHCI_CTRL_TUNED_CLK; ctrl &= ~SDHCI_CTRL_EXEC_TUNING; @@ -2286,10 +2290,8 @@ static bool sdhci_request_done(struct sdhci_host *host) for (i = 0; i < SDHCI_MAX_MRQS; i++) { mrq = host->mrqs_done[i]; - if (mrq) { - host->mrqs_done[i] = NULL; + if (mrq) break; - } } if (!mrq) { @@ -2320,6 +2322,17 @@ static bool sdhci_request_done(struct sdhci_host *host) * upon error conditions. */ if (sdhci_needs_reset(host, mrq)) { + /* + * Do not finish until command and data lines are available for + * reset. Note there can only be one other mrq, so it cannot + * also be in mrqs_done, otherwise host->cmd and host->data_cmd + * would both be null. + */ + if (host->cmd || host->data_cmd) { + spin_unlock_irqrestore(&host->lock, flags); + return true; + } + /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ @@ -2327,10 +2340,8 @@ static bool sdhci_request_done(struct sdhci_host *host) /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ - if (!host->cmd) - sdhci_do_reset(host, SDHCI_RESET_CMD); - if (!host->data_cmd) - sdhci_do_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); host->pending_reset = false; } @@ -2338,6 +2349,8 @@ static bool sdhci_request_done(struct sdhci_host *host) if (!sdhci_has_requests(host)) sdhci_led_deactivate(host); + host->mrqs_done[i] = NULL; + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -2512,9 +2525,6 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (!host->data) { struct mmc_command *data_cmd = host->data_cmd; - if (data_cmd) - host->data_cmd = NULL; - /* * The "data complete" interrupt is also used to * indicate that a busy state has ended. See comment @@ -2522,11 +2532,13 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) */ if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) { if (intmask & SDHCI_INT_DATA_TIMEOUT) { + host->data_cmd = NULL; data_cmd->error = -ETIMEDOUT; sdhci_finish_mrq(host, data_cmd->mrq); return; } if (intmask & SDHCI_INT_DATA_END) { + host->data_cmd = NULL; /* * Some cards handle busy-end interrupt * before the command completed, so make @@ -2912,6 +2924,10 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) spin_unlock_irqrestore(&host->lock, flags); } + if ((mmc->caps2 & MMC_CAP2_HS400_ES) && + mmc->ops->hs400_enhanced_strobe) + mmc->ops->hs400_enhanced_strobe(mmc, &mmc->ios); + spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = false; diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 83deda4bb4d6d..6f9563a964885 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -133,7 +133,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) return -ENOMEM; bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + if (bytes_recv < 0 || bytes_recv < if_version_length) { pr_err("Could not read IF version\n"); r = -EIO; goto err; diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index f5e3011e31fcd..5daf2f4be0cd7 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c @@ -612,7 +612,7 @@ int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node, ret = nvm_register(dev); - ns->lba_shift = ilog2(dev->sec_size) - 9; + ns->lba_shift = ilog2(dev->sec_size); if (sysfs_create_group(&dev->dev.kobj, attrs)) pr_warn("%s: failed to create sysfs group for identification\n", diff --git a/drivers/of/base.c b/drivers/of/base.c index d687e6de24a07..a0bccb54a9bd1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2077,8 +2077,6 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) name = of_get_property(of_aliases, "stdout", NULL); if (name) of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); - if (of_stdout) - console_set_by_of(); } if (!of_aliases) diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c index e0b22dab9b7ac..e04f69beb42d0 100644 --- a/drivers/pci/host/pcie-rockchip.c +++ b/drivers/pci/host/pcie-rockchip.c @@ -190,6 +190,9 @@ struct rockchip_pcie { struct reset_control *mgmt_rst; struct reset_control *mgmt_sticky_rst; struct reset_control *pipe_rst; + struct reset_control *pm_rst; + struct reset_control *aclk_rst; + struct reset_control *pclk_rst; struct clk *aclk_pcie; struct clk *aclk_perf_pcie; struct clk *hclk_pcie; @@ -408,6 +411,44 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) gpiod_set_value(rockchip->ep_gpio, 0); + err = reset_control_assert(rockchip->aclk_rst); + if (err) { + dev_err(dev, "assert aclk_rst err %d\n", err); + return err; + } + + err = reset_control_assert(rockchip->pclk_rst); + if (err) { + dev_err(dev, "assert pclk_rst err %d\n", err); + return err; + } + + err = reset_control_assert(rockchip->pm_rst); + if (err) { + dev_err(dev, "assert pm_rst err %d\n", err); + return err; + } + + udelay(10); + + err = reset_control_deassert(rockchip->pm_rst); + if (err) { + dev_err(dev, "deassert pm_rst err %d\n", err); + return err; + } + + err = reset_control_deassert(rockchip->aclk_rst); + if (err) { + dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err); + return err; + } + + err = reset_control_deassert(rockchip->pclk_rst); + if (err) { + dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err); + return err; + } + err = phy_init(rockchip->phy); if (err < 0) { dev_err(dev, "fail to init phy, err %d\n", err); @@ -781,6 +822,27 @@ static int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) return PTR_ERR(rockchip->pipe_rst); } + rockchip->pm_rst = devm_reset_control_get(dev, "pm"); + if (IS_ERR(rockchip->pm_rst)) { + if (PTR_ERR(rockchip->pm_rst) != -EPROBE_DEFER) + dev_err(dev, "missing pm reset property in node\n"); + return PTR_ERR(rockchip->pm_rst); + } + + rockchip->pclk_rst = devm_reset_control_get(dev, "pclk"); + if (IS_ERR(rockchip->pclk_rst)) { + if (PTR_ERR(rockchip->pclk_rst) != -EPROBE_DEFER) + dev_err(dev, "missing pclk reset property in node\n"); + return PTR_ERR(rockchip->pclk_rst); + } + + rockchip->aclk_rst = devm_reset_control_get(dev, "aclk"); + if (IS_ERR(rockchip->aclk_rst)) { + if (PTR_ERR(rockchip->aclk_rst) != -EPROBE_DEFER) + dev_err(dev, "missing aclk reset property in node\n"); + return PTR_ERR(rockchip->aclk_rst); + } + rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH); if (IS_ERR(rockchip->ep_gpio)) { dev_err(dev, "missing ep-gpios property in node\n"); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 66c4d8f422337..9526e341988ba 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -121,6 +121,14 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return -EINVAL; } + /* + * If we have a shadow copy in RAM, the PCI device doesn't respond + * to the shadow range, so we don't need to claim it, and upstream + * bridges don't need to route the range to the device. + */ + if (res->flags & IORESOURCE_ROM_SHADOW) + return 0; + root = pci_find_parent_resource(dev, res); if (!root) { dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 153f3122283de..b6b316de055c7 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -107,7 +107,7 @@ int soc_pcmcia_regulator_set(struct soc_pcmcia_socket *skt, ret = regulator_enable(r->reg); } else { - regulator_disable(r->reg); + ret = regulator_disable(r->reg); } if (ret == 0) r->on = on; diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c index 32ae78c8ca176..c85fb0b59729d 100644 --- a/drivers/phy/phy-da8xx-usb.c +++ b/drivers/phy/phy-da8xx-usb.c @@ -198,7 +198,8 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) } else { int ret; - ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0"); + ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", + "ohci-da8xx"); if (ret) dev_warn(dev, "Failed to create usb11 phy lookup\n"); ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", @@ -216,7 +217,7 @@ static int da8xx_usb_phy_remove(struct platform_device *pdev) if (!pdev->dev.of_node) { phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); - phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0"); + phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); } return 0; diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c index a2b4c6b58aea6..6904633cad687 100644 --- a/drivers/phy/phy-rockchip-pcie.c +++ b/drivers/phy/phy-rockchip-pcie.c @@ -249,21 +249,10 @@ static int rockchip_pcie_phy_init(struct phy *phy) static int rockchip_pcie_phy_exit(struct phy *phy) { struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); - int err = 0; clk_disable_unprepare(rk_phy->clk_pciephy_ref); - err = reset_control_deassert(rk_phy->phy_rst); - if (err) { - dev_err(&phy->dev, "deassert phy_rst err %d\n", err); - goto err_reset; - } - - return err; - -err_reset: - clk_prepare_enable(rk_phy->clk_pciephy_ref); - return err; + return 0; } static const struct phy_ops ops = { diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index b9342a2af7b36..fec34f5213c46 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -264,7 +264,7 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } - if (data->cfg->enable_pmu_unk1) { + if (phy->pmu && data->cfg->enable_pmu_unk1) { val = readl(phy->pmu + REG_PMU_UNK1); writel(val & ~2, phy->pmu + REG_PMU_UNK1); } diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c index c8c72e8259d38..87b46390b6959 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c @@ -26,7 +26,7 @@ #define ASPEED_G5_NR_PINS 228 -#define COND1 SIG_DESC_BIT(SCU90, 6, 0) +#define COND1 { SCU90, BIT(6), 0, 0 } #define COND2 { SCU94, GENMASK(1, 0), 0, 0 } #define B14 0 diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 7f77007163985..5d1e505c3c63d 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -844,6 +844,6 @@ static struct platform_driver iproc_gpio_driver = { static int __init iproc_gpio_init(void) { - return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe); + return platform_driver_register(&iproc_gpio_driver); } arch_initcall_sync(iproc_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c index 35783db1c10ba..c8deb8be1da78 100644 --- a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c @@ -741,6 +741,6 @@ static struct platform_driver nsp_gpio_driver = { static int __init nsp_gpio_init(void) { - return platform_driver_probe(&nsp_gpio_driver, nsp_gpio_probe); + return platform_driver_register(&nsp_gpio_driver); } arch_initcall_sync(nsp_gpio_init); diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 47613201269af..79c4e14a5a75e 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -687,6 +687,7 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev, if (!info->functions) return -ENOMEM; + info->group_index = 0; if (flat_funcs) { info->ngroups = of_get_child_count(np); } else { diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 30389f4ccab49..c43b1e9a06aff 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1652,12 +1652,15 @@ static int chv_pinctrl_probe(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int chv_pinctrl_suspend(struct device *dev) +static int chv_pinctrl_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&chv_lock, flags); + pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK); for (i = 0; i < pctrl->community->npins; i++) { @@ -1678,15 +1681,20 @@ static int chv_pinctrl_suspend(struct device *dev) ctx->padctrl1 = readl(reg); } + raw_spin_unlock_irqrestore(&chv_lock, flags); + return 0; } -static int chv_pinctrl_resume(struct device *dev) +static int chv_pinctrl_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&chv_lock, flags); + /* * Mask all interrupts before restoring per-pin configuration * registers because we don't know in which state BIOS left them @@ -1731,12 +1739,15 @@ static int chv_pinctrl_resume(struct device *dev) chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK); + raw_spin_unlock_irqrestore(&chv_lock, flags); + return 0; } #endif static const struct dev_pm_ops chv_pinctrl_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend_noirq, + chv_pinctrl_resume_noirq) }; static const struct acpi_device_id chv_pinctrl_acpi_match[] = { diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 99da4cf91031b..b7bb371679692 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1512,7 +1512,7 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, if (info->irqmux_base || gpio_irq > 0) { err = gpiochip_irqchip_add(&bank->gpio_chip, &st_gpio_irqchip, 0, handle_simple_irq, - IRQ_TYPE_LEVEL_LOW); + IRQ_TYPE_NONE); if (err) { gpiochip_remove(&bank->gpio_chip); dev_info(dev, "could not add irqchip\n"); diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 200667f08c373..efc43711ff5cb 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -1092,9 +1092,11 @@ int stm32_pctl_probe(struct platform_device *pdev) return -EINVAL; } - ret = stm32_pctrl_dt_setup_irq(pdev, pctl); - if (ret) - return ret; + if (of_find_property(np, "interrupt-parent", NULL)) { + ret = stm32_pctrl_dt_setup_irq(pdev, pctl); + if (ret) + return ret; + } for_each_child_of_node(np, child) if (of_property_read_bool(child, "gpio-controller")) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index a2323941e6770..a7614fc542b52 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -933,6 +933,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"), }, }, + { + .ident = "Lenovo Yoga 900", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "VIUU4"), + }, + }, { .ident = "Lenovo YOGA 910-13IKB", .matches = { diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index ed5874217ee76..12dbb50633761 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -264,7 +264,7 @@ check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; if (acpi_match_device_ids(dev, ids) == 0) - if (acpi_create_platform_device(dev)) + if (acpi_create_platform_device(dev, NULL)) dev_info(&dev->dev, "intel-hid: created platform device\n"); diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index 146d02f8c9bc0..78080763df517 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -164,7 +164,7 @@ check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; if (acpi_match_device_ids(dev, ids) == 0) - if (acpi_create_platform_device(dev)) + if (acpi_create_platform_device(dev, NULL)) dev_info(&dev->dev, "intel-vbtn: created platform device\n"); diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c index feac4576b8371..2df07ee8f3c33 100644 --- a/drivers/platform/x86/toshiba-wmi.c +++ b/drivers/platform/x86/toshiba-wmi.c @@ -24,14 +24,15 @@ #include #include #include +#include MODULE_AUTHOR("Azael Avalos"); MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver"); MODULE_LICENSE("GPL"); -#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" +#define WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" -MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID); +MODULE_ALIAS("wmi:"WMI_EVENT_GUID); static struct input_dev *toshiba_wmi_input_dev; @@ -63,6 +64,16 @@ static void toshiba_wmi_notify(u32 value, void *context) kfree(response.pointer); } +static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = { + { + .ident = "Toshiba laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + }, + }, + {} +}; + static int __init toshiba_wmi_input_setup(void) { acpi_status status; @@ -81,7 +92,7 @@ static int __init toshiba_wmi_input_setup(void) if (err) goto err_free_dev; - status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID, + status = wmi_install_notify_handler(WMI_EVENT_GUID, toshiba_wmi_notify, NULL); if (ACPI_FAILURE(status)) { err = -EIO; @@ -95,7 +106,7 @@ static int __init toshiba_wmi_input_setup(void) return 0; err_remove_notifier: - wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + wmi_remove_notify_handler(WMI_EVENT_GUID); err_free_keymap: sparse_keymap_free(toshiba_wmi_input_dev); err_free_dev: @@ -105,7 +116,7 @@ static int __init toshiba_wmi_input_setup(void) static void toshiba_wmi_input_destroy(void) { - wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + wmi_remove_notify_handler(WMI_EVENT_GUID); sparse_keymap_free(toshiba_wmi_input_dev); input_unregister_device(toshiba_wmi_input_dev); } @@ -114,7 +125,8 @@ static int __init toshiba_wmi_init(void) { int ret; - if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + if (!wmi_has_guid(WMI_EVENT_GUID) || + !dmi_check_system(toshiba_wmi_dmi_table)) return -ENODEV; ret = toshiba_wmi_input_setup(); @@ -130,7 +142,7 @@ static int __init toshiba_wmi_init(void) static void __exit toshiba_wmi_exit(void) { - if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + if (wmi_has_guid(WMI_EVENT_GUID)) toshiba_wmi_input_destroy(); } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index dd3d59806ffa0..19cd49ad92dc3 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -996,8 +996,9 @@ static u32 rtc_handler(void *context) struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned char rtc_control = 0; unsigned char rtc_intr; + unsigned long flags; - spin_lock_irq(&rtc_lock); + spin_lock_irqsave(&rtc_lock, flags); if (cmos_rtc.suspend_ctrl) rtc_control = CMOS_READ(RTC_CONTROL); if (rtc_control & RTC_AIE) { @@ -1006,7 +1007,7 @@ static u32 rtc_handler(void *context) rtc_intr = CMOS_READ(RTC_INTR_FLAGS); rtc_update_irq(cmos->rtc, 1, rtc_intr); } - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); pm_wakeup_event(dev, 0); acpi_clear_event(ACPI_EVENT_RTC); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d1421139e6eac..2ffe029ff2b6f 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2081,9 +2081,10 @@ void cxgbi_cleanup_task(struct iscsi_task *task) /* never reached the xmit task callout */ if (tdata->skb) __kfree_skb(tdata->skb); - memset(tdata, 0, sizeof(*tdata)); task_release_itt(task, task->hdr_itt); + memset(tdata, 0, sizeof(*tdata)); + iscsi_tcp_cleanup_task(task); } EXPORT_SYMBOL_GPL(cxgbi_cleanup_task); diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 241829e596680..7bb20684e9fab 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -793,6 +793,7 @@ static void alua_rtpg_work(struct work_struct *work) WARN_ON(pg->flags & ALUA_PG_RUN_RTPG); WARN_ON(pg->flags & ALUA_PG_RUN_STPG); spin_unlock_irqrestore(&pg->lock, flags); + kref_put(&pg->kref, release_port_group); return; } if (pg->flags & ALUA_SYNC_STPG) @@ -890,6 +891,7 @@ static void alua_rtpg_queue(struct alua_port_group *pg, /* Do not queue if the worker is already running */ if (!(pg->flags & ALUA_PG_RUNNING)) { kref_get(&pg->kref); + sdev = NULL; start_queue = 1; } } @@ -901,7 +903,8 @@ static void alua_rtpg_queue(struct alua_port_group *pg, if (start_queue && !queue_delayed_work(alua_wq, &pg->rtpg_work, msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS))) { - scsi_device_put(sdev); + if (sdev) + scsi_device_put(sdev); kref_put(&pg->kref, release_port_group); } } diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index ca86c885dfaab..3aaea713bf371 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -2233,7 +2233,7 @@ struct megasas_instance_template { }; #define MEGASAS_IS_LOGICAL(scp) \ - (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 + ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1) #define MEGASAS_DEV_INDEX(scp) \ (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 209a969a979d8..8aa769a2d919d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1273,9 +1273,9 @@ scsih_target_alloc(struct scsi_target *starget) sas_target_priv_data->handle = raid_device->handle; sas_target_priv_data->sas_address = raid_device->wwid; sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; - sas_target_priv_data->raid_device = raid_device; if (ioc->is_warpdrive) - raid_device->starget = starget; + sas_target_priv_data->raid_device = raid_device; + raid_device->starget = starget; } spin_unlock_irqrestore(&ioc->raid_device_lock, flags); return 0; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index ace65db1d2a25..567fa080e261c 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -707,6 +707,11 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) srb_t *sp; int rval; + if (unlikely(test_bit(UNLOADING, &base_vha->dpc_flags))) { + cmd->result = DID_NO_CONNECT << 16; + goto qc24_fail_command; + } + if (ha->flags.eeh_busy) { if (ha->flags.pci_channel_io_perm_failure) { ql_dbg(ql_dbg_aer, vha, 0x9010, @@ -1451,6 +1456,15 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { + /* Get a reference to the sp and drop the lock. + * The reference ensures this sp->done() call + * - and not the call in qla2xxx_eh_abort() - + * ends the SCSI command (with result 'res'). + */ + sp_get(sp); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + qla2xxx_eh_abort(GET_CMD_SP(sp)); + spin_lock_irqsave(&ha->hardware_lock, flags); req->outstanding_cmds[cnt] = NULL; sp->done(vha, sp, res); } @@ -2341,6 +2355,8 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) { scsi_qla_host_t *vha = shost_priv(shost); + if (test_bit(UNLOADING, &vha->dpc_flags)) + return 1; if (!vha->host) return 1; if (time > vha->hw->loop_reset_delay * HZ) diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c index 4a0d3cdc607cd..15ca09cd16f34 100644 --- a/drivers/scsi/vmw_pvscsi.c +++ b/drivers/scsi/vmw_pvscsi.c @@ -793,6 +793,7 @@ static int pvscsi_abort(struct scsi_cmnd *cmd) unsigned long flags; int result = SUCCESS; DECLARE_COMPLETION_ONSTACK(abort_cmp); + int done; scmd_printk(KERN_DEBUG, cmd, "task abort on host %u, %p\n", adapter->host->host_no, cmd); @@ -824,10 +825,10 @@ static int pvscsi_abort(struct scsi_cmnd *cmd) pvscsi_abort_cmd(adapter, ctx); spin_unlock_irqrestore(&adapter->hw_lock, flags); /* Wait for 2 secs for the completion. */ - wait_for_completion_timeout(&abort_cmp, msecs_to_jiffies(2000)); + done = wait_for_completion_timeout(&abort_cmp, msecs_to_jiffies(2000)); spin_lock_irqsave(&adapter->hw_lock, flags); - if (!completion_done(&abort_cmp)) { + if (!done) { /* * Failed to abort the command, unmark the fact that it * was requested to be aborted. diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h index c097d2ccbde31..d41292ef85f2f 100644 --- a/drivers/scsi/vmw_pvscsi.h +++ b/drivers/scsi/vmw_pvscsi.h @@ -26,7 +26,7 @@ #include -#define PVSCSI_DRIVER_VERSION_STRING "1.0.6.0-k" +#define PVSCSI_DRIVER_VERSION_STRING "1.0.7.0-k" #define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128 diff --git a/drivers/staging/comedi/drivers/ni_tio.c b/drivers/staging/comedi/drivers/ni_tio.c index 7043eb0543f6b..5ab49a798164b 100644 --- a/drivers/staging/comedi/drivers/ni_tio.c +++ b/drivers/staging/comedi/drivers/ni_tio.c @@ -207,7 +207,8 @@ static int ni_tio_clock_period_ps(const struct ni_gpct *counter, * clock period is specified by user with prescaling * already taken into account. */ - return counter->clock_period_ps; + *period_ps = counter->clock_period_ps; + return 0; } switch (generic_clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) { diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 34307ac3f2558..d33d6fe078ad7 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -186,6 +186,7 @@ int arche_platform_change_state(enum arche_platform_state state, exit: spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); mutex_unlock(&arche_pdata->platform_state_mutex); + put_device(&pdev->dev); of_node_put(np); return ret; } diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 5eecf1cb10288..3892a74704108 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -655,6 +655,7 @@ static void ad5933_work(struct work_struct *work) __be16 buf[2]; int val[2]; unsigned char status; + int ret; mutex_lock(&indio_dev->mlock); if (st->state == AD5933_CTRL_INIT_START_FREQ) { @@ -662,19 +663,22 @@ static void ad5933_work(struct work_struct *work) ad5933_cmd(st, AD5933_CTRL_START_SWEEP); st->state = AD5933_CTRL_START_SWEEP; schedule_delayed_work(&st->work, st->poll_time_jiffies); - mutex_unlock(&indio_dev->mlock); - return; + goto out; } - ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + if (ret) + goto out; if (status & AD5933_STAT_DATA_VALID) { int scan_count = bitmap_weight(indio_dev->active_scan_mask, indio_dev->masklength); - ad5933_i2c_read(st->client, + ret = ad5933_i2c_read(st->client, test_bit(1, indio_dev->active_scan_mask) ? AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, scan_count * 2, (u8 *)buf); + if (ret) + goto out; if (scan_count == 2) { val[0] = be16_to_cpu(buf[0]); @@ -686,8 +690,7 @@ static void ad5933_work(struct work_struct *work) } else { /* no data available - try again later */ schedule_delayed_work(&st->work, st->poll_time_jiffies); - mutex_unlock(&indio_dev->mlock); - return; + goto out; } if (status & AD5933_STAT_SWEEP_DONE) { @@ -700,7 +703,7 @@ static void ad5933_work(struct work_struct *work) ad5933_cmd(st, AD5933_CTRL_INC_FREQ); schedule_delayed_work(&st->work, st->poll_time_jiffies); } - +out: mutex_unlock(&indio_dev->mlock); } diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c index a324322ee0ad1..499952c8ef391 100644 --- a/drivers/staging/nvec/nvec_ps2.c +++ b/drivers/staging/nvec/nvec_ps2.c @@ -106,13 +106,12 @@ static int nvec_mouse_probe(struct platform_device *pdev) { struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); struct serio *ser_dev; - char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 }; - ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL); + ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ser_dev) return -ENOMEM; - ser_dev->id.type = SERIO_PS_PSTHRU; + ser_dev->id.type = SERIO_8042; ser_dev->write = ps2_sendcommand; ser_dev->start = ps2_startstreaming; ser_dev->stop = ps2_stopstreaming; @@ -127,9 +126,6 @@ static int nvec_mouse_probe(struct platform_device *pdev) serio_register_port(ser_dev); - /* mouse reset */ - nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset)); - return 0; } diff --git a/drivers/staging/sm750fb/ddk750_reg.h b/drivers/staging/sm750fb/ddk750_reg.h index 955247979aaa4..4ed6d8d7712ae 100644 --- a/drivers/staging/sm750fb/ddk750_reg.h +++ b/drivers/staging/sm750fb/ddk750_reg.h @@ -601,13 +601,13 @@ #define PANEL_PLANE_TL 0x08001C #define PANEL_PLANE_TL_TOP_SHIFT 16 -#define PANEL_PLANE_TL_TOP_MASK (0xeff << 16) -#define PANEL_PLANE_TL_LEFT_MASK 0xeff +#define PANEL_PLANE_TL_TOP_MASK (0x7ff << 16) +#define PANEL_PLANE_TL_LEFT_MASK 0x7ff #define PANEL_PLANE_BR 0x080020 #define PANEL_PLANE_BR_BOTTOM_SHIFT 16 -#define PANEL_PLANE_BR_BOTTOM_MASK (0xeff << 16) -#define PANEL_PLANE_BR_RIGHT_MASK 0xeff +#define PANEL_PLANE_BR_BOTTOM_MASK (0x7ff << 16) +#define PANEL_PLANE_BR_RIGHT_MASK 0x7ff #define PANEL_HORIZONTAL_TOTAL 0x080024 #define PANEL_HORIZONTAL_TOTAL_TOTAL_SHIFT 16 diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 78f0f85bebdc2..fada988512a16 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -932,8 +932,6 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) DECLARE_WAITQUEUE(wait, current); struct async_icount old, new; - if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)) - return -EINVAL; do { spin_lock_irq(&acm->read_lock); old = acm->oldcount; @@ -1161,6 +1159,8 @@ static int acm_probe(struct usb_interface *intf, if (quirks == IGNORE_DEVICE) return -ENODEV; + memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header)); + num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; /* handle quirks deadly to normal probing*/ diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7287a763cd0cc..fea446900cadd 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -769,15 +769,14 @@ static int dwc3_core_init(struct dwc3 *dwc) return 0; err4: - phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); err3: - phy_power_off(dwc->usb3_generic_phy); + phy_power_off(dwc->usb2_generic_phy); err2: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); err1: usb_phy_shutdown(dwc->usb2_phy); diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 89a2f712fdfe3..aaaf256f71dd6 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "core.h" diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index fe1811650dbc3..5d1bd13a56c11 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -588,14 +588,6 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle high/super speed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (((dev->gadget->speed == USB_SPEED_HIGH || - dev->gadget->speed == USB_SPEED_SUPER)) && - !list_empty(&dev->tx_reqs)) - ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0) - : 0; - retval = usb_ep_queue(in, req, GFP_ATOMIC); switch (retval) { default: diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index d793f548dfe26..a9a1e4c40480c 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -995,6 +995,14 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev) } val = readl(base + ext_cap_offset); + /* Auto handoff never worked for these devices. Force it and continue */ + if ((pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241) || + (pdev->vendor == PCI_VENDOR_ID_RENESAS + && pdev->device == 0x0014)) { + val = (val | XHCI_HC_OS_OWNED) & ~XHCI_HC_BIOS_OWNED; + writel(val, base + ext_cap_offset); + } + /* If the BIOS owns the HC, signal that the OS wants it, and wait */ if (val & XHCI_HC_BIOS_OWNED) { writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset); diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 210b7e43a6fd4..2440f88e07a35 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -479,7 +479,8 @@ static int da8xx_probe(struct platform_device *pdev) glue->phy = devm_phy_get(&pdev->dev, "usb-phy"); if (IS_ERR(glue->phy)) { - dev_err(&pdev->dev, "failed to get phy\n"); + if (PTR_ERR(glue->phy) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get phy\n"); return PTR_ERR(glue->phy); } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 27dadc0d9114b..e01116e4c0671 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2114,11 +2114,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->io.ep_offset = musb_flat_ep_offset; musb->io.ep_select = musb_flat_ep_select; } - /* And override them with platform specific ops if specified. */ - if (musb->ops->ep_offset) - musb->io.ep_offset = musb->ops->ep_offset; - if (musb->ops->ep_select) - musb->io.ep_select = musb->ops->ep_select; /* At least tusb6010 has its own offsets */ if (musb->ops->ep_offset) diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c index d059ad4d0dbdc..97ee1b46db698 100644 --- a/drivers/uwb/lc-rc.c +++ b/drivers/uwb/lc-rc.c @@ -56,8 +56,11 @@ static struct uwb_rc *uwb_rc_find_by_index(int index) struct uwb_rc *rc = NULL; dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } + return rc; } @@ -467,7 +470,9 @@ struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc) if (dev) { rc = dev_get_drvdata(dev); __uwb_rc_get(rc); + put_device(dev); } + return rc; } EXPORT_SYMBOL_GPL(__uwb_rc_try_get); @@ -520,8 +525,11 @@ struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev) dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev, find_rc_grandpa); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } + return rc; } EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa); @@ -553,8 +561,10 @@ struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr) struct uwb_rc *rc = NULL; dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } return rc; } diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c index c1304b8d49853..678e93741ae15 100644 --- a/drivers/uwb/pal.c +++ b/drivers/uwb/pal.c @@ -97,6 +97,8 @@ static bool uwb_rc_class_device_exists(struct uwb_rc *target_rc) dev = class_find_device(&uwb_rc_class, NULL, target_rc, find_rc); + put_device(dev); + return (dev != NULL); } diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index da6e2ce77495b..23eced02aaf68 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -48,4 +48,5 @@ menuconfig VFIO_NOIOMMU source "drivers/vfio/pci/Kconfig" source "drivers/vfio/platform/Kconfig" +source "drivers/vfio/mdev/Kconfig" source "virt/lib/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 7b8a31f63fea8..4a23c13b6be44 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o obj-$(CONFIG_VFIO_PCI) += pci/ obj-$(CONFIG_VFIO_PLATFORM) += platform/ +obj-$(CONFIG_VFIO_MDEV) += mdev/ diff --git a/drivers/vfio/mdev/Kconfig b/drivers/vfio/mdev/Kconfig new file mode 100644 index 0000000000000..14fdb106a827a --- /dev/null +++ b/drivers/vfio/mdev/Kconfig @@ -0,0 +1,17 @@ + +config VFIO_MDEV + tristate "Mediated device driver framework" + depends on VFIO + default n + help + Provides a framework to virtualize devices. + See Documentation/vfio-mediated-device.txt for more details. + + If you don't know what do here, say N. + +config VFIO_MDEV_DEVICE + tristate "VFIO driver for Mediated devices" + depends on VFIO && VFIO_MDEV + default n + help + VFIO based driver for Mediated devices. diff --git a/drivers/vfio/mdev/Makefile b/drivers/vfio/mdev/Makefile new file mode 100644 index 0000000000000..fa2d5ea466eea --- /dev/null +++ b/drivers/vfio/mdev/Makefile @@ -0,0 +1,5 @@ + +mdev-y := mdev_core.o mdev_sysfs.o mdev_driver.o + +obj-$(CONFIG_VFIO_MDEV) += mdev.o +obj-$(CONFIG_VFIO_MDEV_DEVICE) += vfio_mdev.o diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c new file mode 100644 index 0000000000000..be1ee89ee9172 --- /dev/null +++ b/drivers/vfio/mdev/mdev_core.c @@ -0,0 +1,385 @@ +/* + * Mediated device Core Driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "NVIDIA Corporation" +#define DRIVER_DESC "Mediated device Core Driver" + +static LIST_HEAD(parent_list); +static DEFINE_MUTEX(parent_list_lock); +static struct class_compat *mdev_bus_compat_class; + +static int _find_mdev_device(struct device *dev, void *data) +{ + struct mdev_device *mdev; + + if (!dev_is_mdev(dev)) + return 0; + + mdev = to_mdev_device(dev); + + if (uuid_le_cmp(mdev->uuid, *(uuid_le *)data) == 0) + return 1; + + return 0; +} + +static bool mdev_device_exist(struct parent_device *parent, uuid_le uuid) +{ + struct device *dev; + + dev = device_find_child(parent->dev, &uuid, _find_mdev_device); + if (dev) { + put_device(dev); + return true; + } + + return false; +} + +/* Should be called holding parent_list_lock */ +static struct parent_device *__find_parent_device(struct device *dev) +{ + struct parent_device *parent; + + list_for_each_entry(parent, &parent_list, next) { + if (parent->dev == dev) + return parent; + } + return NULL; +} + +static void mdev_release_parent(struct kref *kref) +{ + struct parent_device *parent = container_of(kref, struct parent_device, + ref); + struct device *dev = parent->dev; + + kfree(parent); + put_device(dev); +} + +static +inline struct parent_device *mdev_get_parent(struct parent_device *parent) +{ + if (parent) + kref_get(&parent->ref); + + return parent; +} + +static inline void mdev_put_parent(struct parent_device *parent) +{ + if (parent) + kref_put(&parent->ref, mdev_release_parent); +} + +static int mdev_device_create_ops(struct kobject *kobj, + struct mdev_device *mdev) +{ + struct parent_device *parent = mdev->parent; + int ret; + + ret = parent->ops->create(kobj, mdev); + if (ret) + return ret; + + ret = sysfs_create_groups(&mdev->dev.kobj, + parent->ops->mdev_attr_groups); + if (ret) + parent->ops->remove(mdev); + + return ret; +} + +/* + * mdev_device_remove_ops gets called from sysfs's 'remove' and when parent + * device is being unregistered from mdev device framework. + * - 'force_remove' is set to 'false' when called from sysfs's 'remove' which + * indicates that if the mdev device is active, used by VMM or userspace + * application, vendor driver could return error then don't remove the device. + * - 'force_remove' is set to 'true' when called from mdev_unregister_device() + * which indicate that parent device is being removed from mdev device + * framework so remove mdev device forcefully. + */ +static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) +{ + struct parent_device *parent = mdev->parent; + int ret; + + /* + * Vendor driver can return error if VMM or userspace application is + * using this mdev device. + */ + ret = parent->ops->remove(mdev); + if (ret && !force_remove) + return -EBUSY; + + sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups); + return 0; +} + +static int mdev_device_remove_cb(struct device *dev, void *data) +{ + if (!dev_is_mdev(dev)) + return 0; + + return mdev_device_remove(dev, data ? *(bool *)data : true); +} + +/* + * mdev_register_device : Register a device + * @dev: device structure representing parent device. + * @ops: Parent device operation structure to be registered. + * + * Add device to list of registered parent devices. + * Returns a negative value on error, otherwise 0. + */ +int mdev_register_device(struct device *dev, const struct parent_ops *ops) +{ + int ret; + struct parent_device *parent; + + /* check for mandatory ops */ + if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups) + return -EINVAL; + + dev = get_device(dev); + if (!dev) + return -EINVAL; + + mutex_lock(&parent_list_lock); + + /* Check for duplicate */ + parent = __find_parent_device(dev); + if (parent) { + ret = -EEXIST; + goto add_dev_err; + } + + parent = kzalloc(sizeof(*parent), GFP_KERNEL); + if (!parent) { + ret = -ENOMEM; + goto add_dev_err; + } + + kref_init(&parent->ref); + mutex_init(&parent->lock); + + parent->dev = dev; + parent->ops = ops; + + if (!mdev_bus_compat_class) { + mdev_bus_compat_class = class_compat_register("mdev_bus"); + if (!mdev_bus_compat_class) { + ret = -ENOMEM; + goto add_dev_err; + } + } + + ret = parent_create_sysfs_files(parent); + if (ret) + goto add_dev_err; + + ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL); + if (ret) + dev_warn(dev, "Failed to create compatibility class link\n"); + + list_add(&parent->next, &parent_list); + mutex_unlock(&parent_list_lock); + + dev_info(dev, "MDEV: Registered\n"); + return 0; + +add_dev_err: + mutex_unlock(&parent_list_lock); + if (parent) + mdev_put_parent(parent); + else + put_device(dev); + return ret; +} +EXPORT_SYMBOL(mdev_register_device); + +/* + * mdev_unregister_device : Unregister a parent device + * @dev: device structure representing parent device. + * + * Remove device from list of registered parent devices. Give a chance to free + * existing mediated devices for given device. + */ + +void mdev_unregister_device(struct device *dev) +{ + struct parent_device *parent; + bool force_remove = true; + + mutex_lock(&parent_list_lock); + parent = __find_parent_device(dev); + + if (!parent) { + mutex_unlock(&parent_list_lock); + return; + } + dev_info(dev, "MDEV: Unregistering\n"); + + list_del(&parent->next); + class_compat_remove_link(mdev_bus_compat_class, dev, NULL); + + device_for_each_child(dev, (void *)&force_remove, + mdev_device_remove_cb); + + parent_remove_sysfs_files(parent); + + mutex_unlock(&parent_list_lock); + mdev_put_parent(parent); +} +EXPORT_SYMBOL(mdev_unregister_device); + +static void mdev_device_release(struct device *dev) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + dev_dbg(&mdev->dev, "MDEV: destroying\n"); + kfree(mdev); +} + +int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) +{ + int ret; + struct mdev_device *mdev; + struct parent_device *parent; + struct mdev_type *type = to_mdev_type(kobj); + + parent = mdev_get_parent(type->parent); + if (!parent) + return -EINVAL; + + mutex_lock(&parent->lock); + + /* Check for duplicate */ + if (mdev_device_exist(parent, uuid)) { + ret = -EEXIST; + goto create_err; + } + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) { + ret = -ENOMEM; + goto create_err; + } + + memcpy(&mdev->uuid, &uuid, sizeof(uuid_le)); + mdev->parent = parent; + kref_init(&mdev->ref); + + mdev->dev.parent = dev; + mdev->dev.bus = &mdev_bus_type; + mdev->dev.release = mdev_device_release; + dev_set_name(&mdev->dev, "%pUl", uuid.b); + + ret = device_register(&mdev->dev); + if (ret) { + put_device(&mdev->dev); + goto create_err; + } + + ret = mdev_device_create_ops(kobj, mdev); + if (ret) + goto create_failed; + + ret = mdev_create_sysfs_files(&mdev->dev, type); + if (ret) { + mdev_device_remove_ops(mdev, true); + goto create_failed; + } + + mdev->type_kobj = kobj; + dev_dbg(&mdev->dev, "MDEV: created\n"); + + mutex_unlock(&parent->lock); + return ret; + +create_failed: + device_unregister(&mdev->dev); + +create_err: + mutex_unlock(&parent->lock); + mdev_put_parent(parent); + return ret; +} + +int mdev_device_remove(struct device *dev, bool force_remove) +{ + struct mdev_device *mdev; + struct parent_device *parent; + struct mdev_type *type; + int ret; + + mdev = to_mdev_device(dev); + type = to_mdev_type(mdev->type_kobj); + parent = mdev->parent; + mutex_lock(&parent->lock); + + ret = mdev_device_remove_ops(mdev, force_remove); + if (ret) { + mutex_unlock(&parent->lock); + return ret; + } + + mdev_remove_sysfs_files(dev, type); + device_unregister(dev); + mutex_unlock(&parent->lock); + mdev_put_parent(parent); + return ret; +} + +static int __init mdev_init(void) +{ + int ret; + + ret = mdev_bus_register(); + + /* + * Attempt to load known vfio_mdev. This gives us a working environment + * without the user needing to explicitly load vfio_mdev driver. + */ + if (!ret) + request_module_nowait("vfio_mdev"); + + return ret; +} + +static void __exit mdev_exit(void) +{ + if (mdev_bus_compat_class) + class_compat_unregister(mdev_bus_compat_class); + + mdev_bus_unregister(); +} + +module_init(mdev_init) +module_exit(mdev_exit) + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/mdev/mdev_driver.c b/drivers/vfio/mdev/mdev_driver.c new file mode 100644 index 0000000000000..6f0391f6f9b60 --- /dev/null +++ b/drivers/vfio/mdev/mdev_driver.c @@ -0,0 +1,119 @@ +/* + * MDEV driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "mdev_private.h" + +static int mdev_attach_iommu(struct mdev_device *mdev) +{ + int ret; + struct iommu_group *group; + + group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); + + ret = iommu_group_add_device(group, &mdev->dev); + if (!ret) + dev_info(&mdev->dev, "MDEV: group_id = %d\n", + iommu_group_id(group)); + + iommu_group_put(group); + return ret; +} + +static void mdev_detach_iommu(struct mdev_device *mdev) +{ + iommu_group_remove_device(&mdev->dev); + dev_info(&mdev->dev, "MDEV: detaching iommu\n"); +} + +static int mdev_probe(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = to_mdev_device(dev); + int ret; + + ret = mdev_attach_iommu(mdev); + if (ret) + return ret; + + if (drv && drv->probe) { + ret = drv->probe(dev); + if (ret) + mdev_detach_iommu(mdev); + } + + return ret; +} + +static int mdev_remove(struct device *dev) +{ + struct mdev_driver *drv = to_mdev_driver(dev->driver); + struct mdev_device *mdev = to_mdev_device(dev); + + if (drv && drv->remove) + drv->remove(dev); + + mdev_detach_iommu(mdev); + + return 0; +} + +struct bus_type mdev_bus_type = { + .name = "mdev", + .probe = mdev_probe, + .remove = mdev_remove, +}; +EXPORT_SYMBOL_GPL(mdev_bus_type); + +/** + * mdev_register_driver - register a new MDEV driver + * @drv: the driver to register + * @owner: module owner of driver to be registered + * + * Returns a negative value on error, otherwise 0. + **/ +int mdev_register_driver(struct mdev_driver *drv, struct module *owner) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &mdev_bus_type; + drv->driver.owner = owner; + + /* register with core */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(mdev_register_driver); + +/* + * mdev_unregister_driver - unregister MDEV driver + * @drv: the driver to unregister + */ +void mdev_unregister_driver(struct mdev_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(mdev_unregister_driver); + +int mdev_bus_register(void) +{ + return bus_register(&mdev_bus_type); +} + +void mdev_bus_unregister(void) +{ + bus_unregister(&mdev_bus_type); +} diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h new file mode 100644 index 0000000000000..d35097cbf3d75 --- /dev/null +++ b/drivers/vfio/mdev/mdev_private.h @@ -0,0 +1,41 @@ +/* + * Mediated device interal definitions + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_PRIVATE_H +#define MDEV_PRIVATE_H + +int mdev_bus_register(void); +void mdev_bus_unregister(void); + +struct mdev_type { + struct kobject kobj; + struct kobject *devices_kobj; + struct parent_device *parent; + struct list_head next; + struct attribute_group *group; +}; + +#define to_mdev_type_attr(_attr) \ + container_of(_attr, struct mdev_type_attribute, attr) +#define to_mdev_type(_kobj) \ + container_of(_kobj, struct mdev_type, kobj) + +int parent_create_sysfs_files(struct parent_device *parent); +void parent_remove_sysfs_files(struct parent_device *parent); + +int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type); +void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type); + +int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid); +int mdev_device_remove(struct device *dev, bool force_remove); + +#endif /* MDEV_PRIVATE_H */ diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c new file mode 100644 index 0000000000000..1a53deb2ee102 --- /dev/null +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -0,0 +1,286 @@ +/* + * File attributes for Mediated devices + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +/* Static functions */ + +static ssize_t mdev_type_attr_show(struct kobject *kobj, + struct attribute *__attr, char *buf) +{ + struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); + struct mdev_type *type = to_mdev_type(kobj); + ssize_t ret = -EIO; + + if (attr->show) + ret = attr->show(kobj, type->parent->dev, buf); + return ret; +} + +static ssize_t mdev_type_attr_store(struct kobject *kobj, + struct attribute *__attr, + const char *buf, size_t count) +{ + struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); + struct mdev_type *type = to_mdev_type(kobj); + ssize_t ret = -EIO; + + if (attr->store) + ret = attr->store(&type->kobj, type->parent->dev, buf, count); + return ret; +} + +static const struct sysfs_ops mdev_type_sysfs_ops = { + .show = mdev_type_attr_show, + .store = mdev_type_attr_store, +}; + +static ssize_t create_store(struct kobject *kobj, struct device *dev, + const char *buf, size_t count) +{ + char *str; + uuid_le uuid; + int ret; + + if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) + return -EINVAL; + + str = kstrndup(buf, count, GFP_KERNEL); + if (!str) + return -ENOMEM; + + ret = uuid_le_to_bin(str, &uuid); + kfree(str); + if (ret) + return ret; + + ret = mdev_device_create(kobj, dev, uuid); + if (ret) + return ret; + + return count; +} + +MDEV_TYPE_ATTR_WO(create); + +static void mdev_type_release(struct kobject *kobj) +{ + struct mdev_type *type = to_mdev_type(kobj); + + pr_debug("Releasing group %s\n", kobj->name); + kfree(type); +} + +static struct kobj_type mdev_type_ktype = { + .sysfs_ops = &mdev_type_sysfs_ops, + .release = mdev_type_release, +}; + +struct mdev_type *add_mdev_supported_type(struct parent_device *parent, + struct attribute_group *group) +{ + struct mdev_type *type; + int ret; + + if (!group->name) { + pr_err("%s: Type name empty!\n", __func__); + return ERR_PTR(-EINVAL); + } + + type = kzalloc(sizeof(*type), GFP_KERNEL); + if (!type) + return ERR_PTR(-ENOMEM); + + type->kobj.kset = parent->mdev_types_kset; + + ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, + "%s-%s", dev_driver_string(parent->dev), + group->name); + if (ret) { + kfree(type); + return ERR_PTR(ret); + } + + ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr); + if (ret) + goto attr_create_failed; + + type->devices_kobj = kobject_create_and_add("devices", &type->kobj); + if (!type->devices_kobj) { + ret = -ENOMEM; + goto attr_devices_failed; + } + + ret = sysfs_create_files(&type->kobj, + (const struct attribute **)group->attrs); + if (ret) { + ret = -ENOMEM; + goto attrs_failed; + } + + type->group = group; + type->parent = parent; + return type; + +attrs_failed: + kobject_put(type->devices_kobj); +attr_devices_failed: + sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); +attr_create_failed: + kobject_del(&type->kobj); + kobject_put(&type->kobj); + return ERR_PTR(ret); +} + +static void remove_mdev_supported_type(struct mdev_type *type) +{ + sysfs_remove_files(&type->kobj, + (const struct attribute **)type->group->attrs); + kobject_put(type->devices_kobj); + sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); + kobject_del(&type->kobj); + kobject_put(&type->kobj); +} + +static int add_mdev_supported_type_groups(struct parent_device *parent) +{ + int i; + + for (i = 0; parent->ops->supported_type_groups[i]; i++) { + struct mdev_type *type; + + type = add_mdev_supported_type(parent, + parent->ops->supported_type_groups[i]); + if (IS_ERR(type)) { + struct mdev_type *ltype, *tmp; + + list_for_each_entry_safe(ltype, tmp, &parent->type_list, + next) { + list_del(<ype->next); + remove_mdev_supported_type(ltype); + } + return PTR_ERR(type); + } + list_add(&type->next, &parent->type_list); + } + return 0; +} + +/* mdev sysfs functions */ +void parent_remove_sysfs_files(struct parent_device *parent) +{ + struct mdev_type *type, *tmp; + + list_for_each_entry_safe(type, tmp, &parent->type_list, next) { + list_del(&type->next); + remove_mdev_supported_type(type); + } + + sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups); + kset_unregister(parent->mdev_types_kset); +} + +int parent_create_sysfs_files(struct parent_device *parent) +{ + int ret; + + parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", + NULL, &parent->dev->kobj); + + if (!parent->mdev_types_kset) + return -ENOMEM; + + INIT_LIST_HEAD(&parent->type_list); + + ret = sysfs_create_groups(&parent->dev->kobj, + parent->ops->dev_attr_groups); + if (ret) + goto create_err; + + ret = add_mdev_supported_type_groups(parent); + if (ret) + sysfs_remove_groups(&parent->dev->kobj, + parent->ops->dev_attr_groups); + else + return ret; + +create_err: + kset_unregister(parent->mdev_types_kset); + return ret; +} + +static ssize_t remove_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val && device_remove_file_self(dev, attr)) { + int ret; + + ret = mdev_device_remove(dev, false); + if (ret) { + device_create_file(dev, attr); + return ret; + } + } + + return count; +} + +static DEVICE_ATTR_WO(remove); + +static const struct attribute *mdev_device_attrs[] = { + &dev_attr_remove.attr, + NULL, +}; + +int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type) +{ + int ret; + + ret = sysfs_create_files(&dev->kobj, mdev_device_attrs); + if (ret) + return ret; + + ret = sysfs_create_link(type->devices_kobj, &dev->kobj, dev_name(dev)); + if (ret) + goto device_link_failed; + + ret = sysfs_create_link(&dev->kobj, &type->kobj, "mdev_type"); + if (ret) + goto type_link_failed; + + return ret; + +type_link_failed: + sysfs_remove_link(type->devices_kobj, dev_name(dev)); +device_link_failed: + sysfs_remove_files(&dev->kobj, mdev_device_attrs); + return ret; +} + +void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type) +{ + sysfs_remove_link(&dev->kobj, "mdev_type"); + sysfs_remove_link(type->devices_kobj, dev_name(dev)); + sysfs_remove_files(&dev->kobj, mdev_device_attrs); +} diff --git a/drivers/vfio/mdev/vfio_mdev.c b/drivers/vfio/mdev/vfio_mdev.c new file mode 100644 index 0000000000000..ffc36758cb84c --- /dev/null +++ b/drivers/vfio/mdev/vfio_mdev.c @@ -0,0 +1,148 @@ +/* + * VFIO based driver for Mediated device + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mdev_private.h" + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "NVIDIA Corporation" +#define DRIVER_DESC "VFIO based driver for Mediated device" + +static int vfio_mdev_open(void *device_data) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + int ret; + + if (unlikely(!parent->ops->open)) + return -EINVAL; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + ret = parent->ops->open(mdev); + if (ret) + module_put(THIS_MODULE); + + return ret; +} + +static void vfio_mdev_release(void *device_data) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + + if (likely(parent->ops->release)) + parent->ops->release(mdev); + + module_put(THIS_MODULE); +} + +static long vfio_mdev_unlocked_ioctl(void *device_data, + unsigned int cmd, unsigned long arg) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + + if (unlikely(!parent->ops->ioctl)) + return -EINVAL; + + return parent->ops->ioctl(mdev, cmd, arg); +} + +static ssize_t vfio_mdev_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + + if (unlikely(!parent->ops->read)) + return -EINVAL; + + return parent->ops->read(mdev, buf, count, ppos); +} + +static ssize_t vfio_mdev_write(void *device_data, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + + if (unlikely(!parent->ops->write)) + return -EINVAL; + + return parent->ops->write(mdev, buf, count, ppos); +} + +static int vfio_mdev_mmap(void *device_data, struct vm_area_struct *vma) +{ + struct mdev_device *mdev = device_data; + struct parent_device *parent = mdev->parent; + + if (unlikely(!parent->ops->mmap)) + return -EINVAL; + + return parent->ops->mmap(mdev, vma); +} + +static const struct vfio_device_ops vfio_mdev_dev_ops = { + .name = "vfio-mdev", + .open = vfio_mdev_open, + .release = vfio_mdev_release, + .ioctl = vfio_mdev_unlocked_ioctl, + .read = vfio_mdev_read, + .write = vfio_mdev_write, + .mmap = vfio_mdev_mmap, +}; + +int vfio_mdev_probe(struct device *dev) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + return vfio_add_group_dev(dev, &vfio_mdev_dev_ops, mdev); +} + +void vfio_mdev_remove(struct device *dev) +{ + vfio_del_group_dev(dev); +} + +struct mdev_driver vfio_mdev_driver = { + .name = "vfio_mdev", + .probe = vfio_mdev_probe, + .remove = vfio_mdev_remove, +}; + +static int __init vfio_mdev_init(void) +{ + return mdev_register_driver(&vfio_mdev_driver, THIS_MODULE); +} + +static void __exit vfio_mdev_exit(void) +{ + mdev_unregister_driver(&vfio_mdev_driver); +} + +module_init(vfio_mdev_init) +module_exit(vfio_mdev_exit) + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 031bc08d000d4..dcd7c2a996183 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -558,10 +558,9 @@ static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev, static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev, struct vfio_info_cap *caps) { - struct vfio_info_cap_header *header; struct vfio_region_info_cap_sparse_mmap *sparse; size_t end, size; - int nr_areas = 2, i = 0; + int nr_areas = 2, i = 0, ret; end = pci_resource_len(vdev->pdev, vdev->msix_bar); @@ -572,13 +571,10 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev, size = sizeof(*sparse) + (nr_areas * sizeof(*sparse->areas)); - header = vfio_info_cap_add(caps, size, - VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1); - if (IS_ERR(header)) - return PTR_ERR(header); + sparse = kzalloc(size, GFP_KERNEL); + if (!sparse) + return -ENOMEM; - sparse = container_of(header, - struct vfio_region_info_cap_sparse_mmap, header); sparse->nr_areas = nr_areas; if (vdev->msix_offset & PAGE_MASK) { @@ -594,26 +590,11 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev, i++; } - return 0; -} - -static int region_type_cap(struct vfio_pci_device *vdev, - struct vfio_info_cap *caps, - unsigned int type, unsigned int subtype) -{ - struct vfio_info_cap_header *header; - struct vfio_region_info_cap_type *cap; - - header = vfio_info_cap_add(caps, sizeof(*cap), - VFIO_REGION_INFO_CAP_TYPE, 1); - if (IS_ERR(header)) - return PTR_ERR(header); - - cap = container_of(header, struct vfio_region_info_cap_type, header); - cap->type = type; - cap->subtype = subtype; + ret = vfio_info_add_capability(caps, VFIO_REGION_INFO_CAP_SPARSE_MMAP, + sparse); + kfree(sparse); - return 0; + return ret; } int vfio_pci_register_dev_region(struct vfio_pci_device *vdev, @@ -752,6 +733,9 @@ static long vfio_pci_ioctl(void *device_data, break; default: + { + struct vfio_region_info_cap_type cap_type; + if (info.index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions) return -EINVAL; @@ -762,11 +746,16 @@ static long vfio_pci_ioctl(void *device_data, info.size = vdev->region[i].size; info.flags = vdev->region[i].flags; - ret = region_type_cap(vdev, &caps, - vdev->region[i].type, - vdev->region[i].subtype); + cap_type.type = vdev->region[i].type; + cap_type.subtype = vdev->region[i].subtype; + + ret = vfio_info_add_capability(&caps, + VFIO_REGION_INFO_CAP_TYPE, + &cap_type); if (ret) return ret; + + } } if (caps.size) { @@ -829,45 +818,25 @@ static long vfio_pci_ioctl(void *device_data, } else if (cmd == VFIO_DEVICE_SET_IRQS) { struct vfio_irq_set hdr; - size_t size; u8 *data = NULL; int max, ret = 0; + size_t data_size = 0; minsz = offsetofend(struct vfio_irq_set, count); if (copy_from_user(&hdr, (void __user *)arg, minsz)) 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; - max = vfio_pci_get_irq_count(vdev, hdr.index); - if (hdr.start >= max || hdr.start + hdr.count > max) - 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 (size) { - if (hdr.argsz - minsz < hdr.count * size) - return -EINVAL; + ret = vfio_set_irqs_validate_and_prepare(&hdr, max, + VFIO_PCI_NUM_IRQS, &data_size); + if (ret) + return ret; + if (data_size) { data = memdup_user((void __user *)(arg + minsz), - hdr.count * size); + data_size); if (IS_ERR(data)) return PTR_ERR(data); } diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c index d781428307546..4c27f4be3c3d0 100644 --- a/drivers/vfio/platform/vfio_platform_common.c +++ b/drivers/vfio/platform/vfio_platform_common.c @@ -364,36 +364,21 @@ static long vfio_platform_ioctl(void *device_data, struct vfio_irq_set hdr; u8 *data = NULL; int ret = 0; + size_t data_size = 0; minsz = offsetofend(struct vfio_irq_set, count); if (copy_from_user(&hdr, (void __user *)arg, minsz)) return -EFAULT; - if (hdr.argsz < minsz) - return -EINVAL; - - if (hdr.index >= vdev->num_irqs) - return -EINVAL; - - if (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; - - 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; - - if (hdr.argsz - minsz < size) - return -EINVAL; + ret = vfio_set_irqs_validate_and_prepare(&hdr, vdev->num_irqs, + vdev->num_irqs, &data_size); + if (ret) + return ret; - data = memdup_user((void __user *)(arg + minsz), size); + if (data_size) { + data = memdup_user((void __user *)(arg + minsz), + data_size); if (IS_ERR(data)) return PTR_ERR(data); } diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index d1d70e0b011bf..c8150674787cb 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -480,6 +480,21 @@ static struct vfio_group *vfio_group_get_from_minor(int minor) return group; } +static struct vfio_group *vfio_group_get_from_dev(struct device *dev) +{ + struct iommu_group *iommu_group; + struct vfio_group *group; + + iommu_group = iommu_group_get(dev); + if (!iommu_group) + return NULL; + + group = vfio_group_get_from_iommu(iommu_group); + iommu_group_put(iommu_group); + + return group; +} + /** * Device objects - create, release, get, put, search */ @@ -811,16 +826,10 @@ EXPORT_SYMBOL_GPL(vfio_add_group_dev); */ struct vfio_device *vfio_device_get_from_dev(struct device *dev) { - struct iommu_group *iommu_group; struct vfio_group *group; struct vfio_device *device; - iommu_group = iommu_group_get(dev); - if (!iommu_group) - return NULL; - - group = vfio_group_get_from_iommu(iommu_group); - iommu_group_put(iommu_group); + group = vfio_group_get_from_dev(dev); if (!group) return NULL; @@ -1376,6 +1385,23 @@ static bool vfio_group_viable(struct vfio_group *group) group, vfio_dev_viable) == 0); } +static int vfio_group_add_container_user(struct vfio_group *group) +{ + if (!atomic_inc_not_zero(&group->container_users)) + return -EINVAL; + + if (group->noiommu) { + atomic_dec(&group->container_users); + return -EPERM; + } + if (!group->container->iommu_driver || !vfio_group_viable(group)) { + atomic_dec(&group->container_users); + return -EINVAL; + } + + return 0; +} + static const struct file_operations vfio_device_fops; static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) @@ -1685,23 +1711,14 @@ static const struct file_operations vfio_device_fops = { struct vfio_group *vfio_group_get_external_user(struct file *filep) { struct vfio_group *group = filep->private_data; + int ret; if (filep->f_op != &vfio_group_fops) return ERR_PTR(-EINVAL); - if (!atomic_inc_not_zero(&group->container_users)) - return ERR_PTR(-EINVAL); - - if (group->noiommu) { - atomic_dec(&group->container_users); - return ERR_PTR(-EPERM); - } - - if (!group->container->iommu_driver || - !vfio_group_viable(group)) { - atomic_dec(&group->container_users); - return ERR_PTR(-EINVAL); - } + ret = vfio_group_add_container_user(group); + if (ret) + return ERR_PTR(ret); vfio_group_get(group); @@ -1780,7 +1797,288 @@ void vfio_info_cap_shift(struct vfio_info_cap *caps, size_t offset) for (tmp = caps->buf; tmp->next; tmp = (void *)tmp + tmp->next - offset) tmp->next += offset; } -EXPORT_SYMBOL_GPL(vfio_info_cap_shift); +EXPORT_SYMBOL(vfio_info_cap_shift); + +static int sparse_mmap_cap(struct vfio_info_cap *caps, void *cap_type) +{ + struct vfio_info_cap_header *header; + struct vfio_region_info_cap_sparse_mmap *sparse_cap, *sparse = cap_type; + size_t size; + + size = sizeof(*sparse) + sparse->nr_areas * sizeof(*sparse->areas); + header = vfio_info_cap_add(caps, size, + VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1); + if (IS_ERR(header)) + return PTR_ERR(header); + + sparse_cap = container_of(header, + struct vfio_region_info_cap_sparse_mmap, header); + sparse_cap->nr_areas = sparse->nr_areas; + memcpy(sparse_cap->areas, sparse->areas, + sparse->nr_areas * sizeof(*sparse->areas)); + return 0; +} + +static int region_type_cap(struct vfio_info_cap *caps, void *cap_type) +{ + struct vfio_info_cap_header *header; + struct vfio_region_info_cap_type *type_cap, *cap = cap_type; + + header = vfio_info_cap_add(caps, sizeof(*cap), + VFIO_REGION_INFO_CAP_TYPE, 1); + if (IS_ERR(header)) + return PTR_ERR(header); + + type_cap = container_of(header, struct vfio_region_info_cap_type, + header); + type_cap->type = cap->type; + type_cap->subtype = cap->subtype; + return 0; +} + +int vfio_info_add_capability(struct vfio_info_cap *caps, int cap_type_id, + void *cap_type) +{ + int ret = -EINVAL; + + if (!cap_type) + return 0; + + switch (cap_type_id) { + case VFIO_REGION_INFO_CAP_SPARSE_MMAP: + ret = sparse_mmap_cap(caps, cap_type); + break; + + case VFIO_REGION_INFO_CAP_TYPE: + ret = region_type_cap(caps, cap_type); + break; + } + + return ret; +} +EXPORT_SYMBOL(vfio_info_add_capability); + +int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr, int num_irqs, + int max_irq_type, size_t *data_size) +{ + unsigned long minsz; + size_t size; + + minsz = offsetofend(struct vfio_irq_set, count); + + if ((hdr->argsz < minsz) || (hdr->index >= max_irq_type) || + (hdr->count >= (U32_MAX - hdr->start)) || + (hdr->flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | + VFIO_IRQ_SET_ACTION_TYPE_MASK))) + return -EINVAL; + + if (data_size) + *data_size = 0; + + if (hdr->start >= num_irqs || hdr->start + hdr->count > num_irqs) + 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 (size) { + if (hdr->argsz - minsz < hdr->count * size) + return -EINVAL; + + if (!data_size) + return -EINVAL; + + *data_size = hdr->count * size; + } + + return 0; +} +EXPORT_SYMBOL(vfio_set_irqs_validate_and_prepare); + +/* + * Pin a set of guest PFNs and return their associated host PFNs for local + * domain only. + * @dev [in] : device + * @user_pfn [in]: array of user/guest PFNs to be unpinned. + * @npage [in] : count of elements in user_pfn array. This count should not + * be greater VFIO_PIN_PAGES_MAX_ENTRIES. + * @prot [in] : protection flags + * @phys_pfn[out]: array of host PFNs + * Return error or number of pages pinned. + */ +int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, int npage, + int prot, unsigned long *phys_pfn) +{ + struct vfio_container *container; + struct vfio_group *group; + struct vfio_iommu_driver *driver; + int ret; + + if (!dev || !user_pfn || !phys_pfn || !npage) + return -EINVAL; + + if (npage > VFIO_PIN_PAGES_MAX_ENTRIES) + return -E2BIG; + + group = vfio_group_get_from_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + ret = vfio_group_add_container_user(group); + if (ret) + goto err_pin_pages; + + container = group->container; + down_read(&container->group_lock); + + driver = container->iommu_driver; + if (likely(driver && driver->ops->pin_pages)) + ret = driver->ops->pin_pages(container->iommu_data, user_pfn, + npage, prot, phys_pfn); + else + ret = -ENOTTY; + + up_read(&container->group_lock); + vfio_group_try_dissolve_container(group); + +err_pin_pages: + vfio_group_put(group); + return ret; +} +EXPORT_SYMBOL(vfio_pin_pages); + +/* + * Unpin set of host PFNs for local domain only. + * @dev [in] : device + * @user_pfn [in]: array of user/guest PFNs to be unpinned. Number of user/guest + * PFNs should not be greater than VFIO_PIN_PAGES_MAX_ENTRIES. + * @npage [in] : count of elements in user_pfn array. This count should not + * be greater than VFIO_PIN_PAGES_MAX_ENTRIES. + * Return error or number of pages unpinned. + */ +int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, int npage) +{ + struct vfio_container *container; + struct vfio_group *group; + struct vfio_iommu_driver *driver; + int ret; + + if (!dev || !user_pfn || !npage) + return -EINVAL; + + if (npage > VFIO_PIN_PAGES_MAX_ENTRIES) + return -E2BIG; + + group = vfio_group_get_from_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + ret = vfio_group_add_container_user(group); + if (ret) + goto err_unpin_pages; + + container = group->container; + down_read(&container->group_lock); + + driver = container->iommu_driver; + if (likely(driver && driver->ops->unpin_pages)) + ret = driver->ops->unpin_pages(container->iommu_data, user_pfn, + npage); + else + ret = -ENOTTY; + + up_read(&container->group_lock); + vfio_group_try_dissolve_container(group); + +err_unpin_pages: + vfio_group_put(group); + return ret; +} +EXPORT_SYMBOL(vfio_unpin_pages); + +int vfio_register_notifier(struct device *dev, struct notifier_block *nb) +{ + struct vfio_container *container; + struct vfio_group *group; + struct vfio_iommu_driver *driver; + int ret; + + if (!dev || !nb) + return -EINVAL; + + group = vfio_group_get_from_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + ret = vfio_group_add_container_user(group); + if (ret) + goto err_register_nb; + + container = group->container; + down_read(&container->group_lock); + + driver = container->iommu_driver; + if (likely(driver && driver->ops->register_notifier)) + ret = driver->ops->register_notifier(container->iommu_data, nb); + else + ret = -ENOTTY; + + up_read(&container->group_lock); + vfio_group_try_dissolve_container(group); + +err_register_nb: + vfio_group_put(group); + return ret; +} +EXPORT_SYMBOL(vfio_register_notifier); + +int vfio_unregister_notifier(struct device *dev, struct notifier_block *nb) +{ + struct vfio_container *container; + struct vfio_group *group; + struct vfio_iommu_driver *driver; + int ret; + + if (!dev || !nb) + return -EINVAL; + + group = vfio_group_get_from_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + ret = vfio_group_add_container_user(group); + if (ret) + goto err_unregister_nb; + + container = group->container; + down_read(&container->group_lock); + + driver = container->iommu_driver; + if (likely(driver && driver->ops->unregister_notifier)) + ret = driver->ops->unregister_notifier(container->iommu_data, + nb); + else + ret = -ENOTTY; + + up_read(&container->group_lock); + vfio_group_try_dissolve_container(group); + +err_unregister_nb: + vfio_group_put(group); + return ret; +} +EXPORT_SYMBOL(vfio_unregister_notifier); /** * Module/class support diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 2ba19424e4a18..51810a95416ea 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #define DRIVER_VERSION "0.2" #define DRIVER_AUTHOR "Alex Williamson " @@ -55,8 +58,10 @@ MODULE_PARM_DESC(disable_hugepages, struct vfio_iommu { struct list_head domain_list; + struct vfio_domain *external_domain; /* domain for external user */ struct mutex lock; struct rb_root dma_list; + struct blocking_notifier_head notifier; bool v2; bool nesting; }; @@ -75,6 +80,9 @@ struct vfio_dma { unsigned long vaddr; /* Process virtual addr */ size_t size; /* Map size (bytes) */ int prot; /* IOMMU_READ/WRITE */ + bool iommu_mapped; + struct task_struct *task; + struct rb_root pfn_list; /* Ex-user pinned pfn list */ }; struct vfio_group { @@ -82,6 +90,21 @@ struct vfio_group { struct list_head next; }; +/* + * Guest RAM pinning working set or DMA target + */ +struct vfio_pfn { + struct rb_node node; + dma_addr_t iova; /* Device address */ + unsigned long pfn; /* Host pfn */ + atomic_t ref_count; +}; + +#define IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) \ + (!list_empty(&iommu->domain_list)) + +static int put_pfn(unsigned long pfn, int prot); + /* * This code handles mapping and unmapping of user data buffers * into DMA'ble space using the IOMMU @@ -130,6 +153,97 @@ static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old) rb_erase(&old->node, &iommu->dma_list); } +/* + * Helper Functions for host iova-pfn list + */ +static struct vfio_pfn *vfio_find_vpfn(struct vfio_dma *dma, dma_addr_t iova) +{ + struct vfio_pfn *vpfn; + struct rb_node *node = dma->pfn_list.rb_node; + + while (node) { + vpfn = rb_entry(node, struct vfio_pfn, node); + + if (iova < vpfn->iova) + node = node->rb_left; + else if (iova > vpfn->iova) + node = node->rb_right; + else + return vpfn; + } + return NULL; +} + +static void vfio_link_pfn(struct vfio_dma *dma, + struct vfio_pfn *new) +{ + struct rb_node **link, *parent = NULL; + struct vfio_pfn *vpfn; + + link = &dma->pfn_list.rb_node; + while (*link) { + parent = *link; + vpfn = rb_entry(parent, struct vfio_pfn, node); + + if (new->iova < vpfn->iova) + link = &(*link)->rb_left; + else + link = &(*link)->rb_right; + } + + rb_link_node(&new->node, parent, link); + rb_insert_color(&new->node, &dma->pfn_list); +} + +static void vfio_unlink_pfn(struct vfio_dma *dma, struct vfio_pfn *old) +{ + rb_erase(&old->node, &dma->pfn_list); +} + +static int vfio_add_to_pfn_list(struct vfio_dma *dma, dma_addr_t iova, + unsigned long pfn) +{ + struct vfio_pfn *vpfn; + + vpfn = kzalloc(sizeof(*vpfn), GFP_KERNEL); + if (!vpfn) + return -ENOMEM; + + vpfn->iova = iova; + vpfn->pfn = pfn; + atomic_set(&vpfn->ref_count, 1); + vfio_link_pfn(dma, vpfn); + return 0; +} + +static void vfio_remove_from_pfn_list(struct vfio_dma *dma, + struct vfio_pfn *vpfn) +{ + vfio_unlink_pfn(dma, vpfn); + kfree(vpfn); +} + +static struct vfio_pfn *vfio_iova_get_vfio_pfn(struct vfio_dma *dma, + unsigned long iova) +{ + struct vfio_pfn *vpfn = vfio_find_vpfn(dma, iova); + + if (vpfn) + atomic_inc(&vpfn->ref_count); + return vpfn; +} + +static int vfio_iova_put_vfio_pfn(struct vfio_dma *dma, struct vfio_pfn *vpfn) +{ + int ret = 0; + + if (atomic_dec_and_test(&vpfn->ref_count)) { + ret = put_pfn(vpfn->pfn, dma->prot); + vfio_remove_from_pfn_list(dma, vpfn); + } + return ret; +} + struct vwork { struct mm_struct *mm; long npage; @@ -150,17 +264,22 @@ static void vfio_lock_acct_bg(struct work_struct *work) kfree(vwork); } -static void vfio_lock_acct(long npage) +static void vfio_lock_acct(struct task_struct *task, long npage) { struct vwork *vwork; struct mm_struct *mm; - if (!current->mm || !npage) + if (!npage) + return; + + mm = get_task_mm(task); + if (!mm) return; /* process exited or nothing to do */ - if (down_write_trylock(¤t->mm->mmap_sem)) { - current->mm->locked_vm += npage; - up_write(¤t->mm->mmap_sem); + if (down_write_trylock(&mm->mmap_sem)) { + mm->locked_vm += npage; + up_write(&mm->mmap_sem); + mmput(mm); return; } @@ -170,11 +289,8 @@ static void vfio_lock_acct(long npage) * wouldn't need this silliness */ vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); - if (!vwork) - return; - mm = get_task_mm(current); - if (!mm) { - kfree(vwork); + if (!vwork) { + mmput(mm); return; } INIT_WORK(&vwork->work, vfio_lock_acct_bg); @@ -228,20 +344,36 @@ static int put_pfn(unsigned long pfn, int prot) return 0; } -static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) +static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + int prot, unsigned long *pfn) { struct page *page[1]; struct vm_area_struct *vma; - int ret = -EFAULT; + int ret; + + if (mm == current->mm) { + ret = get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), + page); + } else { + unsigned int flags = 0; + + if (prot & IOMMU_WRITE) + flags |= FOLL_WRITE; + + down_read(&mm->mmap_sem); + ret = get_user_pages_remote(NULL, mm, vaddr, 1, flags, page, + NULL); + up_read(&mm->mmap_sem); + } - if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) { + if (ret == 1) { *pfn = page_to_pfn(page[0]); return 0; } - down_read(¤t->mm->mmap_sem); + down_read(&mm->mmap_sem); - vma = find_vma_intersection(current->mm, vaddr, vaddr + 1); + vma = find_vma_intersection(mm, vaddr, vaddr + 1); if (vma && vma->vm_flags & VM_PFNMAP) { *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; @@ -249,8 +381,7 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) ret = 0; } - up_read(¤t->mm->mmap_sem); - + up_read(&mm->mmap_sem); return ret; } @@ -259,88 +390,299 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) * the iommu can only map chunks of consecutive pfns anyway, so get the * first page and all consecutive pages with the same locking. */ -static long vfio_pin_pages(unsigned long vaddr, long npage, - int prot, unsigned long *pfn_base) +static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + long npage, unsigned long *pfn_base) { - unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - bool lock_cap = capable(CAP_IPC_LOCK); - long ret, i; + unsigned long limit; + bool lock_cap = ns_capable(task_active_pid_ns(dma->task)->user_ns, + CAP_IPC_LOCK); + struct mm_struct *mm; + long ret, i = 0, lock_acct = 0; bool rsvd; + dma_addr_t iova = vaddr - dma->vaddr + dma->iova; - if (!current->mm) + mm = get_task_mm(dma->task); + if (!mm) return -ENODEV; - ret = vaddr_get_pfn(vaddr, prot, pfn_base); + ret = vaddr_get_pfn(mm, vaddr, dma->prot, pfn_base); if (ret) - return ret; + goto pin_pg_remote_exit; rsvd = is_invalid_reserved_pfn(*pfn_base); + limit = task_rlimit(dma->task, RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (!rsvd && !lock_cap && current->mm->locked_vm + 1 > limit) { - put_pfn(*pfn_base, prot); - pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, - limit << PAGE_SHIFT); - return -ENOMEM; + /* + * Reserved pages aren't counted against the user, externally pinned + * pages are already counted against the user. + */ + if (!rsvd && !vfio_find_vpfn(dma, iova)) { + if (!lock_cap && mm->locked_vm + 1 > limit) { + put_pfn(*pfn_base, dma->prot); + pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, + limit << PAGE_SHIFT); + ret = -ENOMEM; + goto pin_pg_remote_exit; + } + lock_acct++; } - if (unlikely(disable_hugepages)) { - if (!rsvd) - vfio_lock_acct(1); - return 1; - } + i++; + if (likely(!disable_hugepages)) { + /* Lock all the consecutive pages from pfn_base */ + for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; i < npage; + i++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) { + unsigned long pfn = 0; - /* Lock all the consecutive pages from pfn_base */ - for (i = 1, vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) { - unsigned long pfn = 0; + ret = vaddr_get_pfn(mm, vaddr, dma->prot, &pfn); + if (ret) + break; - ret = vaddr_get_pfn(vaddr, prot, &pfn); - if (ret) - break; + if (pfn != *pfn_base + i || + rsvd != is_invalid_reserved_pfn(pfn)) { + put_pfn(pfn, dma->prot); + break; + } - if (pfn != *pfn_base + i || - rsvd != is_invalid_reserved_pfn(pfn)) { - put_pfn(pfn, prot); - break; + if (!rsvd && !vfio_find_vpfn(dma, iova)) { + if (!lock_cap && + mm->locked_vm + lock_acct + 1 > limit) { + put_pfn(pfn, dma->prot); + pr_warn("%s: RLIMIT_MEMLOCK (%ld) " + "exceeded\n", __func__, + limit << PAGE_SHIFT); + break; + } + lock_acct++; + } } + } - if (!rsvd && !lock_cap && - current->mm->locked_vm + i + 1 > limit) { - put_pfn(pfn, prot); - pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", - __func__, limit << PAGE_SHIFT); - break; + vfio_lock_acct(dma->task, lock_acct); + ret = i; + +pin_pg_remote_exit: + mmput(mm); + return ret; +} + +static long vfio_unpin_pages_remote(struct vfio_dma *dma, dma_addr_t iova, + unsigned long pfn, long npage, + bool do_accounting) +{ + long unlocked = 0, locked = 0; + long i; + + for (i = 0; i < npage; i++) { + if (put_pfn(pfn++, dma->prot)) { + unlocked++; + if (vfio_find_vpfn(dma, iova + (i << PAGE_SHIFT))) + locked++; } } - if (!rsvd) - vfio_lock_acct(i); + if (do_accounting) + vfio_lock_acct(dma->task, locked - unlocked); - return i; + return unlocked; } -static long vfio_unpin_pages(unsigned long pfn, long npage, - int prot, bool do_accounting) +static int vfio_pin_page_external(struct vfio_dma *dma, unsigned long vaddr, + unsigned long *pfn_base, bool do_accounting) { - unsigned long unlocked = 0; - long i; + unsigned long limit; + bool lock_cap = ns_capable(task_active_pid_ns(dma->task)->user_ns, + CAP_IPC_LOCK); + struct mm_struct *mm; + int ret; + bool rsvd; + + mm = get_task_mm(dma->task); + if (!mm) + return -ENODEV; + + ret = vaddr_get_pfn(mm, vaddr, dma->prot, pfn_base); + if (ret) + goto pin_page_exit; + + rsvd = is_invalid_reserved_pfn(*pfn_base); + limit = task_rlimit(dma->task, RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + if (!rsvd && !lock_cap && mm->locked_vm + 1 > limit) { + put_pfn(*pfn_base, dma->prot); + pr_warn("%s: Task %s (%d) RLIMIT_MEMLOCK (%ld) exceeded\n", + __func__, dma->task->comm, task_pid_nr(dma->task), + limit << PAGE_SHIFT); + ret = -ENOMEM; + goto pin_page_exit; + } - for (i = 0; i < npage; i++) - unlocked += put_pfn(pfn++, prot); + if (!rsvd && do_accounting) + vfio_lock_acct(dma->task, 1); + ret = 1; + +pin_page_exit: + mmput(mm); + return ret; +} + +static int vfio_unpin_page_external(struct vfio_dma *dma, dma_addr_t iova, + bool do_accounting) +{ + int unlocked; + struct vfio_pfn *vpfn = vfio_find_vpfn(dma, iova); + + if (!vpfn) + return 0; + + unlocked = vfio_iova_put_vfio_pfn(dma, vpfn); if (do_accounting) - vfio_lock_acct(-unlocked); + vfio_lock_acct(dma->task, -unlocked); return unlocked; } -static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma) +static int vfio_iommu_type1_pin_pages(void *iommu_data, + unsigned long *user_pfn, + int npage, int prot, + unsigned long *phys_pfn) +{ + struct vfio_iommu *iommu = iommu_data; + int i, j, ret; + unsigned long remote_vaddr; + struct vfio_dma *dma; + bool do_accounting; + + if (!iommu || !user_pfn || !phys_pfn) + return -EINVAL; + + /* Supported for v2 version only */ + if (!iommu->v2) + return -EACCES; + + mutex_lock(&iommu->lock); + + /* Fail if notifier list is empty */ + if ((!iommu->external_domain) || (!iommu->notifier.head)) { + ret = -EINVAL; + goto pin_done; + } + + /* + * If iommu capable domain exist in the container then all pages are + * already pinned and accounted. Accouting should be done if there is no + * iommu capable domain in the container. + */ + do_accounting = !IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu); + + for (i = 0; i < npage; i++) { + dma_addr_t iova; + struct vfio_pfn *vpfn; + + iova = user_pfn[i] << PAGE_SHIFT; + dma = vfio_find_dma(iommu, iova, 0); + if (!dma) { + ret = -EINVAL; + goto pin_unwind; + } + + if ((dma->prot & prot) != prot) { + ret = -EPERM; + goto pin_unwind; + } + + vpfn = vfio_iova_get_vfio_pfn(dma, iova); + if (vpfn) { + phys_pfn[i] = vpfn->pfn; + continue; + } + + remote_vaddr = dma->vaddr + iova - dma->iova; + ret = vfio_pin_page_external(dma, remote_vaddr, &phys_pfn[i], + do_accounting); + if (ret <= 0) { + WARN_ON(!ret); + goto pin_unwind; + } + + ret = vfio_add_to_pfn_list(dma, iova, phys_pfn[i]); + if (ret) { + vfio_unpin_page_external(dma, iova, do_accounting); + goto pin_unwind; + } + } + + ret = i; + goto pin_done; + +pin_unwind: + phys_pfn[i] = 0; + for (j = 0; j < i; j++) { + dma_addr_t iova; + + iova = user_pfn[j] << PAGE_SHIFT; + dma = vfio_find_dma(iommu, iova, 0); + vfio_unpin_page_external(dma, iova, do_accounting); + phys_pfn[j] = 0; + } +pin_done: + mutex_unlock(&iommu->lock); + return ret; +} + +static int vfio_iommu_type1_unpin_pages(void *iommu_data, + unsigned long *user_pfn, + int npage) +{ + struct vfio_iommu *iommu = iommu_data; + bool do_accounting; + int i; + + if (!iommu || !user_pfn) + return -EINVAL; + + /* Supported for v2 version only */ + if (!iommu->v2) + return -EACCES; + + mutex_lock(&iommu->lock); + + if (!iommu->external_domain) { + mutex_unlock(&iommu->lock); + return -EINVAL; + } + + do_accounting = !IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu); + for (i = 0; i < npage; i++) { + struct vfio_dma *dma; + dma_addr_t iova; + + iova = user_pfn[i] << PAGE_SHIFT; + dma = vfio_find_dma(iommu, iova, 0); + if (!dma) + goto unpin_exit; + vfio_unpin_page_external(dma, iova, do_accounting); + } + +unpin_exit: + mutex_unlock(&iommu->lock); + return i > npage ? npage : (i > 0 ? i : -EINVAL); +} + +static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, + bool do_accounting) { dma_addr_t iova = dma->iova, end = dma->iova + dma->size; struct vfio_domain *domain, *d; long unlocked = 0; if (!dma->size) - return; + return 0; + + if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) + return 0; + /* * We use the IOMMU to track the physical addresses, otherwise we'd * need a much more complicated tracking system. Unfortunately that @@ -382,21 +724,28 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma) if (WARN_ON(!unmapped)) break; - unlocked += vfio_unpin_pages(phys >> PAGE_SHIFT, - unmapped >> PAGE_SHIFT, - dma->prot, false); + unlocked += vfio_unpin_pages_remote(dma, iova, + phys >> PAGE_SHIFT, + unmapped >> PAGE_SHIFT, + false); iova += unmapped; cond_resched(); } - vfio_lock_acct(-unlocked); + dma->iommu_mapped = false; + if (do_accounting) { + vfio_lock_acct(dma->task, -unlocked); + return 0; + } + return unlocked; } static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) { - vfio_unmap_unpin(iommu, dma); + vfio_unmap_unpin(iommu, dma, true); vfio_unlink_dma(iommu, dma); + put_task_struct(dma->task); kfree(dma); } @@ -430,9 +779,9 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu, struct vfio_iommu_type1_dma_unmap *unmap) { uint64_t mask; - struct vfio_dma *dma; + struct vfio_dma *dma, *dma_last = NULL; size_t unmapped = 0; - int ret = 0; + int ret = 0, retries = 0; mask = ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1; @@ -442,7 +791,7 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu, return -EINVAL; WARN_ON(mask & PAGE_MASK); - +again: mutex_lock(&iommu->lock); /* @@ -492,6 +841,38 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu, while ((dma = vfio_find_dma(iommu, unmap->iova, unmap->size))) { if (!iommu->v2 && unmap->iova > dma->iova) break; + /* + * Task with same address space who mapped this iova range is + * allowed to unmap the iova range. + */ + if (dma->task->mm != current->mm) + break; + + if (!RB_EMPTY_ROOT(&dma->pfn_list)) { + struct vfio_iommu_type1_dma_unmap nb_unmap; + + if (dma_last == dma) { + BUG_ON(++retries > 10); + } else { + dma_last = dma; + retries = 0; + } + + nb_unmap.iova = dma->iova; + nb_unmap.size = dma->size; + + /* + * Notify anyone (mdev vendor drivers) to invalidate and + * unmap iovas within the range we're about to unmap. + * Vendor drivers MUST unpin pages in response to an + * invalidation. + */ + mutex_unlock(&iommu->lock); + blocking_notifier_call_chain(&iommu->notifier, + VFIO_IOMMU_NOTIFY_DMA_UNMAP, + &nb_unmap); + goto again; + } unmapped += dma->size; vfio_remove_dma(iommu, dma); } @@ -558,17 +939,56 @@ static int vfio_iommu_map(struct vfio_iommu *iommu, dma_addr_t iova, return ret; } +static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma, + size_t map_size) +{ + dma_addr_t iova = dma->iova; + unsigned long vaddr = dma->vaddr; + size_t size = map_size; + long npage; + unsigned long pfn; + int ret = 0; + + while (size) { + /* Pin a contiguous chunk of memory */ + npage = vfio_pin_pages_remote(dma, vaddr + dma->size, + size >> PAGE_SHIFT, &pfn); + if (npage <= 0) { + WARN_ON(!npage); + ret = (int)npage; + break; + } + + /* Map it! */ + ret = vfio_iommu_map(iommu, iova + dma->size, pfn, npage, + dma->prot); + if (ret) { + vfio_unpin_pages_remote(dma, iova + dma->size, pfn, + npage, true); + break; + } + + size -= npage << PAGE_SHIFT; + dma->size += npage << PAGE_SHIFT; + } + + dma->iommu_mapped = true; + + if (ret) + vfio_remove_dma(iommu, dma); + + return ret; +} + static int vfio_dma_do_map(struct vfio_iommu *iommu, struct vfio_iommu_type1_dma_map *map) { dma_addr_t iova = map->iova; unsigned long vaddr = map->vaddr; size_t size = map->size; - long npage; int ret = 0, prot = 0; uint64_t mask; struct vfio_dma *dma; - unsigned long pfn; /* Verify that none of our __u64 fields overflow */ if (map->size != size || map->vaddr != vaddr || map->iova != iova) @@ -594,47 +1014,33 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, mutex_lock(&iommu->lock); if (vfio_find_dma(iommu, iova, size)) { - mutex_unlock(&iommu->lock); - return -EEXIST; + ret = -EEXIST; + goto out_unlock; } dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (!dma) { - mutex_unlock(&iommu->lock); - return -ENOMEM; + ret = -ENOMEM; + goto out_unlock; } dma->iova = iova; dma->vaddr = vaddr; dma->prot = prot; + get_task_struct(current); + dma->task = current; + dma->pfn_list = RB_ROOT; /* Insert zero-sized and grow as we map chunks of it */ vfio_link_dma(iommu, dma); - while (size) { - /* Pin a contiguous chunk of memory */ - npage = vfio_pin_pages(vaddr + dma->size, - size >> PAGE_SHIFT, prot, &pfn); - if (npage <= 0) { - WARN_ON(!npage); - ret = (int)npage; - break; - } - - /* Map it! */ - ret = vfio_iommu_map(iommu, iova + dma->size, pfn, npage, prot); - if (ret) { - vfio_unpin_pages(pfn, npage, prot, true); - break; - } - - size -= npage << PAGE_SHIFT; - dma->size += npage << PAGE_SHIFT; - } - - if (ret) - vfio_remove_dma(iommu, dma); + /* Don't pin and map if container doesn't contain IOMMU capable domain*/ + if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) + dma->size = size; + else + ret = vfio_pin_map_dma(iommu, dma, size); +out_unlock: mutex_unlock(&iommu->lock); return ret; } @@ -662,10 +1068,6 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, d = list_first_entry(&iommu->domain_list, struct vfio_domain, next); n = rb_first(&iommu->dma_list); - /* If there's not a domain, there better not be any mappings */ - if (WARN_ON(n && !d)) - return -EINVAL; - for (; n; n = rb_next(n)) { struct vfio_dma *dma; dma_addr_t iova; @@ -674,21 +1076,49 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, iova = dma->iova; while (iova < dma->iova + dma->size) { - phys_addr_t phys = iommu_iova_to_phys(d->domain, iova); + phys_addr_t phys; size_t size; - if (WARN_ON(!phys)) { - iova += PAGE_SIZE; - continue; + if (dma->iommu_mapped) { + phys_addr_t p; + dma_addr_t i; + + phys = iommu_iova_to_phys(d->domain, iova); + + if (WARN_ON(!phys)) { + iova += PAGE_SIZE; + continue; + } + + size = PAGE_SIZE; + p = phys + size; + i = iova + size; + while (i < dma->iova + dma->size && + p == iommu_iova_to_phys(d->domain, i)) { + size += PAGE_SIZE; + p += PAGE_SIZE; + i += PAGE_SIZE; + } + } else { + unsigned long pfn; + unsigned long vaddr = dma->vaddr + + (iova - dma->iova); + size_t n = dma->iova + dma->size - iova; + long npage; + + npage = vfio_pin_pages_remote(dma, vaddr, + n >> PAGE_SHIFT, + &pfn); + if (npage <= 0) { + WARN_ON(!npage); + ret = (int)npage; + return ret; + } + + phys = pfn << PAGE_SHIFT; + size = npage << PAGE_SHIFT; } - size = PAGE_SIZE; - - while (iova + size < dma->iova + dma->size && - phys + size == iommu_iova_to_phys(d->domain, - iova + size)) - size += PAGE_SIZE; - ret = iommu_map(domain->domain, iova, phys, size, dma->prot | domain->prot); if (ret) @@ -696,8 +1126,8 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, iova += size; } + dma->iommu_mapped = true; } - return 0; } @@ -734,22 +1164,39 @@ static void vfio_test_domain_fgsp(struct vfio_domain *domain) __free_pages(pages, order); } +static struct vfio_group *find_iommu_group(struct vfio_domain *domain, + struct iommu_group *iommu_group) +{ + struct vfio_group *g; + + list_for_each_entry(g, &domain->group_list, next) { + if (g->iommu_group == iommu_group) + return g; + } + + return NULL; +} + static int vfio_iommu_type1_attach_group(void *iommu_data, struct iommu_group *iommu_group) { struct vfio_iommu *iommu = iommu_data; - struct vfio_group *group, *g; + struct vfio_group *group; struct vfio_domain *domain, *d; - struct bus_type *bus = NULL; + struct bus_type *bus = NULL, *mdev_bus; int ret; mutex_lock(&iommu->lock); list_for_each_entry(d, &iommu->domain_list, next) { - list_for_each_entry(g, &d->group_list, next) { - if (g->iommu_group != iommu_group) - continue; + if (find_iommu_group(d, iommu_group)) { + mutex_unlock(&iommu->lock); + return -EINVAL; + } + } + if (iommu->external_domain) { + if (find_iommu_group(iommu->external_domain, iommu_group)) { mutex_unlock(&iommu->lock); return -EINVAL; } @@ -769,6 +1216,25 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, if (ret) goto out_free; + mdev_bus = symbol_get(mdev_bus_type); + + if (mdev_bus) { + if ((bus == mdev_bus) && !iommu_present(bus)) { + symbol_put(mdev_bus_type); + if (!iommu->external_domain) { + INIT_LIST_HEAD(&domain->group_list); + iommu->external_domain = domain; + } else + kfree(domain); + + list_add(&group->next, + &iommu->external_domain->group_list); + mutex_unlock(&iommu->lock); + return 0; + } + symbol_put(mdev_bus_type); + } + domain->domain = iommu_domain_alloc(bus); if (!domain->domain) { ret = -EIO; @@ -859,6 +1325,44 @@ static void vfio_iommu_unmap_unpin_all(struct vfio_iommu *iommu) vfio_remove_dma(iommu, rb_entry(node, struct vfio_dma, node)); } +static void vfio_iommu_unmap_unpin_reaccount(struct vfio_iommu *iommu) +{ + struct rb_node *n, *p; + + n = rb_first(&iommu->dma_list); + for (; n; n = rb_next(n)) { + struct vfio_dma *dma; + long locked = 0, unlocked = 0; + + dma = rb_entry(n, struct vfio_dma, node); + unlocked += vfio_unmap_unpin(iommu, dma, false); + p = rb_first(&dma->pfn_list); + for (; p; p = rb_next(p)) { + struct vfio_pfn *vpfn = rb_entry(p, struct vfio_pfn, + node); + + if (!is_invalid_reserved_pfn(vpfn->pfn)) + locked++; + } + vfio_lock_acct(dma->task, locked - unlocked); + } +} + +static void vfio_sanity_check_pfn_list(struct vfio_iommu *iommu) +{ + struct rb_node *n; + + n = rb_first(&iommu->dma_list); + for (; n; n = rb_next(n)) { + struct vfio_dma *dma; + + dma = rb_entry(n, struct vfio_dma, node); + + if (WARN_ON(!RB_EMPTY_ROOT(&dma->pfn_list))) + break; + } +} + static void vfio_iommu_type1_detach_group(void *iommu_data, struct iommu_group *iommu_group) { @@ -868,31 +1372,55 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, mutex_lock(&iommu->lock); - list_for_each_entry(domain, &iommu->domain_list, next) { - list_for_each_entry(group, &domain->group_list, next) { - if (group->iommu_group != iommu_group) - continue; - - iommu_detach_group(domain->domain, iommu_group); + if (iommu->external_domain) { + group = find_iommu_group(iommu->external_domain, iommu_group); + if (group) { list_del(&group->next); kfree(group); - /* - * Group ownership provides privilege, if the group - * list is empty, the domain goes away. If it's the - * last domain, then all the mappings go away too. - */ - if (list_empty(&domain->group_list)) { - if (list_is_singular(&iommu->domain_list)) + + if (list_empty(&iommu->external_domain->group_list)) { + vfio_sanity_check_pfn_list(iommu); + + if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) vfio_iommu_unmap_unpin_all(iommu); - iommu_domain_free(domain->domain); - list_del(&domain->next); - kfree(domain); + + kfree(iommu->external_domain); + iommu->external_domain = NULL; + } + goto detach_group_done; + } + } + + list_for_each_entry(domain, &iommu->domain_list, next) { + group = find_iommu_group(domain, iommu_group); + if (!group) + continue; + + iommu_detach_group(domain->domain, iommu_group); + list_del(&group->next); + kfree(group); + /* + * Group ownership provides privilege, if the group list is + * empty, the domain goes away. If it's the last domain with + * iommu and external domain doesn't exist, then all the + * mappings go away too. If it's the last domain with iommu and + * external domain exist, update accounting + */ + if (list_empty(&domain->group_list)) { + if (list_is_singular(&iommu->domain_list)) { + if (!iommu->external_domain) + vfio_iommu_unmap_unpin_all(iommu); + else + vfio_iommu_unmap_unpin_reaccount(iommu); } - goto done; + iommu_domain_free(domain->domain); + list_del(&domain->next); + kfree(domain); } + break; } -done: +detach_group_done: mutex_unlock(&iommu->lock); } @@ -920,31 +1448,46 @@ static void *vfio_iommu_type1_open(unsigned long arg) INIT_LIST_HEAD(&iommu->domain_list); iommu->dma_list = RB_ROOT; mutex_init(&iommu->lock); + BLOCKING_INIT_NOTIFIER_HEAD(&iommu->notifier); return iommu; } +static void vfio_release_domain(struct vfio_domain *domain, bool external) +{ + struct vfio_group *group, *group_tmp; + + list_for_each_entry_safe(group, group_tmp, + &domain->group_list, next) { + if (!external) + iommu_detach_group(domain->domain, group->iommu_group); + list_del(&group->next); + kfree(group); + } + + if (!external) + iommu_domain_free(domain->domain); +} + static void vfio_iommu_type1_release(void *iommu_data) { struct vfio_iommu *iommu = iommu_data; struct vfio_domain *domain, *domain_tmp; - struct vfio_group *group, *group_tmp; + + if (iommu->external_domain) { + vfio_release_domain(iommu->external_domain, true); + vfio_sanity_check_pfn_list(iommu); + kfree(iommu->external_domain); + } vfio_iommu_unmap_unpin_all(iommu); list_for_each_entry_safe(domain, domain_tmp, &iommu->domain_list, next) { - list_for_each_entry_safe(group, group_tmp, - &domain->group_list, next) { - iommu_detach_group(domain->domain, group->iommu_group); - list_del(&group->next); - kfree(group); - } - iommu_domain_free(domain->domain); + vfio_release_domain(domain, false); list_del(&domain->next); kfree(domain); } - kfree(iommu); } @@ -1040,14 +1583,34 @@ static long vfio_iommu_type1_ioctl(void *iommu_data, return -ENOTTY; } +static int vfio_iommu_type1_register_notifier(void *iommu_data, + struct notifier_block *nb) +{ + struct vfio_iommu *iommu = iommu_data; + + return blocking_notifier_chain_register(&iommu->notifier, nb); +} + +static int vfio_iommu_type1_unregister_notifier(void *iommu_data, + struct notifier_block *nb) +{ + struct vfio_iommu *iommu = iommu_data; + + return blocking_notifier_chain_unregister(&iommu->notifier, nb); +} + static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = { - .name = "vfio-iommu-type1", - .owner = THIS_MODULE, - .open = vfio_iommu_type1_open, - .release = vfio_iommu_type1_release, - .ioctl = vfio_iommu_type1_ioctl, - .attach_group = vfio_iommu_type1_attach_group, - .detach_group = vfio_iommu_type1_detach_group, + .name = "vfio-iommu-type1", + .owner = THIS_MODULE, + .open = vfio_iommu_type1_open, + .release = vfio_iommu_type1_release, + .ioctl = vfio_iommu_type1_ioctl, + .attach_group = vfio_iommu_type1_attach_group, + .detach_group = vfio_iommu_type1_detach_group, + .pin_pages = vfio_iommu_type1_pin_pages, + .unpin_pages = vfio_iommu_type1_unpin_pages, + .register_notifier = vfio_iommu_type1_register_notifier, + .unregister_notifier = vfio_iommu_type1_unregister_notifier, }; static int __init vfio_iommu_type1_init(void) diff --git a/fs/aio.c b/fs/aio.c index 1157e13a36d68..428484f2f8413 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1078,6 +1078,17 @@ static void aio_complete(struct kiocb *kiocb, long res, long res2) unsigned tail, pos, head; unsigned long flags; + if (kiocb->ki_flags & IOCB_WRITE) { + struct file *file = kiocb->ki_filp; + + /* + * Tell lockdep we inherited freeze protection from submission + * thread. + */ + __sb_writers_acquired(file_inode(file)->i_sb, SB_FREEZE_WRITE); + file_end_write(file); + } + /* * Special case handling for sync iocbs: * - events go directly into the iocb for fast handling @@ -1392,122 +1403,106 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) return -EINVAL; } -typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); - -static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len, - struct iovec **iovec, - bool compat, - struct iov_iter *iter) +static int aio_setup_rw(int rw, struct iocb *iocb, struct iovec **iovec, + bool vectored, bool compat, struct iov_iter *iter) { + void __user *buf = (void __user *)(uintptr_t)iocb->aio_buf; + size_t len = iocb->aio_nbytes; + + if (!vectored) { + ssize_t ret = import_single_range(rw, buf, len, *iovec, iter); + *iovec = NULL; + return ret; + } #ifdef CONFIG_COMPAT if (compat) - return compat_import_iovec(rw, - (struct compat_iovec __user *)buf, - len, UIO_FASTIOV, iovec, iter); + return compat_import_iovec(rw, buf, len, UIO_FASTIOV, iovec, + iter); #endif - return import_iovec(rw, (struct iovec __user *)buf, - len, UIO_FASTIOV, iovec, iter); + return import_iovec(rw, buf, len, UIO_FASTIOV, iovec, iter); } -/* - * aio_run_iocb: - * Performs the initial checks and io submission. - */ -static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, - char __user *buf, size_t len, bool compat) +static inline ssize_t aio_ret(struct kiocb *req, ssize_t ret) +{ + switch (ret) { + case -EIOCBQUEUED: + return ret; + case -ERESTARTSYS: + case -ERESTARTNOINTR: + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * There's no easy way to restart the syscall since other AIO's + * may be already running. Just fail this IO with EINTR. + */ + ret = -EINTR; + /*FALLTHRU*/ + default: + aio_complete(req, ret, 0); + return 0; + } +} + +static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored, + bool compat) { struct file *file = req->ki_filp; - ssize_t ret; - int rw; - fmode_t mode; - rw_iter_op *iter_op; struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct iov_iter iter; + ssize_t ret; - switch (opcode) { - case IOCB_CMD_PREAD: - case IOCB_CMD_PREADV: - mode = FMODE_READ; - rw = READ; - iter_op = file->f_op->read_iter; - goto rw_common; - - case IOCB_CMD_PWRITE: - case IOCB_CMD_PWRITEV: - mode = FMODE_WRITE; - rw = WRITE; - iter_op = file->f_op->write_iter; - goto rw_common; -rw_common: - if (unlikely(!(file->f_mode & mode))) - return -EBADF; - - if (!iter_op) - return -EINVAL; - - if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) - ret = aio_setup_vectored_rw(rw, buf, len, - &iovec, compat, &iter); - else { - ret = import_single_range(rw, buf, len, iovec, &iter); - iovec = NULL; - } - if (!ret) - ret = rw_verify_area(rw, file, &req->ki_pos, - iov_iter_count(&iter)); - if (ret < 0) { - kfree(iovec); - return ret; - } - - if (rw == WRITE) - file_start_write(file); - - ret = iter_op(req, &iter); - - if (rw == WRITE) - file_end_write(file); - kfree(iovec); - break; - - case IOCB_CMD_FDSYNC: - if (!file->f_op->aio_fsync) - return -EINVAL; - - ret = file->f_op->aio_fsync(req, 1); - break; + if (unlikely(!(file->f_mode & FMODE_READ))) + return -EBADF; + if (unlikely(!file->f_op->read_iter)) + return -EINVAL; - case IOCB_CMD_FSYNC: - if (!file->f_op->aio_fsync) - return -EINVAL; + ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter); + if (ret) + return ret; + ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) + ret = aio_ret(req, file->f_op->read_iter(req, &iter)); + kfree(iovec); + return ret; +} - ret = file->f_op->aio_fsync(req, 0); - break; +static ssize_t aio_write(struct kiocb *req, struct iocb *iocb, bool vectored, + bool compat) +{ + struct file *file = req->ki_filp; + struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; + struct iov_iter iter; + ssize_t ret; - default: - pr_debug("EINVAL: no operation provided\n"); + if (unlikely(!(file->f_mode & FMODE_WRITE))) + return -EBADF; + if (unlikely(!file->f_op->write_iter)) return -EINVAL; - } - if (ret != -EIOCBQUEUED) { + ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter); + if (ret) + return ret; + ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) { + req->ki_flags |= IOCB_WRITE; + file_start_write(file); + ret = aio_ret(req, file->f_op->write_iter(req, &iter)); /* - * There's no easy way to restart the syscall since other AIO's - * may be already running. Just fail this IO with EINTR. + * We release freeze protection in aio_complete(). Fool lockdep + * by telling it the lock got released so that it doesn't + * complain about held lock when we return to userspace. */ - if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR || - ret == -ERESTARTNOHAND || - ret == -ERESTART_RESTARTBLOCK)) - ret = -EINTR; - aio_complete(req, ret, 0); + __sb_writers_release(file_inode(file)->i_sb, SB_FREEZE_WRITE); } - - return 0; + kfree(iovec); + return ret; } static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb, bool compat) { struct aio_kiocb *req; + struct file *file; ssize_t ret; /* enforce forwards compatibility on users */ @@ -1530,7 +1525,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, if (unlikely(!req)) return -EAGAIN; - req->common.ki_filp = fget(iocb->aio_fildes); + req->common.ki_filp = file = fget(iocb->aio_fildes); if (unlikely(!req->common.ki_filp)) { ret = -EBADF; goto out_put_req; @@ -1565,13 +1560,29 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_user_iocb = user_iocb; req->ki_user_data = iocb->aio_data; - ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, - (char __user *)(unsigned long)iocb->aio_buf, - iocb->aio_nbytes, - compat); - if (ret) - goto out_put_req; + get_file(file); + switch (iocb->aio_lio_opcode) { + case IOCB_CMD_PREAD: + ret = aio_read(&req->common, iocb, false, compat); + break; + case IOCB_CMD_PWRITE: + ret = aio_write(&req->common, iocb, false, compat); + break; + case IOCB_CMD_PREADV: + ret = aio_read(&req->common, iocb, true, compat); + break; + case IOCB_CMD_PWRITEV: + ret = aio_write(&req->common, iocb, true, compat); + break; + default: + pr_debug("invalid aio operation %d\n", iocb->aio_lio_opcode); + ret = -EINVAL; + break; + } + fput(file); + if (ret && ret != -EIOCBQUEUED) + goto out_put_req; return 0; out_put_req: put_reqs_available(ctx, 1); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 18630e8002080..f995e3528a331 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -1770,7 +1770,6 @@ const struct file_operations ceph_file_fops = { .fsync = ceph_fsync, .lock = ceph_lock, .flock = ceph_flock, - .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .unlocked_ioctl = ceph_ioctl, .compat_ioctl = ceph_ioctl, diff --git a/fs/coredump.c b/fs/coredump.c index 281b768000e66..eb9c92c9b20f5 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -423,7 +424,9 @@ static int coredump_wait(int exit_code, struct core_state *core_state) if (core_waiters > 0) { struct core_thread *ptr; + freezer_do_not_count(); wait_for_completion(&core_state->startup); + freezer_count(); /* * Wait for all the threads to become inactive, so that * all the thread context (extended register state, like diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 7555ba889d1fc..ebecfb8fba067 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -314,7 +314,8 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat /* Match the full socket address */ if (!rpc_cmp_addr_port(sap, clap)) /* Match all xprt_switch full socket addresses */ - if (!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, + if (IS_ERR(clp->cl_rpcclient) || + !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, sap)) continue; diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index c8162c660c440..5551e8ef67fd0 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -98,7 +98,7 @@ char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen, return end; } namelen = strlen(base); - if (flags & NFS_PATH_CANONICAL) { + if (*end == '/') { /* Strip off excess slashes in base string */ while (namelen > 0 && base[namelen - 1] == '/') namelen--; diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index b62973045a3e0..a61350f75c741 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -178,12 +178,14 @@ static int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid, __must_hold(&tbl->slot_tbl_lock) { struct nfs4_slot *slot; + int ret; slot = nfs4_lookup_slot(tbl, slotid); - if (IS_ERR(slot)) - return PTR_ERR(slot); - *seq_nr = slot->seq_nr; - return 0; + ret = PTR_ERR_OR_ZERO(slot); + if (!ret) + *seq_nr = slot->seq_nr; + + return ret; } /* @@ -196,7 +198,7 @@ static int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid, static bool nfs4_slot_seqid_in_use(struct nfs4_slot_table *tbl, u32 slotid, u32 seq_nr) { - u32 cur_seq; + u32 cur_seq = 0; bool ret = false; spin_lock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 56b2d96f9103e..259ef85f435aa 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -146,6 +146,8 @@ set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh, u32 id; int i; + if (fsinfo->nlayouttypes == 0) + goto out_no_driver; if (!(server->nfs_client->cl_exchange_flags & (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) { printk(KERN_ERR "NFS: %s: cl_exchange_flags 0x%x\n", diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index a186135790012..0ee19ecc982d4 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1544,8 +1544,6 @@ const struct file_operations ntfs_dir_ops = { .iterate = ntfs_readdir, /* Read directory contents. */ #ifdef NTFS_RW .fsync = ntfs_dir_fsync, /* Sync a directory to disk. */ - /*.aio_fsync = ,*/ /* Sync all outstanding async - i/o operations on a kiocb. */ #endif /* NTFS_RW */ /*.ioctl = ,*/ /* Perform function on the mounted filesystem. */ diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index e7054e2ac9227..3ecb9f337b7d3 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -3699,7 +3699,7 @@ static void ocfs2_dx_dir_transfer_leaf(struct inode *dir, u32 split_hash, static int ocfs2_dx_dir_rebalance_credits(struct ocfs2_super *osb, struct ocfs2_dx_root_block *dx_root) { - int credits = ocfs2_clusters_to_blocks(osb->sb, 2); + int credits = ocfs2_clusters_to_blocks(osb->sb, 3); credits += ocfs2_calc_extend_credits(osb->sb, &dx_root->dr_list); credits += ocfs2_quota_trans_credits(osb->sb); diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index eb09aa0267238..d484068ca7160 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -141,6 +141,9 @@ static struct client_debug_mask client_debug_mask; */ static DEFINE_MUTEX(orangefs_debug_lock); +/* Used to protect data in ORANGEFS_KMOD_DEBUG_HELP_FILE */ +static DEFINE_MUTEX(orangefs_help_file_lock); + /* * initialize kmod debug operations, create orangefs debugfs dir and * ORANGEFS_KMOD_DEBUG_HELP_FILE. @@ -289,6 +292,8 @@ static void *help_start(struct seq_file *m, loff_t *pos) gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_start: start\n"); + mutex_lock(&orangefs_help_file_lock); + if (*pos == 0) payload = m->private; @@ -305,6 +310,7 @@ static void *help_next(struct seq_file *m, void *v, loff_t *pos) static void help_stop(struct seq_file *m, void *p) { gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_stop: start\n"); + mutex_unlock(&orangefs_help_file_lock); } static int help_show(struct seq_file *m, void *v) @@ -610,32 +616,54 @@ static int orangefs_prepare_cdm_array(char *debug_array_string) * /sys/kernel/debug/orangefs/debug-help can be catted to * see all the available kernel and client debug keywords. * - * When the kernel boots, we have no idea what keywords the + * When orangefs.ko initializes, we have no idea what keywords the * client supports, nor their associated masks. * - * We pass through this function once at boot and stamp a + * We pass through this function once at module-load and stamp a * boilerplate "we don't know" message for the client in the * debug-help file. We pass through here again when the client * starts and then we can fill out the debug-help file fully. * * The client might be restarted any number of times between - * reboots, we only build the debug-help file the first time. + * module reloads, we only build the debug-help file the first time. */ int orangefs_prepare_debugfs_help_string(int at_boot) { - int rc = -EINVAL; - int i; - int byte_count = 0; char *client_title = "Client Debug Keywords:\n"; char *kernel_title = "Kernel Debug Keywords:\n"; + size_t string_size = DEBUG_HELP_STRING_SIZE; + size_t result_size; + size_t i; + char *new; + int rc = -EINVAL; gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); - if (at_boot) { - byte_count += strlen(HELP_STRING_UNINITIALIZED); + if (at_boot) client_title = HELP_STRING_UNINITIALIZED; - } else { - /* + + /* build a new debug_help_string. */ + new = kzalloc(DEBUG_HELP_STRING_SIZE, GFP_KERNEL); + if (!new) { + rc = -ENOMEM; + goto out; + } + + /* + * strlcat(dst, src, size) will append at most + * "size - strlen(dst) - 1" bytes of src onto dst, + * null terminating the result, and return the total + * length of the string it tried to create. + * + * We'll just plow through here building our new debug + * help string and let strlcat take care of assuring that + * dst doesn't overflow. + */ + strlcat(new, client_title, string_size); + + if (!at_boot) { + + /* * fill the client keyword/mask array and remember * how many elements there were. */ @@ -644,64 +672,40 @@ int orangefs_prepare_debugfs_help_string(int at_boot) if (cdm_element_count <= 0) goto out; - /* Count the bytes destined for debug_help_string. */ - byte_count += strlen(client_title); - for (i = 0; i < cdm_element_count; i++) { - byte_count += strlen(cdm_array[i].keyword + 2); - if (byte_count >= DEBUG_HELP_STRING_SIZE) { - pr_info("%s: overflow 1!\n", __func__); - goto out; - } + strlcat(new, "\t", string_size); + strlcat(new, cdm_array[i].keyword, string_size); + strlcat(new, "\n", string_size); } - - gossip_debug(GOSSIP_UTILS_DEBUG, - "%s: cdm_element_count:%d:\n", - __func__, - cdm_element_count); } - byte_count += strlen(kernel_title); + strlcat(new, "\n", string_size); + strlcat(new, kernel_title, string_size); + for (i = 0; i < num_kmod_keyword_mask_map; i++) { - byte_count += - strlen(s_kmod_keyword_mask_map[i].keyword + 2); - if (byte_count >= DEBUG_HELP_STRING_SIZE) { - pr_info("%s: overflow 2!\n", __func__); - goto out; - } + strlcat(new, "\t", string_size); + strlcat(new, s_kmod_keyword_mask_map[i].keyword, string_size); + result_size = strlcat(new, "\n", string_size); } - /* build debug_help_string. */ - debug_help_string = kzalloc(DEBUG_HELP_STRING_SIZE, GFP_KERNEL); - if (!debug_help_string) { - rc = -ENOMEM; + /* See if we tried to put too many bytes into "new"... */ + if (result_size >= string_size) { + kfree(new); goto out; } - strcat(debug_help_string, client_title); - - if (!at_boot) { - for (i = 0; i < cdm_element_count; i++) { - strcat(debug_help_string, "\t"); - strcat(debug_help_string, cdm_array[i].keyword); - strcat(debug_help_string, "\n"); - } - } - - strcat(debug_help_string, "\n"); - strcat(debug_help_string, kernel_title); - - for (i = 0; i < num_kmod_keyword_mask_map; i++) { - strcat(debug_help_string, "\t"); - strcat(debug_help_string, s_kmod_keyword_mask_map[i].keyword); - strcat(debug_help_string, "\n"); + if (at_boot) { + debug_help_string = new; + } else { + mutex_lock(&orangefs_help_file_lock); + memset(debug_help_string, 0, DEBUG_HELP_STRING_SIZE); + strlcat(debug_help_string, new, string_size); + mutex_unlock(&orangefs_help_file_lock); } rc = 0; -out: - - return rc; +out: return rc; } @@ -959,8 +963,12 @@ int orangefs_debugfs_new_client_string(void __user *arg) ret = copy_from_user(&client_debug_array_string, (void __user *)arg, ORANGEFS_MAX_DEBUG_STRING_LEN); - if (ret != 0) + + if (ret != 0) { + pr_info("%s: CLIENT_STRING: copy_from_user failed\n", + __func__); return -EIO; + } /* * The real client-core makes an effort to ensure @@ -975,45 +983,18 @@ int orangefs_debugfs_new_client_string(void __user *arg) client_debug_array_string[ORANGEFS_MAX_DEBUG_STRING_LEN - 1] = '\0'; - if (ret != 0) { - pr_info("%s: CLIENT_STRING: copy_from_user failed\n", - __func__); - return -EIO; - } - pr_info("%s: client debug array string has been received.\n", __func__); if (!help_string_initialized) { - /* Free the "we don't know yet" default string... */ - kfree(debug_help_string); - - /* build a proper debug help string */ + /* Build a proper debug help string. */ if (orangefs_prepare_debugfs_help_string(0)) { gossip_err("%s: no debug help string \n", __func__); return -EIO; } - /* Replace the boilerplate boot-time debug-help file. */ - debugfs_remove(help_file_dentry); - - help_file_dentry = - debugfs_create_file( - ORANGEFS_KMOD_DEBUG_HELP_FILE, - 0444, - debug_dir, - debug_help_string, - &debug_help_fops); - - if (!help_file_dentry) { - gossip_err("%s: debugfs_create_file failed for" - " :%s:!\n", - __func__, - ORANGEFS_KMOD_DEBUG_HELP_FILE); - return -EIO; - } } debug_mask_to_string(&client_debug_mask, 1); diff --git a/fs/orangefs/orangefs-mod.c b/fs/orangefs/orangefs-mod.c index 2e5b03065f345..4113eb0495bf9 100644 --- a/fs/orangefs/orangefs-mod.c +++ b/fs/orangefs/orangefs-mod.c @@ -124,7 +124,7 @@ static int __init orangefs_init(void) * unknown at boot time. * * orangefs_prepare_debugfs_help_string will be used again - * later to rebuild the debug-help file after the client starts + * later to rebuild the debug-help-string after the client starts * and passes along the needed info. The argument signifies * which time orangefs_prepare_debugfs_help_string is being * called. @@ -152,7 +152,9 @@ static int __init orangefs_init(void) ret = register_filesystem(&orangefs_fs_type); if (ret == 0) { - pr_info("orangefs: module version %s loaded\n", ORANGEFS_VERSION); + pr_info("%s: module version %s loaded\n", + __func__, + ORANGEFS_VERSION); ret = 0; goto out; } diff --git a/fs/splice.c b/fs/splice.c index 153d4f3bd441f..dcaf185a57311 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -299,13 +299,8 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, { struct iov_iter to; struct kiocb kiocb; - loff_t isize; int idx, ret; - isize = i_size_read(in->f_mapping->host); - if (unlikely(*ppos >= isize)) - return 0; - iov_iter_pipe(&to, ITER_PIPE | READ, pipe, len); idx = to.idx; init_sync_kiocb(&kiocb, in); diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 613c5cf194364..5c2929f94bd3b 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -199,9 +199,9 @@ xfs_defer_intake_work( struct xfs_defer_pending *dfp; list_for_each_entry(dfp, &dop->dop_intake, dfp_list) { - trace_xfs_defer_intake_work(tp->t_mountp, dfp); dfp->dfp_intent = dfp->dfp_type->create_intent(tp, dfp->dfp_count); + trace_xfs_defer_intake_work(tp->t_mountp, dfp); list_sort(tp->t_mountp, &dfp->dfp_work, dfp->dfp_type->diff_items); list_for_each(li, &dfp->dfp_work) @@ -221,21 +221,14 @@ xfs_defer_trans_abort( struct xfs_defer_pending *dfp; trace_xfs_defer_trans_abort(tp->t_mountp, dop); - /* - * If the transaction was committed, drop the intent reference - * since we're bailing out of here. The other reference is - * dropped when the intent hits the AIL. If the transaction - * was not committed, the intent is freed by the intent item - * unlock handler on abort. - */ - if (!dop->dop_committed) - return; - /* Abort intent items. */ + /* Abort intent items that don't have a done item. */ list_for_each_entry(dfp, &dop->dop_pending, dfp_list) { trace_xfs_defer_pending_abort(tp->t_mountp, dfp); - if (!dfp->dfp_done) + if (dfp->dfp_intent && !dfp->dfp_done) { dfp->dfp_type->abort_intent(dfp->dfp_intent); + dfp->dfp_intent = NULL; + } } /* Shut down FS. */ diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 40e887068da21..0504ef8f3aa31 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -118,9 +118,9 @@ do { \ #define this_cpu_generic_read(pcp) \ ({ \ typeof(pcp) __ret; \ - preempt_disable(); \ + preempt_disable_notrace(); \ __ret = raw_cpu_generic_read(pcp); \ - preempt_enable(); \ + preempt_enable_notrace(); \ __ret; \ }) diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index af0254c094247..4df64a1fc09e7 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -14,6 +14,8 @@ * [_sdata, _edata]: contains .data.* sections, may also contain .rodata.* * and/or .init.* sections. * [__start_rodata, __end_rodata]: contains .rodata.* sections + * [__start_data_ro_after_init, __end_data_ro_after_init]: + * contains data.ro_after_init section * [__init_begin, __init_end]: contains .init.* sections, but .init.text.* * may be out of this range on some architectures. * [_sinittext, _einittext]: contains .init.text.* sections @@ -31,6 +33,7 @@ extern char _data[], _sdata[], _edata[]; extern char __bss_start[], __bss_stop[]; extern char __init_begin[], __init_end[]; extern char _sinittext[], _einittext[]; +extern char __start_data_ro_after_init[], __end_data_ro_after_init[]; extern char _end[]; extern char __per_cpu_load[], __per_cpu_start[], __per_cpu_end[]; extern char __kprobes_text_start[], __kprobes_text_end[]; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 30747960bc54a..31e1d639abeda 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -259,7 +259,10 @@ * own by defining an empty RO_AFTER_INIT_DATA. */ #ifndef RO_AFTER_INIT_DATA -#define RO_AFTER_INIT_DATA *(.data..ro_after_init) +#define RO_AFTER_INIT_DATA \ + __start_data_ro_after_init = .; \ + *(.data..ro_after_init) \ + __end_data_ro_after_init = .; #endif /* diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index f5ae1f436a4bd..b3141a0e609b7 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -149,12 +149,12 @@ struct drm_framebuffer { */ unsigned int offsets[4]; /** - * @modifier: Data layout modifier, per buffer. This is used to describe + * @modifier: Data layout modifier. This is used to describe * tiling, or also special layouts (like compression) of auxiliary * buffers. For userspace created object this is copied from * drm_mode_fb_cmd2. */ - uint64_t modifier[4]; + uint64_t modifier; /** * @width: Logical width of the visible area of the framebuffer, in * pixels. diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 689a8b9b9c8fd..61a3d90f32b33 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -555,7 +555,8 @@ int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); int acpi_device_modalias(struct device *, char *, int); void acpi_walk_dep_device_list(acpi_handle handle); -struct platform_device *acpi_create_platform_device(struct acpi_device *); +struct platform_device *acpi_create_platform_device(struct acpi_device *, + struct property_entry *); #define ACPI_PTR(_ptr) (_ptr) static inline void acpi_device_set_enumerated(struct acpi_device *adev) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 96337b15a60d5..a8e66344bacc2 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -258,6 +258,8 @@ struct ceph_watch_item { struct ceph_entity_addr addr; }; +#define CEPH_LINGER_ID_START 0xffff000000000000ULL + struct ceph_osd_client { struct ceph_client *client; diff --git a/include/linux/console.h b/include/linux/console.h index 3672809234a72..d530c4627e54e 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -173,12 +173,6 @@ static inline void console_sysfs_notify(void) #endif extern bool console_suspend_enabled; -#ifdef CONFIG_OF -extern void console_set_by_of(void); -#else -static inline void console_set_by_of(void) {} -#endif - /* Suspend and resume console messages over PM events */ extern void suspend_console(void); extern void resume_console(void); diff --git a/include/linux/frontswap.h b/include/linux/frontswap.h index c46d2aa16d812..1d18af0345543 100644 --- a/include/linux/frontswap.h +++ b/include/linux/frontswap.h @@ -106,8 +106,9 @@ static inline void frontswap_invalidate_area(unsigned type) static inline void frontswap_init(unsigned type, unsigned long *map) { - if (frontswap_enabled()) - __frontswap_init(type, map); +#ifdef CONFIG_FRONTSWAP + __frontswap_init(type, map); +#endif } #endif /* _LINUX_FRONTSWAP_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 16d2b6e874d67..dc0478c07b2ab 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -321,6 +321,7 @@ struct writeback_control; #define IOCB_HIPRI (1 << 3) #define IOCB_DSYNC (1 << 4) #define IOCB_SYNC (1 << 5) +#define IOCB_WRITE (1 << 6) struct kiocb { struct file *ki_filp; @@ -1709,7 +1710,6 @@ struct file_operations { int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); - int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); diff --git a/include/linux/mdev.h b/include/linux/mdev.h new file mode 100644 index 0000000000000..ec819e9a115af --- /dev/null +++ b/include/linux/mdev.h @@ -0,0 +1,168 @@ +/* + * Mediated device definition + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MDEV_H +#define MDEV_H + +/* Parent device */ +struct parent_device { + struct device *dev; + const struct parent_ops *ops; + + /* internal */ + struct kref ref; + struct mutex lock; + struct list_head next; + struct kset *mdev_types_kset; + struct list_head type_list; +}; + +/* Mediated device */ +struct mdev_device { + struct device dev; + struct parent_device *parent; + uuid_le uuid; + void *driver_data; + + /* internal */ + struct kref ref; + struct list_head next; + struct kobject *type_kobj; +}; + +/** + * struct parent_ops - Structure to be registered for each parent device to + * register the device to mdev module. + * + * @owner: The module owner. + * @dev_attr_groups: Attributes of the parent device. + * @mdev_attr_groups: Attributes of the mediated device. + * @supported_type_groups: Attributes to define supported types. It is mandatory + * to provide supported types. + * @create: Called to allocate basic resources in parent device's + * driver for a particular mediated device. It is + * mandatory to provide create ops. + * @kobj: kobject of type for which 'create' is called. + * @mdev: mdev_device structure on of mediated device + * that is being created + * Returns integer: success (0) or error (< 0) + * @remove: Called to free resources in parent device's driver for a + * a mediated device. It is mandatory to provide 'remove' + * ops. + * @mdev: mdev_device device structure which is being + * destroyed + * Returns integer: success (0) or error (< 0) + * @open: Open mediated device. + * @mdev: mediated device. + * Returns integer: success (0) or error (< 0) + * @release: release mediated device + * @mdev: mediated device. + * @read: Read emulation callback + * @mdev: mediated device structure + * @buf: read buffer + * @count: number of bytes to read + * @ppos: address. + * Retuns number on bytes read on success or error. + * @write: Write emulation callback + * @mdev: mediated device structure + * @buf: write buffer + * @count: number of bytes to be written + * @ppos: address. + * Retuns number on bytes written on success or error. + * @ioctl: IOCTL callback + * @mdev: mediated device structure + * @cmd: ioctl command + * @arg: arguments to ioctl + * @mmap: mmap callback + * @mdev: mediated device structure + * @vma: vma structure + * Parent device that support mediated device should be registered with mdev + * module with parent_ops structure. + **/ + +struct parent_ops { + struct module *owner; + const struct attribute_group **dev_attr_groups; + const struct attribute_group **mdev_attr_groups; + struct attribute_group **supported_type_groups; + + int (*create)(struct kobject *kobj, struct mdev_device *mdev); + int (*remove)(struct mdev_device *mdev); + int (*open)(struct mdev_device *mdev); + void (*release)(struct mdev_device *mdev); + ssize_t (*read)(struct mdev_device *mdev, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*write)(struct mdev_device *mdev, const char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*ioctl)(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg); + int (*mmap)(struct mdev_device *mdev, struct vm_area_struct *vma); +}; + +/* interface for exporting mdev supported type attributes */ +struct mdev_type_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct device *dev, char *buf); + ssize_t (*store)(struct kobject *kobj, struct device *dev, + const char *buf, size_t count); +}; + +#define MDEV_TYPE_ATTR(_name, _mode, _show, _store) \ +struct mdev_type_attribute mdev_type_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) +#define MDEV_TYPE_ATTR_RW(_name) \ + struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RW(_name) +#define MDEV_TYPE_ATTR_RO(_name) \ + struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RO(_name) +#define MDEV_TYPE_ATTR_WO(_name) \ + struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_WO(_name) + +/** + * struct mdev_driver - Mediated device driver + * @name: driver name + * @probe: called when new device created + * @remove: called when device removed + * @driver: device driver structure + * + **/ +struct mdev_driver { + const char *name; + int (*probe)(struct device *dev); + void (*remove)(struct device *dev); + struct device_driver driver; +}; + +#define to_mdev_driver(drv) container_of(drv, struct mdev_driver, driver) +#define to_mdev_device(dev) container_of(dev, struct mdev_device, dev) + +static inline void *mdev_get_drvdata(struct mdev_device *mdev) +{ + return mdev->driver_data; +} + +static inline void mdev_set_drvdata(struct mdev_device *mdev, void *data) +{ + mdev->driver_data = data; +} + +extern struct bus_type mdev_bus_type; + +#define dev_is_mdev(d) ((d)->bus == &mdev_bus_type) + +extern int mdev_register_device(struct device *dev, + const struct parent_ops *ops); +extern void mdev_unregister_device(struct device *dev); + +extern int mdev_register_driver(struct mdev_driver *drv, struct module *owner); +extern void mdev_unregister_driver(struct mdev_driver *drv); + +#endif /* MDEV_H */ diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 2cb7531e7d7a6..ae6ac5e3e2f4c 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -57,6 +57,9 @@ struct mutex { #endif #ifdef CONFIG_MUTEX_SPIN_ON_OWNER struct optimistic_spin_queue osq; /* Spinner MCS lock */ + int waiter_spinning; +#elif defined(CONFIG_SMP) + bool yield_to_waiter; #endif #ifdef CONFIG_DEBUG_MUTEXES void *magic; diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index ee1bed7dbfc63..78bb0d7f6b11a 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -253,6 +253,13 @@ static inline int phy_set_mode(struct phy *phy, enum phy_mode mode) return -ENOSYS; } +static inline int phy_reset(struct phy *phy) +{ + if (!phy) + return 0; + return -ENOSYS; +} + static inline int phy_get_bus_width(struct phy *phy) { return -ENOSYS; diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 0ecae0b1cd341..15ff0421b4239 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -75,7 +75,15 @@ struct vfio_iommu_driver_ops { struct iommu_group *group); void (*detach_group)(void *iommu_data, struct iommu_group *group); - + int (*pin_pages)(void *iommu_data, unsigned long *user_pfn, + int npage, int prot, + unsigned long *phys_pfn); + int (*unpin_pages)(void *iommu_data, + unsigned long *user_pfn, int npage); + int (*register_notifier)(void *iommu_data, + struct notifier_block *nb); + int (*unregister_notifier)(void *iommu_data, + struct notifier_block *nb); }; extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops); @@ -92,6 +100,21 @@ extern int vfio_external_user_iommu_id(struct vfio_group *group); extern long vfio_external_check_extension(struct vfio_group *group, unsigned long arg); +#define VFIO_PIN_PAGES_MAX_ENTRIES (PAGE_SIZE/sizeof(unsigned long)) + +extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, + int npage, int prot, unsigned long *phys_pfn); +extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, + int npage); + +#define VFIO_IOMMU_NOTIFY_DMA_UNMAP (1) + +extern int vfio_register_notifier(struct device *dev, + struct notifier_block *nb); + +extern int vfio_unregister_notifier(struct device *dev, + struct notifier_block *nb); + /* * Sub-module helpers */ @@ -103,6 +126,13 @@ extern struct vfio_info_cap_header *vfio_info_cap_add( struct vfio_info_cap *caps, size_t size, u16 id, u16 version); extern void vfio_info_cap_shift(struct vfio_info_cap *caps, size_t offset); +extern int vfio_info_add_capability(struct vfio_info_cap *caps, + int cap_type_id, void *cap_type); + +extern int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr, + int num_irqs, int max_irq_type, + size_t *data_size); + struct pci_dev; #ifdef CONFIG_EEH extern void vfio_spapr_pci_eeh_open(struct pci_dev *pdev); diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index cee8c00f3d3e6..9924bc9cbc7c2 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -155,6 +155,7 @@ struct snd_compr { 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; diff --git a/include/sound/core.h b/include/sound/core.h index 31079ea5e4846..f7d8c10c4c45a 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -308,8 +308,8 @@ __printf(4, 5) 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 /** @@ -319,8 +319,8 @@ void __snd_printk(unsigned int level, const char *file, int line, * 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 /** @@ -330,10 +330,10 @@ void __snd_printk(unsigned int level, const char *file, int line, * 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 @@ -383,8 +383,8 @@ static inline bool snd_printd_ratelimit(void) { return false; } * 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, ...) {} diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index ebf622f5b2380..728790b923549 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -408,17 +408,20 @@ struct drm_mode_fb_cmd2 { * offsets[1]. Note that offsets[0] will generally * be 0 (but this is not required). * - * To accommodate tiled, compressed, etc formats, a per-plane + * To accommodate tiled, compressed, etc formats, a * modifier can be specified. The default value of zero * indicates "native" format as specified by the fourcc. - * Vendor specific modifier token. This allows, for example, - * different tiling/swizzling pattern on different planes. - * See discussion above of DRM_FORMAT_MOD_xxx. + * Vendor specific modifier token. Note that even though + * it looks like we have a modifier per-plane, we in fact + * do not. The modifier for each plane must be identical. + * Thus all combinations of different data layouts for + * multi plane formats must be enumerated as separate + * modifiers. */ __u32 handles[4]; __u32 pitches[4]; /* pitch for each plane */ __u32 offsets[4]; /* offset of each plane */ - __u64 modifier[4]; /* ie, tiling, compressed (per plane) */ + __u64 modifier[4]; /* ie, tiling, compress */ }; #define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 255a2113f53cb..519eff362c1ca 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -203,6 +203,16 @@ struct vfio_device_info { }; #define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) +/* + * Vendor driver using Mediated device framework should provide device_api + * attribute in supported type attribute groups. Device API string should be one + * of the following corresponding to device flags in vfio_device_info structure. + */ + +#define VFIO_DEVICE_API_PCI_STRING "vfio-pci" +#define VFIO_DEVICE_API_PLATFORM_STRING "vfio-platform" +#define VFIO_DEVICE_API_AMBA_STRING "vfio-amba" + /** * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, * struct vfio_region_info) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 33d00a4ce6567..819d895edfdca 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -18,12 +18,6 @@ #include #include -#ifndef __KERNEL__ -#error This API is an early revision and not enabled in the current -#error kernel release, it will be enabled in a future kernel version -#error with incompatible changes to what is here. -#endif - /* * Maximum number of channels topology kcontrol can represent. */ diff --git a/integration-manifest b/integration-manifest new file mode 100644 index 0000000000000..5785d41010805 --- /dev/null +++ b/integration-manifest @@ -0,0 +1,20 @@ +origin/drm-intel-fixes bc9db5ad3253c8e17969bd802c47b73e63f125ab + drm/i915: Assume non-DP++ port if dvo_port is HDMI and there's no AUX ch specified in the VBT +drm-upstream/drm-fixes 51a4c38a5511c0027c54d330f7dd2239f6c95b82 + Merge branch 'mediatek-drm-fixes-2016-11-11' of https://github.com/ckhu-mediatek/linux.git-tags into drm-fixes +origin/drm-intel-next-fixes 105f1a65b04a8f4f7abec11b200b1fb54f3d4b46 + drm/i915: Fix conflict resolution from backmerge of v4.8-rc8 to drm-next +origin/drm-intel-next-queued 6288c79ea519f6ca3a54d87974b48ca6a8b986af + drm/i915: Add a few more sanity checks for stolen handling +drm-upstream/drm-next b7c0e47d98249c2ddf21ea197b651093c6aaee00 + Merge tag 'drm-vc4-next-2016-11-16' of https://github.com/anholt/linux into drm-next +sound-upstream/for-next 43e575fabbaa1cc9e25dd0bb31eb6657ac7092ec + ALSA: core: Fix kernel-doc warnings +sound-upstream/for-linus 6ff1a25318ebf688ef9593fe09cd449f6fb4ad31 + ALSA: usb-audio: Fix use-after-free of usb_device at disconnect +origin/topic/drm-fixes c6935931c1894ff857616ff8549b61236a19148f + Linux 4.8-rc5 +origin/topic/drm-misc 27bfa74b9029a4205b16ec8c3a7090287e6e9995 + drm: also move DSI panels to the front of the connector list +origin/topic/core-for-CI 15bdcbcc55853f70237aca4baa4152f8396799d4 + kbuild: Disable PIE by default diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index a70b90db3909c..f9f18361d7abf 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -55,6 +55,9 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) mutex_clear_owner(lock); #ifdef CONFIG_MUTEX_SPIN_ON_OWNER osq_lock_init(&lock->osq); + lock->waiter_spinning = false; +#elif defined(CONFIG_SMP) + lock->yield_to_waiter = false; #endif debug_mutex_init(lock, name, key); @@ -71,6 +74,9 @@ EXPORT_SYMBOL(__mutex_init); */ __visible void __sched __mutex_lock_slowpath(atomic_t *lock_count); + +static inline bool need_yield_to_waiter(struct mutex *lock); + /** * mutex_lock - acquire the mutex * @lock: the mutex to be acquired @@ -99,7 +105,10 @@ void __sched mutex_lock(struct mutex *lock) * The locking fastpath is the 1->0 transition from * 'unlocked' into 'locked' state. */ - __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath); + if (!need_yield_to_waiter(lock)) + __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath); + else + __mutex_lock_slowpath(&lock->count); mutex_set_owner(lock); } @@ -273,11 +282,16 @@ static inline int mutex_can_spin_on_owner(struct mutex *lock) /* * Atomically try to take the lock when it is available + * + * For waiter-spinner, the count needs to be set to -1 first which will be + * cleared to 0 later on if the list becomes empty. For regular spinner, + * the count will be set to 0 as the the woken waiter will set it to -1, + * if necessary. */ -static inline bool mutex_try_to_acquire(struct mutex *lock) +static inline bool mutex_try_to_acquire(struct mutex *lock, int waiter) { return !mutex_is_locked(lock) && - (atomic_cmpxchg_acquire(&lock->count, 1, 0) == 1); + (atomic_cmpxchg_acquire(&lock->count, 1, waiter ? -1 : 0) == 1); } /* @@ -302,22 +316,43 @@ static inline bool mutex_try_to_acquire(struct mutex *lock) * * Returns true when the lock was taken, otherwise false, indicating * that we need to jump to the slowpath and sleep. + * + * The waiter flag is set to true if the spinner is a waiter in the wait + * queue. The waiter-spinner will spin on the lock directly and concurrently + * with the spinner at the head of the OSQ, if present. */ static bool mutex_optimistic_spin(struct mutex *lock, - struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx) + struct ww_acquire_ctx *ww_ctx, + const bool use_ww_ctx, int waiter) { struct task_struct *task = current; + bool acquired = false; - if (!mutex_can_spin_on_owner(lock)) - goto done; + if (!waiter) { + /* + * The purpose of the mutex_can_spin_on_owner() function is + * to eliminate the overhead of osq_lock() and osq_unlock() + * in case spinning isn't possible. As a waiter-spinner + * is not going to take OSQ lock anyway, there is no need + * to call mutex_can_spin_on_owner(). + */ + if (!mutex_can_spin_on_owner(lock)) + goto done; - /* - * In order to avoid a stampede of mutex spinners trying to - * acquire the mutex all at once, the spinners need to take a - * MCS (queued) lock first before spinning on the owner field. - */ - if (!osq_lock(&lock->osq)) - goto done; + /* + * In order to avoid a stampede of mutex spinners trying to + * acquire the mutex all at once, the spinners need to take a + * MCS (queued) lock first before spinning on the owner field. + */ + if (!osq_lock(&lock->osq)) + goto done; + } else { + /* + * Turn on the waiter spinning flag to discourage the spinner + * from getting the lock. + */ + lock->waiter_spinning = true; + } while (true) { struct task_struct *owner; @@ -338,6 +373,17 @@ static bool mutex_optimistic_spin(struct mutex *lock, break; } + /* + * For regular opt-spinner, it waits until the waiter_spinning + * flag isn't set. This will ensure forward progress for + * the waiter spinner. + */ + if (!waiter && READ_ONCE(lock->waiter_spinning)) { + if (need_resched()) + break; + goto relax_cpu; + } + /* * If there's an owner, wait for it to either * release the lock or go to sleep. @@ -347,7 +393,7 @@ static bool mutex_optimistic_spin(struct mutex *lock, break; /* Try to acquire the mutex if it is unlocked. */ - if (mutex_try_to_acquire(lock)) { + if (mutex_try_to_acquire(lock, waiter)) { lock_acquired(&lock->dep_map, ip); if (use_ww_ctx) { @@ -358,8 +404,8 @@ static bool mutex_optimistic_spin(struct mutex *lock, } mutex_set_owner(lock); - osq_unlock(&lock->osq); - return true; + acquired = true; + break; } /* @@ -371,6 +417,7 @@ static bool mutex_optimistic_spin(struct mutex *lock, if (!owner && (need_resched() || rt_task(task))) break; +relax_cpu: /* * The cpu_relax() call is a compiler barrier which forces * everything in this loop to be re-loaded. We don't need @@ -380,14 +427,17 @@ static bool mutex_optimistic_spin(struct mutex *lock, cpu_relax_lowlatency(); } - osq_unlock(&lock->osq); + if (!waiter) + osq_unlock(&lock->osq); + else + lock->waiter_spinning = false; done: /* * If we fell out of the spin path because of need_resched(), * reschedule now, before we try-lock the mutex. This avoids getting * scheduled out right after we obtained the mutex. */ - if (need_resched()) { + if (!acquired && need_resched()) { /* * We _should_ have TASK_RUNNING here, but just in case * we do not, make it so, otherwise we might get stuck. @@ -396,11 +446,53 @@ static bool mutex_optimistic_spin(struct mutex *lock, schedule_preempt_disabled(); } - return false; + return acquired; } + #else static bool mutex_optimistic_spin(struct mutex *lock, - struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx) + struct ww_acquire_ctx *ww_ctx, + const bool use_ww_ctx, int waiter) +{ + return false; +} +#endif + +#if !defined(CONFIG_MUTEX_SPIN_ON_OWNER) && defined(CONFIG_SMP) +#define MUTEX_WAKEUP_THRESHOLD 16 + +static inline void do_yield_to_waiter(struct mutex *lock, int *wakeups) +{ + *wakeups += 1; + + if (*wakeups < MUTEX_WAKEUP_THRESHOLD) + return; + + if (lock->yield_to_waiter != true) + lock->yield_to_waiter = true; +} + +static inline void clear_yield_to_waiter(struct mutex *lock) +{ + lock->yield_to_waiter = false; +} + +static inline bool need_yield_to_waiter(struct mutex *lock) +{ + return lock->yield_to_waiter; +} +#else +static inline void do_yield_to_waiter(struct mutex *lock, int *wakeups) +{ + return; +} + +static inline void clear_yield_to_waiter(struct mutex *lock) +{ + return; +} + +static inline bool need_yield_to_waiter(struct mutex *lock) { return false; } @@ -509,7 +601,9 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, struct task_struct *task = current; struct mutex_waiter waiter; unsigned long flags; + bool acquired = false; /* True if the lock is acquired */ int ret; + int wakeups = 0; if (use_ww_ctx) { struct ww_mutex *ww = container_of(lock, struct ww_mutex, base); @@ -520,7 +614,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, preempt_disable(); mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip); - if (mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx)) { + if (mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, false)) { /* got the lock, yay! */ preempt_enable(); return 0; @@ -532,7 +626,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, * Once more, try to acquire the lock. Only try-lock the mutex if * it is unlocked to reduce unnecessary xchg() operations. */ - if (!mutex_is_locked(lock) && + if (!need_yield_to_waiter(lock) && !mutex_is_locked(lock) && (atomic_xchg_acquire(&lock->count, 0) == 1)) goto skip_wait; @@ -545,7 +639,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, lock_contended(&lock->dep_map, ip); - for (;;) { + while (!acquired) { /* * Lets try to take the lock again - this is needed even if * we get here for the first time (shortly after failing to @@ -556,9 +650,14 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, * other waiters. We only attempt the xchg if the count is * non-negative in order to avoid unnecessary xchg operations: */ - if (atomic_read(&lock->count) >= 0 && - (atomic_xchg_acquire(&lock->count, -1) == 1)) + if ((!need_yield_to_waiter(lock) || wakeups > 1) && + atomic_read(&lock->count) >= 0 && + (atomic_xchg_acquire(&lock->count, -1) == 1)) { + if (wakeups > 1) + clear_yield_to_waiter(lock); + break; + } /* * got a signal? (This code gets eliminated in the @@ -580,7 +679,14 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, /* didn't get the lock, go to sleep: */ spin_unlock_mutex(&lock->wait_lock, flags); schedule_preempt_disabled(); + + /* + * Optimistically spinning on the mutex without the wait lock. + */ + acquired = mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, + true); spin_lock_mutex(&lock->wait_lock, flags); + do_yield_to_waiter(lock, &wakeups); } __set_task_state(task, TASK_RUNNING); @@ -590,6 +696,9 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, atomic_set(&lock->count, 0); debug_mutex_free_waiter(&waiter); + if (acquired) + goto unlock; + skip_wait: /* got the lock - cleanup and rejoice! */ lock_acquired(&lock->dep_map, ip); @@ -600,6 +709,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, ww_mutex_set_context_slowpath(ww, ww_ctx); } +unlock: spin_unlock_mutex(&lock->wait_lock, flags); preempt_enable(); return 0; @@ -789,10 +899,13 @@ __mutex_lock_interruptible_slowpath(struct mutex *lock); */ int __sched mutex_lock_interruptible(struct mutex *lock) { - int ret; + int ret = 1; might_sleep(); - ret = __mutex_fastpath_lock_retval(&lock->count); + + if (!need_yield_to_waiter(lock)) + ret = __mutex_fastpath_lock_retval(&lock->count); + if (likely(!ret)) { mutex_set_owner(lock); return 0; @@ -804,10 +917,13 @@ EXPORT_SYMBOL(mutex_lock_interruptible); int __sched mutex_lock_killable(struct mutex *lock) { - int ret; + int ret = 1; might_sleep(); - ret = __mutex_fastpath_lock_retval(&lock->count); + + if (!need_yield_to_waiter(lock)) + ret = __mutex_fastpath_lock_retval(&lock->count); + if (likely(!ret)) { mutex_set_owner(lock); return 0; @@ -917,11 +1033,12 @@ EXPORT_SYMBOL(mutex_trylock); int __sched __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) { - int ret; + int ret = 1; might_sleep(); - ret = __mutex_fastpath_lock_retval(&lock->base.count); + if (!need_yield_to_waiter(&lock->base)) + ret = __mutex_fastpath_lock_retval(&lock->base.count); if (likely(!ret)) { ww_mutex_set_context_fastpath(lock, ctx); @@ -935,11 +1052,12 @@ EXPORT_SYMBOL(__ww_mutex_lock); int __sched __ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) { - int ret; + int ret = 1; might_sleep(); - ret = __mutex_fastpath_lock_retval(&lock->base.count); + if (!need_yield_to_waiter(&lock->base)) + ret = __mutex_fastpath_lock_retval(&lock->base.count); if (likely(!ret)) { ww_mutex_set_context_fastpath(lock, ctx); diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c index 084452e34a125..bdff5ed57f10a 100644 --- a/kernel/power/suspend_test.c +++ b/kernel/power/suspend_test.c @@ -203,8 +203,10 @@ static int __init test_suspend(void) /* RTCs have initialized by now too ... can we use one? */ dev = class_find_device(rtc_class, NULL, NULL, has_wakealarm); - if (dev) + if (dev) { rtc = rtc_class_open(dev_name(dev)); + put_device(dev); + } if (!rtc) { printk(warn_no_rtc); return 0; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index de08fc90baafa..5028f4fd504ac 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -253,17 +253,6 @@ static int preferred_console = -1; int console_set_on_cmdline; EXPORT_SYMBOL(console_set_on_cmdline); -#ifdef CONFIG_OF -static bool of_specified_console; - -void console_set_by_of(void) -{ - of_specified_console = true; -} -#else -# define of_specified_console false -#endif - /* Flag: console code may call schedule() */ static int console_may_schedule; @@ -2657,7 +2646,7 @@ void register_console(struct console *newcon) * didn't select a console we take the first one * that registers here. */ - if (preferred_console < 0 && !of_specified_console) { + if (preferred_console < 0) { if (newcon->index < 0) newcon->index = 0; if (newcon->setup == NULL || diff --git a/lib/stackdepot.c b/lib/stackdepot.c index 4d830e2999893..f87d138e96724 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -192,6 +192,7 @@ void depot_fetch_stack(depot_stack_handle_t handle, struct stack_trace *trace) trace->entries = stack->entries; trace->skip = 0; } +EXPORT_SYMBOL_GPL(depot_fetch_stack); /** * depot_save_stack - save stack in a stack depot. @@ -283,3 +284,4 @@ depot_stack_handle_t depot_save_stack(struct stack_trace *trace, fast_exit: return retval; } +EXPORT_SYMBOL_GPL(depot_save_stack); diff --git a/mm/cma.c b/mm/cma.c index 384c2cb51b56b..c960459eda7e6 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -385,6 +385,9 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) bitmap_maxno = cma_bitmap_maxno(cma); bitmap_count = cma_bitmap_pages_to_bits(cma, count); + if (bitmap_count > bitmap_maxno) + return NULL; + for (;;) { mutex_lock(&cma->lock); bitmap_no = bitmap_find_next_zero_area_off(cma->bitmap, diff --git a/mm/filemap.c b/mm/filemap.c index c7fe2f16503f9..50b52fe51937c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1732,6 +1732,9 @@ static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos, if (inode->i_blkbits == PAGE_SHIFT || !mapping->a_ops->is_partially_uptodate) goto page_not_up_to_date; + /* pipes can't handle partially uptodate pages */ + if (unlikely(iter->type & ITER_PIPE)) + goto page_not_up_to_date; if (!trylock_page(page)) goto page_not_up_to_date; /* Did it get truncated before we got the lock? */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index ec49d9ef1eefd..418bf01a50ed1 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1826,11 +1826,17 @@ static void return_unused_surplus_pages(struct hstate *h, * is not the case is if a reserve map was changed between calls. It * is the responsibility of the caller to notice the difference and * take appropriate action. + * + * vma_add_reservation is used in error paths where a reservation must + * be restored when a newly allocated huge page must be freed. It is + * to be called after calling vma_needs_reservation to determine if a + * reservation exists. */ enum vma_resv_mode { VMA_NEEDS_RESV, VMA_COMMIT_RESV, VMA_END_RESV, + VMA_ADD_RESV, }; static long __vma_reservation_common(struct hstate *h, struct vm_area_struct *vma, unsigned long addr, @@ -1856,6 +1862,14 @@ static long __vma_reservation_common(struct hstate *h, region_abort(resv, idx, idx + 1); ret = 0; break; + case VMA_ADD_RESV: + if (vma->vm_flags & VM_MAYSHARE) + ret = region_add(resv, idx, idx + 1); + else { + region_abort(resv, idx, idx + 1); + ret = region_del(resv, idx, idx + 1); + } + break; default: BUG(); } @@ -1903,6 +1917,56 @@ static void vma_end_reservation(struct hstate *h, (void)__vma_reservation_common(h, vma, addr, VMA_END_RESV); } +static long vma_add_reservation(struct hstate *h, + struct vm_area_struct *vma, unsigned long addr) +{ + return __vma_reservation_common(h, vma, addr, VMA_ADD_RESV); +} + +/* + * This routine is called to restore a reservation on error paths. In the + * specific error paths, a huge page was allocated (via alloc_huge_page) + * and is about to be freed. If a reservation for the page existed, + * alloc_huge_page would have consumed the reservation and set PagePrivate + * in the newly allocated page. When the page is freed via free_huge_page, + * the global reservation count will be incremented if PagePrivate is set. + * However, free_huge_page can not adjust the reserve map. Adjust the + * reserve map here to be consistent with global reserve count adjustments + * to be made by free_huge_page. + */ +static void restore_reserve_on_error(struct hstate *h, + struct vm_area_struct *vma, unsigned long address, + struct page *page) +{ + if (unlikely(PagePrivate(page))) { + long rc = vma_needs_reservation(h, vma, address); + + if (unlikely(rc < 0)) { + /* + * Rare out of memory condition in reserve map + * manipulation. Clear PagePrivate so that + * global reserve count will not be incremented + * by free_huge_page. This will make it appear + * as though the reservation for this page was + * consumed. This may prevent the task from + * faulting in the page at a later time. This + * is better than inconsistent global huge page + * accounting of reserve counts. + */ + ClearPagePrivate(page); + } else if (rc) { + rc = vma_add_reservation(h, vma, address); + if (unlikely(rc < 0)) + /* + * See above comment about rare out of + * memory condition. + */ + ClearPagePrivate(page); + } else + vma_end_reservation(h, vma, address); + } +} + struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr, int avoid_reserve) { @@ -3498,6 +3562,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, spin_unlock(ptl); mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); out_release_all: + restore_reserve_on_error(h, vma, address, new_page); put_page(new_page); out_release_old: put_page(old_page); @@ -3680,6 +3745,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, spin_unlock(ptl); backout_unlocked: unlock_page(page); + restore_reserve_on_error(h, vma, address, page); put_page(page); goto out; } diff --git a/mm/kmemleak.c b/mm/kmemleak.c index e5355a5b423fa..d1380ed93fdf0 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1414,6 +1414,7 @@ static void kmemleak_scan(void) /* data/bss scanning */ scan_large_block(_sdata, _edata); scan_large_block(__bss_start, __bss_stop); + scan_large_block(__start_data_ro_after_init, __end_data_ro_after_init); #ifdef CONFIG_SMP /* per-cpu sections scanning */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index de88f33519c0d..19e796d36a629 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1112,10 +1112,10 @@ int memory_failure(unsigned long pfn, int trapno, int flags) } if (!PageHuge(p) && PageTransHuge(hpage)) { - lock_page(hpage); - if (!PageAnon(hpage) || unlikely(split_huge_page(hpage))) { - unlock_page(hpage); - if (!PageAnon(hpage)) + lock_page(p); + if (!PageAnon(p) || unlikely(split_huge_page(p))) { + unlock_page(p); + if (!PageAnon(p)) pr_err("Memory failure: %#lx: non anonymous thp\n", pfn); else @@ -1126,9 +1126,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) put_hwpoison_page(p); return -EBUSY; } - unlock_page(hpage); - get_hwpoison_page(p); - put_hwpoison_page(hpage); + unlock_page(p); VM_BUG_ON_PAGE(!page_count(p), p); hpage = compound_head(p); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 072d791dce2d5..6de9440e3ae2d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3658,7 +3658,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, /* Make sure we know about allocations which stall for too long */ if (time_after(jiffies, alloc_start + stall_timeout)) { warn_alloc(gfp_mask, - "page alloction stalls for %ums, order:%u\n", + "page allocation stalls for %ums, order:%u", jiffies_to_msecs(jiffies-alloc_start), order); stall_timeout += 10 * HZ; } diff --git a/mm/shmem.c b/mm/shmem.c index ad7813d73ea79..166ebf5d2bced 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1483,6 +1483,8 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp, copy_highpage(newpage, oldpage); flush_dcache_page(newpage); + __SetPageLocked(newpage); + __SetPageSwapBacked(newpage); SetPageUptodate(newpage); set_page_private(newpage, swap_index); SetPageSwapCache(newpage); diff --git a/mm/slab_common.c b/mm/slab_common.c index 71f0b28a1bec8..329b038438639 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -533,8 +533,8 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg, s = create_cache(cache_name, root_cache->object_size, root_cache->size, root_cache->align, - root_cache->flags, root_cache->ctor, - memcg, root_cache); + root_cache->flags & CACHE_CREATE_MASK, + root_cache->ctor, memcg, root_cache); /* * If we could not create a memcg cache, do not complain, because * that's not critical at all as we can always proceed with the root diff --git a/mm/swapfile.c b/mm/swapfile.c index 2210de290b54d..f30438970cd17 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2224,6 +2224,8 @@ static unsigned long read_swap_header(struct swap_info_struct *p, swab32s(&swap_header->info.version); swab32s(&swap_header->info.last_page); swab32s(&swap_header->info.nr_badpages); + if (swap_header->info.nr_badpages > MAX_SWAP_BADPAGES) + return 0; for (i = 0; i < swap_header->info.nr_badpages; i++) swab32s(&swap_header->info.badpages[i]); } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index f2481cb4e6b21..7e945c63c7efb 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -117,63 +117,27 @@ static void vunmap_page_range(unsigned long addr, unsigned long end) } while (pgd++, addr = next, addr != end); } -static int vmap_pte_range(pmd_t *pmd, unsigned long addr, - unsigned long end, pgprot_t prot, struct page **pages, int *nr) -{ - pte_t *pte; - - /* - * nr is a running index into the array which helps higher level - * callers keep track of where we're up to. - */ - - pte = pte_alloc_kernel(pmd, addr); - if (!pte) - return -ENOMEM; - do { - struct page *page = pages[*nr]; - - if (WARN_ON(!pte_none(*pte))) - return -EBUSY; - if (WARN_ON(!page)) - return -ENOMEM; - set_pte_at(&init_mm, addr, pte, mk_pte(page, prot)); - (*nr)++; - } while (pte++, addr += PAGE_SIZE, addr != end); - return 0; -} +struct vmap_page { + pgprot_t prot; + struct page **pages; + unsigned long count; +}; -static int vmap_pmd_range(pud_t *pud, unsigned long addr, - unsigned long end, pgprot_t prot, struct page **pages, int *nr) +static int vmap_page(pte_t *pte, pgtable_t token, + unsigned long addr, void *data) { - pmd_t *pmd; - unsigned long next; - - pmd = pmd_alloc(&init_mm, pud, addr); - if (!pmd) - return -ENOMEM; - do { - next = pmd_addr_end(addr, end); - if (vmap_pte_range(pmd, addr, next, prot, pages, nr)) - return -ENOMEM; - } while (pmd++, addr = next, addr != end); - return 0; -} + struct vmap_page *v = data; + struct page *page; -static int vmap_pud_range(pgd_t *pgd, unsigned long addr, - unsigned long end, pgprot_t prot, struct page **pages, int *nr) -{ - pud_t *pud; - unsigned long next; + if (WARN_ON(!pte_none(*pte))) + return -EBUSY; - pud = pud_alloc(&init_mm, pgd, addr); - if (!pud) + page = v->pages[v->count]; + if (WARN_ON(!page)) return -ENOMEM; - do { - next = pud_addr_end(addr, end); - if (vmap_pmd_range(pud, addr, next, prot, pages, nr)) - return -ENOMEM; - } while (pud++, addr = next, addr != end); + + set_pte_at(&init_mm, addr, pte, mk_pte(page, v->prot)); + v->count++; return 0; } @@ -186,22 +150,19 @@ static int vmap_pud_range(pgd_t *pgd, unsigned long addr, static int vmap_page_range_noflush(unsigned long start, unsigned long end, pgprot_t prot, struct page **pages) { - pgd_t *pgd; - unsigned long next; - unsigned long addr = start; - int err = 0; - int nr = 0; + struct vmap_page v = { prot, pages }; + int err; - BUG_ON(addr >= end); - pgd = pgd_offset_k(addr); - do { - next = pgd_addr_end(addr, end); - err = vmap_pud_range(pgd, addr, next, prot, pages, &nr); - if (err) - return err; - } while (pgd++, addr = next, addr != end); + if ((end - start) >> PAGE_SHIFT > INT_MAX) + return -EINVAL; + + err = apply_to_page_range(&init_mm, start, end - start, vmap_page, &v); + if (unlikely(err)) { + vunmap_page_range(start, start + (v.count << PAGE_SHIFT)); + return err; + } - return nr; + return v.count; } static int vmap_page_range(unsigned long start, unsigned long end, diff --git a/net/ceph/ceph_fs.c b/net/ceph/ceph_fs.c index 7d54e944de5e0..dcbe67ff3e2b2 100644 --- a/net/ceph/ceph_fs.c +++ b/net/ceph/ceph_fs.c @@ -34,7 +34,8 @@ void ceph_file_layout_from_legacy(struct ceph_file_layout *fl, fl->stripe_count = le32_to_cpu(legacy->fl_stripe_count); fl->object_size = le32_to_cpu(legacy->fl_object_size); fl->pool_id = le32_to_cpu(legacy->fl_pg_pool); - if (fl->pool_id == 0) + if (fl->pool_id == 0 && fl->stripe_unit == 0 && + fl->stripe_count == 0 && fl->object_size == 0) fl->pool_id = -1; } EXPORT_SYMBOL(ceph_file_layout_from_legacy); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index d9bf7a1d0a583..e6ae15bc41b74 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -4094,6 +4094,7 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client) osd_init(&osdc->homeless_osd); osdc->homeless_osd.o_osdc = osdc; osdc->homeless_osd.o_osd = CEPH_HOMELESS_OSD; + osdc->last_linger_id = CEPH_LINGER_ID_START; osdc->linger_requests = RB_ROOT; osdc->map_checks = RB_ROOT; osdc->linger_map_checks = RB_ROOT; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 34dd7b26ee5f1..62a482790937b 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -2753,14 +2753,18 @@ EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout); void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt) { + rcu_read_lock(); xprt_switch_put(rcu_dereference(clnt->cl_xpi.xpi_xpswitch)); + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put); void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) { + rcu_read_lock(); rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch), xprt); + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt); @@ -2770,9 +2774,8 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, struct rpc_xprt_switch *xps; bool ret; - xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch); - rcu_read_lock(); + xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch); ret = rpc_xprt_switch_has_addr(xps, sap); rcu_read_unlock(); return ret; diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 2109495627866..26b26beef2d4a 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -44,18 +44,20 @@ * being done. * * When the underlying transport disconnects, MRs are left in one of - * three states: + * four states: * * INVALID: The MR was not in use before the QP entered ERROR state. - * (Or, the LOCAL_INV WR has not completed or flushed yet). - * - * STALE: The MR was being registered or unregistered when the QP - * entered ERROR state, and the pending WR was flushed. * * VALID: The MR was registered before the QP entered ERROR state. * - * When frwr_op_map encounters STALE and VALID MRs, they are recovered - * with ib_dereg_mr and then are re-initialized. Beause MR recovery + * FLUSHED_FR: The MR was being registered when the QP entered ERROR + * state, and the pending WR was flushed. + * + * FLUSHED_LI: The MR was being invalidated when the QP entered ERROR + * state, and the pending WR was flushed. + * + * When frwr_op_map encounters FLUSHED and VALID MRs, they are recovered + * with ib_dereg_mr and then are re-initialized. Because MR recovery * allocates fresh resources, it is deferred to a workqueue, and the * recovered MRs are placed back on the rb_mws list when recovery is * complete. frwr_op_map allocates another MR for the current RPC while @@ -177,12 +179,15 @@ __frwr_reset_mr(struct rpcrdma_ia *ia, struct rpcrdma_mw *r) static void frwr_op_recover_mr(struct rpcrdma_mw *mw) { + enum rpcrdma_frmr_state state = mw->frmr.fr_state; struct rpcrdma_xprt *r_xprt = mw->mw_xprt; struct rpcrdma_ia *ia = &r_xprt->rx_ia; int rc; rc = __frwr_reset_mr(ia, mw); - ib_dma_unmap_sg(ia->ri_device, mw->mw_sg, mw->mw_nents, mw->mw_dir); + if (state != FRMR_FLUSHED_LI) + ib_dma_unmap_sg(ia->ri_device, + mw->mw_sg, mw->mw_nents, mw->mw_dir); if (rc) goto out_release; @@ -262,10 +267,8 @@ frwr_op_maxpages(struct rpcrdma_xprt *r_xprt) } static void -__frwr_sendcompletion_flush(struct ib_wc *wc, struct rpcrdma_frmr *frmr, - const char *wr) +__frwr_sendcompletion_flush(struct ib_wc *wc, const char *wr) { - frmr->fr_state = FRMR_IS_STALE; if (wc->status != IB_WC_WR_FLUSH_ERR) pr_err("rpcrdma: %s: %s (%u/0x%x)\n", wr, ib_wc_status_msg(wc->status), @@ -288,7 +291,8 @@ frwr_wc_fastreg(struct ib_cq *cq, struct ib_wc *wc) if (wc->status != IB_WC_SUCCESS) { cqe = wc->wr_cqe; frmr = container_of(cqe, struct rpcrdma_frmr, fr_cqe); - __frwr_sendcompletion_flush(wc, frmr, "fastreg"); + frmr->fr_state = FRMR_FLUSHED_FR; + __frwr_sendcompletion_flush(wc, "fastreg"); } } @@ -308,7 +312,8 @@ frwr_wc_localinv(struct ib_cq *cq, struct ib_wc *wc) if (wc->status != IB_WC_SUCCESS) { cqe = wc->wr_cqe; frmr = container_of(cqe, struct rpcrdma_frmr, fr_cqe); - __frwr_sendcompletion_flush(wc, frmr, "localinv"); + frmr->fr_state = FRMR_FLUSHED_LI; + __frwr_sendcompletion_flush(wc, "localinv"); } } @@ -328,8 +333,10 @@ frwr_wc_localinv_wake(struct ib_cq *cq, struct ib_wc *wc) /* WARNING: Only wr_cqe and status are reliable at this point */ cqe = wc->wr_cqe; frmr = container_of(cqe, struct rpcrdma_frmr, fr_cqe); - if (wc->status != IB_WC_SUCCESS) - __frwr_sendcompletion_flush(wc, frmr, "localinv"); + if (wc->status != IB_WC_SUCCESS) { + frmr->fr_state = FRMR_FLUSHED_LI; + __frwr_sendcompletion_flush(wc, "localinv"); + } complete(&frmr->fr_linv_done); } diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 0d35b761c883d..6e1bba3582036 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -216,7 +216,8 @@ struct rpcrdma_rep { enum rpcrdma_frmr_state { FRMR_IS_INVALID, /* ready to be used */ FRMR_IS_VALID, /* in use */ - FRMR_IS_STALE, /* failed completion */ + FRMR_FLUSHED_FR, /* flushed FASTREG WR */ + FRMR_FLUSHED_LI, /* flushed LOCALINV WR */ }; struct rpcrdma_frmr { diff --git a/samples/vfio-mdev/Makefile b/samples/vfio-mdev/Makefile new file mode 100644 index 0000000000000..a932edbe38eb9 --- /dev/null +++ b/samples/vfio-mdev/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for mtty.c file +# +KERNEL_DIR:=/lib/modules/$(shell uname -r)/build + +obj-m:=mtty.o + +modules clean modules_install: + $(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) $@ + +default: modules + +module: modules diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c new file mode 100644 index 0000000000000..6b633a4ea3339 --- /dev/null +++ b/samples/vfio-mdev/mtty.c @@ -0,0 +1,1503 @@ +/* + * Mediated virtual PCI serial host device driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia + * Kirti Wankhede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Sample driver that creates mdev device that simulates serial port over PCI + * card. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * #defines + */ + +#define VERSION_STRING "0.1" +#define DRIVER_AUTHOR "NVIDIA Corporation" + +#define MTTY_CLASS_NAME "mtty" + +#define MTTY_NAME "mtty" + +#define MTTY_STRING_LEN 16 + +#define MTTY_CONFIG_SPACE_SIZE 0xff +#define MTTY_IO_BAR_SIZE 0x8 +#define MTTY_MMIO_BAR_SIZE 0x100000 + +#define STORE_LE16(addr, val) (*(u16 *)addr = val) +#define STORE_LE32(addr, val) (*(u32 *)addr = val) + +#define MAX_FIFO_SIZE 16 + +#define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) + +#define MTTY_VFIO_PCI_OFFSET_SHIFT 40 + +#define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT) +#define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ + ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT) +#define MTTY_VFIO_PCI_OFFSET_MASK \ + (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1) +#define MAX_MTTYS 24 + +/* + * Global Structures + */ + +struct mtty_dev { + dev_t vd_devt; + struct class *vd_class; + struct cdev vd_cdev; + struct idr vd_idr; + struct device dev; +} mtty_dev; + +struct mdev_region_info { + u64 start; + u64 phys_start; + u32 size; + u64 vfio_offset; +}; + +#if defined(DEBUG_REGS) +const char *wr_reg[] = { + "TX", + "IER", + "FCR", + "LCR", + "MCR", + "LSR", + "MSR", + "SCR" +}; + +const char *rd_reg[] = { + "RX", + "IER", + "IIR", + "LCR", + "MCR", + "LSR", + "MSR", + "SCR" +}; +#endif + +/* loop back buffer */ +struct rxtx { + u8 fifo[MAX_FIFO_SIZE]; + u8 head, tail; + u8 count; +}; + +struct serial_port { + u8 uart_reg[8]; /* 8 registers */ + struct rxtx rxtx; /* loop back buffer */ + bool dlab; + bool overrun; + u16 divisor; + u8 fcr; /* FIFO control register */ + u8 max_fifo_size; + u8 intr_trigger_level; /* interrupt trigger level */ +}; + +/* State of each mdev device */ +struct mdev_state { + int irq_fd; + struct eventfd_ctx *intx_evtfd; + struct eventfd_ctx *msi_evtfd; + int irq_index; + u8 *vconfig; + struct mutex ops_lock; + struct mdev_device *mdev; + struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; + u32 bar_mask[VFIO_PCI_NUM_REGIONS]; + struct list_head next; + struct serial_port s[2]; + struct mutex rxtx_lock; + struct vfio_device_info dev_info; + int nr_ports; +}; + +struct mutex mdev_list_lock; +struct list_head mdev_devices_list; + +static const struct file_operations vd_fops = { + .owner = THIS_MODULE, +}; + +/* function prototypes */ + +static int mtty_trigger_interrupt(uuid_le uuid); + +/* Helper functions */ +static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid) +{ + struct mdev_state *mds; + + list_for_each_entry(mds, &mdev_devices_list, next) { + if (uuid_le_cmp(mds->mdev->uuid, uuid) == 0) + return mds; + } + + return NULL; +} + +void dump_buffer(char *buf, uint32_t count) +{ +#if defined(DEBUG) + int i; + + pr_info("Buffer:\n"); + for (i = 0; i < count; i++) { + pr_info("%2x ", *(buf + i)); + if ((i + 1) % 16 == 0) + pr_info("\n"); + } +#endif +} + +static void mtty_create_config_space(struct mdev_state *mdev_state) +{ + /* PCI dev ID */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); + + /* Control: I/O+, Mem-, BusMaster- */ + STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); + + /* Status: capabilities list absent */ + STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); + + /* Rev ID */ + mdev_state->vconfig[0x8] = 0x10; + + /* programming interface class : 16550-compatible serial controller */ + mdev_state->vconfig[0x9] = 0x02; + + /* Sub class : 00 */ + mdev_state->vconfig[0xa] = 0x00; + + /* Base class : Simple Communication controllers */ + mdev_state->vconfig[0xb] = 0x07; + + /* base address registers */ + /* BAR0: IO space */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); + mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; + + if (mdev_state->nr_ports == 2) { + /* BAR1: IO space */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); + mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; + } + + /* Subsystem ID */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); + + mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ + mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ + + /* Vendor specific data */ + mdev_state->vconfig[0x40] = 0x23; + mdev_state->vconfig[0x43] = 0x80; + mdev_state->vconfig[0x44] = 0x23; + mdev_state->vconfig[0x48] = 0x23; + mdev_state->vconfig[0x4c] = 0x23; + + mdev_state->vconfig[0x60] = 0x50; + mdev_state->vconfig[0x61] = 0x43; + mdev_state->vconfig[0x62] = 0x49; + mdev_state->vconfig[0x63] = 0x20; + mdev_state->vconfig[0x64] = 0x53; + mdev_state->vconfig[0x65] = 0x65; + mdev_state->vconfig[0x66] = 0x72; + mdev_state->vconfig[0x67] = 0x69; + mdev_state->vconfig[0x68] = 0x61; + mdev_state->vconfig[0x69] = 0x6c; + mdev_state->vconfig[0x6a] = 0x2f; + mdev_state->vconfig[0x6b] = 0x55; + mdev_state->vconfig[0x6c] = 0x41; + mdev_state->vconfig[0x6d] = 0x52; + mdev_state->vconfig[0x6e] = 0x54; +} + +static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, + char *buf, u32 count) +{ + u32 cfg_addr, bar_mask, bar_index = 0; + + switch (offset) { + case 0x04: /* device control */ + case 0x06: /* device status */ + /* do nothing */ + break; + case 0x3c: /* interrupt line */ + mdev_state->vconfig[0x3c] = buf[0]; + break; + case 0x3d: + /* + * Interrupt Pin is hardwired to INTA. + * This field is write protected by hardware + */ + break; + case 0x10: /* BAR0 */ + case 0x14: /* BAR1 */ + if (offset == 0x10) + bar_index = 0; + else if (offset == 0x14) + bar_index = 1; + + if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { + STORE_LE32(&mdev_state->vconfig[offset], 0); + break; + } + + cfg_addr = *(u32 *)buf; + pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); + + if (cfg_addr == 0xffffffff) { + bar_mask = mdev_state->bar_mask[bar_index]; + cfg_addr = (cfg_addr & bar_mask); + } + + cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); + STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); + break; + case 0x18: /* BAR2 */ + case 0x1c: /* BAR3 */ + case 0x20: /* BAR4 */ + STORE_LE32(&mdev_state->vconfig[offset], 0); + break; + default: + pr_info("PCI config write @0x%x of %d bytes not handled\n", + offset, count); + break; + } +} + +static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, + u16 offset, char *buf, u32 count) +{ + u8 data = *buf; + + /* Handle data written by guest */ + switch (offset) { + case UART_TX: + /* if DLAB set, data is LSB of divisor */ + if (mdev_state->s[index].dlab) { + mdev_state->s[index].divisor |= data; + break; + } + + mutex_lock(&mdev_state->rxtx_lock); + + /* save in TX buffer */ + if (mdev_state->s[index].rxtx.count < + mdev_state->s[index].max_fifo_size) { + mdev_state->s[index].rxtx.fifo[ + mdev_state->s[index].rxtx.head] = data; + mdev_state->s[index].rxtx.count++; + CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); + mdev_state->s[index].overrun = false; + + /* + * Trigger interrupt if receive data interrupt is + * enabled and fifo reached trigger level + */ + if ((mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_RDI) && + (mdev_state->s[index].rxtx.count == + mdev_state->s[index].intr_trigger_level)) { + /* trigger interrupt */ +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Fifo level trigger\n", + index); +#endif + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + } else { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Buffer Overflow\n", index); +#endif + mdev_state->s[index].overrun = true; + + /* + * Trigger interrupt if receiver line status interrupt + * is enabled + */ + if (mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_RLSI) + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + mutex_unlock(&mdev_state->rxtx_lock); + break; + + case UART_IER: + /* if DLAB set, data is MSB of divisor */ + if (mdev_state->s[index].dlab) + mdev_state->s[index].divisor |= (u16)data << 8; + else { + mdev_state->s[index].uart_reg[offset] = data; + mutex_lock(&mdev_state->rxtx_lock); + if ((data & UART_IER_THRI) && + (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail)) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: IER_THRI write\n", + index); +#endif + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + + mutex_unlock(&mdev_state->rxtx_lock); + } + + break; + + case UART_FCR: + mdev_state->s[index].fcr = data; + + mutex_lock(&mdev_state->rxtx_lock); + if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { + /* clear loop back FIFO */ + mdev_state->s[index].rxtx.count = 0; + mdev_state->s[index].rxtx.head = 0; + mdev_state->s[index].rxtx.tail = 0; + } + mutex_unlock(&mdev_state->rxtx_lock); + + switch (data & UART_FCR_TRIGGER_MASK) { + case UART_FCR_TRIGGER_1: + mdev_state->s[index].intr_trigger_level = 1; + break; + + case UART_FCR_TRIGGER_4: + mdev_state->s[index].intr_trigger_level = 4; + break; + + case UART_FCR_TRIGGER_8: + mdev_state->s[index].intr_trigger_level = 8; + break; + + case UART_FCR_TRIGGER_14: + mdev_state->s[index].intr_trigger_level = 14; + break; + } + + /* + * Set trigger level to 1 otherwise or implement timer with + * timeout of 4 characters and on expiring that timer set + * Recevice data timeout in IIR register + */ + mdev_state->s[index].intr_trigger_level = 1; + if (data & UART_FCR_ENABLE_FIFO) + mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; + else { + mdev_state->s[index].max_fifo_size = 1; + mdev_state->s[index].intr_trigger_level = 1; + } + + break; + + case UART_LCR: + if (data & UART_LCR_DLAB) { + mdev_state->s[index].dlab = true; + mdev_state->s[index].divisor = 0; + } else + mdev_state->s[index].dlab = false; + + mdev_state->s[index].uart_reg[offset] = data; + break; + + case UART_MCR: + mdev_state->s[index].uart_reg[offset] = data; + + if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && + (data & UART_MCR_OUT2)) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: MCR_OUT2 write\n", index); +#endif + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + + if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && + (data & (UART_MCR_RTS | UART_MCR_DTR))) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: MCR RTS/DTR write\n", index); +#endif + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + break; + + case UART_LSR: + case UART_MSR: + /* do nothing */ + break; + + case UART_SCR: + mdev_state->s[index].uart_reg[offset] = data; + break; + + default: + break; + } +} + +static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state, + u16 offset, char *buf, u32 count) +{ + /* Handle read requests by guest */ + switch (offset) { + case UART_RX: + /* if DLAB set, data is LSB of divisor */ + if (mdev_state->s[index].dlab) { + *buf = (u8)mdev_state->s[index].divisor; + break; + } + + mutex_lock(&mdev_state->rxtx_lock); + /* return data in tx buffer */ + if (mdev_state->s[index].rxtx.head != + mdev_state->s[index].rxtx.tail) { + *buf = mdev_state->s[index].rxtx.fifo[ + mdev_state->s[index].rxtx.tail]; + mdev_state->s[index].rxtx.count--; + CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.tail); + } + + if (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail) { + /* + * Trigger interrupt if tx buffer empty interrupt is + * enabled and fifo is empty + */ +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Buffer Empty\n", index); +#endif + if (mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_THRI) + mtty_trigger_interrupt(mdev_state->mdev->uuid); + } + mutex_unlock(&mdev_state->rxtx_lock); + + break; + + case UART_IER: + if (mdev_state->s[index].dlab) { + *buf = (u8)(mdev_state->s[index].divisor >> 8); + break; + } + *buf = mdev_state->s[index].uart_reg[offset] & 0x0f; + break; + + case UART_IIR: + { + u8 ier = mdev_state->s[index].uart_reg[UART_IER]; + *buf = 0; + + mutex_lock(&mdev_state->rxtx_lock); + /* Interrupt priority 1: Parity, overrun, framing or break */ + if ((ier & UART_IER_RLSI) && mdev_state->s[index].overrun) + *buf |= UART_IIR_RLSI; + + /* Interrupt priority 2: Fifo trigger level reached */ + if ((ier & UART_IER_RDI) && + (mdev_state->s[index].rxtx.count == + mdev_state->s[index].intr_trigger_level)) + *buf |= UART_IIR_RDI; + + /* Interrupt priotiry 3: transmitter holding register empty */ + if ((ier & UART_IER_THRI) && + (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail)) + *buf |= UART_IIR_THRI; + + /* Interrupt priotiry 4: Modem status: CTS, DSR, RI or DCD */ + if ((ier & UART_IER_MSI) && + (mdev_state->s[index].uart_reg[UART_MCR] & + (UART_MCR_RTS | UART_MCR_DTR))) + *buf |= UART_IIR_MSI; + + /* bit0: 0=> interrupt pending, 1=> no interrupt is pending */ + if (*buf == 0) + *buf = UART_IIR_NO_INT; + + /* set bit 6 & 7 to be 16550 compatible */ + *buf |= 0xC0; + mutex_unlock(&mdev_state->rxtx_lock); + } + break; + + case UART_LCR: + case UART_MCR: + *buf = mdev_state->s[index].uart_reg[offset]; + break; + + case UART_LSR: + { + u8 lsr = 0; + + mutex_lock(&mdev_state->rxtx_lock); + /* atleast one char in FIFO */ + if (mdev_state->s[index].rxtx.head != + mdev_state->s[index].rxtx.tail) + lsr |= UART_LSR_DR; + + /* if FIFO overrun */ + if (mdev_state->s[index].overrun) + lsr |= UART_LSR_OE; + + /* transmit FIFO empty and tramsitter empty */ + if (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail) + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + + mutex_unlock(&mdev_state->rxtx_lock); + *buf = lsr; + break; + } + case UART_MSR: + *buf = UART_MSR_DSR | UART_MSR_DDSR | UART_MSR_DCD; + + mutex_lock(&mdev_state->rxtx_lock); + /* if AFE is 1 and FIFO have space, set CTS bit */ + if (mdev_state->s[index].uart_reg[UART_MCR] & + UART_MCR_AFE) { + if (mdev_state->s[index].rxtx.count < + mdev_state->s[index].max_fifo_size) + *buf |= UART_MSR_CTS | UART_MSR_DCTS; + } else + *buf |= UART_MSR_CTS | UART_MSR_DCTS; + mutex_unlock(&mdev_state->rxtx_lock); + + break; + + case UART_SCR: + *buf = mdev_state->s[index].uart_reg[offset]; + break; + + default: + break; + } +} + +static void mdev_read_base(struct mdev_state *mdev_state) +{ + int index, pos; + u32 start_lo, start_hi; + u32 mem_type; + + pos = PCI_BASE_ADDRESS_0; + + for (index = 0; index <= VFIO_PCI_BAR5_REGION_INDEX; index++) { + + if (!mdev_state->region_info[index].size) + continue; + + start_lo = (*(u32 *)(mdev_state->vconfig + pos)) & + PCI_BASE_ADDRESS_MEM_MASK; + mem_type = (*(u32 *)(mdev_state->vconfig + pos)) & + PCI_BASE_ADDRESS_MEM_TYPE_MASK; + + switch (mem_type) { + case PCI_BASE_ADDRESS_MEM_TYPE_64: + start_hi = (*(u32 *)(mdev_state->vconfig + pos + 4)); + pos += 4; + break; + case PCI_BASE_ADDRESS_MEM_TYPE_32: + case PCI_BASE_ADDRESS_MEM_TYPE_1M: + /* 1M mem BAR treated as 32-bit BAR */ + default: + /* mem unknown type treated as 32-bit BAR */ + start_hi = 0; + break; + } + pos += 4; + mdev_state->region_info[index].start = ((u64)start_hi << 32) | + start_lo; + } +} + +static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, + loff_t pos, bool is_write) +{ + struct mdev_state *mdev_state; + unsigned int index; + loff_t offset; + int ret = 0; + + if (!mdev || !buf) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) { + pr_err("%s mdev_state not found\n", __func__); + return -EINVAL; + } + + mutex_lock(&mdev_state->ops_lock); + + index = MTTY_VFIO_PCI_OFFSET_TO_INDEX(pos); + offset = pos & MTTY_VFIO_PCI_OFFSET_MASK; + switch (index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + +#if defined(DEBUG) + pr_info("%s: PCI config space %s at offset 0x%llx\n", + __func__, is_write ? "write" : "read", offset); +#endif + if (is_write) { + dump_buffer(buf, count); + handle_pci_cfg_write(mdev_state, offset, buf, count); + } else { + memcpy(buf, (mdev_state->vconfig + offset), count); + dump_buffer(buf, count); + } + + break; + + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + if (!mdev_state->region_info[index].start) + mdev_read_base(mdev_state); + + if (is_write) { + dump_buffer(buf, count); + +#if defined(DEBUG_REGS) + pr_info("%s: BAR%d WR @0x%llx %s val:0x%02x dlab:%d\n", + __func__, index, offset, wr_reg[offset], + (u8)*buf, mdev_state->s[index].dlab); +#endif + handle_bar_write(index, mdev_state, offset, buf, count); + } else { + handle_bar_read(index, mdev_state, offset, buf, count); + dump_buffer(buf, count); + +#if defined(DEBUG_REGS) + pr_info("%s: BAR%d RD @0x%llx %s val:0x%02x dlab:%d\n", + __func__, index, offset, rd_reg[offset], + (u8)*buf, mdev_state->s[index].dlab); +#endif + } + break; + + default: + ret = -1; + goto accessfailed; + } + + ret = count; + + +accessfailed: + mutex_unlock(&mdev_state->ops_lock); + + return ret; +} + +int mtty_create(struct kobject *kobj, struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + char name[MTTY_STRING_LEN]; + int nr_ports = 0, i; + + if (!mdev) + return -EINVAL; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(mdev->parent->dev), i + 1); + if (!strcmp(kobj->name, name)) { + nr_ports = i + 1; + break; + } + } + + if (!nr_ports) + return -EINVAL; + + mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); + if (mdev_state == NULL) + return -ENOMEM; + + mdev_state->nr_ports = nr_ports; + mdev_state->irq_index = -1; + mdev_state->s[0].max_fifo_size = MAX_FIFO_SIZE; + mdev_state->s[1].max_fifo_size = MAX_FIFO_SIZE; + mutex_init(&mdev_state->rxtx_lock); + mdev_state->vconfig = kzalloc(MTTY_CONFIG_SPACE_SIZE, GFP_KERNEL); + + if (mdev_state->vconfig == NULL) { + kfree(mdev_state); + return -ENOMEM; + } + + mutex_init(&mdev_state->ops_lock); + mdev_state->mdev = mdev; + mdev_set_drvdata(mdev, mdev_state); + + mtty_create_config_space(mdev_state); + + mutex_lock(&mdev_list_lock); + list_add(&mdev_state->next, &mdev_devices_list); + mutex_unlock(&mdev_list_lock); + + return 0; +} + +int mtty_remove(struct mdev_device *mdev) +{ + struct mdev_state *mds, *tmp_mds; + struct mdev_state *mdev_state = mdev_get_drvdata(mdev); + int ret = -EINVAL; + + mutex_lock(&mdev_list_lock); + list_for_each_entry_safe(mds, tmp_mds, &mdev_devices_list, next) { + if (mdev_state == mds) { + list_del(&mdev_state->next); + mdev_set_drvdata(mdev, NULL); + kfree(mdev_state->vconfig); + kfree(mdev_state); + ret = 0; + break; + } + } + mutex_unlock(&mdev_list_lock); + + return ret; +} + +int mtty_reset(struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + pr_info("%s: called\n", __func__); + + return 0; +} + +ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 2; + } else { + u8 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 1; + } + + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; + +read_err: + return -EFAULT; +} + +ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 2; + } else { + u8 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 1; + } + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; +write_err: + return -EFAULT; +} + +static int mtty_set_irqs(struct mdev_device *mdev, uint32_t flags, + unsigned int index, unsigned int start, + unsigned int count, void *data) +{ + int ret = 0; + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + mutex_lock(&mdev_state->ops_lock); + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + { + if (flags & VFIO_IRQ_SET_DATA_NONE) { + pr_info("%s: disable INTx\n", __func__); + if (mdev_state->intx_evtfd) + eventfd_ctx_put(mdev_state->intx_evtfd); + break; + } + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int fd = *(int *)data; + + if (fd > 0) { + struct eventfd_ctx *evt; + + evt = eventfd_ctx_fdget(fd); + if (IS_ERR(evt)) { + ret = PTR_ERR(evt); + break; + } + mdev_state->intx_evtfd = evt; + mdev_state->irq_fd = fd; + mdev_state->irq_index = index; + break; + } + } + break; + } + } + break; + case VFIO_PCI_MSI_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + if (flags & VFIO_IRQ_SET_DATA_NONE) { + if (mdev_state->msi_evtfd) + eventfd_ctx_put(mdev_state->msi_evtfd); + pr_info("%s: disable MSI\n", __func__); + mdev_state->irq_index = VFIO_PCI_INTX_IRQ_INDEX; + break; + } + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int fd = *(int *)data; + struct eventfd_ctx *evt; + + if (fd <= 0) + break; + + if (mdev_state->msi_evtfd) + break; + + evt = eventfd_ctx_fdget(fd); + if (IS_ERR(evt)) { + ret = PTR_ERR(evt); + break; + } + mdev_state->msi_evtfd = evt; + mdev_state->irq_fd = fd; + mdev_state->irq_index = index; + } + break; + } + break; + case VFIO_PCI_MSIX_IRQ_INDEX: + pr_info("%s: MSIX_IRQ\n", __func__); + break; + case VFIO_PCI_ERR_IRQ_INDEX: + pr_info("%s: ERR_IRQ\n", __func__); + break; + case VFIO_PCI_REQ_IRQ_INDEX: + pr_info("%s: REQ_IRQ\n", __func__); + break; + } + + mutex_unlock(&mdev_state->ops_lock); + return ret; +} + +static int mtty_trigger_interrupt(uuid_le uuid) +{ + int ret = -1; + struct mdev_state *mdev_state; + + mdev_state = find_mdev_state_by_uuid(uuid); + + if (!mdev_state) { + pr_info("%s: mdev not found\n", __func__); + return -EINVAL; + } + + if ((mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX) && + (!mdev_state->msi_evtfd)) + return -EINVAL; + else if ((mdev_state->irq_index == VFIO_PCI_INTX_IRQ_INDEX) && + (!mdev_state->intx_evtfd)) { + pr_info("%s: Intr eventfd not found\n", __func__); + return -EINVAL; + } + + if (mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX) + ret = eventfd_signal(mdev_state->msi_evtfd, 1); + else + ret = eventfd_signal(mdev_state->intx_evtfd, 1); + +#if defined(DEBUG_INTR) + pr_info("Intx triggered\n"); +#endif + if (ret != 1) + pr_err("%s: eventfd signal failed (%d)\n", __func__, ret); + + return ret; +} + +int mtty_get_region_info(struct mdev_device *mdev, + struct vfio_region_info *region_info, + u16 *cap_type_id, void **cap_type) +{ + unsigned int size = 0; + struct mdev_state *mdev_state; + int bar_index; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + mutex_lock(&mdev_state->ops_lock); + bar_index = region_info->index; + + switch (bar_index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + size = MTTY_CONFIG_SPACE_SIZE; + break; + case VFIO_PCI_BAR0_REGION_INDEX: + size = MTTY_IO_BAR_SIZE; + break; + case VFIO_PCI_BAR1_REGION_INDEX: + if (mdev_state->nr_ports == 2) + size = MTTY_IO_BAR_SIZE; + break; + default: + size = 0; + break; + } + + mdev_state->region_info[bar_index].size = size; + mdev_state->region_info[bar_index].vfio_offset = + MTTY_VFIO_PCI_INDEX_TO_OFFSET(bar_index); + + region_info->size = size; + region_info->offset = MTTY_VFIO_PCI_INDEX_TO_OFFSET(bar_index); + region_info->flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + mutex_unlock(&mdev_state->ops_lock); + return 0; +} + +int mtty_get_irq_info(struct mdev_device *mdev, struct vfio_irq_info *irq_info) +{ + switch (irq_info->index) { + case VFIO_PCI_INTX_IRQ_INDEX: + case VFIO_PCI_MSI_IRQ_INDEX: + case VFIO_PCI_REQ_IRQ_INDEX: + break; + + default: + return -EINVAL; + } + + irq_info->flags = VFIO_IRQ_INFO_EVENTFD; + irq_info->count = 1; + + if (irq_info->index == VFIO_PCI_INTX_IRQ_INDEX) + irq_info->flags |= (VFIO_IRQ_INFO_MASKABLE | + VFIO_IRQ_INFO_AUTOMASKED); + else + irq_info->flags |= VFIO_IRQ_INFO_NORESIZE; + + return 0; +} + +int mtty_get_device_info(struct mdev_device *mdev, + struct vfio_device_info *dev_info) +{ + dev_info->flags = VFIO_DEVICE_FLAGS_PCI; + dev_info->num_regions = VFIO_PCI_NUM_REGIONS; + dev_info->num_irqs = VFIO_PCI_NUM_IRQS; + + return 0; +} + +static long mtty_ioctl(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + unsigned long minsz; + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -ENODEV; + + switch (cmd) { + case VFIO_DEVICE_GET_INFO: + { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + ret = mtty_get_device_info(mdev, &info); + if (ret) + return ret; + + memcpy(&mdev_state->dev_info, &info, sizeof(info)); + + return copy_to_user((void __user *)arg, &info, minsz); + } + case VFIO_DEVICE_GET_REGION_INFO: + { + struct vfio_region_info info; + u16 cap_type_id = 0; + void *cap_type = NULL; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + ret = mtty_get_region_info(mdev, &info, &cap_type_id, + &cap_type); + if (ret) + return ret; + + return copy_to_user((void __user *)arg, &info, minsz); + } + + case VFIO_DEVICE_GET_IRQ_INFO: + { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if ((info.argsz < minsz) || + (info.index >= mdev_state->dev_info.num_irqs)) + return -EINVAL; + + ret = mtty_get_irq_info(mdev, &info); + if (ret) + return ret; + + if (info.count == -1) + return -EINVAL; + + return copy_to_user((void __user *)arg, &info, minsz); + } + case VFIO_DEVICE_SET_IRQS: + { + struct vfio_irq_set hdr; + u8 *data = NULL, *ptr = NULL; + size_t data_size = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + ret = vfio_set_irqs_validate_and_prepare(&hdr, + mdev_state->dev_info.num_irqs, + VFIO_PCI_NUM_IRQS, + &data_size); + if (ret) + return ret; + + if (data_size) { + ptr = data = memdup_user((void __user *)(arg + minsz), + data_size); + if (IS_ERR(data)) + return PTR_ERR(data); + } + + ret = mtty_set_irqs(mdev, hdr.flags, hdr.index, hdr.start, + hdr.count, data); + + kfree(ptr); + return ret; + } + case VFIO_DEVICE_RESET: + return mtty_reset(mdev); + } + return -ENOTTY; +} + +int mtty_open(struct mdev_device *mdev) +{ + pr_info("%s\n", __func__); + return 0; +} + +void mtty_close(struct mdev_device *mdev) +{ + pr_info("%s\n", __func__); +} + +static ssize_t +sample_mtty_dev_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "This is phy device\n"); +} + +static DEVICE_ATTR_RO(sample_mtty_dev); + +static struct attribute *mtty_dev_attrs[] = { + &dev_attr_sample_mtty_dev.attr, + NULL, +}; + +static const struct attribute_group mtty_dev_group = { + .name = "mtty_dev", + .attrs = mtty_dev_attrs, +}; + +const struct attribute_group *mtty_dev_groups[] = { + &mtty_dev_group, + NULL, +}; + +static ssize_t +sample_mdev_dev_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + if (mdev) + return sprintf(buf, "This is MDEV %s\n", dev_name(&mdev->dev)); + + return sprintf(buf, "\n"); +} + +static DEVICE_ATTR_RO(sample_mdev_dev); + +static struct attribute *mdev_dev_attrs[] = { + &dev_attr_sample_mdev_dev.attr, + NULL, +}; + +static const struct attribute_group mdev_dev_group = { + .name = "vendor", + .attrs = mdev_dev_attrs, +}; + +const struct attribute_group *mdev_dev_groups[] = { + &mdev_dev_group, + NULL, +}; + +static ssize_t +name_show(struct kobject *kobj, struct device *dev, char *buf) +{ + char name[MTTY_STRING_LEN]; + int i; + const char *name_str[2] = {"Single port serial", "Dual port serial"}; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(dev), i + 1); + if (!strcmp(kobj->name, name)) + return sprintf(buf, "%s\n", name_str[i]); + } + + return -EINVAL; +} + +MDEV_TYPE_ATTR_RO(name); + +static ssize_t +available_instances_show(struct kobject *kobj, struct device *dev, char *buf) +{ + char name[MTTY_STRING_LEN]; + int i; + struct mdev_state *mds; + int ports = 0, used = 0; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(dev), i + 1); + if (!strcmp(kobj->name, name)) { + ports = i + 1; + break; + } + } + + if (!ports) + return -EINVAL; + + list_for_each_entry(mds, &mdev_devices_list, next) + used += mds->nr_ports; + + return sprintf(buf, "%d\n", (MAX_MTTYS - used)/ports); +} + +MDEV_TYPE_ATTR_RO(available_instances); + + +static ssize_t device_api_show(struct kobject *kobj, struct device *dev, + char *buf) +{ + return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); +} + +MDEV_TYPE_ATTR_RO(device_api); + +static struct attribute *mdev_types_attrs[] = { + &mdev_type_attr_name.attr, + &mdev_type_attr_device_api.attr, + &mdev_type_attr_available_instances.attr, + NULL, +}; + +static struct attribute_group mdev_type_group1 = { + .name = "1", + .attrs = mdev_types_attrs, +}; + +static struct attribute_group mdev_type_group2 = { + .name = "2", + .attrs = mdev_types_attrs, +}; + +struct attribute_group *mdev_type_groups[] = { + &mdev_type_group1, + &mdev_type_group2, + NULL, +}; + +struct parent_ops mdev_fops = { + .owner = THIS_MODULE, + .dev_attr_groups = mtty_dev_groups, + .mdev_attr_groups = mdev_dev_groups, + .supported_type_groups = mdev_type_groups, + .create = mtty_create, + .remove = mtty_remove, + .open = mtty_open, + .release = mtty_close, + .read = mtty_read, + .write = mtty_write, + .ioctl = mtty_ioctl, +}; + +static void mtty_device_release(struct device *dev) +{ + dev_dbg(dev, "mtty: released\n"); +} + +static int __init mtty_dev_init(void) +{ + int ret = 0; + + pr_info("mtty_dev: %s\n", __func__); + + memset(&mtty_dev, 0, sizeof(mtty_dev)); + + idr_init(&mtty_dev.vd_idr); + + ret = alloc_chrdev_region(&mtty_dev.vd_devt, 0, MINORMASK, MTTY_NAME); + + if (ret < 0) { + pr_err("Error: failed to register mtty_dev, err:%d\n", ret); + return ret; + } + + cdev_init(&mtty_dev.vd_cdev, &vd_fops); + cdev_add(&mtty_dev.vd_cdev, mtty_dev.vd_devt, MINORMASK); + + pr_info("major_number:%d\n", MAJOR(mtty_dev.vd_devt)); + + mtty_dev.vd_class = class_create(THIS_MODULE, MTTY_CLASS_NAME); + + if (IS_ERR(mtty_dev.vd_class)) { + pr_err("Error: failed to register mtty_dev class\n"); + goto failed1; + } + + mtty_dev.dev.class = mtty_dev.vd_class; + mtty_dev.dev.release = mtty_device_release; + dev_set_name(&mtty_dev.dev, "%s", MTTY_NAME); + + ret = device_register(&mtty_dev.dev); + if (ret) + goto failed2; + + if (mdev_register_device(&mtty_dev.dev, &mdev_fops) != 0) + goto failed3; + + mutex_init(&mdev_list_lock); + INIT_LIST_HEAD(&mdev_devices_list); + + goto all_done; + +failed3: + + device_unregister(&mtty_dev.dev); +failed2: + class_destroy(mtty_dev.vd_class); + +failed1: + cdev_del(&mtty_dev.vd_cdev); + unregister_chrdev_region(mtty_dev.vd_devt, MINORMASK); + +all_done: + return ret; +} + +static void __exit mtty_dev_exit(void) +{ + mtty_dev.dev.bus = NULL; + mdev_unregister_device(&mtty_dev.dev); + + device_unregister(&mtty_dev.dev); + idr_destroy(&mtty_dev.vd_idr); + cdev_del(&mtty_dev.vd_cdev); + unregister_chrdev_region(mtty_dev.vd_devt, MINORMASK); + class_destroy(mtty_dev.vd_class); + mtty_dev.vd_class = NULL; + pr_info("mtty_dev: Unloaded!\n"); +} + +module_init(mtty_dev_init) +module_exit(mtty_dev_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_INFO(supported, "Test driver that simulate serial port over PCI"); +MODULE_VERSION(VERSION_STRING); +MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 53449a6ff6aa7..7c321a603b079 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -36,6 +36,7 @@ warning-2 += -Wshadow warning-2 += $(call cc-option, -Wlogical-op) warning-2 += $(call cc-option, -Wmissing-field-initializers) warning-2 += $(call cc-option, -Wsign-compare) +warning-2 += $(call cc-option, -Wmaybe-uninitialized) warning-3 := -Wbad-function-cast warning-3 += -Wcast-qual diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan index dd779c40c8e6a..3b1b13818d594 100644 --- a/scripts/Makefile.ubsan +++ b/scripts/Makefile.ubsan @@ -17,4 +17,8 @@ endif ifdef CONFIG_UBSAN_NULL CFLAGS_UBSAN += $(call cc-option, -fsanitize=null) endif + + # -fsanitize=* options makes GCC less smart than usual and + # increase number of 'maybe-uninitialized false-positives + CFLAGS_UBSAN += $(call cc-option, -Wno-maybe-uninitialized) endif diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index 19f5adfd877dc..d9ff038c1b284 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -8,6 +8,9 @@ # of the GNU General Public License, incorporated herein by reference. import sys, os, re +from signal import signal, SIGPIPE, SIG_DFL + +signal(SIGPIPE, SIG_DFL) if len(sys.argv) != 3: sys.stderr.write("usage: %s file1 file2\n" % sys.argv[0]) diff --git a/sound/core/info.c b/sound/core/info.c index 895362a696c95..8ab72e0f59329 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -325,10 +325,15 @@ static ssize_t snd_info_text_entry_write(struct file *file, size_t next; int err = 0; + if (!entry->c.text.write) + return -EIO; pos = *offset; if (!valid_pos(pos, count)) return -EIO; next = pos + count; + /* don't handle too large text inputs */ + if (next > 16 * 1024) + return -EIO; mutex_lock(&entry->access); buf = data->wbuffer; if (!buf) { @@ -366,7 +371,9 @@ static int snd_info_seq_show(struct seq_file *seq, void *p) struct snd_info_private_data *data = seq->private; struct snd_info_entry *entry = data->entry; - if (entry->c.text.read) { + if (!entry->c.text.read) { + return -EIO; + } else { data->rbuffer->buffer = (char *)seq; /* XXX hack! */ entry->c.text.read(entry, data->rbuffer); } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index ebc9fdfe64df6..698a014195152 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2501,7 +2501,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long 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; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b450a27588c8e..2096bb0835c8a 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1610,7 +1610,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device) 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; @@ -1691,7 +1691,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) } } 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; diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index 369cef212ea90..cd9e9f31720f2 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -528,7 +528,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, 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); diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 82259ca61e644..1ef7cdf1d3e81 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1907,7 +1907,7 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem) * 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. diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index edabe13716601..92bc06d012883 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -84,7 +84,7 @@ MODULE_DESCRIPTION("Avance Logic ALS4000"); 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 diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index 151815b857a0d..53abcd3eccbd6 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -36,7 +36,7 @@ #include #include -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define VORTEX_GAME_DWAIT 20 /* 20 ms */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 80c4a44561979..79b2e6b7d88b8 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -212,7 +212,7 @@ MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)"); 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 diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 73f593526b2d7..aeedc270ed9b0 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -48,7 +48,7 @@ MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738}," "{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 diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 615d8a99d8c87..8f0f5f24e40e7 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1194,7 +1194,7 @@ static void snd_cs4281_proc_init(struct cs4281 *chip) * 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) { diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 528102cc2d5d0..fde3cd48258ce 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2718,7 +2718,7 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) * 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) { diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index db7a2e5e4a14e..4733b68c9eb09 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -37,7 +37,7 @@ MODULE_LICENSE("GPL"); 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 #endif diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 7e760fed07282..51736c2b5a007 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -79,7 +79,7 @@ MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73}," "{Ectiva,EV1938}}"); #endif -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK #endif diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 6813558294845..e8d943071a8c2 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -72,7 +72,7 @@ MODULE_SUPPORTED_DEVICE("{{ESS,ES1938}," "{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 diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 8146fb76a4adf..2ec2b1ce0af6a 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -126,7 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e}," "{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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2f909dd8b7b85..48b8701eb5129 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -420,7 +420,7 @@ static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) } /* 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) { @@ -6907,8 +6907,6 @@ static const struct hda_fixup alc662_fixups[] = { .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x40f000f0 }, /* disabled */ { 0x16, 0x40f000f0 }, /* disabled */ - { 0x18, 0x01014011 }, /* LO */ - { 0x1a, 0x01014012 }, /* LO */ { } } }, diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 6a23302297c9c..4d9d320a79711 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -13,7 +13,8 @@ static void (*old_vmaster_hook)(void *, int); static bool is_thinkpad(struct hda_codec *codec) { return (codec->core.subsystem_id >> 16 == 0x17aa) && - (acpi_dev_found("LEN0068") || acpi_dev_found("IBM0068")); + (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") || + acpi_dev_found("IBM0068")); } static void update_tpacpi_mute_led(void *private_data, int enabled) diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index ada5f01d479c1..19c9df6b0f3df 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -110,7 +110,7 @@ #include #include -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index e1a13870bb804..a6aa48c5b9693 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -45,7 +45,7 @@ MODULE_DESCRIPTION("S3 SonicVibes PCI"); 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 diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 27f0ed8409798..92ad2d7a6bf8b 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3120,7 +3120,7 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device) * 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) { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 38a17b4342a65..2d8c14e3f8d29 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -72,7 +72,7 @@ MODULE_DESCRIPTION("VIA VT82xx audio"); 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 diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h index 149d4cb469988..aa9bb065f3856 100644 --- a/sound/pci/ymfpci/ymfpci.h +++ b/sound/pci/ymfpci/ymfpci.h @@ -176,7 +176,7 @@ #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 diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 18baea2f7d654..84f86745c30e9 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -148,11 +148,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"), }; static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { - { "Capture", NULL, "AINA" }, - { "Capture", NULL, "AINB" }, + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, - { "AOUTA", NULL, "Playback" }, - { "AOUTB", NULL, "Playback" }, + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, }; /** diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 1152aa5e7c394..cf37936bfe3aa 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -880,7 +880,8 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* DAI */ - SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL, + DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT), SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), /* Output Muxes */ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index b904492d77447..90b5948e0ff36 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -364,7 +364,12 @@ static int hdmi_of_xlate_dai_name(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name) { - int id = args->args[0]; + int id; + + if (args->args_count) + id = args->args[0]; + else + id = 0; if (id < ARRAY_SIZE(hdmi_dai_name)) { *dai_name = hdmi_dai_name[id]; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 55558643166fd..2db8179047ae8 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -249,6 +249,11 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic) snd_soc_dapm_force_enable_pin(dapm, "LDO1"); snd_soc_dapm_sync(dapm); + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL1, 0x1001, 0); + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL2, 0x4, 0x4); + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24); msleep(50); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 01a18d88f1eb1..00ff2788879e2 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -1547,11 +1547,11 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) msleep(sleep_time[i]); val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) & 0x0003; + dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n", + __func__, val, sleep_time[i]); i++; if (val == 0x1 || val == 0x2 || val == 0x3) break; - dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n", - __func__, val, sleep_time[i]); } dev_dbg(codec->dev, "%s val = %d\n", __func__, val); switch (val) { diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 7b31ee9b82bc8..d6e00c77edcd7 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -424,7 +424,7 @@ static const struct snd_soc_dai_ops stih407_dac_ops = { static const struct regmap_config stih407_sas_regmap = { .reg_bits = 32, .val_bits = 32, - + .fast_io = true, .max_register = STIH407_AUDIO_DAC_CTRL, .reg_defaults = stih407_sas_reg_defaults, .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index df5e5cb33baab..810369f687d71 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -341,20 +341,9 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec, return ret; } } - - gpiod_set_value(priv->pdn_gpio, 0); - usleep_range(5000, 6000); - - regcache_cache_only(priv->regmap, false); - ret = regcache_sync(priv->regmap); - if (ret) - return ret; } break; case SND_SOC_BIAS_OFF: - regcache_cache_only(priv->regmap, true); - gpiod_set_value(priv->pdn_gpio, 1); - if (!IS_ERR(priv->mclk)) clk_disable_unprepare(priv->mclk); break; @@ -401,16 +390,6 @@ static const struct snd_kcontrol_new tas5711_controls[] = { TAS571X_SOFT_MUTE_REG, TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 1, 1), - - SOC_DOUBLE_R_RANGE("CH1 Mixer Volume", - TAS5717_CH1_LEFT_CH_MIX_REG, - TAS5717_CH1_RIGHT_CH_MIX_REG, - 16, 0, 0x80, 0), - - SOC_DOUBLE_R_RANGE("CH2 Mixer Volume", - TAS5717_CH2_LEFT_CH_MIX_REG, - TAS5717_CH2_RIGHT_CH_MIX_REG, - 16, 0, 0x80, 0), }; static const struct regmap_range tas571x_readonly_regs_range[] = { @@ -488,6 +467,16 @@ static const struct snd_kcontrol_new tas5717_controls[] = { TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 1, 1), + SOC_DOUBLE_R_RANGE("CH1 Mixer Volume", + TAS5717_CH1_LEFT_CH_MIX_REG, + TAS5717_CH1_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + + SOC_DOUBLE_R_RANGE("CH2 Mixer Volume", + TAS5717_CH2_LEFT_CH_MIX_REG, + TAS5717_CH2_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + /* * The biquads are named according to the register names. * Please note that TI's TAS57xx Graphical Development Environment @@ -747,13 +736,14 @@ static int tas571x_i2c_probe(struct i2c_client *client, /* pulse the active low reset line for ~100us */ usleep_range(100, 200); gpiod_set_value(priv->reset_gpio, 0); - usleep_range(12000, 20000); + usleep_range(13500, 20000); } ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); if (ret) return ret; + usleep_range(50000, 60000); memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); priv->codec_driver.component_driver.controls = priv->chip->controls; @@ -770,9 +760,6 @@ static int tas571x_i2c_probe(struct i2c_client *client, return ret; } - regcache_cache_only(priv->regmap, true); - gpiod_set_value(priv->pdn_gpio, 1); - return snd_soc_register_codec(&client->dev, &priv->codec_driver, &tas571x_dai, 1); } diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 26eb5a0a55754..fd5d1e0910382 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -47,6 +47,7 @@ config SND_SOC_INTEL_SST_MATCH config SND_SOC_INTEL_HASWELL tristate + select SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_BAYTRAIL tristate @@ -56,7 +57,6 @@ config SND_SOC_INTEL_HASWELL_MACH depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM depends on DW_DMAC_CORE select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help @@ -138,7 +138,6 @@ config SND_SOC_INTEL_BROADWELL_MACH I2C_DESIGNWARE_PLATFORM depends on DW_DMAC_CORE select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT286 help diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index ba5c0d71720ab..0a88537ca58a1 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -416,6 +416,7 @@ static const struct dmi_system_id cht_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { } }; diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 6532b8f0ab2fc..865a21e557cce 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -130,8 +130,8 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &broxton_headset, NULL, 0); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2989c164dafe3..06fa5e85dd0e0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -674,7 +674,7 @@ static int skl_probe(struct pci_dev *pci, if (skl->nhlt == NULL) { err = -ENODEV; - goto out_free; + goto out_display_power_off; } skl_nhlt_update_topology_bin(skl); @@ -746,6 +746,9 @@ static int skl_probe(struct pci_dev *pci, skl_machine_device_unregister(skl); out_nhlt_free: skl_nhlt_free(skl->nhlt); +out_display_power_off: + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); out_free: skl->init_failed = 1; skl_free(ebus); @@ -785,8 +788,7 @@ static void skl_remove(struct pci_dev *pci) release_firmware(skl->tplg); - if (pci_dev_run_wake(pci)) - pm_runtime_get_noresume(&pci->dev); + pm_runtime_get_noresume(&pci->dev); /* codec removal, invoke bus_device_remove */ snd_hdac_ext_bus_device_remove(ebus); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f2bf8661dd21f..823b5a236d8dc 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -208,7 +208,7 @@ config SND_PXA2XX_SOC_IMOTE2 config SND_MMP_SOC_BROWNSTONE tristate "SoC Audio support for Marvell Brownstone" - depends on SND_MMP_SOC && MACH_BROWNSTONE + depends on SND_MMP_SOC && MACH_BROWNSTONE && I2C select SND_MMP_SOC_SSPA select MFD_WM8994 select SND_SOC_WM8994 diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 3cde9fb977fa7..eff3f9a8b685f 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -586,3 +586,6 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) return 0; } EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); + +MODULE_DESCRIPTION("QTi LPASS CPU Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index e2ff538a8aa5b..07000f53db449 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -61,7 +61,40 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - int ret; + struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; + int ret, dma_ch, dir = substream->stream; + struct lpass_pcm_data *data; + + data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->i2s_port = cpu_dai->driver->id; + runtime->private_data = data; + + if (v->alloc_dma_channel) + dma_ch = v->alloc_dma_channel(drvdata, dir); + if (dma_ch < 0) + return dma_ch; + + drvdata->substream[dma_ch] = substream; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_DMACTL_REG(v, dma_ch, dir), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + data->rdma_ch = dma_ch; + else + data->wrdma_ch = dma_ch; snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); @@ -80,13 +113,40 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) return 0; } +static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; + struct lpass_pcm_data *data; + int dma_ch, dir = substream->stream; + + data = runtime->private_data; + v = drvdata->variant; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + dma_ch = data->rdma_ch; + else + dma_ch = data->wrdma_ch; + + drvdata->substream[dma_ch] = NULL; + + if (v->free_dma_channel) + v->free_dma_channel(drvdata, dma_ch); + + return 0; +} + static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *pcm_data = drvdata->private_data; + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); @@ -179,7 +239,8 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *pcm_data = drvdata->private_data; + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int reg; int ret; @@ -203,7 +264,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *pcm_data = drvdata->private_data; + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; int ret, ch, dir = substream->stream; @@ -257,7 +319,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *pcm_data = drvdata->private_data; + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; int ret, ch, dir = substream->stream; @@ -333,7 +396,8 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *pcm_data = drvdata->private_data; + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; int ret, ch, dir = substream->stream; @@ -374,6 +438,7 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, static const struct snd_pcm_ops lpass_platform_pcm_ops = { .open = lpass_platform_pcmops_open, + .close = lpass_platform_pcmops_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = lpass_platform_pcmops_hw_params, .hw_free = lpass_platform_pcmops_hw_free, @@ -470,117 +535,45 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm_substream *psubstream, *csubstream; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_variant *v = drvdata->variant; int ret = -EINVAL; - struct lpass_pcm_data *data; size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->i2s_port = cpu_dai->driver->id; - drvdata->private_data = data; - psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (psubstream) { - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata, - SNDRV_PCM_STREAM_PLAYBACK); - - if (data->rdma_ch < 0) - return data->rdma_ch; - - drvdata->substream[data->rdma_ch] = psubstream; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, soc_runtime->platform->dev, size, &psubstream->dma_buffer); - if (ret) - goto playback_alloc_err; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); if (ret) { - dev_err(soc_runtime->dev, - "%s() error writing to rdmactl reg: %d\n", - __func__, ret); - goto capture_alloc_err; + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } } csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; if (csubstream) { - if (v->alloc_dma_channel) - data->wrdma_ch = v->alloc_dma_channel(drvdata, - SNDRV_PCM_STREAM_CAPTURE); - - if (data->wrdma_ch < 0) { - ret = data->wrdma_ch; - goto capture_alloc_err; - } - - drvdata->substream[data->wrdma_ch] = csubstream; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, soc_runtime->platform->dev, size, &csubstream->dma_buffer); - if (ret) - goto capture_alloc_err; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); if (ret) { - dev_err(soc_runtime->dev, - "%s() error writing to wrdmactl reg: %d\n", - __func__, ret); - goto capture_reg_err; + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + return ret; } + } return 0; - -capture_reg_err: - if (csubstream) - snd_dma_free_pages(&csubstream->dma_buffer); - -capture_alloc_err: - if (psubstream) - snd_dma_free_pages(&psubstream->dma_buffer); - - playback_alloc_err: - dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); - - return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_soc_pcm_runtime *rt; - struct lpass_data *drvdata; - struct lpass_pcm_data *data; - struct lpass_variant *v; struct snd_pcm_substream *substream; - int ch, i; + int i; for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { substream = pcm->streams[i].substream; if (substream) { - rt = substream->private_data; - drvdata = snd_soc_platform_get_drvdata(rt->platform); - data = drvdata->private_data; - - ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ? data->rdma_ch - : data->wrdma_ch; - v = drvdata->variant; - drvdata->substream[ch] = NULL; - if (v->free_dma_channel) - v->free_dma_channel(drvdata, ch); - snd_dma_free_pages(&substream->dma_buffer); substream->dma_buffer.area = NULL; substream->dma_buffer.addr = 0; diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 35b3cea8207d8..924971b6ded54 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -59,7 +59,6 @@ struct lpass_data { struct clk *pcnoc_mport_clk; struct clk *pcnoc_sway_clk; - void *private_data; }; /* Vairant data per each SOC */ diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index 97d6700b10093..cbc0023c2bc82 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -383,11 +383,6 @@ static int s3c_ac97_probe(struct platform_device *pdev) goto err4; } - ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component, - s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai)); - if (ret) - goto err5; - ret = samsung_asoc_dma_platform_register(&pdev->dev, ac97_pdata->dma_filter, NULL, NULL); @@ -396,6 +391,11 @@ static int s3c_ac97_probe(struct platform_device *pdev) goto err5; } + ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component, + s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai)); + if (ret) + goto err5; + return 0; err5: free_irq(irq_res->start, NULL); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 7e32cf4581f8a..7825bff45ae3a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1237,14 +1237,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get drvdata\n"); return -EFAULT; } - ret = devm_snd_soc_register_component(&sec_dai->pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); + ret = samsung_asoc_dma_platform_register(&pdev->dev, + sec_dai->filter, "tx-sec", NULL); if (ret != 0) return ret; - return samsung_asoc_dma_platform_register(&pdev->dev, - sec_dai->filter, "tx-sec", NULL); + return devm_snd_soc_register_component(&sec_dai->pdev->dev, + &samsung_i2s_component, + &sec_dai->i2s_dai_drv, 1); } pri_dai = i2s_alloc_dai(pdev, false); @@ -1314,6 +1314,11 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (quirks & QUIRK_PRI_6CHAN) pri_dai->i2s_dai_drv.playback.channels_max = 6; + ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, + NULL, NULL); + if (ret < 0) + goto err_disable_clk; + if (quirks & QUIRK_SEC_DAI) { sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { @@ -1353,10 +1358,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_free_dai; - ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, - NULL, NULL); - if (ret < 0) - goto err_free_dai; pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 43e367a9acc36..c484985812ed6 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -565,24 +565,25 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; + ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); + goto err5; + } + pm_runtime_enable(&pdev->dev); ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component, &s3c_pcm_dai[pdev->id], 1); if (ret != 0) { dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret); - goto err5; - } - - ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, - NULL, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); - goto err5; + goto err6; } return 0; - +err6: + pm_runtime_disable(&pdev->dev); err5: clk_disable_unprepare(pcm->pclk); err4: diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 3e89fbc0c51d0..0a4718207e6ec 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -168,19 +168,19 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev) s3c2412_i2s_pcm_stereo_in.addr = res->start + S3C2412_IISRXD; s3c2412_i2s_pcm_stereo_in.filter_data = pdata->dma_capture; - ret = s3c_i2sv2_register_component(&pdev->dev, -1, - &s3c2412_i2s_component, - &s3c2412_i2s_dai); + ret = samsung_asoc_dma_platform_register(&pdev->dev, + pdata->dma_filter, + NULL, NULL); if (ret) { - pr_err("failed to register the dai\n"); + pr_err("failed to register the DMA: %d\n", ret); return ret; } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - pdata->dma_filter, - NULL, NULL); + ret = s3c_i2sv2_register_component(&pdev->dev, -1, + &s3c2412_i2s_component, + &s3c2412_i2s_dai); if (ret) - pr_err("failed to register the DMA: %d\n", ret); + pr_err("failed to register the dai\n"); return ret; } diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index c78a936a30995..9052f6a7073ec 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -474,18 +474,18 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO; s3c24xx_i2s_pcm_stereo_in.filter_data = pdata->dma_capture; - ret = devm_snd_soc_register_component(&pdev->dev, - &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1); + ret = samsung_asoc_dma_platform_register(&pdev->dev, + pdata->dma_filter, + NULL, NULL); if (ret) { - pr_err("failed to register the dai\n"); + pr_err("failed to register the dma: %d\n", ret); return ret; } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - pdata->dma_filter, - NULL, NULL); + ret = devm_snd_soc_register_component(&pdev->dev, + &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1); if (ret) - pr_err("failed to register the dma: %d\n", ret); + pr_err("failed to register the dai\n"); return ret; } diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 26c1fbed4d354..779504f54bc07 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -416,15 +416,6 @@ static int spdif_probe(struct platform_device *pdev) goto err3; } - dev_set_drvdata(&pdev->dev, spdif); - - ret = devm_snd_soc_register_component(&pdev->dev, - &samsung_spdif_component, &samsung_spdif_dai, 1); - if (ret != 0) { - dev_err(&pdev->dev, "fail to register dai\n"); - goto err4; - } - spdif_stereo_out.addr_width = 2; spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF; filter = NULL; @@ -432,7 +423,6 @@ static int spdif_probe(struct platform_device *pdev) spdif_stereo_out.filter_data = spdif_pdata->dma_playback; filter = spdif_pdata->dma_filter; } - spdif->dma_playback = &spdif_stereo_out; ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, @@ -442,6 +432,15 @@ static int spdif_probe(struct platform_device *pdev) goto err4; } + dev_set_drvdata(&pdev->dev, spdif); + + ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_spdif_component, &samsung_spdif_dai, 1); + if (ret != 0) { + dev_err(&pdev->dev, "fail to register dai\n"); + goto err4; + } + return 0; err4: iounmap(spdif->regs); diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 1bc8ebc2528eb..ad54d4cf58ada 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -614,7 +614,11 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, iec958->status[3] = ucontrol->value.iec958.status[3]; mutex_unlock(&player->ctrl_lock); - uni_player_set_channel_status(player, NULL); + if (player->substream && player->substream->runtime) + uni_player_set_channel_status(player, + player->substream->runtime); + else + uni_player_set_channel_status(player, NULL); return 0; } diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index e047ec06d5382..56ed9472e89fe 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -765,11 +765,11 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) - return NULL; + return ERR_PTR(-ENOMEM); card->dai_link = sun4i_codec_create_link(dev, &card->num_links); if (!card->dai_link) - return NULL; + return ERR_PTR(-ENOMEM); card->dev = dev; card->name = "sun4i-codec"; @@ -829,12 +829,6 @@ static int sun4i_codec_probe(struct platform_device *pdev) return PTR_ERR(scodec->clk_module); } - /* Enable the bus clock */ - if (clk_prepare_enable(scodec->clk_apb)) { - dev_err(&pdev->dev, "Failed to enable the APB clock\n"); - return -EINVAL; - } - scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", GPIOD_OUT_LOW); if (IS_ERR(scodec->gpio_pa)) { @@ -844,6 +838,12 @@ static int sun4i_codec_probe(struct platform_device *pdev) return ret; } + /* Enable the bus clock */ + if (clk_prepare_enable(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to enable the APB clock\n"); + return -EINVAL; + } + /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; scodec->playback_dma_data.maxburst = 4; @@ -876,7 +876,8 @@ static int sun4i_codec_probe(struct platform_device *pdev) } card = sun4i_codec_create_card(&pdev->dev); - if (!card) { + if (IS_ERR(card)) { + ret = PTR_ERR(card); dev_err(&pdev->dev, "Failed to create our card\n"); goto err_unregister_codec; } diff --git a/sound/usb/card.c b/sound/usb/card.c index 9e5276d6dda05..2ddc034673a8e 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -315,7 +315,8 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); - dev_set_drvdata(&chip->dev->dev, NULL); + if (!atomic_read(&chip->shutdown)) + dev_set_drvdata(&chip->dev->dev, NULL); kfree(chip); return 0; } diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c index b4bf76971dc97..1eef0aed64239 100644 --- a/tools/power/cpupower/utils/cpufreq-set.c +++ b/tools/power/cpupower/utils/cpufreq-set.c @@ -296,7 +296,7 @@ int cmd_freq_set(int argc, char **argv) struct cpufreq_affected_cpus *cpus; if (!bitmask_isbitset(cpus_chosen, cpu) || - cpupower_is_cpu_online(cpu)) + cpupower_is_cpu_online(cpu) != 1) continue; cpus = cpufreq_get_related_cpus(cpu); @@ -316,10 +316,7 @@ int cmd_freq_set(int argc, char **argv) cpu <= bitmask_last(cpus_chosen); cpu++) { if (!bitmask_isbitset(cpus_chosen, cpu) || - cpupower_is_cpu_online(cpu)) - continue; - - if (cpupower_is_cpu_online(cpu) != 1) + cpupower_is_cpu_online(cpu) != 1) continue; printf(_("Setting cpu: %d\n"), cpu); diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c index e18b30ddcdce9..ebe1b9fa3c4d3 100644 --- a/virt/kvm/arm/vgic/vgic-mmio.c +++ b/virt/kvm/arm/vgic/vgic-mmio.c @@ -453,17 +453,33 @@ struct vgic_io_device *kvm_to_vgic_iodev(const struct kvm_io_device *dev) return container_of(dev, struct vgic_io_device, dev); } -static bool check_region(const struct vgic_register_region *region, +static bool check_region(const struct kvm *kvm, + const struct vgic_register_region *region, gpa_t addr, int len) { - if ((region->access_flags & VGIC_ACCESS_8bit) && len == 1) - return true; - if ((region->access_flags & VGIC_ACCESS_32bit) && - len == sizeof(u32) && !(addr & 3)) - return true; - if ((region->access_flags & VGIC_ACCESS_64bit) && - len == sizeof(u64) && !(addr & 7)) - return true; + int flags, nr_irqs = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; + + switch (len) { + case sizeof(u8): + flags = VGIC_ACCESS_8bit; + break; + case sizeof(u32): + flags = VGIC_ACCESS_32bit; + break; + case sizeof(u64): + flags = VGIC_ACCESS_64bit; + break; + default: + return false; + } + + if ((region->access_flags & flags) && IS_ALIGNED(addr, len)) { + if (!region->bits_per_irq) + return true; + + /* Do we access a non-allocated IRQ? */ + return VGIC_ADDR_TO_INTID(addr, region->bits_per_irq) < nr_irqs; + } return false; } @@ -477,7 +493,7 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions, addr - iodev->base_addr); - if (!region || !check_region(region, addr, len)) { + if (!region || !check_region(vcpu->kvm, region, addr, len)) { memset(val, 0, len); return 0; } @@ -510,10 +526,7 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions, addr - iodev->base_addr); - if (!region) - return 0; - - if (!check_region(region, addr, len)) + if (!region || !check_region(vcpu->kvm, region, addr, len)) return 0; switch (iodev->iodev_type) { diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h index 4c34d39d44a0e..84961b4e4422f 100644 --- a/virt/kvm/arm/vgic/vgic-mmio.h +++ b/virt/kvm/arm/vgic/vgic-mmio.h @@ -50,15 +50,15 @@ extern struct kvm_io_device_ops kvm_io_gic_ops; #define VGIC_ADDR_IRQ_MASK(bits) (((bits) * 1024 / 8) - 1) /* - * (addr & mask) gives us the byte offset for the INT ID, so we want to - * divide this with 'bytes per irq' to get the INT ID, which is given - * by '(bits) / 8'. But we do this with fixed-point-arithmetic and - * take advantage of the fact that division by a fraction equals - * multiplication with the inverted fraction, and scale up both the - * numerator and denominator with 8 to support at most 64 bits per IRQ: + * (addr & mask) gives us the _byte_ offset for the INT ID. + * We multiply this by 8 the get the _bit_ offset, then divide this by + * the number of bits to learn the actual INT ID. + * But instead of a division (which requires a "long long div" implementation), + * we shift by the binary logarithm of . + * This assumes that is a power of two. */ #define VGIC_ADDR_TO_INTID(addr, bits) (((addr) & VGIC_ADDR_IRQ_MASK(bits)) * \ - 64 / (bits) / 8) + 8 >> ilog2(bits)) /* * Some VGIC registers store per-IRQ information, with a different number diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c index 2893d5ba523ad..6440b56ec90e2 100644 --- a/virt/kvm/arm/vgic/vgic.c +++ b/virt/kvm/arm/vgic/vgic.c @@ -273,6 +273,18 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq) * no more work for us to do. */ spin_unlock(&irq->irq_lock); + + /* + * We have to kick the VCPU here, because we could be + * queueing an edge-triggered interrupt for which we + * get no EOI maintenance interrupt. In that case, + * while the IRQ is already on the VCPU's AP list, the + * VCPU could have EOI'ed the original interrupt and + * won't see this one until it exits for some other + * reason. + */ + if (vcpu) + kvm_vcpu_kick(vcpu); return false; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 5c360347a1e9f..fbf04c0c898cd 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -595,7 +595,7 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) stat_data->kvm = kvm; stat_data->offset = p->offset; kvm->debugfs_stat_data[p - debugfs_entries] = stat_data; - if (!debugfs_create_file(p->name, 0444, + if (!debugfs_create_file(p->name, 0644, kvm->debugfs_dentry, stat_data, stat_fops_per_vm[p->kind])) @@ -3661,11 +3661,23 @@ static int vm_stat_get_per_vm(void *data, u64 *val) return 0; } +static int vm_stat_clear_per_vm(void *data, u64 val) +{ + struct kvm_stat_data *stat_data = (struct kvm_stat_data *)data; + + if (val) + return -EINVAL; + + *(ulong *)((void *)stat_data->kvm + stat_data->offset) = 0; + + return 0; +} + static int vm_stat_get_per_vm_open(struct inode *inode, struct file *file) { __simple_attr_check_format("%llu\n", 0ull); return kvm_debugfs_open(inode, file, vm_stat_get_per_vm, - NULL, "%llu\n"); + vm_stat_clear_per_vm, "%llu\n"); } static const struct file_operations vm_stat_get_per_vm_fops = { @@ -3691,11 +3703,26 @@ static int vcpu_stat_get_per_vm(void *data, u64 *val) return 0; } +static int vcpu_stat_clear_per_vm(void *data, u64 val) +{ + int i; + struct kvm_stat_data *stat_data = (struct kvm_stat_data *)data; + struct kvm_vcpu *vcpu; + + if (val) + return -EINVAL; + + kvm_for_each_vcpu(i, vcpu, stat_data->kvm) + *(u64 *)((void *)vcpu + stat_data->offset) = 0; + + return 0; +} + static int vcpu_stat_get_per_vm_open(struct inode *inode, struct file *file) { __simple_attr_check_format("%llu\n", 0ull); return kvm_debugfs_open(inode, file, vcpu_stat_get_per_vm, - NULL, "%llu\n"); + vcpu_stat_clear_per_vm, "%llu\n"); } static const struct file_operations vcpu_stat_get_per_vm_fops = { @@ -3730,7 +3757,26 @@ static int vm_stat_get(void *_offset, u64 *val) return 0; } -DEFINE_SIMPLE_ATTRIBUTE(vm_stat_fops, vm_stat_get, NULL, "%llu\n"); +static int vm_stat_clear(void *_offset, u64 val) +{ + unsigned offset = (long)_offset; + struct kvm *kvm; + struct kvm_stat_data stat_tmp = {.offset = offset}; + + if (val) + return -EINVAL; + + spin_lock(&kvm_lock); + list_for_each_entry(kvm, &vm_list, vm_list) { + stat_tmp.kvm = kvm; + vm_stat_clear_per_vm((void *)&stat_tmp, 0); + } + spin_unlock(&kvm_lock); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(vm_stat_fops, vm_stat_get, vm_stat_clear, "%llu\n"); static int vcpu_stat_get(void *_offset, u64 *val) { @@ -3750,7 +3796,27 @@ static int vcpu_stat_get(void *_offset, u64 *val) return 0; } -DEFINE_SIMPLE_ATTRIBUTE(vcpu_stat_fops, vcpu_stat_get, NULL, "%llu\n"); +static int vcpu_stat_clear(void *_offset, u64 val) +{ + unsigned offset = (long)_offset; + struct kvm *kvm; + struct kvm_stat_data stat_tmp = {.offset = offset}; + + if (val) + return -EINVAL; + + spin_lock(&kvm_lock); + list_for_each_entry(kvm, &vm_list, vm_list) { + stat_tmp.kvm = kvm; + vcpu_stat_clear_per_vm((void *)&stat_tmp, 0); + } + spin_unlock(&kvm_lock); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(vcpu_stat_fops, vcpu_stat_get, vcpu_stat_clear, + "%llu\n"); static const struct file_operations *stat_fops[] = { [KVM_STAT_VCPU] = &vcpu_stat_fops, @@ -3768,7 +3834,7 @@ static int kvm_init_debug(void) kvm_debugfs_num_entries = 0; for (p = debugfs_entries; p->name; ++p, kvm_debugfs_num_entries++) { - if (!debugfs_create_file(p->name, 0444, kvm_debugfs_dir, + if (!debugfs_create_file(p->name, 0644, kvm_debugfs_dir, (void *)(long)p->offset, stat_fops[p->kind])) goto out_dir;