Merge remote-tracking branch 'takashi/for-next' into tag/merge-20191121

This commit is contained in:
Pierre-Louis Bossart 2019-11-21 13:10:57 -06:00
commit a07cc202ba
178 changed files with 3468 additions and 1482 deletions

View File

@ -805,6 +805,7 @@ destructor and PCI entries. Example code is shown first, below.
return -EBUSY;
}
chip->irq = pci->irq;
card->sync_irq = chip->irq;
/* (2) initialization of the chip hardware */
.... /* (not implemented in this document) */
@ -965,6 +966,15 @@ usually like the following:
return IRQ_HANDLED;
}
After requesting the IRQ, you can passed it to ``card->sync_irq``
field:
::
card->irq = chip->irq;
This allows PCM core automatically performing
:c:func:`synchronize_irq()` at the necessary timing like ``hw_free``.
See the later section `sync_stop callback`_ for details.
Now let's write the corresponding destructor for the resources above.
The role of destructor is simple: disable the hardware (if already
@ -1270,21 +1280,23 @@ shows only the skeleton, how to build up the PCM interfaces.
/* 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));
/* the hardware-specific codes will be here */
....
return 0;
}
/* hw_free callback */
static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
/* the hardware-specific codes will be here */
....
return 0;
}
/* prepare callback */
@ -1339,7 +1351,6 @@ shows only the skeleton, how to build up the PCM interfaces.
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,
@ -1351,7 +1362,6 @@ shows only the skeleton, how to build up the PCM interfaces.
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,
@ -1382,9 +1392,9 @@ shows only the skeleton, how to build up the PCM interfaces.
&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);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pci->dev,
64*1024, 64*1024);
return 0;
}
@ -1454,7 +1464,6 @@ 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,
@ -1465,13 +1474,14 @@ 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:
buffer and set up the managed allocation mode.
For that, 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);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pci->dev,
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
@ -1621,8 +1631,7 @@ 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
standard managed buffer allocation mode, you don't need to set the
DMA buffer information by yourself.
In the sections below, important records are explained.
@ -1776,8 +1785,8 @@ 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,
If you use either the managed buffer allocation mode or the standard
API 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
@ -1911,7 +1920,10 @@ 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()`.
leave it as NULL, then PCM core calls the generic ioctl callback
function :c:func:`snd_pcm_lib_ioctl()`. If you need to deal with the
unique setup of channel info or reset procedure, you can pass your own
callback function here.
hw_params callback
~~~~~~~~~~~~~~~~~~~
@ -1929,8 +1941,12 @@ 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,
:c:func:`params_xxx()` macros.
When you set up the managed buffer allocation mode for the substream,
a buffer is already allocated before this callback gets
called. Alternatively, you can call a helper function below for
allocating the buffer, too.
::
@ -1964,18 +1980,23 @@ 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);
``hw_params``.
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.
When you have set up the managed buffer allocation mode for the PCM
substream, the allocated PCM buffer will be automatically released
after this callback gets called. Otherwise you'll have to release the
buffer manually. Typically, when the buffer was allocated from the
pre-allocated pool, you can use the standard API function
:c:func:`snd_pcm_lib_malloc_pages()` like:
::
snd_pcm_lib_free_pages(substream);
prepare callback
~~~~~~~~~~~~~~~~
@ -2048,6 +2069,37 @@ flag set, and you cannot call functions which may sleep. The
triggering the DMA. The other stuff should be initialized
``hw_params`` and ``prepare`` callbacks properly beforehand.
sync_stop callback
~~~~~~~~~~~~~~~~~~
::
static int snd_xxx_sync_stop(struct snd_pcm_substream *substream);
This callback is optional, and NULL can be passed. It's called after
the PCM core stops the stream and changes the stream state
``prepare``, ``hw_params`` or ``hw_free``.
Since the IRQ handler might be still pending, we need to wait until
the pending task finishes before moving to the next step; otherwise it
might lead to a crash due to resource conflicts or access to the freed
resources. A typical behavior is to call a synchronization function
like :c:func:`synchronize_irq()` here.
For majority of drivers that need only a call of
:c:func:`synchronize_irq()`, there is a simpler setup, too.
While keeping NULL to ``sync_stop`` PCM callback, the driver can set
``card->sync_irq`` field to store the valid interrupt number after
requesting an IRQ, instead. Then PCM core will look call
:c:func:`synchronize_irq()` with the given IRQ appropriately.
If the IRQ handler is released at the card destructor, you don't need
to clear ``card->sync_irq``, as the card itself is being released.
So, usually you'll need to add just a single line for assigning
``card->sync_irq`` in the driver code unless the driver re-acquires
the IRQ. When the driver frees and re-acquires the IRQ dynamically
(e.g. for suspend/resume), it needs to clear and re-set
``card->sync_irq`` again appropriately.
pointer callback
~~~~~~~~~~~~~~~~
@ -2095,10 +2147,12 @@ 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.
This callback is optional too. The mmap calls this callback to get the
page fault address.
Since the recent changes, you need no special callback any longer for
the standard SG-buffer or vmalloc-buffer. Hence this callback should
be rarely used.
mmap calllback
~~~~~~~~~~~~~~
@ -3512,7 +3566,7 @@ bus).
::
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(pci), size, max);
&pci->dev, 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
@ -3523,12 +3577,14 @@ The second argument (type) and the third argument (device pointer) are
dependent on the bus. For normal devices, pass the device pointer
(typically identical as ``card->dev``) to 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
scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with the device
pointer (see the `Non-Contiguous Buffers`_
section).
bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type.
You can pass NULL to the device pointer in that case, which is the
default mode implying to allocate with ``GFP_KRENEL`` flag.
If you need a different GFP flag, you can pass it by encoding the flag
into the device pointer via a special macro
:c:func:`snd_dma_continuous_data()`.
For the scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with the
device pointer (see the `Non-Contiguous Buffers`_ section).
Once the buffer is pre-allocated, you can use the allocator in the
``hw_params`` callback:
@ -3539,6 +3595,25 @@ Once the buffer is pre-allocated, you can use the allocator in the
Note that you have to pre-allocate to use this function.
Most of drivers use, though, rather the newly introduced "managed
buffer allocation mode" instead of the manual allocation or release.
This is done by calling :c:func:`snd_pcm_set_managed_buffer_all()`
instead of :c:func:`snd_pcm_lib_preallocate_pages_for_all()`.
::
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&pci->dev, size, max);
where passed arguments are identical in both functions.
The difference in the managed mode is that PCM core will call
:c:func:`snd_pcm_lib_malloc_pages()` internally already before calling
the PCM ``hw_params`` callback, and call :c:func:`snd_pcm_lib_free_pages()`
after the PCM ``hw_free`` callback automatically. So the driver
doesn't have to call these functions explicitly in its callback any
longer. This made many driver code having NULL ``hw_params`` and
``hw_free`` entries.
External Hardware Buffers
-------------------------
@ -3693,20 +3768,26 @@ provides an interface for handling SG-buffers. The API is provided in
``<sound/pcm.h>``.
For creating the SG-buffer handler, call
:c:func:`snd_pcm_lib_preallocate_pages()` or
:c:func:`snd_pcm_lib_preallocate_pages_for_all()` with
:c:func:`snd_pcm_set_managed_buffer()` or
:c:func:`snd_pcm_set_managed_buffer_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
pre-allocator. You need to pass ``&pci->dev``, where pci is
the :c:type:`struct pci_dev <pci_dev>` pointer of the chip as
well. The ``struct snd_sg_buf`` instance is created as
``substream->dma_private``. You can cast the pointer like:
well.
::
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
&pci->dev, size, max);
The ``struct snd_sg_buf`` instance is created as
``substream->dma_private`` in turn. 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
Then in :c:func:`snd_pcm_lib_malloc_pages()` call, the common 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
@ -3715,41 +3796,40 @@ 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.
If you need to release the SG-buffer data explicitly, call the
standard API function :c:func:`snd_pcm_lib_free_pages()` 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 easiest way to achieve it would be to use
:c:func:`snd_pcm_lib_alloc_vmalloc_buffer()` for allocating the buffer
via :c:func:`vmalloc()`, and set :c:func:`snd_pcm_sgbuf_ops_page()` to
the ``page`` callback. At release, you need to call
:c:func:`snd_pcm_lib_free_vmalloc_buffer()`.
If you want to implementation the ``page`` manually, it would be like
this:
example, for an intermediate buffer. In the recent version of kernel,
you can simply allocate it via standard
:c:func:`snd_pcm_lib_malloc_pages()` and co after setting up the
buffer preallocation with ``SNDRV_DMA_TYPE_VMALLOC`` type.
::
#include <linux/vmalloc.h>
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
/* 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);
}
The NULL is passed to the device pointer argument, which indicates
that the default pages (GFP_KERNEL and GFP_HIGHMEM) will be
allocated.
Also, note that zero is passed to both the size and the max size
arguments here. Since each vmalloc call should succeed at any time,
we don't need to pre-allocate the buffers like other continuous
pages.
If you need the 32bit DMA allocation, pass the device pointer encoded
by :c:func:`snd_dma_continuous_data()` with ``GFP_KERNEL|__GFP_DMA32``
argument.
::
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
snd_dma_continuous_data(GFP_KERNEL | __GFP_DMA32), 0, 0);
Proc Interface
==============

View File

@ -353,7 +353,7 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev)
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
NULL,
G723_PERIOD_BYTES * PERIODS,
G723_PERIOD_BYTES * PERIODS);

View File

@ -300,7 +300,7 @@ static int tw686x_snd_pcm_init(struct tw686x_dev *dev)
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(dev->pci_dev),
&dev->pci_dev->dev,
TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,
TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX);
return 0;

View File

@ -378,8 +378,7 @@ int usbtv_audio_init(struct usbtv *usbtv)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usbtv_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), USBTV_AUDIO_BUFFER,
USBTV_AUDIO_BUFFER);
NULL, USBTV_AUDIO_BUFFER, USBTV_AUDIO_BUFFER);
rv = snd_card_register(card);
if (rv)

View File

@ -5854,6 +5854,24 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
return 0;
}
#ifdef CONFIG_ACPI
bool pci_pr3_present(struct pci_dev *pdev)
{
struct acpi_device *adev;
if (acpi_disabled)
return false;
adev = ACPI_COMPANION(&pdev->dev);
if (!adev)
return false;
return adev->power.flags.power_resources &&
acpi_has_method(adev->handle, "_PR3");
}
EXPORT_SYMBOL_GPL(pci_pr3_present);
#endif
/**
* pci_add_dma_alias - Add a DMA devfn alias for a device
* @dev: the PCI device for which alias is added

View File

@ -344,8 +344,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
pr_err("Requested number of channels not supported.\n");
return -EINVAL;
}
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}
/**
@ -359,7 +358,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
*/
static int pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
/**
@ -469,7 +468,6 @@ static const struct snd_pcm_ops pcm_ops = {
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static int split_arg_list(char *buf, u16 *ch_num, char **sample_res)
@ -663,6 +661,8 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id,
pcm->private_data = channel;
strscpy(pcm->name, device_name, sizeof(pcm->name));
snd_pcm_set_ops(pcm, direction, &pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;

View File

@ -585,7 +585,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
sprintf(card->longname, "%s %i", card_name, card->dev->id);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
NULL, 0, BUFF_SIZE_MAX);
err = snd_card_register(card);

View File

@ -2310,9 +2310,11 @@ struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus);
void
pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *));
bool pci_pr3_present(struct pci_dev *pdev);
#else
static inline struct irq_domain *
pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; }
static inline bool pci_pr3_present(struct pci_dev *pdev) { return false; }
#endif
#ifdef CONFIG_EEH

View File

@ -117,6 +117,7 @@ struct snd_card {
struct device card_dev; /* cardX object for sysfs */
const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
bool registered; /* card_dev is registered? */
int sync_irq; /* assigned irq, used for PCM sync */
wait_queue_head_t remove_sleep;
#ifdef CONFIG_PM

View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel-dsp-config.h - Intel DSP config
*
* Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
*/
#ifndef __INTEL_DSP_CONFIG_H__
#define __INTEL_DSP_CONFIG_H__
struct pci_dev;
enum {
SND_INTEL_DSP_DRIVER_ANY = 0,
SND_INTEL_DSP_DRIVER_LEGACY,
SND_INTEL_DSP_DRIVER_SST,
SND_INTEL_DSP_DRIVER_SOF,
SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_SOF
};
#if IS_ENABLED(CONFIG_SND_INTEL_DSP_CONFIG)
int snd_intel_dsp_driver_probe(struct pci_dev *pci);
#else
static inline int snd_intel_dsp_driver_probe(struct pci_dev *pci)
{
return SND_INTEL_DSP_DRIVER_ANY;
}
#endif
#endif

View File

@ -21,7 +21,6 @@ struct snd_dma_device {
struct device *dev; /* generic device */
};
#define snd_dma_pci_data(pci) (&(pci)->dev)
#define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x))
@ -44,6 +43,7 @@ struct snd_dma_device {
#else
#define SNDRV_DMA_TYPE_DEV_IRAM SNDRV_DMA_TYPE_DEV
#endif
#define SNDRV_DMA_TYPE_VMALLOC 7 /* vmalloc'ed buffer */
/*
* info for buffer allocation

View File

@ -59,6 +59,7 @@ struct snd_pcm_ops {
int (*hw_free)(struct snd_pcm_substream *substream);
int (*prepare)(struct snd_pcm_substream *substream);
int (*trigger)(struct snd_pcm_substream *substream, int cmd);
int (*sync_stop)(struct snd_pcm_substream *substream);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*get_time_info)(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts,
@ -395,6 +396,7 @@ struct snd_pcm_runtime {
wait_queue_head_t sleep; /* poll sleep */
wait_queue_head_t tsleep; /* transfer sleep */
struct fasync_struct *fasync;
bool stop_operating; /* sync_stop will be called */
/* -- private section -- */
void *private_data;
@ -414,6 +416,7 @@ struct snd_pcm_runtime {
size_t dma_bytes; /* size of DMA area */
struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
unsigned int buffer_changed:1; /* buffer allocation changed; set only in managed mode */
/* -- audio timestamp config -- */
struct snd_pcm_audio_tstamp_config audio_tstamp_config;
@ -475,6 +478,7 @@ struct snd_pcm_substream {
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/* misc flags */
unsigned int hw_opened: 1;
unsigned int managed_buffer_alloc:1;
};
#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0)
@ -1186,6 +1190,12 @@ void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
void snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,
struct device *data, size_t size, size_t max);
void snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
struct device *data,
size_t size, size_t max);
int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
size_t size, gfp_t gfp_flags);
int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream);
@ -1236,14 +1246,6 @@ static inline int snd_pcm_lib_alloc_vmalloc_32_buffer
*/
#define snd_pcm_substream_sgbuf(substream) \
snd_pcm_get_dma_buf(substream)->private_data
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned long offset);
#else /* !SND_DMA_SGBUF */
/*
* fake using a continuous buffer
*/
#define snd_pcm_sgbuf_ops_page NULL
#endif /* SND_DMA_SGBUF */
/**
@ -1336,8 +1338,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
(IEC958_AES1_CON_PCM_CODER<<8)|\
(IEC958_AES3_CON_FS_48000<<24))
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
const char *snd_pcm_format_name(snd_pcm_format_t format);
/**

View File

@ -118,8 +118,10 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer);
int snd_timer_global_free(struct snd_timer *timer);
int snd_timer_global_register(struct snd_timer *timer);
int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id);
int snd_timer_close(struct snd_timer_instance *timeri);
struct snd_timer_instance *snd_timer_instance_new(const char *owner);
void snd_timer_instance_free(struct snd_timer_instance *timeri);
int snd_timer_open(struct snd_timer_instance *timeri, struct snd_timer_id *tid, unsigned int slave_id);
void snd_timer_close(struct snd_timer_instance *timeri);
unsigned long snd_timer_resolution(struct snd_timer_instance *timeri);
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks);
int snd_timer_stop(struct snd_timer_instance *timeri);

View File

@ -1028,7 +1028,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
/* well, we really should support scatter/gather DMA */
snd_pcm_lib_preallocate_pages_for_all(
dev->pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)),
&macio_get_pci_dev(i2sdev->macio)->dev,
64 * 1024, 64 * 1024);
return 0;

View File

@ -72,11 +72,11 @@ config SND_PCM_OSS
config SND_PCM_OSS_PLUGINS
bool "OSS PCM (digital audio) API - Include plugin system"
depends on SND_PCM_OSS
default y
default y
help
If you disable this option, the ALSA's OSS PCM API will not
support conversion of channels, formats and rates. It will
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
If you disable this option, the ALSA's OSS PCM API will not
support conversion of channels, formats and rates. It will
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
config SND_PCM_TIMER
bool "PCM timer interface" if EXPERT
@ -128,13 +128,13 @@ config SND_SUPPORT_OLD_API
or older).
config SND_PROC_FS
bool "Sound Proc FS Support" if EXPERT
depends on PROC_FS
default y
help
Say 'N' to disable Sound proc FS, which may reduce code size about
9KB on x86_64 platform.
If unsure say Y.
bool "Sound Proc FS Support" if EXPERT
depends on PROC_FS
default y
help
Say 'N' to disable Sound proc FS, which may reduce code size about
9KB on x86_64 platform.
If unsure say Y.
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
@ -142,8 +142,8 @@ config SND_VERBOSE_PROCFS
default y
help
Say Y here to include code for verbose procfs contents (provides
useful information to developers when a problem occurs). On the
other side, it makes the ALSA subsystem larger.
useful information to developers when a problem occurs). On the
other side, it makes the ALSA subsystem larger.
config SND_VERBOSE_PRINTK
bool "Verbose printk"
@ -164,7 +164,7 @@ config SND_DEBUG_VERBOSE
depends on SND_DEBUG
help
Say Y here to enable extra-verbose debugging messages.
Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
So, say Y only if you are ready to be annoyed.

View File

@ -215,6 +215,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
init_waitqueue_head(&card->power_sleep);
#endif
init_waitqueue_head(&card->remove_sleep);
card->sync_irq = -1;
device_initialize(&card->card_dev);
card->card_dev.parent = parent;

View File

@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/genalloc.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_X86
#include <asm/set_memory.h>
#endif
@ -99,6 +100,14 @@ static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
*
*/
static inline gfp_t snd_mem_get_gfp_flags(const struct device *dev,
gfp_t default_gfp)
{
if (!dev)
return default_gfp;
else
return (__force gfp_t)(unsigned long)dev;
}
/**
* snd_dma_alloc_pages - allocate the buffer area according to the given type
@ -116,20 +125,25 @@ static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
{
gfp_t gfp;
if (WARN_ON(!size))
return -ENXIO;
if (WARN_ON(!dmab))
return -ENXIO;
if (WARN_ON(!device))
return -EINVAL;
dmab->dev.type = type;
dmab->dev.dev = device;
dmab->bytes = 0;
switch (type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
dmab->area = alloc_pages_exact(size,
(__force gfp_t)(unsigned long)device);
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL);
dmab->area = alloc_pages_exact(size, gfp);
dmab->addr = 0;
break;
case SNDRV_DMA_TYPE_VMALLOC:
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM);
dmab->area = __vmalloc(size, gfp, PAGE_KERNEL);
dmab->addr = 0;
break;
#ifdef CONFIG_HAS_DMA
@ -215,6 +229,9 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
case SNDRV_DMA_TYPE_CONTINUOUS:
free_pages_exact(dmab->area, dmab->bytes);
break;
case SNDRV_DMA_TYPE_VMALLOC:
vfree(dmab->area);
break;
#ifdef CONFIG_HAS_DMA
#ifdef CONFIG_GENERIC_ALLOCATOR
case SNDRV_DMA_TYPE_DEV_IRAM:

View File

@ -67,4 +67,11 @@ static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
void __snd_pcm_xrun(struct snd_pcm_substream *substream);
void snd_pcm_group_init(struct snd_pcm_group *group);
#ifdef CONFIG_SND_DMA_SGBUF
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned long offset);
#endif
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
#endif /* __SOUND_CORE_PCM_LOCAL_H */

View File

@ -15,6 +15,7 @@
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/initval.h>
#include "pcm_local.h"
static int preallocate_dma = 1;
module_param(preallocate_dma, int, 0444);
@ -193,9 +194,15 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
/*
* pre-allocate the buffer and create a proc file for the substream
*/
static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
size_t size, size_t max)
static void preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max, bool managed)
{
if (snd_BUG_ON(substream->dma_buffer.dev.type))
return;
substream->dma_buffer.dev.type = type;
substream->dma_buffer.dev.dev = data;
if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
preallocate_pcm_pages(substream, size);
@ -203,9 +210,25 @@ static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
if (substream->dma_buffer.bytes > 0)
substream->buffer_bytes_max = substream->dma_buffer.bytes;
substream->dma_max = max;
preallocate_info_init(substream);
if (max > 0)
preallocate_info_init(substream);
if (managed)
substream->managed_buffer_alloc = 1;
}
static void preallocate_pages_for_all(struct snd_pcm *pcm, int type,
void *data, size_t size, size_t max,
bool managed)
{
struct snd_pcm_substream *substream;
int stream;
for (stream = 0; stream < 2; stream++)
for (substream = pcm->streams[stream].substream; substream;
substream = substream->next)
preallocate_pages(substream, type, data, size, max,
managed);
}
/**
* snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
@ -221,9 +244,7 @@ void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max)
{
substream->dma_buffer.dev.type = type;
substream->dma_buffer.dev.dev = data;
snd_pcm_lib_preallocate_pages1(substream, size, max);
preallocate_pages(substream, type, data, size, max, false);
}
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
@ -242,17 +263,57 @@ void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data,
size_t size, size_t max)
{
struct snd_pcm_substream *substream;
int stream;
for (stream = 0; stream < 2; stream++)
for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
snd_pcm_lib_preallocate_pages(substream, type, data, size, max);
preallocate_pages_for_all(pcm, type, data, size, max, false);
}
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
#ifdef CONFIG_SND_DMA_SGBUF
/**
* snd_pcm_set_managed_buffer - set up buffer management for a substream
* @substream: the pcm substream instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
* @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation for the given DMA buffer type, and set the managed
* buffer allocation mode to the given substream.
* In this mode, PCM core will allocate a buffer automatically before PCM
* hw_params ops call, and release the buffer after PCM hw_free ops call
* as well, so that the driver doesn't need to invoke the allocation and
* the release explicitly in its callback.
* When a buffer is actually allocated before the PCM hw_params call, it
* turns on the runtime buffer_changed flag for drivers changing their h/w
* parameters accordingly.
*/
void snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,
struct device *data, size_t size, size_t max)
{
preallocate_pages(substream, type, data, size, max, true);
}
EXPORT_SYMBOL(snd_pcm_set_managed_buffer);
/**
* snd_pcm_set_managed_buffer_all - set up buffer management for all substreams
* for all substreams
* @pcm: the pcm instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
* @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation to all substreams of the given pcm for the specified DMA
* type and size, and set the managed_buffer_alloc flag to each substream.
*/
void snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
struct device *data,
size_t size, size_t max)
{
preallocate_pages_for_all(pcm, type, data, size, max, true);
}
EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);
#ifdef CONFIG_SND_DMA_SGBUF
/*
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
* @substream: the pcm substream instance
* @offset: the buffer offset
@ -270,7 +331,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
return NULL;
return sgbuf->page_table[idx];
}
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
#endif /* CONFIG_SND_DMA_SGBUF */
/**

View File

@ -13,6 +13,7 @@
#include <linux/pm_qos.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@ -177,6 +178,16 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
/* Run PCM ioctl ops */
static int snd_pcm_ops_ioctl(struct snd_pcm_substream *substream,
unsigned cmd, void *arg)
{
if (substream->ops->ioctl)
return substream->ops->ioctl(substream, cmd, arg);
else
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
{
struct snd_pcm *pcm = substream->pcm;
@ -222,7 +233,8 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream)
return false;
if (substream->ops->mmap ||
substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV)
(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV &&
substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV_UC))
return true;
return dma_can_mmap(substream->dma_buffer.dev.dev);
@ -446,8 +458,9 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
if (snd_mask_single(m) && snd_interval_single(i)) {
err = substream->ops->ioctl(substream,
SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
err = snd_pcm_ops_ioctl(substream,
SNDRV_PCM_IOCTL1_FIFO_SIZE,
params);
if (err < 0)
return err;
}
@ -555,6 +568,17 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
#endif
}
static void snd_pcm_sync_stop(struct snd_pcm_substream *substream)
{
if (substream->runtime->stop_operating) {
substream->runtime->stop_operating = false;
if (substream->ops->sync_stop)
substream->ops->sync_stop(substream);
else if (substream->pcm->card->sync_irq > 0)
synchronize_irq(substream->pcm->card->sync_irq);
}
}
/**
* snd_pcm_hw_param_choose - choose a configuration defined by @params
* @pcm: PCM instance
@ -647,6 +671,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (atomic_read(&substream->mmap_count))
return -EBADFD;
snd_pcm_sync_stop(substream);
params->rmask = ~0U;
err = snd_pcm_hw_refine(substream, params);
if (err < 0)
@ -660,6 +686,14 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
goto _error;
if (substream->managed_buffer_alloc) {
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(params));
if (err < 0)
goto _error;
runtime->buffer_changed = err > 0;
}
if (substream->ops->hw_params != NULL) {
err = substream->ops->hw_params(substream, params);
if (err < 0)
@ -721,6 +755,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream);
if (substream->managed_buffer_alloc)
snd_pcm_lib_free_pages(substream);
return err;
}
@ -765,8 +801,11 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
snd_pcm_stream_unlock_irq(substream);
if (atomic_read(&substream->mmap_count))
return -EBADFD;
snd_pcm_sync_stop(substream);
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
if (substream->managed_buffer_alloc)
snd_pcm_lib_free_pages(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
pm_qos_remove_request(&substream->latency_pm_qos_req);
return result;
@ -957,7 +996,7 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
return -EINVAL;
memset(info, 0, sizeof(*info));
info->channel = channel;
return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
return snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
}
static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
@ -1288,6 +1327,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
runtime->status->state = state;
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
runtime->stop_operating = true;
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@ -1564,6 +1604,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
snd_pcm_trigger_tstamp(substream);
runtime->status->state = runtime->status->suspended_state;
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
snd_pcm_sync_stop(substream);
}
static const struct action_ops snd_pcm_action_resume = {
@ -1633,7 +1674,7 @@ static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
if (err < 0)
return err;
runtime->hw_ptr_base = 0;
@ -1684,6 +1725,7 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
{
int err;
snd_pcm_sync_stop(substream);
err = substream->ops->prepare(substream);
if (err < 0)
return err;
@ -3334,7 +3376,18 @@ static inline struct page *
snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
{
void *vaddr = substream->runtime->dma_area + ofs;
return virt_to_page(vaddr);
switch (substream->dma_buffer.dev.type) {
#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
case SNDRV_DMA_TYPE_DEV_UC_SG:
return snd_pcm_sgbuf_ops_page(substream, ofs);
#endif /* CONFIG_SND_DMA_SGBUF */
case SNDRV_DMA_TYPE_VMALLOC:
return vmalloc_to_page(vaddr);
default:
return virt_to_page(vaddr);
}
}
/*
@ -3403,7 +3456,8 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
#endif /* CONFIG_GENERIC_ALLOCATOR */
#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
(substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
return dma_mmap_coherent(substream->dma_buffer.dev.dev,
area,
substream->runtime->dma_area,

View File

@ -272,7 +272,13 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
return -EINVAL;
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
t = snd_timer_instance_new(str);
if (!t)
return -ENOMEM;
t->callback = snd_seq_timer_interrupt;
t->callback_data = q;
t->flags |= SNDRV_TIMER_IFLG_AUTO;
err = snd_timer_open(t, &tmr->alsa_id, q->queue);
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
@ -282,16 +288,14 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
err = snd_timer_open(&t, str, &tid, q->queue);
err = snd_timer_open(t, &tid, q->queue);
}
}
if (err < 0) {
pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
snd_timer_instance_free(t);
return err;
}
t->callback = snd_seq_timer_interrupt;
t->callback_data = q;
t->flags |= SNDRV_TIMER_IFLG_AUTO;
spin_lock_irq(&tmr->lock);
tmr->timeri = t;
spin_unlock_irq(&tmr->lock);
@ -310,8 +314,10 @@ int snd_seq_timer_close(struct snd_seq_queue *q)
t = tmr->timeri;
tmr->timeri = NULL;
spin_unlock_irq(&tmr->lock);
if (t)
if (t) {
snd_timer_close(t);
snd_timer_instance_free(t);
}
return 0;
}

View File

@ -74,6 +74,9 @@ static LIST_HEAD(snd_timer_slave_list);
/* lock for slave active lists */
static DEFINE_SPINLOCK(slave_active_lock);
#define MAX_SLAVE_INSTANCES 1000
static int num_slaves;
static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
@ -85,12 +88,11 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
/*
* create a timer instance with the given owner string.
* when timer is not NULL, increments the module counter
*/
static struct snd_timer_instance *snd_timer_instance_new(char *owner,
struct snd_timer *timer)
struct snd_timer_instance *snd_timer_instance_new(const char *owner)
{
struct snd_timer_instance *timeri;
timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
if (timeri == NULL)
return NULL;
@ -105,15 +107,20 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
INIT_LIST_HEAD(&timeri->slave_list_head);
INIT_LIST_HEAD(&timeri->slave_active_head);
timeri->timer = timer;
if (timer && !try_module_get(timer->module)) {
kfree(timeri->owner);
kfree(timeri);
return NULL;
}
return timeri;
}
EXPORT_SYMBOL(snd_timer_instance_new);
void snd_timer_instance_free(struct snd_timer_instance *timeri)
{
if (timeri) {
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
}
}
EXPORT_SYMBOL(snd_timer_instance_free);
/*
* find a timer instance from the given timer id
@ -160,6 +167,28 @@ static void snd_timer_request(struct snd_timer_id *tid)
#endif
/* move the slave if it belongs to the master; return 1 if match */
static int check_matching_master_slave(struct snd_timer_instance *master,
struct snd_timer_instance *slave)
{
if (slave->slave_class != master->slave_class ||
slave->slave_id != master->slave_id)
return 0;
if (master->timer->num_instances >= master->timer->max_instances)
return -EBUSY;
list_move_tail(&slave->open_list, &master->slave_list_head);
master->timer->num_instances++;
spin_lock_irq(&slave_active_lock);
spin_lock(&master->timer->lock);
slave->master = master;
slave->timer = master->timer;
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
list_add_tail(&slave->active_list, &master->slave_active_head);
spin_unlock(&master->timer->lock);
spin_unlock_irq(&slave_active_lock);
return 1;
}
/*
* look for a master instance matching with the slave id of the given slave.
* when found, relink the open_link of the slave.
@ -170,27 +199,18 @@ static int snd_timer_check_slave(struct snd_timer_instance *slave)
{
struct snd_timer *timer;
struct snd_timer_instance *master;
int err = 0;
/* FIXME: it's really dumb to look up all entries.. */
list_for_each_entry(timer, &snd_timer_list, device_list) {
list_for_each_entry(master, &timer->open_list_head, open_list) {
if (slave->slave_class == master->slave_class &&
slave->slave_id == master->slave_id) {
if (master->timer->num_instances >=
master->timer->max_instances)
return -EBUSY;
list_move_tail(&slave->open_list,
&master->slave_list_head);
master->timer->num_instances++;
spin_lock_irq(&slave_active_lock);
slave->master = master;
slave->timer = master->timer;
spin_unlock_irq(&slave_active_lock);
return 0;
}
err = check_matching_master_slave(master, slave);
if (err != 0) /* match found or error */
goto out;
}
}
return 0;
out:
return err < 0 ? err : 0;
}
/*
@ -202,43 +222,29 @@ static int snd_timer_check_slave(struct snd_timer_instance *slave)
static int snd_timer_check_master(struct snd_timer_instance *master)
{
struct snd_timer_instance *slave, *tmp;
int err = 0;
/* check all pending slaves */
list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
if (slave->slave_class == master->slave_class &&
slave->slave_id == master->slave_id) {
if (master->timer->num_instances >=
master->timer->max_instances)
return -EBUSY;
list_move_tail(&slave->open_list, &master->slave_list_head);
master->timer->num_instances++;
spin_lock_irq(&slave_active_lock);
spin_lock(&master->timer->lock);
slave->master = master;
slave->timer = master->timer;
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
list_add_tail(&slave->active_list,
&master->slave_active_head);
spin_unlock(&master->timer->lock);
spin_unlock_irq(&slave_active_lock);
}
err = check_matching_master_slave(master, slave);
if (err < 0)
break;
}
return 0;
return err < 0 ? err : 0;
}
static int snd_timer_close_locked(struct snd_timer_instance *timeri,
struct device **card_devp_to_put);
static void snd_timer_close_locked(struct snd_timer_instance *timeri,
struct device **card_devp_to_put);
/*
* open a timer instance
* when opening a master, the slave id must be here given.
*/
int snd_timer_open(struct snd_timer_instance **ti,
char *owner, struct snd_timer_id *tid,
int snd_timer_open(struct snd_timer_instance *timeri,
struct snd_timer_id *tid,
unsigned int slave_id)
{
struct snd_timer *timer;
struct snd_timer_instance *timeri = NULL;
struct device *card_dev_to_put = NULL;
int err;
@ -252,21 +258,17 @@ int snd_timer_open(struct snd_timer_instance **ti,
err = -EINVAL;
goto unlock;
}
timeri = snd_timer_instance_new(owner, NULL);
if (!timeri) {
err = -ENOMEM;
if (num_slaves >= MAX_SLAVE_INSTANCES) {
err = -EBUSY;
goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
num_slaves++;
err = snd_timer_check_slave(timeri);
if (err < 0) {
snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
goto unlock;
goto list_added;
}
/* open a master instance */
@ -296,45 +298,40 @@ int snd_timer_open(struct snd_timer_instance **ti,
err = -EBUSY;
goto unlock;
}
timeri = snd_timer_instance_new(owner, timer);
if (!timeri) {
err = -ENOMEM;
if (!try_module_get(timer->module)) {
err = -EBUSY;
goto unlock;
}
/* take a card refcount for safe disconnection */
if (timer->card)
if (timer->card) {
get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
card_dev_to_put = &timer->card->card_dev;
}
if (list_empty(&timer->open_list_head) && timer->hw.open) {
err = timer->hw.open(timer);
if (err) {
kfree(timeri->owner);
kfree(timeri);
timeri = NULL;
if (timer->card)
card_dev_to_put = &timer->card->card_dev;
module_put(timer->module);
goto unlock;
}
}
timeri->timer = timer;
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
list_add_tail(&timeri->open_list, &timer->open_list_head);
timer->num_instances++;
err = snd_timer_check_master(timeri);
if (err < 0) {
list_added:
if (err < 0)
snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
unlock:
mutex_unlock(&register_mutex);
/* put_device() is called after unlock for avoiding deadlock */
if (card_dev_to_put)
if (err < 0 && card_dev_to_put)
put_device(card_dev_to_put);
*ti = timeri;
return err;
}
EXPORT_SYMBOL(snd_timer_open);
@ -343,8 +340,8 @@ EXPORT_SYMBOL(snd_timer_open);
* close a timer instance
* call this with register_mutex down.
*/
static int snd_timer_close_locked(struct snd_timer_instance *timeri,
struct device **card_devp_to_put)
static void snd_timer_close_locked(struct snd_timer_instance *timeri,
struct device **card_devp_to_put)
{
struct snd_timer *timer = timeri->timer;
struct snd_timer_instance *slave, *tmp;
@ -355,7 +352,11 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri,
spin_unlock_irq(&timer->lock);
}
list_del(&timeri->open_list);
if (!list_empty(&timeri->open_list)) {
list_del_init(&timeri->open_list);
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
num_slaves--;
}
/* force to stop the timer */
snd_timer_stop(timeri);
@ -374,6 +375,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri,
/* remove slave links */
spin_lock_irq(&slave_active_lock);
spin_lock(&timer->lock);
timeri->timer = NULL;
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
open_list) {
list_move_tail(&slave->open_list, &snd_timer_slave_list);
@ -391,11 +393,6 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri,
timer = NULL;
}
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
if (timer) {
if (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer);
@ -404,28 +401,24 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri,
*card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
}
return 0;
}
/*
* close a timer instance
*/
int snd_timer_close(struct snd_timer_instance *timeri)
void snd_timer_close(struct snd_timer_instance *timeri)
{
struct device *card_dev_to_put = NULL;
int err;
if (snd_BUG_ON(!timeri))
return -ENXIO;
return;
mutex_lock(&register_mutex);
err = snd_timer_close_locked(timeri, &card_dev_to_put);
snd_timer_close_locked(timeri, &card_dev_to_put);
mutex_unlock(&register_mutex);
/* put_device() is called after unlock for avoiding deadlock */
if (card_dev_to_put)
put_device(card_dev_to_put);
return err;
}
EXPORT_SYMBOL(snd_timer_close);
@ -1474,8 +1467,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
tu = file->private_data;
file->private_data = NULL;
mutex_lock(&tu->ioctl_lock);
if (tu->timeri)
if (tu->timeri) {
snd_timer_close(tu->timeri);
snd_timer_instance_free(tu->timeri);
}
mutex_unlock(&tu->ioctl_lock);
kfree(tu->queue);
kfree(tu->tqueue);
@ -1716,6 +1711,7 @@ static int snd_timer_user_tselect(struct file *file,
tu = file->private_data;
if (tu->timeri) {
snd_timer_close(tu->timeri);
snd_timer_instance_free(tu->timeri);
tu->timeri = NULL;
}
if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
@ -1725,9 +1721,11 @@ static int snd_timer_user_tselect(struct file *file,
sprintf(str, "application %i", current->pid);
if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid);
if (err < 0)
tu->timeri = snd_timer_instance_new(str);
if (!tu->timeri) {
err = -ENOMEM;
goto __err;
}
tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
tu->timeri->callback = tu->tread
@ -1736,6 +1734,12 @@ static int snd_timer_user_tselect(struct file *file,
tu->timeri->callback_data = (void *)tu;
tu->timeri->disconnect = snd_timer_user_disconnect;
err = snd_timer_open(tu->timeri, &tselect.id, current->pid);
if (err < 0) {
snd_timer_instance_free(tu->timeri);
tu->timeri = NULL;
}
__err:
return err;
}

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config SND_MPU401_UART
tristate
select SND_RAWMIDI
tristate
select SND_RAWMIDI
config SND_OPL3_LIB
tristate
@ -90,16 +90,16 @@ config SND_DUMMY
will be called snd-dummy.
config SND_ALOOP
tristate "Generic loopback driver (PCM)"
select SND_PCM
help
Say 'Y' or 'M' to include support for the PCM loopback device.
tristate "Generic loopback driver (PCM)"
select SND_PCM
help
Say 'Y' or 'M' to include support for the PCM loopback device.
This module returns played samples back to the user space using
the standard ALSA PCM device. The devices are routed 0->1 and
1->0, where first number is the playback PCM device and second
1->0, where first number is the playback PCM device and second
number is the capture device. Module creates two PCM devices and
configured number of substreams (see the pcm_substreams module
parameter).
parameter).
The loopback device allows time sychronization with an external
timing source using the time shift universal control (+-20%
@ -142,12 +142,12 @@ config SND_MTS64
select SND_RAWMIDI
help
The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
additional SMPTE Timecode capabilities for the parallel port.
additional SMPTE Timecode capabilities for the parallel port.
Say 'Y' to include support for this device.
To compile this driver as a module, chose 'M' here: the module
will be called snd-mts64.
will be called snd-mts64.
config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver"

View File

@ -28,6 +28,7 @@
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/timer.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("A loopback soundcard");
@ -41,6 +42,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
static int pcm_notify[SNDRV_CARDS];
static char *timer_source[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
@ -52,11 +54,48 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
module_param_array(pcm_notify, int, NULL, 0444);
MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
module_param_array(timer_source, charp, NULL, 0444);
MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default].");
#define NO_PITCH 100000
#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE)
struct loopback_cable;
struct loopback_pcm;
struct loopback_ops {
/* optional
* call in loopback->cable_lock
*/
int (*open)(struct loopback_pcm *dpcm);
/* required
* call in cable->lock
*/
int (*start)(struct loopback_pcm *dpcm);
/* required
* call in cable->lock
*/
int (*stop)(struct loopback_pcm *dpcm);
/* optional */
int (*stop_sync)(struct loopback_pcm *dpcm);
/* optional */
int (*close_substream)(struct loopback_pcm *dpcm);
/* optional
* call in loopback->cable_lock
*/
int (*close_cable)(struct loopback_pcm *dpcm);
/* optional
* call in cable->lock
*/
unsigned int (*pos_update)(struct loopback_cable *cable);
/* optional */
void (*dpcm_info)(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer);
};
struct loopback_cable {
spinlock_t lock;
struct loopback_pcm *streams[2];
@ -65,6 +104,15 @@ struct loopback_cable {
unsigned int valid;
unsigned int running;
unsigned int pause;
/* timer specific */
struct loopback_ops *ops;
/* If sound timer is used */
struct {
int stream;
struct snd_timer_id id;
struct tasklet_struct event_tasklet;
struct snd_timer_instance *instance;
} snd_timer;
};
struct loopback_setup {
@ -85,6 +133,7 @@ struct loopback {
struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
struct snd_pcm *pcm[2];
struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
const char *timer_source;
};
struct loopback_pcm {
@ -102,10 +151,13 @@ struct loopback_pcm {
/* flags */
unsigned int period_update_pending :1;
/* timer stuff */
unsigned int irq_pos; /* fractional IRQ position */
unsigned int period_size_frac;
unsigned int irq_pos; /* fractional IRQ position in jiffies
* ticks
*/
unsigned int period_size_frac; /* period size in jiffies ticks */
unsigned int last_drift;
unsigned long last_jiffies;
/* If jiffies timer is used */
struct timer_list timer;
};
@ -153,7 +205,7 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
}
/* call in cable->lock */
static void loopback_timer_start(struct loopback_pcm *dpcm)
static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
{
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
@ -169,23 +221,101 @@ static void loopback_timer_start(struct loopback_pcm *dpcm)
tick = dpcm->period_size_frac - dpcm->irq_pos;
tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
mod_timer(&dpcm->timer, jiffies + tick);
return 0;
}
/* call in cable->lock */
static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
int err;
/* Loopback device has to use same period as timer card. Therefore
* wake up for each snd_pcm_period_elapsed() call of timer card.
*/
err = snd_timer_start(cable->snd_timer.instance, 1);
if (err < 0) {
/* do not report error if trying to start but already
* running. For example called by opposite substream
* of the same cable
*/
if (err == -EBUSY)
return 0;
pcm_err(dpcm->substream->pcm,
"snd_timer_start(%d,%d,%d) failed with %d",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
err);
}
return err;
}
/* call in cable->lock */
static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
{
del_timer(&dpcm->timer);
dpcm->timer.expires = 0;
return 0;
}
static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
/* call in cable->lock */
static int loopback_snd_timer_stop(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
int err;
/* only stop if both devices (playback and capture) are not running */
if (cable->running ^ cable->pause)
return 0;
err = snd_timer_stop(cable->snd_timer.instance);
if (err < 0) {
pcm_err(dpcm->substream->pcm,
"snd_timer_stop(%d,%d,%d) failed with %d",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
err);
}
return err;
}
static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
{
del_timer_sync(&dpcm->timer);
return 0;
}
#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
/* call in loopback->cable_lock */
static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
/* snd_timer was not opened */
if (!cable->snd_timer.instance)
return 0;
/* wait till drain tasklet has finished if requested */
tasklet_kill(&cable->snd_timer.event_tasklet);
/* will only be called from free_cable() when other stream was
* already closed. Other stream cannot be reopened as long as
* loopback->cable_lock is locked. Therefore no need to lock
* cable->lock;
*/
snd_timer_close(cable->snd_timer.instance);
snd_timer_instance_free(cable->snd_timer.instance);
memset(&cable->snd_timer, 0, sizeof(cable->snd_timer));
return 0;
}
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
@ -249,7 +379,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
int err, stream = 1 << substream->stream;
int err = 0, stream = 1 << substream->stream;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@ -262,7 +392,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
cable->running |= stream;
cable->pause &= ~stream;
loopback_timer_start(dpcm);
err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -271,7 +401,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
cable->running &= ~stream;
cable->pause &= ~stream;
loopback_timer_stop(dpcm);
err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -280,7 +410,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&cable->lock);
cable->pause |= stream;
loopback_timer_stop(dpcm);
err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -290,7 +420,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
dpcm->last_jiffies = jiffies;
cable->pause &= ~stream;
loopback_timer_start(dpcm);
err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -298,7 +428,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
default:
return -EINVAL;
}
return 0;
return err;
}
static void params_change(struct snd_pcm_substream *substream)
@ -312,6 +442,13 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
if (cable->snd_timer.instance) {
cable->hw.period_bytes_min =
frames_to_bytes(runtime, runtime->period_size);
cable->hw.period_bytes_max = cable->hw.period_bytes_min;
}
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@ -319,9 +456,13 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
int bps, salign;
int err, bps, salign;
loopback_timer_stop_sync(dpcm);
if (cable->ops->stop_sync) {
err = cable->ops->stop_sync(dpcm);
if (err < 0)
return err;
}
salign = (snd_pcm_format_physical_width(runtime->format) *
runtime->channels) / 8;
@ -457,7 +598,8 @@ static inline void bytepos_finish(struct loopback_pcm *dpcm,
}
/* call in cable->lock */
static unsigned int loopback_pos_update(struct loopback_cable *cable)
static unsigned int loopback_jiffies_timer_pos_update
(struct loopback_cable *cable)
{
struct loopback_pcm *dpcm_play =
cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
@ -510,14 +652,15 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
return running;
}
static void loopback_timer_function(struct timer_list *t)
static void loopback_jiffies_timer_function(struct timer_list *t)
{
struct loopback_pcm *dpcm = from_timer(dpcm, t, timer);
unsigned long flags;
spin_lock_irqsave(&dpcm->cable->lock, flags);
if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
loopback_timer_start(dpcm);
if (loopback_jiffies_timer_pos_update(dpcm->cable) &
(1 << dpcm->substream->stream)) {
loopback_jiffies_timer_start(dpcm);
if (dpcm->period_update_pending) {
dpcm->period_update_pending = 0;
spin_unlock_irqrestore(&dpcm->cable->lock, flags);
@ -529,6 +672,193 @@ static void loopback_timer_function(struct timer_list *t)
spin_unlock_irqrestore(&dpcm->cable->lock, flags);
}
/* call in cable->lock */
static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime,
unsigned long resolution)
{
if (resolution != runtime->timer_resolution) {
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
/* Worst case estimation of possible values for resolution
* resolution <= (512 * 1024) frames / 8kHz in nsec
* resolution <= 65.536.000.000 nsec
*
* period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz +
* 500.000
* period_size <= 12.582.912.000.000 <64bit
* / 1.000.000 usec/sec
*/
snd_pcm_uframes_t period_size_usec =
resolution / 1000 * runtime->rate;
/* round to nearest sample rate */
snd_pcm_uframes_t period_size =
(period_size_usec + 500 * 1000) / (1000 * 1000);
pcm_err(dpcm->substream->pcm,
"Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card timer %d,%d,%d. Use period size of %lu frames for loopback device.",
runtime->period_size, resolution, period_size,
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
period_size);
return -EINVAL;
}
return 0;
}
static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
int event,
unsigned long resolution)
{
struct loopback_pcm *dpcm_play, *dpcm_capt;
struct snd_pcm_substream *substream_play, *substream_capt;
struct snd_pcm_runtime *valid_runtime;
unsigned int running, elapsed_bytes;
unsigned long flags;
spin_lock_irqsave(&cable->lock, flags);
running = cable->running ^ cable->pause;
/* no need to do anything if no stream is running */
if (!running) {
spin_unlock_irqrestore(&cable->lock, flags);
return;
}
dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
dpcm_play->substream : NULL;
substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
dpcm_capt->substream : NULL;
if (event == SNDRV_TIMER_EVENT_MSTOP) {
if (!dpcm_play ||
dpcm_play->substream->runtime->status->state !=
SNDRV_PCM_STATE_DRAINING) {
spin_unlock_irqrestore(&cable->lock, flags);
return;
}
}
valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
dpcm_play->substream->runtime :
dpcm_capt->substream->runtime;
/* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */
if (event == SNDRV_TIMER_EVENT_TICK) {
/* The hardware rules guarantee that playback and capture period
* are the same. Therefore only one device has to be checked
* here.
*/
if (loopback_snd_timer_check_resolution(valid_runtime,
resolution) < 0) {
spin_unlock_irqrestore(&cable->lock, flags);
if (substream_play)
snd_pcm_stop_xrun(substream_play);
if (substream_capt)
snd_pcm_stop_xrun(substream_capt);
return;
}
}
elapsed_bytes = frames_to_bytes(valid_runtime,
valid_runtime->period_size);
/* The same timer interrupt is used for playback and capture device */
if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
(running & (1 << SNDRV_PCM_STREAM_CAPTURE))) {
copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes);
bytepos_finish(dpcm_play, elapsed_bytes);
bytepos_finish(dpcm_capt, elapsed_bytes);
} else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
bytepos_finish(dpcm_play, elapsed_bytes);
} else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
clear_capture_buf(dpcm_capt, elapsed_bytes);
bytepos_finish(dpcm_capt, elapsed_bytes);
}
spin_unlock_irqrestore(&cable->lock, flags);
if (substream_play)
snd_pcm_period_elapsed(substream_play);
if (substream_capt)
snd_pcm_period_elapsed(substream_capt);
}
static void loopback_snd_timer_function(struct snd_timer_instance *timeri,
unsigned long resolution,
unsigned long ticks)
{
struct loopback_cable *cable = timeri->callback_data;
loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK,
resolution);
}
static void loopback_snd_timer_tasklet(unsigned long arg)
{
struct snd_timer_instance *timeri = (struct snd_timer_instance *)arg;
struct loopback_cable *cable = timeri->callback_data;
loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0);
}
static void loopback_snd_timer_event(struct snd_timer_instance *timeri,
int event,
struct timespec *tstamp,
unsigned long resolution)
{
/* Do not lock cable->lock here because timer->lock is already hold.
* There are other functions which first lock cable->lock and than
* timer->lock e.g.
* loopback_trigger()
* spin_lock(&cable->lock)
* loopback_snd_timer_start()
* snd_timer_start()
* spin_lock(&timer->lock)
* Therefore when using the oposit order of locks here it could result
* in a deadlock.
*/
if (event == SNDRV_TIMER_EVENT_MSTOP) {
struct loopback_cable *cable = timeri->callback_data;
/* sound card of the timer was stopped. Therefore there will not
* be any further timer callbacks. Due to this forward audio
* data from here if in draining state. When still in running
* state the streaming will be aborted by the usual timeout. It
* should not be aborted here because may be the timer sound
* card does only a recovery and the timer is back soon.
* This tasklet triggers loopback_snd_timer_tasklet()
*/
tasklet_schedule(&cable->snd_timer.event_tasklet);
}
}
static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer)
{
snd_iprintf(buffer, " update_pending:\t%u\n",
dpcm->period_update_pending);
snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
dpcm->last_jiffies, jiffies);
snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
}
static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer)
{
struct loopback_cable *cable = dpcm->cable;
snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice);
snd_iprintf(buffer, " timer open:\t\t%s\n",
(cable->snd_timer.stream == SNDRV_PCM_STREAM_CAPTURE) ?
"capture" : "playback");
}
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@ -536,7 +866,8 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t pos;
spin_lock(&dpcm->cable->lock);
loopback_pos_update(dpcm->cable);
if (dpcm->cable->ops->pos_update)
dpcm->cable->ops->pos_update(dpcm->cable);
pos = dpcm->buf_pos;
spin_unlock(&dpcm->cable->lock);
return bytes_to_frames(runtime, pos);
@ -576,8 +907,7 @@ static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
static int loopback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(params));
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
}
static int loopback_hw_free(struct snd_pcm_substream *substream)
@ -589,7 +919,7 @@ static int loopback_hw_free(struct snd_pcm_substream *substream)
mutex_lock(&dpcm->loopback->cable_lock);
cable->valid &= ~(1 << substream->stream);
mutex_unlock(&dpcm->loopback->cable_lock);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static unsigned int get_cable_index(struct snd_pcm_substream *substream)
@ -647,6 +977,23 @@ static int rule_channels(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
static int rule_period_bytes(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct loopback_pcm *dpcm = rule->private;
struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
mutex_lock(&dpcm->loopback->cable_lock);
t.min = cable->hw.period_bytes_min;
t.max = cable->hw.period_bytes_max;
mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = 0;
t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
static void free_cable(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
@ -662,12 +1009,190 @@ static void free_cable(struct snd_pcm_substream *substream)
cable->streams[substream->stream] = NULL;
spin_unlock_irq(&cable->lock);
} else {
struct loopback_pcm *dpcm = substream->runtime->private_data;
if (cable->ops && cable->ops->close_cable && dpcm)
cable->ops->close_cable(dpcm);
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
kfree(cable);
}
}
static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm)
{
timer_setup(&dpcm->timer, loopback_jiffies_timer_function, 0);
return 0;
}
static struct loopback_ops loopback_jiffies_timer_ops = {
.open = loopback_jiffies_timer_open,
.start = loopback_jiffies_timer_start,
.stop = loopback_jiffies_timer_stop,
.stop_sync = loopback_jiffies_timer_stop_sync,
.close_substream = loopback_jiffies_timer_stop_sync,
.pos_update = loopback_jiffies_timer_pos_update,
.dpcm_info = loopback_jiffies_timer_dpcm_info,
};
static int loopback_parse_timer_id(const char *str,
struct snd_timer_id *tid)
{
/* [<pref>:](<card name>|<card idx>)[{.,}<dev idx>[{.,}<subdev idx>]] */
const char * const sep_dev = ".,";
const char * const sep_pref = ":";
const char *name = str;
char *sep, save = '\0';
int card_idx = 0, dev = 0, subdev = 0;
int err;
sep = strpbrk(str, sep_pref);
if (sep)
name = sep + 1;
sep = strpbrk(name, sep_dev);
if (sep) {
save = *sep;
*sep = '\0';
}
err = kstrtoint(name, 0, &card_idx);
if (err == -EINVAL) {
/* Must be the name, not number */
for (card_idx = 0; card_idx < snd_ecards_limit; card_idx++) {
struct snd_card *card = snd_card_ref(card_idx);
if (card) {
if (!strcmp(card->id, name))
err = 0;
snd_card_unref(card);
}
if (!err)
break;
}
}
if (sep) {
*sep = save;
if (!err) {
char *sep2, save2 = '\0';
sep2 = strpbrk(sep + 1, sep_dev);
if (sep2) {
save2 = *sep2;
*sep2 = '\0';
}
err = kstrtoint(sep + 1, 0, &dev);
if (sep2) {
*sep2 = save2;
if (!err)
err = kstrtoint(sep2 + 1, 0, &subdev);
}
}
}
if (!err && tid) {
tid->card = card_idx;
tid->device = dev;
tid->subdevice = subdev;
}
return err;
}
/* call in loopback->cable_lock */
static int loopback_snd_timer_open(struct loopback_pcm *dpcm)
{
int err = 0;
struct snd_timer_id tid = {
.dev_class = SNDRV_TIMER_CLASS_PCM,
.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION,
};
struct snd_timer_instance *timeri;
struct loopback_cable *cable = dpcm->cable;
spin_lock_irq(&cable->lock);
/* check if timer was already opened. It is only opened once
* per playback and capture subdevice (aka cable).
*/
if (cable->snd_timer.instance)
goto unlock;
err = loopback_parse_timer_id(dpcm->loopback->timer_source, &tid);
if (err < 0) {
pcm_err(dpcm->substream->pcm,
"Parsing timer source \'%s\' failed with %d",
dpcm->loopback->timer_source, err);
goto unlock;
}
cable->snd_timer.stream = dpcm->substream->stream;
cable->snd_timer.id = tid;
timeri = snd_timer_instance_new(dpcm->loopback->card->id);
if (!timeri) {
err = -ENOMEM;
goto unlock;
}
/* The callback has to be called from another tasklet. If
* SNDRV_TIMER_IFLG_FAST is specified it will be called from the
* snd_pcm_period_elapsed() call of the selected sound card.
* snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave().
* Due to our callback loopback_snd_timer_function() also calls
* snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave().
* This would end up in a dead lock.
*/
timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
timeri->callback = loopback_snd_timer_function;
timeri->callback_data = (void *)cable;
timeri->ccallback = loopback_snd_timer_event;
/* initialise a tasklet used for draining */
tasklet_init(&cable->snd_timer.event_tasklet,
loopback_snd_timer_tasklet, (unsigned long)timeri);
/* snd_timer_close() and snd_timer_open() should not be called with
* locked spinlock because both functions can block on a mutex. The
* mutex loopback->cable_lock is kept locked. Therefore snd_timer_open()
* cannot be called a second time by the other device of the same cable.
* Therefore the following issue cannot happen:
* [proc1] Call loopback_timer_open() ->
* Unlock cable->lock for snd_timer_close/open() call
* [proc2] Call loopback_timer_open() -> snd_timer_open(),
* snd_timer_start()
* [proc1] Call snd_timer_open() and overwrite running timer
* instance
*/
spin_unlock_irq(&cable->lock);
err = snd_timer_open(timeri, &cable->snd_timer.id, current->pid);
spin_lock_irq(&cable->lock);
if (err < 0) {
pcm_err(dpcm->substream->pcm,
"snd_timer_open (%d,%d,%d) failed with %d",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
err);
snd_timer_instance_free(timeri);
goto unlock;
}
cable->snd_timer.instance = timeri;
unlock:
spin_unlock_irq(&cable->lock);
return err;
}
/* stop_sync() is not required for sound timer because it does not need to be
* restarted in loopback_prepare() on Xrun recovery
*/
static struct loopback_ops loopback_snd_timer_ops = {
.open = loopback_snd_timer_open,
.start = loopback_snd_timer_start,
.stop = loopback_snd_timer_stop,
.close_cable = loopback_snd_timer_close_cable,
.dpcm_info = loopback_snd_timer_dpcm_info,
};
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@ -685,7 +1210,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
}
dpcm->loopback = loopback;
dpcm->substream = substream;
timer_setup(&dpcm->timer, loopback_timer_function, 0);
cable = loopback->cables[substream->number][dev];
if (!cable) {
@ -696,9 +1220,20 @@ static int loopback_open(struct snd_pcm_substream *substream)
}
spin_lock_init(&cable->lock);
cable->hw = loopback_pcm_hardware;
if (loopback->timer_source)
cable->ops = &loopback_snd_timer_ops;
else
cable->ops = &loopback_jiffies_timer_ops;
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
runtime->private_data = dpcm;
if (cable->ops->open) {
err = cable->ops->open(dpcm);
if (err < 0)
goto unlock;
}
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
@ -724,7 +1259,22 @@ static int loopback_open(struct snd_pcm_substream *substream)
if (err < 0)
goto unlock;
runtime->private_data = dpcm;
/* In case of sound timer the period time of both devices of the same
* loop has to be the same.
* This rule only takes effect if a sound timer was chosen
*/
if (cable->snd_timer.instance) {
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
rule_period_bytes, dpcm,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
if (err < 0)
goto unlock;
}
/* loopback_runtime_free() has not to be called if kfree(dpcm) was
* already called here. Otherwise it will end up with a double free.
*/
runtime->private_free = loopback_runtime_free;
if (get_notify(dpcm))
runtime->hw = loopback_pcm_hardware;
@ -748,12 +1298,14 @@ static int loopback_close(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
int err = 0;
loopback_timer_stop_sync(dpcm);
if (dpcm->cable->ops->close_substream)
err = dpcm->cable->ops->close_substream(dpcm);
mutex_lock(&loopback->cable_lock);
free_cable(substream);
mutex_unlock(&loopback->cable_lock);
return 0;
return err;
}
static const struct snd_pcm_ops loopback_pcm_ops = {
@ -765,7 +1317,6 @@ static const struct snd_pcm_ops loopback_pcm_ops = {
.prepare = loopback_prepare,
.trigger = loopback_trigger,
.pointer = loopback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static int loopback_pcm_new(struct loopback *loopback,
@ -780,6 +1331,8 @@ static int loopback_pcm_new(struct loopback *loopback,
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
pcm->private_data = loopback;
pcm->info_flags = 0;
@ -1076,13 +1629,8 @@ static void print_dpcm_info(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps);
snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign);
snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift);
snd_iprintf(buffer, " update_pending:\t%u\n",
dpcm->period_update_pending);
snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
dpcm->last_jiffies, jiffies);
snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
if (dpcm->cable->ops->dpcm_info)
dpcm->cable->ops->dpcm_info(dpcm, buffer);
}
static void print_substream_info(struct snd_info_buffer *buffer,
@ -1118,7 +1666,7 @@ static void print_cable_info(struct snd_info_entry *entry,
mutex_unlock(&loopback->cable_lock);
}
static int loopback_proc_new(struct loopback *loopback, int cidx)
static int loopback_cable_proc_new(struct loopback *loopback, int cidx)
{
char name[32];
@ -1127,6 +1675,48 @@ static int loopback_proc_new(struct loopback *loopback, int cidx)
print_cable_info);
}
static void loopback_set_timer_source(struct loopback *loopback,
const char *value)
{
if (loopback->timer_source) {
devm_kfree(loopback->card->dev, loopback->timer_source);
loopback->timer_source = NULL;
}
if (value && *value)
loopback->timer_source = devm_kstrdup(loopback->card->dev,
value, GFP_KERNEL);
}
static void print_timer_source_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct loopback *loopback = entry->private_data;
mutex_lock(&loopback->cable_lock);
snd_iprintf(buffer, "%s\n",
loopback->timer_source ? loopback->timer_source : "");
mutex_unlock(&loopback->cable_lock);
}
static void change_timer_source_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct loopback *loopback = entry->private_data;
char line[64];
mutex_lock(&loopback->cable_lock);
if (!snd_info_get_line(buffer, line, sizeof(line)))
loopback_set_timer_source(loopback, strim(line));
mutex_unlock(&loopback->cable_lock);
}
static int loopback_timer_source_proc_new(struct loopback *loopback)
{
return snd_card_rw_proc_new(loopback->card, "timer_source", loopback,
print_timer_source_info,
change_timer_source_info);
}
static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;
@ -1146,6 +1736,8 @@ static int loopback_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
loopback->card = card;
loopback_set_timer_source(loopback, timer_source[dev]);
mutex_init(&loopback->cable_lock);
err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
@ -1157,8 +1749,9 @@ static int loopback_probe(struct platform_device *devptr)
err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
if (err < 0)
goto __nodev;
loopback_proc_new(loopback, 0);
loopback_proc_new(loopback, 1);
loopback_cable_proc_new(loopback, 0);
loopback_cable_proc_new(loopback, 1);
loopback_timer_source_proc_new(loopback);
strcpy(card->driver, "Loopback");
strcpy(card->shortname, "Loopback");
sprintf(card->longname, "Loopback %i", dev + 1);

View File

@ -702,7 +702,7 @@ static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
if (!fake_buffer) {
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
NULL,
0, 64*1024);
}
return 0;

View File

@ -1242,7 +1242,7 @@ snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device)
ml403_ac97cr->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
NULL,
64 * 1024,
128 * 1024);
return 0;

View File

@ -352,8 +352,8 @@ int snd_pcsp_new_pcm(struct snd_pcsp *chip)
snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data
(GFP_KERNEL), PCSP_BUFFER_SIZE,
NULL,
PCSP_BUFFER_SIZE,
PCSP_BUFFER_SIZE);
return 0;

View File

@ -778,8 +778,7 @@ static snd_pcm_uframes_t vx_pcm_playback_pointer(struct snd_pcm_substream *subs)
static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_alloc_vmalloc_32_buffer
(subs, params_buffer_bytes(hw_params));
return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params));
}
/*
@ -787,7 +786,7 @@ static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
*/
static int vx_pcm_hw_free(struct snd_pcm_substream *subs)
{
return snd_pcm_lib_free_vmalloc_buffer(subs);
return snd_pcm_lib_free_pages(subs);
}
/*
@ -867,7 +866,6 @@ static const struct snd_pcm_ops vx_pcm_playback_ops = {
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_playback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
@ -1088,7 +1086,6 @@ static const struct snd_pcm_ops vx_pcm_capture_ops = {
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_capture_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
@ -1233,6 +1230,9 @@ int snd_vx_pcm_new(struct vx_core *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops);
if (ins)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
snd_dma_continuous_data(GFP_KERNEL | GFP_DMA32),
0, 0);
pcm->private_data = chip;
pcm->private_free = snd_vx_pcm_free;

View File

@ -77,7 +77,7 @@ config SND_BEBOB
tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
select SND_FIREWIRE_LIB
select SND_HWDEP
help
help
Say Y here to include support for FireWire devices based
on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
* Edirol FA-66/FA-101
@ -111,8 +111,8 @@ config SND_BEBOB
* M-Audio FireWire 1814/ProjectMix IO
* Digidesign Mbox 2 Pro
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
config SND_FIREWIRE_DIGI00X
tristate "Digidesign Digi 002/003 family support"

View File

@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/pcm.h>
@ -52,10 +53,6 @@
#define CIP_FMT_AM 0x10
#define AMDTP_FDF_NO_DATA 0xff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
#define QUEUE_LENGTH 48
// For iso header, tstamp and 2 CIP header.
#define IR_CTX_HEADER_SIZE_CIP 16
// For iso header and tstamp.
@ -180,6 +177,8 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime)
{
struct snd_pcm_hardware *hw = &runtime->hw;
unsigned int ctx_header_size;
unsigned int maximum_usec_per_period;
int err;
hw->info = SNDRV_PCM_INFO_BATCH |
@ -200,19 +199,36 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
/*
* Currently firewire-lib processes 16 packets in one software
* interrupt callback. This equals to 2msec but actually the
* interval of the interrupts has a jitter.
* Additionally, even if adding a constraint to fit period size to
* 2msec, actual calculated frames per period doesn't equal to 2msec,
* depending on sampling rate.
* Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
* Here let us use 5msec for safe period interrupt.
*/
// Linux driver for 1394 OHCI controller voluntarily flushes isoc
// context when total size of accumulated context header reaches
// PAGE_SIZE. This kicks tasklet for the isoc context and brings
// callback in the middle of scheduled interrupts.
// Although AMDTP streams in the same domain use the same events per
// IRQ, use the largest size of context header between IT/IR contexts.
// Here, use the value of context header in IR context is for both
// contexts.
if (!(s->flags & CIP_NO_HEADER))
ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
else
ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE /
CYCLES_PER_SECOND / ctx_header_size;
// In IEC 61883-6, one isoc packet can transfer events up to the value
// of syt interval. This comes from the interval of isoc cycle. As 1394
// OHCI controller can generate hardware IRQ per isoc packet, the
// interval is 125 usec.
// However, there are two ways of transmission in IEC 61883-6; blocking
// and non-blocking modes. In blocking mode, the sequence of isoc packet
// includes 'empty' or 'NODATA' packets which include no event. In
// non-blocking mode, the number of events per packet is variable up to
// the syt interval.
// Due to the above protocol design, the minimum PCM frames per
// interrupt should be double of the value of syt interval, thus it is
// 250 usec.
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
5000, UINT_MAX);
250, maximum_usec_per_period);
if (err < 0)
goto end;
@ -436,11 +452,12 @@ static void pcm_period_tasklet(unsigned long data)
snd_pcm_period_elapsed(pcm);
}
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params)
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
bool sched_irq)
{
int err;
params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
params->interrupt = sched_irq;
params->tag = s->tag;
params->sy = 0;
@ -451,18 +468,18 @@ static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params)
goto end;
}
if (++s->packet_index >= QUEUE_LENGTH)
if (++s->packet_index >= s->queue_size)
s->packet_index = 0;
end:
return err;
}
static inline int queue_out_packet(struct amdtp_stream *s,
struct fw_iso_packet *params)
struct fw_iso_packet *params, bool sched_irq)
{
params->skip =
!!(params->header_length == 0 && params->payload_length == 0);
return queue_packet(s, params);
return queue_packet(s, params, sched_irq);
}
static inline int queue_in_packet(struct amdtp_stream *s,
@ -472,7 +489,7 @@ static inline int queue_in_packet(struct amdtp_stream *s,
params->header_length = s->ctx_data.tx.ctx_header_size;
params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
params->skip = false;
return queue_packet(s, params);
return queue_packet(s, params, false);
}
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
@ -669,13 +686,14 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
}
// Align to actual cycle count for the packet which is going to be scheduled.
// This module queued the same number of isochronous cycle as QUEUE_LENGTH to
// skip isochronous cycle, therefore it's OK to just increment the cycle by
// QUEUE_LENGTH for scheduled cycle.
static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
// This module queued the same number of isochronous cycle as the size of queue
// to kip isochronous cycle, therefore it's OK to just increment the cycle by
// the size of queue for scheduled cycle.
static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp,
unsigned int queue_size)
{
u32 cycle = compute_cycle_count(ctx_header_tstamp);
return increment_cycle_count(cycle, QUEUE_LENGTH);
return increment_cycle_count(cycle, queue_size);
}
static int generate_device_pkt_descs(struct amdtp_stream *s,
@ -689,7 +707,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
unsigned int index = (s->packet_index + i) % s->queue_size;
unsigned int cycle;
unsigned int payload_length;
unsigned int data_blocks;
@ -730,9 +748,9 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s,
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
unsigned int index = (s->packet_index + i) % s->queue_size;
desc->cycle = compute_it_cycle(*ctx_header);
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size);
desc->syt = calculate_syt(s, desc->cycle);
desc->data_blocks = calculate_data_blocks(s, desc->syt);
@ -773,22 +791,40 @@ static void process_ctx_payloads(struct amdtp_stream *s,
update_pcm_pointers(s, pcm, pcm_frames);
}
static void amdtp_stream_master_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data);
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data);
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
const __be32 *ctx_header = header;
unsigned int packets = header_length / sizeof(*ctx_header);
unsigned int events_per_period = s->ctx_data.rx.events_per_period;
unsigned int event_count = s->ctx_data.rx.event_count;
unsigned int packets;
bool is_irq_target;
int i;
if (s->packet_index < 0)
return;
// Calculate the number of packets in buffer and check XRUN.
packets = header_length / sizeof(*ctx_header);
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
process_ctx_payloads(s, s->pkt_descs, packets);
is_irq_target =
!!(context->callback.sc == amdtp_stream_master_callback ||
context->callback.sc == amdtp_stream_master_first_callback);
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt;
@ -796,6 +832,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
struct fw_iso_packet params;
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
} template = { {0}, {0} };
bool sched_irq = false;
if (s->ctx_data.rx.syt_override < 0)
syt = desc->syt;
@ -806,13 +843,21 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
desc->data_blocks, desc->data_block_counter,
syt, i);
if (queue_out_packet(s, &template.params) < 0) {
if (is_irq_target) {
event_count += desc->data_blocks;
if (event_count >= events_per_period) {
event_count -= events_per_period;
sched_irq = true;
}
}
if (queue_out_packet(s, &template.params, sched_irq) < 0) {
cancel_stream(s);
return;
}
}
fw_iso_context_queue_flush(s->context);
s->ctx_data.rx.event_count = event_count;
}
static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
@ -820,15 +865,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
void *private_data)
{
struct amdtp_stream *s = private_data;
unsigned int packets;
__be32 *ctx_header = header;
unsigned int packets;
int i;
int err;
if (s->packet_index < 0)
return;
// The number of packets in buffer.
// Calculate the number of packets in buffer and check XRUN.
packets = header_length / s->ctx_data.tx.ctx_header_size;
err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
@ -849,11 +894,40 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
return;
}
}
fw_iso_context_queue_flush(s->context);
}
/* this is executed one time */
static void amdtp_stream_master_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data)
{
struct amdtp_domain *d = private_data;
struct amdtp_stream *irq_target = d->irq_target;
struct amdtp_stream *s;
out_stream_callback(context, tstamp, header_length, header, irq_target);
if (amdtp_streaming_error(irq_target))
goto error;
list_for_each_entry(s, &d->streams, list) {
if (s != irq_target && amdtp_stream_running(s)) {
fw_iso_context_flush_completions(s->context);
if (amdtp_streaming_error(s))
goto error;
}
}
return;
error:
if (amdtp_stream_running(irq_target))
cancel_stream(irq_target);
list_for_each_entry(s, &d->streams, list) {
if (amdtp_stream_running(s))
cancel_stream(s);
}
}
// this is executed one time.
static void amdtp_stream_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data)
@ -874,7 +948,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
context->callback.sc = in_stream_callback;
} else {
cycle = compute_it_cycle(*ctx_header);
cycle = compute_it_cycle(*ctx_header, s->queue_size);
context->callback.sc = out_stream_callback;
}
@ -884,17 +958,42 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
context->callback.sc(context, tstamp, header_length, header, s);
}
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data)
{
struct amdtp_domain *d = private_data;
struct amdtp_stream *s = d->irq_target;
const __be32 *ctx_header = header;
s->callbacked = true;
wake_up(&s->callback_wait);
s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size);
context->callback.sc = amdtp_stream_master_callback;
context->callback.sc(context, tstamp, header_length, header, d);
}
/**
* amdtp_stream_start - start transferring packets
* @s: the AMDTP stream to start
* @channel: the isochronous channel on the bus
* @speed: firewire speed code
* @d: the AMDTP domain to which the AMDTP stream belongs
* @is_irq_target: whether isoc context for the AMDTP stream is used to generate
* hardware IRQ.
* @start_cycle: the isochronous cycle to start the context. Start immediately
* if negative value is given.
*
* The stream cannot be started until it has been configured with
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started.
*/
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
struct amdtp_domain *d, bool is_irq_target,
int start_cycle)
{
static const struct {
unsigned int data_block;
@ -908,10 +1007,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
[CIP_SFC_88200] = { 0, 67 },
[CIP_SFC_176400] = { 0, 67 },
};
unsigned int events_per_buffer = d->events_per_buffer;
unsigned int events_per_period = d->events_per_period;
unsigned int idle_irq_interval;
unsigned int ctx_header_size;
unsigned int max_ctx_payload_size;
enum dma_data_direction dir;
int type, tag, err;
fw_iso_callback_t ctx_cb;
void *ctx_data;
mutex_lock(&s->mutex);
@ -922,6 +1026,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
}
if (s->direction == AMDTP_IN_STREAM) {
// NOTE: IT context should be used for constant IRQ.
if (is_irq_target) {
err = -EINVAL;
goto err_unlock;
}
s->data_block_counter = UINT_MAX;
} else {
entry = &initial_state[s->sfc];
@ -953,14 +1063,37 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
}
err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
// This is a case that AMDTP streams in domain run just for MIDI
// substream. Use the number of events equivalent to 10 msec as
// interval of hardware IRQ.
if (events_per_period == 0)
events_per_period = amdtp_rate_table[s->sfc] / 100;
if (events_per_buffer == 0)
events_per_buffer = events_per_period * 3;
idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
amdtp_rate_table[s->sfc]);
s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
amdtp_rate_table[s->sfc]);
err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size,
max_ctx_payload_size, dir);
if (err < 0)
goto err_unlock;
if (is_irq_target) {
s->ctx_data.rx.events_per_period = events_per_period;
s->ctx_data.rx.event_count = 0;
ctx_cb = amdtp_stream_master_first_callback;
ctx_data = d;
} else {
ctx_cb = amdtp_stream_first_callback;
ctx_data = s;
}
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
type, channel, speed, ctx_header_size,
amdtp_stream_first_callback, s);
ctx_cb, ctx_data);
if (IS_ERR(s->context)) {
err = PTR_ERR(s->context);
if (err == -EBUSY)
@ -981,7 +1114,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
else
s->tag = TAG_CIP;
s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs),
s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs),
GFP_KERNEL);
if (!s->pkt_descs) {
err = -ENOMEM;
@ -991,12 +1124,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->packet_index = 0;
do {
struct fw_iso_packet params;
if (s->direction == AMDTP_IN_STREAM) {
err = queue_in_packet(s, &params);
} else {
bool sched_irq = false;
params.header_length = 0;
params.payload_length = 0;
err = queue_out_packet(s, &params);
if (is_irq_target) {
sched_irq = !((s->packet_index + 1) %
idle_irq_interval);
}
err = queue_out_packet(s, &params, sched_irq);
}
if (err < 0)
goto err_pkt_descs;
@ -1008,7 +1150,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false;
err = fw_iso_context_start(s->context, -1, 0, tag);
err = fw_iso_context_start(s->context, start_cycle, 0, tag);
if (err < 0)
goto err_pkt_descs;
@ -1029,54 +1171,69 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
}
/**
* amdtp_stream_pcm_pointer - get the PCM buffer position
* amdtp_domain_stream_pcm_pointer - get the PCM buffer position
* @d: the AMDTP domain.
* @s: the AMDTP stream that transports the PCM data
*
* Returns the current buffer position, in frames.
*/
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
struct amdtp_stream *s)
{
/*
* This function is called in software IRQ context of period_tasklet or
* process context.
*
* When the software IRQ context was scheduled by software IRQ context
* of IR/IT contexts, queued packets were already handled. Therefore,
* no need to flush the queue in buffer anymore.
*
* When the process context reach here, some packets will be already
* queued in the buffer. These packets should be handled immediately
* to keep better granularity of PCM pointer.
*
* Later, the process context will sometimes schedules software IRQ
* context of the period_tasklet. Then, no need to flush the queue by
* the same reason as described for IR/IT contexts.
*/
if (!in_interrupt() && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
struct amdtp_stream *irq_target = d->irq_target;
if (irq_target && amdtp_stream_running(irq_target)) {
// This function is called in software IRQ context of
// period_tasklet or process context.
//
// When the software IRQ context was scheduled by software IRQ
// context of IT contexts, queued packets were already handled.
// Therefore, no need to flush the queue in buffer furthermore.
//
// When the process context reach here, some packets will be
// already queued in the buffer. These packets should be handled
// immediately to keep better granularity of PCM pointer.
//
// Later, the process context will sometimes schedules software
// IRQ context of the period_tasklet. Then, no need to flush the
// queue by the same reason as described in the above
if (!in_interrupt()) {
// Queued packet should be processed without any kernel
// preemption to keep latency against bus cycle.
preempt_disable();
fw_iso_context_flush_completions(irq_target->context);
preempt_enable();
}
}
return READ_ONCE(s->pcm_buffer_pointer);
}
EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer);
/**
* amdtp_stream_pcm_ack - acknowledge queued PCM frames
* amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames
* @d: the AMDTP domain.
* @s: the AMDTP stream that transfers the PCM frames
*
* Returns zero always.
*/
int amdtp_stream_pcm_ack(struct amdtp_stream *s)
int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s)
{
/*
* Process isochronous packets for recent isochronous cycle to handle
* queued PCM frames.
*/
if (amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
struct amdtp_stream *irq_target = d->irq_target;
// Process isochronous packets for recent isochronous cycle to handle
// queued PCM frames.
if (irq_target && amdtp_stream_running(irq_target)) {
// Queued packet should be processed without any kernel
// preemption to keep latency against bus cycle.
preempt_disable();
fw_iso_context_flush_completions(irq_target->context);
preempt_enable();
}
return 0;
}
EXPORT_SYMBOL(amdtp_stream_pcm_ack);
EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack);
/**
* amdtp_stream_update - update the stream after a bus reset
@ -1143,6 +1300,8 @@ int amdtp_domain_init(struct amdtp_domain *d)
{
INIT_LIST_HEAD(&d->streams);
d->events_per_period = 0;
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_init);
@ -1184,26 +1343,105 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
}
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle)
{
int generation;
int rcode;
__be32 reg;
u32 data;
// This is a request to local 1394 OHCI controller and expected to
// complete without any event waiting.
generation = fw_card->generation;
smp_rmb(); // node_id vs. generation.
rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST,
fw_card->node_id, generation, SCODE_100,
CSR_REGISTER_BASE + CSR_CYCLE_TIME,
&reg, sizeof(reg));
if (rcode != RCODE_COMPLETE)
return -EIO;
data = be32_to_cpu(reg);
*cur_cycle = data >> 12;
return 0;
}
/**
* amdtp_domain_start - start sending packets for isoc context in the domain.
* @d: the AMDTP domain.
* @ir_delay_cycle: the cycle delay to start all IR contexts.
*/
int amdtp_domain_start(struct amdtp_domain *d)
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
{
struct amdtp_stream *s;
int err = 0;
int cycle;
int err;
// Select an IT context as IRQ target.
list_for_each_entry(s, &d->streams, list) {
err = amdtp_stream_start(s, s->channel, s->speed);
if (err < 0)
if (s->direction == AMDTP_OUT_STREAM)
break;
}
if (!s)
return -ENXIO;
d->irq_target = s;
if (err < 0) {
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
if (ir_delay_cycle > 0) {
struct fw_card *fw_card = fw_parent_device(s->unit)->card;
err = get_current_cycle_time(fw_card, &cycle);
if (err < 0)
return err;
// No need to care overflow in cycle field because of enough
// width.
cycle += ir_delay_cycle;
// Round up to sec field.
if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) {
unsigned int sec;
// The sec field can overflow.
sec = (cycle & 0xffffe000) >> 13;
cycle = (++sec << 13) |
((cycle & 0x00001fff) / CYCLES_PER_SECOND);
}
// In OHCI 1394 specification, lower 2 bits are available for
// sec field.
cycle &= 0x00007fff;
} else {
cycle = -1;
}
list_for_each_entry(s, &d->streams, list) {
int cycle_match;
if (s->direction == AMDTP_IN_STREAM) {
cycle_match = cycle;
} else {
// IT context starts immediately.
cycle_match = -1;
}
if (s != d->irq_target) {
err = amdtp_stream_start(s, s->channel, s->speed, d,
false, cycle_match);
if (err < 0)
goto error;
}
}
s = d->irq_target;
err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1);
if (err < 0)
goto error;
return 0;
error:
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
return err;
}
EXPORT_SYMBOL_GPL(amdtp_domain_start);
@ -1216,10 +1454,17 @@ void amdtp_domain_stop(struct amdtp_domain *d)
{
struct amdtp_stream *s, *next;
if (d->irq_target)
amdtp_stream_stop(d->irq_target);
list_for_each_entry_safe(s, next, &d->streams, list) {
list_del(&s->list);
amdtp_stream_stop(s);
if (s != d->irq_target)
amdtp_stream_stop(s);
}
d->events_per_period = 0;
d->irq_target = NULL;
}
EXPORT_SYMBOL_GPL(amdtp_domain_stop);

View File

@ -117,6 +117,7 @@ struct amdtp_stream {
/* For packet processing. */
struct fw_iso_context *context;
struct iso_packets_buffer buffer;
unsigned int queue_size;
int packet_index;
struct pkt_desc *pkt_descs;
int tag;
@ -142,6 +143,10 @@ struct amdtp_stream {
// To generate CIP header.
unsigned int fdf;
int syt_override;
// To generate constant hardware IRQ.
unsigned int event_count;
unsigned int events_per_period;
} rx;
} ctx_data;
@ -194,8 +199,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
int amdtp_stream_pcm_ack(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
@ -272,6 +275,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
struct amdtp_domain {
struct list_head streams;
unsigned int events_per_period;
unsigned int events_per_buffer;
struct amdtp_stream *irq_target;
};
int amdtp_domain_init(struct amdtp_domain *d);
@ -280,7 +288,21 @@ void amdtp_domain_destroy(struct amdtp_domain *d);
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed);
int amdtp_domain_start(struct amdtp_domain *d);
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle);
void amdtp_domain_stop(struct amdtp_domain *d);
static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,
unsigned int events_per_period,
unsigned int events_per_buffer)
{
d->events_per_period = events_per_period;
d->events_per_buffer = events_per_buffer;
return 0;
}
unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
struct amdtp_stream *s);
int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s);
#endif

View File

@ -217,7 +217,9 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src);
int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate);
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);

View File

@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream)
return err;
mutex_lock(&bebob->mutex);
err = snd_bebob_stream_reserve_duplex(bebob, 0);
err = snd_bebob_stream_reserve_duplex(bebob, 0, 0, 0);
if (err >= 0) {
++bebob->substreams_counter;
err = snd_bebob_stream_start_duplex(bebob);

View File

@ -129,18 +129,17 @@ pcm_init_hw_params(struct snd_bebob *bebob,
return err;
}
static int
pcm_open(struct snd_pcm_substream *substream)
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
struct amdtp_domain *d = &bebob->domain;
enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_lock_try(bebob);
if (err < 0)
goto end;
return err;
err = pcm_init_hw_params(bebob, substream);
if (err < 0)
@ -150,15 +149,20 @@ pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
/*
* When source of clock is internal or any PCM stream are running,
* the available sampling rate is limited at current sampling rate.
*/
mutex_lock(&bebob->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
amdtp_stream_pcm_running(&bebob->tx_stream) ||
amdtp_stream_pcm_running(&bebob->rx_stream)) {
(bebob->substreams_counter > 0 && d->events_per_period > 0)) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int sampling_rate;
err = spec->get(bebob, &sampling_rate);
if (err < 0) {
mutex_unlock(&bebob->mutex);
dev_err(&bebob->unit->device,
"fail to get sampling rate: %d\n", err);
goto err_locked;
@ -166,11 +170,31 @@ pcm_open(struct snd_pcm_substream *substream)
substream->runtime->hw.rate_min = sampling_rate;
substream->runtime->hw.rate_max = sampling_rate;
if (frames_per_period > 0) {
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&bebob->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&bebob->mutex);
goto err_locked;
}
}
}
mutex_unlock(&bebob->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_bebob_stream_lock_release(bebob);
return err;
@ -190,16 +214,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&bebob->mutex);
err = snd_bebob_stream_reserve_duplex(bebob, rate);
err = snd_bebob_stream_reserve_duplex(bebob, rate,
frames_per_period, frames_per_buffer);
if (err >= 0)
++bebob->substreams_counter;
mutex_unlock(&bebob->mutex);
@ -221,7 +247,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&bebob->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int
@ -286,31 +312,33 @@ pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
static snd_pcm_uframes_t
pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&bebob->tx_stream);
return amdtp_domain_stream_pcm_pointer(&bebob->domain,
&bebob->tx_stream);
}
static snd_pcm_uframes_t
pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&bebob->rx_stream);
return amdtp_domain_stream_pcm_pointer(&bebob->domain,
&bebob->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
return amdtp_stream_pcm_ack(&bebob->tx_stream);
return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
return amdtp_stream_pcm_ack(&bebob->rx_stream);
return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
}
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
@ -325,7 +353,6 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -337,7 +364,6 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -351,6 +377,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
"%s PCM", bebob->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
end:
return err;
}

View File

@ -7,7 +7,7 @@
#include "./bebob.h"
#define CALLBACK_TIMEOUT 2000
#define CALLBACK_TIMEOUT 2500
#define FW_ISO_RESOURCE_DELAY 1000
/*
@ -398,36 +398,19 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
return err;
}
static int make_both_connections(struct snd_bebob *bebob)
{
int err = 0;
err = cmp_connection_establish(&bebob->out_conn);
if (err < 0)
return err;
err = cmp_connection_establish(&bebob->in_conn);
if (err < 0) {
cmp_connection_break(&bebob->out_conn);
return err;
}
return 0;
}
static void
break_both_connections(struct snd_bebob *bebob)
static void break_both_connections(struct snd_bebob *bebob)
{
cmp_connection_break(&bebob->in_conn);
cmp_connection_break(&bebob->out_conn);
/* These models seems to be in transition state for a longer time. */
if (bebob->maudio_special_quirk != NULL)
msleep(200);
// These models seem to be in transition state for a longer time. When
// accessing in the state, any transactions is corrupted. In the worst
// case, the device is going to reboot.
if (bebob->version < 2)
msleep(600);
}
static int
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
int err = 0;
@ -437,18 +420,19 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
else
conn = &bebob->out_conn;
/* channel mapping */
// channel mapping.
if (bebob->maudio_special_quirk == NULL) {
err = map_data_channels(bebob, stream);
if (err < 0)
goto end;
return err;
}
// start amdtp stream.
err = amdtp_domain_add_stream(&bebob->domain, stream,
conn->resources.channel, conn->speed);
end:
return err;
err = cmp_connection_establish(conn);
if (err < 0)
return err;
return amdtp_domain_add_stream(&bebob->domain, stream,
conn->resources.channel, conn->speed);
}
static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
@ -553,7 +537,9 @@ static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
}
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
int err;
@ -606,6 +592,14 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
cmp_connection_release(&bebob->out_conn);
return err;
}
err = amdtp_domain_set_events_per_period(&bebob->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
cmp_connection_release(&bebob->out_conn);
cmp_connection_release(&bebob->in_conn);
return err;
}
}
return 0;
@ -627,7 +621,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
}
if (!amdtp_stream_running(&bebob->rx_stream)) {
enum snd_bebob_clock_type src;
struct amdtp_stream *master, *slave;
unsigned int curr_rate;
unsigned int ir_delay_cycle;
if (bebob->maudio_special_quirk) {
err = bebob->spec->rate->get(bebob, &curr_rate);
@ -635,19 +632,40 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
return err;
}
err = make_both_connections(bebob);
err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
return err;
err = start_stream(bebob, &bebob->rx_stream);
if (src != SND_BEBOB_CLOCK_TYPE_SYT) {
master = &bebob->tx_stream;
slave = &bebob->rx_stream;
} else {
master = &bebob->rx_stream;
slave = &bebob->tx_stream;
}
err = start_stream(bebob, master);
if (err < 0)
goto error;
err = start_stream(bebob, &bebob->tx_stream);
err = start_stream(bebob, slave);
if (err < 0)
goto error;
err = amdtp_domain_start(&bebob->domain);
// The device postpones start of transmission mostly for 1 sec
// after receives packets firstly. For safe, IR context starts
// 0.4 sec (=3200 cycles) later to version 1 or 2 firmware,
// 2.0 sec (=16000 cycles) for version 3 firmware. This is
// within 2.5 sec (=CALLBACK_TIMEOUT).
// Furthermore, some devices transfer isoc packets with
// discontinuous counter in the beginning of packet streaming.
// The delay has an effect to avoid detection of this
// discontinuity.
if (bebob->version < 2)
ir_delay_cycle = 3200;
else
ir_delay_cycle = 16000;
err = amdtp_domain_start(&bebob->domain, ir_delay_cycle);
if (err < 0)
goto error;

View File

@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream)
mutex_lock(&dice->mutex);
err = snd_dice_stream_reserve_duplex(dice, 0);
err = snd_dice_stream_reserve_duplex(dice, 0, 0, 0);
if (err >= 0) {
++dice->substreams_counter;
err = snd_dice_stream_start_duplex(dice);

View File

@ -164,13 +164,14 @@ static int init_hw_info(struct snd_dice *dice,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
struct amdtp_domain *d = &dice->domain;
unsigned int source;
bool internal;
int err;
err = snd_dice_stream_lock_try(dice);
if (err < 0)
goto end;
return err;
err = init_hw_info(dice, substream);
if (err < 0)
@ -195,27 +196,56 @@ static int pcm_open(struct snd_pcm_substream *substream)
break;
}
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
mutex_lock(&dice->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if (!internal ||
amdtp_stream_pcm_running(&dice->tx_stream[0]) ||
amdtp_stream_pcm_running(&dice->tx_stream[1]) ||
amdtp_stream_pcm_running(&dice->rx_stream[0]) ||
amdtp_stream_pcm_running(&dice->rx_stream[1])) {
(dice->substreams_counter > 0 && d->events_per_period > 0)) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int rate;
err = snd_dice_transaction_get_rate(dice, &rate);
if (err < 0)
if (err < 0) {
mutex_unlock(&dice->mutex);
goto err_locked;
}
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
if (frames_per_period > 0) {
// For double_pcm_frame quirk.
if (rate > 96000) {
frames_per_period *= 2;
frames_per_buffer *= 2;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&dice->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&dice->mutex);
goto err_locked;
}
}
}
mutex_unlock(&dice->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_dice_stream_lock_release(dice);
return err;
@ -236,16 +266,23 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_dice *dice = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int events_per_period = params_period_size(hw_params);
unsigned int events_per_buffer = params_buffer_size(hw_params);
mutex_lock(&dice->mutex);
err = snd_dice_stream_reserve_duplex(dice, rate);
// For double_pcm_frame quirk.
if (rate > 96000) {
events_per_period /= 2;
events_per_buffer /= 2;
}
err = snd_dice_stream_reserve_duplex(dice, rate,
events_per_period, events_per_buffer);
if (err >= 0)
++dice->substreams_counter;
mutex_unlock(&dice->mutex);
@ -267,7 +304,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&dice->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int capture_prepare(struct snd_pcm_substream *substream)
@ -341,14 +378,14 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(stream);
return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(stream);
return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
}
static int capture_ack(struct snd_pcm_substream *substream)
@ -356,7 +393,7 @@ static int capture_ack(struct snd_pcm_substream *substream)
struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
return amdtp_stream_pcm_ack(stream);
return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
}
static int playback_ack(struct snd_pcm_substream *substream)
@ -364,7 +401,7 @@ static int playback_ack(struct snd_pcm_substream *substream)
struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
return amdtp_stream_pcm_ack(stream);
return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
}
int snd_dice_create_pcm(struct snd_dice *dice)
@ -379,7 +416,6 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.trigger = capture_trigger,
.pointer = capture_pointer,
.ack = capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -391,7 +427,6 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.trigger = playback_trigger,
.pointer = playback_pointer,
.ack = playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
unsigned int capture, playback;
@ -421,6 +456,10 @@ int snd_dice_create_pcm(struct snd_dice *dice)
if (playback > 0)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&playback_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
}
return 0;

View File

@ -278,7 +278,9 @@ static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
snd_dice_transaction_clear_enable(dice);
}
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
unsigned int events_per_period,
unsigned int events_per_buffer)
{
unsigned int curr_rate;
int err;
@ -324,6 +326,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
&rx_params);
if (err < 0)
goto error;
err = amdtp_domain_set_events_per_period(&dice->domain,
events_per_period, events_per_buffer);
if (err < 0)
goto error;
}
return 0;
@ -455,7 +462,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
goto error;
}
err = amdtp_domain_start(&dice->domain);
err = amdtp_domain_start(&dice->domain, 0);
if (err < 0)
goto error;

View File

@ -210,7 +210,9 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice);
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
int snd_dice_stream_init_duplex(struct snd_dice *dice);
void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate);
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
unsigned int events_per_period,
unsigned int events_per_buffer);
void snd_dice_stream_update_duplex(struct snd_dice *dice);
int snd_dice_stream_detect_current_formats(struct snd_dice *dice);

View File

@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream)
return err;
mutex_lock(&dg00x->mutex);
err = snd_dg00x_stream_reserve_duplex(dg00x, 0);
err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0);
if (err >= 0) {
++dg00x->substreams_counter;
err = snd_dg00x_stream_start_duplex(dg00x);

View File

@ -100,14 +100,14 @@ static int pcm_init_hw_params(struct snd_dg00x *dg00x,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dg00x *dg00x = substream->private_data;
struct amdtp_domain *d = &dg00x->domain;
enum snd_dg00x_clock clock;
bool detect;
unsigned int rate;
int err;
err = snd_dg00x_stream_lock_try(dg00x);
if (err < 0)
goto end;
return err;
err = pcm_init_hw_params(dg00x, substream);
if (err < 0)
@ -127,19 +127,49 @@ static int pcm_open(struct snd_pcm_substream *substream)
}
}
mutex_lock(&dg00x->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
amdtp_stream_pcm_running(&dg00x->rx_stream) ||
amdtp_stream_pcm_running(&dg00x->tx_stream)) {
(dg00x->substreams_counter > 0 && d->events_per_period > 0)) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int rate;
err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
if (err < 0)
if (err < 0) {
mutex_unlock(&dg00x->mutex);
goto err_locked;
}
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
if (frames_per_period > 0) {
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&dg00x->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&dg00x->mutex);
goto err_locked;
}
}
}
mutex_unlock(&dg00x->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_dg00x_stream_lock_release(dg00x);
return err;
@ -160,16 +190,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_dg00x *dg00x = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&dg00x->mutex);
err = snd_dg00x_stream_reserve_duplex(dg00x, rate);
err = snd_dg00x_stream_reserve_duplex(dg00x, rate,
frames_per_period, frames_per_buffer);
if (err >= 0)
++dg00x->substreams_counter;
mutex_unlock(&dg00x->mutex);
@ -191,7 +223,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&dg00x->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@ -268,28 +300,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_dg00x *dg00x = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_dg00x *dg00x = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_dg00x *dg00x = substream->private_data;
return amdtp_stream_pcm_ack(&dg00x->tx_stream);
return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_dg00x *dg00x = substream->private_data;
return amdtp_stream_pcm_ack(&dg00x->rx_stream);
return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream);
}
int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
@ -304,7 +336,6 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -316,7 +347,6 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -330,6 +360,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
"%s PCM", dg00x->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -283,7 +283,9 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
destroy_stream(dg00x, &dg00x->tx_stream);
}
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
int err;
@ -315,6 +317,14 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
fw_iso_resources_free(&dg00x->rx_resources);
return err;
}
err = amdtp_domain_set_events_per_period(&dg00x->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
fw_iso_resources_free(&dg00x->rx_resources);
fw_iso_resources_free(&dg00x->tx_resources);
return err;
}
}
return 0;
@ -365,7 +375,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
if (err < 0)
goto error;
err = amdtp_domain_start(&dg00x->domain);
err = amdtp_domain_start(&dg00x->domain, 0);
if (err < 0)
goto error;

View File

@ -141,7 +141,9 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
bool *detect);
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate);
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);

View File

@ -139,6 +139,7 @@ static int pcm_init_hw_params(struct snd_ff *ff,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
struct amdtp_domain *d = &ff->domain;
unsigned int rate;
enum snd_ff_clock_src src;
int i, err;
@ -155,16 +156,21 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto release_lock;
mutex_lock(&ff->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if (src != SND_FF_CLOCK_SRC_INTERNAL) {
for (i = 0; i < CIP_SFC_COUNT; ++i) {
if (amdtp_rate_table[i] == rate)
break;
}
/*
* The unit is configured at sampling frequency which packet
* streaming engine can't support.
*/
// The unit is configured at sampling frequency which packet
// streaming engine can't support.
if (i >= CIP_SFC_COUNT) {
mutex_unlock(&ff->mutex);
err = -EIO;
goto release_lock;
}
@ -172,14 +178,34 @@ static int pcm_open(struct snd_pcm_substream *substream)
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
} else {
if (amdtp_stream_pcm_running(&ff->rx_stream) ||
amdtp_stream_pcm_running(&ff->tx_stream)) {
if (ff->substreams_counter > 0) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
rate = amdtp_rate_table[ff->rx_stream.sfc];
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&ff->mutex);
goto release_lock;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&ff->mutex);
goto release_lock;
}
}
}
mutex_unlock(&ff->mutex);
snd_pcm_set_sync(substream);
return 0;
@ -204,16 +230,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_ff *ff = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&ff->mutex);
err = snd_ff_stream_reserve_duplex(ff, rate);
err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
frames_per_buffer);
if (err >= 0)
++ff->substreams_counter;
mutex_unlock(&ff->mutex);
@ -235,7 +263,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&ff->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@ -312,28 +340,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_ff *ff = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&ff->tx_stream);
return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_ff *ff = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&ff->rx_stream);
return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
return amdtp_stream_pcm_ack(&ff->tx_stream);
return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
return amdtp_stream_pcm_ack(&ff->rx_stream);
return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
}
int snd_ff_create_pcm_devices(struct snd_ff *ff)
@ -348,7 +376,6 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
@ -360,7 +387,6 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -374,6 +400,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
"%s PCM", ff->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -106,7 +106,9 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
destroy_stream(ff, &ff->tx_stream);
}
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
enum snd_ff_clock_src src;
@ -150,6 +152,14 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
err = ff->spec->protocol->allocate_resources(ff, rate);
if (err < 0)
return err;
err = amdtp_domain_set_events_per_period(&ff->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
fw_iso_resources_free(&ff->tx_resources);
fw_iso_resources_free(&ff->rx_resources);
return err;
}
}
return 0;
@ -174,6 +184,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
int spd = fw_parent_device(ff->unit)->max_speed;
unsigned int ir_delay_cycle;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
@ -189,7 +200,14 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
if (err < 0)
goto error;
err = amdtp_domain_start(&ff->domain);
// The device postpones start of transmission mostly for several
// cycles after receiving packets firstly.
if (ff->spec->protocol == &snd_ff_protocol_ff800)
ir_delay_cycle = 800; // = 100 msec
else
ir_delay_cycle = 16; // = 2 msec
err = amdtp_domain_start(&ff->domain, ir_delay_cycle);
if (err < 0)
goto error;

View File

@ -139,7 +139,9 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
enum snd_ff_stream_mode *mode);
int snd_ff_stream_init_duplex(struct snd_ff *ff);
void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate);
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
void snd_ff_stream_stop_duplex(struct snd_ff *ff);
void snd_ff_stream_update_duplex(struct snd_ff *ff);

View File

@ -207,7 +207,9 @@ int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
int snd_efw_stream_init_duplex(struct snd_efw *efw);
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate);
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_efw_stream_start_duplex(struct snd_efw *efw);
void snd_efw_stream_stop_duplex(struct snd_efw *efw);
void snd_efw_stream_update_duplex(struct snd_efw *efw);

View File

@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream)
goto end;
mutex_lock(&efw->mutex);
err = snd_efw_stream_reserve_duplex(efw, 0);
err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0);
if (err >= 0) {
++efw->substreams_counter;
err = snd_efw_stream_start_duplex(efw);

View File

@ -173,13 +173,13 @@ pcm_init_hw_params(struct snd_efw *efw,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
unsigned int sampling_rate;
struct amdtp_domain *d = &efw->domain;
enum snd_efw_clock_source clock_source;
int err;
err = snd_efw_stream_lock_try(efw);
if (err < 0)
goto end;
return err;
err = pcm_init_hw_params(efw, substream);
if (err < 0)
@ -189,23 +189,49 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
mutex_lock(&efw->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
amdtp_stream_pcm_running(&efw->tx_stream) ||
amdtp_stream_pcm_running(&efw->rx_stream)) {
(efw->substreams_counter > 0 && d->events_per_period > 0)) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int sampling_rate;
err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
if (err < 0)
if (err < 0) {
mutex_unlock(&efw->mutex);
goto err_locked;
}
substream->runtime->hw.rate_min = sampling_rate;
substream->runtime->hw.rate_max = sampling_rate;
if (frames_per_period > 0) {
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&efw->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&efw->mutex);
goto err_locked;
}
}
}
mutex_unlock(&efw->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_efw_stream_lock_release(efw);
return err;
@ -224,16 +250,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_efw *efw = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&efw->mutex);
err = snd_efw_stream_reserve_duplex(efw, rate);
err = snd_efw_stream_reserve_duplex(efw, rate,
frames_per_period, frames_per_buffer);
if (err >= 0)
++efw->substreams_counter;
mutex_unlock(&efw->mutex);
@ -255,7 +283,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&efw->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@ -319,26 +347,28 @@ static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_efw *efw = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&efw->tx_stream);
return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_efw *efw = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&efw->rx_stream);
return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
return amdtp_stream_pcm_ack(&efw->tx_stream);
return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
return amdtp_stream_pcm_ack(&efw->rx_stream);
return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream);
}
int snd_efw_create_pcm_devices(struct snd_efw *efw)
@ -353,7 +383,6 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -365,7 +394,6 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -378,6 +406,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
end:
return err;
}

View File

@ -181,7 +181,9 @@ static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
}
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
int err;
@ -228,6 +230,14 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
cmp_connection_release(&efw->in_conn);
return err;
}
err = amdtp_domain_set_events_per_period(&efw->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
cmp_connection_release(&efw->in_conn);
cmp_connection_release(&efw->out_conn);
return err;
}
}
return 0;
@ -262,7 +272,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
if (err < 0)
goto error;
err = amdtp_domain_start(&efw->domain);
err = amdtp_domain_start(&efw->domain, 0);
if (err < 0)
goto error;

View File

@ -288,8 +288,7 @@ static int isight_hw_params(struct snd_pcm_substream *substream,
struct isight *isight = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
@ -337,7 +336,7 @@ static int isight_hw_free(struct snd_pcm_substream *substream)
isight_stop_streaming(isight);
mutex_unlock(&isight->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int isight_start_streaming(struct isight *isight)
@ -453,7 +452,6 @@ static int isight_create_pcm(struct isight *isight)
.prepare = isight_prepare,
.trigger = isight_trigger,
.pointer = isight_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -465,6 +463,8 @@ static int isight_create_pcm(struct isight *isight)
strcpy(pcm->name, "iSight");
isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
isight->pcm->ops = &ops;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream)
mutex_lock(&motu->mutex);
err = snd_motu_stream_reserve_duplex(motu, 0);
err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0);
if (err >= 0) {
++motu->substreams_counter;
err = snd_motu_stream_start_duplex(motu);

View File

@ -134,8 +134,8 @@ static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
struct amdtp_domain *d = &motu->domain;
enum snd_motu_clock_source src;
unsigned int rate;
int err;
err = snd_motu_stream_lock_try(motu);
@ -152,28 +152,51 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
err = protocol->get_clock_source(motu, &src);
if (err < 0)
goto err_locked;
if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
amdtp_stream_pcm_running(&motu->tx_stream) ||
amdtp_stream_pcm_running(&motu->rx_stream)) {
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL &&
src != SND_MOTU_CLOCK_SOURCE_SPH) ||
(motu->substreams_counter > 0 && d->events_per_period > 0)) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int rate;
err = protocol->get_clock_rate(motu, &rate);
if (err < 0)
goto err_locked;
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
if (frames_per_period > 0) {
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&motu->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&motu->mutex);
goto err_locked;
}
}
}
snd_pcm_set_sync(substream);
mutex_unlock(&motu->mutex);
return err;
return 0;
err_locked:
mutex_unlock(&motu->mutex);
snd_motu_stream_lock_release(motu);
@ -195,16 +218,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_motu *motu = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&motu->mutex);
err = snd_motu_stream_reserve_duplex(motu, rate);
err = snd_motu_stream_reserve_duplex(motu, rate,
frames_per_period, frames_per_buffer);
if (err >= 0)
++motu->substreams_counter;
mutex_unlock(&motu->mutex);
@ -226,7 +251,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&motu->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int capture_prepare(struct snd_pcm_substream *substream)
@ -295,27 +320,27 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_pointer(&motu->tx_stream);
return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_pointer(&motu->rx_stream);
return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream);
}
static int capture_ack(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_ack(&motu->tx_stream);
return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream);
}
static int playback_ack(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_ack(&motu->rx_stream);
return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);
}
int snd_motu_create_pcm_devices(struct snd_motu *motu)
@ -330,7 +355,6 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
.trigger = capture_trigger,
.pointer = capture_pointer,
.ack = capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -342,7 +366,6 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
.trigger = playback_trigger,
.pointer = playback_pointer,
.ack = playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -355,6 +378,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -16,9 +16,11 @@ static const char *const clock_names[] = {
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PDIF on coaxial interface",
[SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
[SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
[SND_MOTU_CLOCK_SOURCE_SPH] = "Source packet header",
[SND_MOTU_CLOCK_SOURCE_UNKNOWN] = "Unknown",
};
static void proc_read_clock(struct snd_info_entry *entry,

View File

@ -12,10 +12,8 @@
#define V2_CLOCK_RATE_SHIFT 3
#define V2_CLOCK_SRC_MASK 0x00000007
#define V2_CLOCK_SRC_SHIFT 0
#define V2_CLOCK_TRAVELER_FETCH_DISABLE 0x04000000
#define V2_CLOCK_TRAVELER_FETCH_ENABLE 0x03000000
#define V2_CLOCK_8PRE_FETCH_DISABLE 0x02000000
#define V2_CLOCK_8PRE_FETCH_ENABLE 0x00000000
#define V2_CLOCK_FETCH_ENABLE 0x02000000
#define V2_CLOCK_MODEL_SPECIFIC 0x04000000
#define V2_IN_OUT_CONF_OFFSET 0x0c04
#define V2_OPT_OUT_IFACE_MASK 0x00000c00
@ -26,10 +24,20 @@
#define V2_OPT_IFACE_MODE_ADAT 1
#define V2_OPT_IFACE_MODE_SPDIF 2
static int get_clock_rate(u32 data, unsigned int *rate)
{
unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
if (index >= ARRAY_SIZE(snd_motu_clock_rates))
return -EIO;
*rate = snd_motu_clock_rates[index];
return 0;
}
static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
{
__be32 reg;
unsigned int index;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
@ -37,13 +45,7 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
if (err < 0)
return err;
index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
if (index >= ARRAY_SIZE(snd_motu_clock_rates))
return -EIO;
*rate = snd_motu_clock_rates[index];
return 0;
return get_clock_rate(be32_to_cpu(reg), rate);
}
static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
@ -69,51 +71,44 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
data &= ~V2_CLOCK_RATE_MASK;
data |= i << V2_CLOCK_RATE_SHIFT;
if (motu->spec == &snd_motu_spec_traveler) {
data &= ~V2_CLOCK_TRAVELER_FETCH_ENABLE;
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
}
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
}
static int v2_get_clock_source(struct snd_motu *motu,
enum snd_motu_clock_source *src)
static int get_clock_source(struct snd_motu *motu, u32 data,
enum snd_motu_clock_source *src)
{
__be32 reg;
unsigned int index;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
unsigned int index = data & V2_CLOCK_SRC_MASK;
if (index > 5)
return -EIO;
/* To check the configuration of optical interface. */
err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
switch (index) {
case 0:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
case 1:
{
__be32 reg;
// To check the configuration of optical interface.
int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET,
&reg, sizeof(reg));
if (err < 0)
return err;
if (be32_to_cpu(reg) & 0x00000200)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
break;
}
case 2:
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
break;
case 3:
*src = SND_MOTU_CLOCK_SOURCE_SPH;
break;
case 4:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
break;
@ -127,44 +122,65 @@ static int v2_get_clock_source(struct snd_motu *motu,
return 0;
}
static int v2_get_clock_source(struct snd_motu *motu,
enum snd_motu_clock_source *src)
{
__be32 reg;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
return get_clock_source(motu, be32_to_cpu(reg), src);
}
static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
{
enum snd_motu_clock_source src;
__be32 reg;
u32 data;
int err = 0;
if (motu->spec == &snd_motu_spec_traveler ||
motu->spec == &snd_motu_spec_8pre) {
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
&reg, sizeof(reg));
// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
if (motu->spec == &snd_motu_spec_828mk2)
return 0;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
err = get_clock_source(motu, data, &src);
if (err < 0)
return err;
data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
if (enable)
data |= V2_CLOCK_FETCH_ENABLE;
if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) {
// Expected for Traveler and 896HD, which implements Altera
// Cyclone EP1C3.
data |= V2_CLOCK_MODEL_SPECIFIC;
} else {
// For UltraLite and 8pre, which implements Xilinx Spartan
// XC3S200.
unsigned int rate;
err = get_clock_rate(data, &rate);
if (err < 0)
return err;
data = be32_to_cpu(reg);
if (motu->spec == &snd_motu_spec_traveler) {
data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
V2_CLOCK_TRAVELER_FETCH_ENABLE);
if (enable)
data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
else
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
} else if (motu->spec == &snd_motu_spec_8pre) {
data &= ~(V2_CLOCK_8PRE_FETCH_DISABLE |
V2_CLOCK_8PRE_FETCH_ENABLE);
if (enable)
data |= V2_CLOCK_8PRE_FETCH_DISABLE;
else
data |= V2_CLOCK_8PRE_FETCH_ENABLE;
}
reg = cpu_to_be32(data);
err = snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
&reg, sizeof(reg));
if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
data |= V2_CLOCK_MODEL_SPECIFIC;
}
return err;
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
}
static void calculate_fixed_part(struct snd_motu_packet_format *formats,
@ -191,7 +207,7 @@ static void calculate_fixed_part(struct snd_motu_packet_format *formats,
pcm_chunks[1] += 2;
}
} else {
if (flags & SND_MOTU_SPEC_RX_SEPARETED_MAIN) {
if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
}

View File

@ -104,6 +104,8 @@ static int v3_get_clock_source(struct snd_motu *motu,
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
} else if (val == 0x01) {
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
} else if (val == 0x02) {
*src = SND_MOTU_CLOCK_SOURCE_SPH;
} else if (val == 0x10) {
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
} else if (val == 0x18 || val == 0x19) {
@ -187,7 +189,7 @@ static void calculate_fixed_part(struct snd_motu_packet_format *formats,
pcm_chunks[1] += 2;
}
} else {
if (flags & SND_MOTU_SPEC_RX_SEPARETED_MAIN) {
if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
}

View File

@ -133,7 +133,9 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
return 0;
}
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
int err;
@ -171,6 +173,14 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
fw_iso_resources_free(&motu->tx_resources);
return err;
}
err = amdtp_domain_set_events_per_period(&motu->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
fw_iso_resources_free(&motu->tx_resources);
fw_iso_resources_free(&motu->rx_resources);
return err;
}
}
return 0;
@ -250,7 +260,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
if (err < 0)
goto stop_streams;
err = amdtp_domain_start(&motu->domain);
err = amdtp_domain_start(&motu->domain, 0);
if (err < 0)
goto stop_streams;

View File

@ -172,13 +172,13 @@ static void motu_bus_update(struct fw_unit *unit)
snd_motu_transaction_reregister(motu);
}
static const struct snd_motu_spec motu_828mk2 = {
const struct snd_motu_spec snd_motu_spec_828mk2 = {
.name = "828mk2",
.protocol = &snd_motu_protocol_v2,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN |
SND_MOTU_SPEC_RX_SEPARATED_MAIN |
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
@ -187,7 +187,7 @@ static const struct snd_motu_spec motu_828mk2 = {
.analog_out_ports = 8,
};
const struct snd_motu_spec snd_motu_spec_traveler = {
static const struct snd_motu_spec motu_traveler = {
.name = "Traveler",
.protocol = &snd_motu_protocol_v2,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
@ -202,7 +202,20 @@ const struct snd_motu_spec snd_motu_spec_traveler = {
.analog_out_ports = 8,
};
const struct snd_motu_spec snd_motu_spec_8pre = {
static const struct snd_motu_spec motu_ultralite = {
.name = "UltraLite",
.protocol = &snd_motu_protocol_v2,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding.
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
SND_MOTU_SPEC_TX_MIDI_2ND_Q |
SND_MOTU_SPEC_RX_SEPARATED_MAIN,
.analog_in_ports = 8,
.analog_out_ports = 8,
};
static const struct snd_motu_spec motu_8pre = {
.name = "8pre",
.protocol = &snd_motu_protocol_v2,
// In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for
@ -224,7 +237,7 @@ static const struct snd_motu_spec motu_828mk3 = {
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_TX_REVERB_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN |
SND_MOTU_SPEC_RX_SEPARATED_MAIN |
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
SND_MOTU_SPEC_HAS_OPT_IFACE_B |
SND_MOTU_SPEC_RX_MIDI_3RD_Q |
@ -240,7 +253,7 @@ static const struct snd_motu_spec motu_audio_express = {
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN |
SND_MOTU_SPEC_RX_SEPARATED_MAIN |
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
.analog_in_ports = 2,
@ -253,7 +266,7 @@ static const struct snd_motu_spec motu_4pre = {
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN,
SND_MOTU_SPEC_RX_SEPARATED_MAIN,
.analog_in_ports = 2,
.analog_out_ports = 2,
};
@ -270,9 +283,10 @@ static const struct snd_motu_spec motu_4pre = {
}
static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x000003, &motu_828mk2),
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
SND_MOTU_DEV_ENTRY(0x000009, &motu_traveler),
SND_MOTU_DEV_ENTRY(0x00000d, &motu_ultralite),
SND_MOTU_DEV_ENTRY(0x00000f, &motu_8pre),
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),

View File

@ -86,7 +86,7 @@ enum snd_motu_spec_flags {
SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200,
SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400,
SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800,
SND_MOTU_SPEC_RX_SEPARETED_MAIN = 0x1000,
SND_MOTU_SPEC_RX_SEPARATED_MAIN = 0x1000,
};
#define SND_MOTU_CLOCK_RATE_COUNT 6
@ -104,6 +104,7 @@ enum snd_motu_clock_source {
SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
SND_MOTU_CLOCK_SOURCE_SPH,
SND_MOTU_CLOCK_SOURCE_UNKNOWN,
};
@ -129,8 +130,7 @@ struct snd_motu_spec {
extern const struct snd_motu_protocol snd_motu_protocol_v2;
extern const struct snd_motu_protocol snd_motu_protocol_v3;
extern const struct snd_motu_spec snd_motu_spec_traveler;
extern const struct snd_motu_spec snd_motu_spec_8pre;
extern const struct snd_motu_spec snd_motu_spec_828mk2;
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
@ -154,7 +154,9 @@ void snd_motu_transaction_unregister(struct snd_motu *motu);
int snd_motu_stream_init_duplex(struct snd_motu *motu);
void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate);
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_motu_stream_start_duplex(struct snd_motu *motu);
void snd_motu_stream_stop_duplex(struct snd_motu *motu);
int snd_motu_stream_lock_try(struct snd_motu *motu);

View File

@ -18,7 +18,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
mutex_lock(&oxfw->mutex);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0);
if (err >= 0) {
++oxfw->substreams_count;
err = snd_oxfw_stream_start_duplex(oxfw);
@ -45,7 +45,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
mutex_lock(&oxfw->mutex);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0);
if (err >= 0) {
++oxfw->substreams_count;
err = snd_oxfw_stream_start_duplex(oxfw);

View File

@ -170,30 +170,56 @@ static int limit_to_current_params(struct snd_pcm_substream *substream)
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
struct amdtp_domain *d = &oxfw->domain;
int err;
err = snd_oxfw_stream_lock_try(oxfw);
if (err < 0)
goto end;
return err;
err = init_hw_params(oxfw, substream);
if (err < 0)
goto err_locked;
/*
* When any PCM streams are already running, the available sampling
* rate is limited at current value.
*/
if (amdtp_stream_pcm_running(&oxfw->tx_stream) ||
amdtp_stream_pcm_running(&oxfw->rx_stream)) {
mutex_lock(&oxfw->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
err = limit_to_current_params(substream);
if (err < 0)
goto end;
if (err < 0) {
mutex_unlock(&oxfw->mutex);
goto err_locked;
}
if (frames_per_period > 0) {
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&oxfw->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&oxfw->mutex);
goto err_locked;
}
}
}
mutex_unlock(&oxfw->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_oxfw_stream_lock_release(oxfw);
return err;
@ -213,18 +239,20 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_oxfw *oxfw = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int channels = params_channels(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&oxfw->mutex);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
rate, channels);
rate, channels, frames_per_period,
frames_per_buffer);
if (err >= 0)
++oxfw->substreams_count;
mutex_unlock(&oxfw->mutex);
@ -238,18 +266,20 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_oxfw *oxfw = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int channels = params_channels(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&oxfw->mutex);
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
rate, channels);
rate, channels, frames_per_period,
frames_per_buffer);
if (err >= 0)
++oxfw->substreams_count;
mutex_unlock(&oxfw->mutex);
@ -271,7 +301,7 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&oxfw->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
@ -286,7 +316,7 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&oxfw->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@ -361,27 +391,27 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
{
struct snd_oxfw *oxfw = sbstm->private_data;
return amdtp_stream_pcm_pointer(&oxfw->tx_stream);
return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
{
struct snd_oxfw *oxfw = sbstm->private_data;
return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
return amdtp_stream_pcm_ack(&oxfw->tx_stream);
return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_oxfw *oxfw = substream->private_data;
return amdtp_stream_pcm_ack(&oxfw->rx_stream);
return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
}
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
@ -396,7 +426,6 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -408,7 +437,6 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
unsigned int cap = 0;
@ -426,6 +454,8 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
if (cap > 0)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -244,7 +244,9 @@ static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
struct amdtp_stream *stream,
unsigned int rate, unsigned int pcm_channels)
unsigned int rate, unsigned int pcm_channels,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
struct snd_oxfw_stream_formation formation;
enum avc_general_plug_dir dir;
@ -305,6 +307,15 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
return err;
}
}
err = amdtp_domain_set_events_per_period(&oxfw->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output)
cmp_connection_release(&oxfw->out_conn);
return err;
}
}
return 0;
@ -344,7 +355,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
}
}
err = amdtp_domain_start(&oxfw->domain);
err = amdtp_domain_start(&oxfw->domain, 0);
if (err < 0)
goto error;

View File

@ -103,7 +103,9 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw);
int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
struct amdtp_stream *stream,
unsigned int rate, unsigned int pcm_channels);
unsigned int rate, unsigned int pcm_channels,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw);
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw);
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw);

View File

@ -43,13 +43,13 @@ static int pcm_init_hw_params(struct snd_tscm *tscm,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_tscm *tscm = substream->private_data;
struct amdtp_domain *d = &tscm->domain;
enum snd_tscm_clock clock;
unsigned int rate;
int err;
err = snd_tscm_stream_lock_try(tscm);
if (err < 0)
goto end;
return err;
err = pcm_init_hw_params(tscm, substream);
if (err < 0)
@ -59,19 +59,46 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
if (clock != SND_TSCM_CLOCK_INTERNAL ||
amdtp_stream_pcm_running(&tscm->rx_stream) ||
amdtp_stream_pcm_running(&tscm->tx_stream)) {
mutex_lock(&tscm->mutex);
// When source of clock is not internal or any stream is reserved for
// transmission of PCM frames, the available sampling rate is limited
// at current one.
if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
unsigned int frames_per_period = d->events_per_period;
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int rate;
err = snd_tscm_stream_get_rate(tscm, &rate);
if (err < 0)
if (err < 0) {
mutex_unlock(&tscm->mutex);
goto err_locked;
}
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
frames_per_period, frames_per_period);
if (err < 0) {
mutex_unlock(&tscm->mutex);
goto err_locked;
}
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
frames_per_buffer, frames_per_buffer);
if (err < 0) {
mutex_unlock(&tscm->mutex);
goto err_locked;
}
}
mutex_unlock(&tscm->mutex);
snd_pcm_set_sync(substream);
end:
return err;
return 0;
err_locked:
snd_tscm_stream_lock_release(tscm);
return err;
@ -92,16 +119,18 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_tscm *tscm = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
mutex_lock(&tscm->mutex);
err = snd_tscm_stream_reserve_duplex(tscm, rate);
err = snd_tscm_stream_reserve_duplex(tscm, rate,
frames_per_period, frames_per_buffer);
if (err >= 0)
++tscm->substreams_counter;
mutex_unlock(&tscm->mutex);
@ -123,7 +152,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_unlock(&tscm->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
@ -200,28 +229,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_tscm *tscm = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&tscm->tx_stream);
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_tscm *tscm = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&tscm->rx_stream);
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
}
static int pcm_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_tscm *tscm = substream->private_data;
return amdtp_stream_pcm_ack(&tscm->tx_stream);
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
}
static int pcm_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_tscm *tscm = substream->private_data;
return amdtp_stream_pcm_ack(&tscm->rx_stream);
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
}
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
@ -236,7 +265,6 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.ack = pcm_capture_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
@ -248,7 +276,6 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.ack = pcm_playback_ack,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm *pcm;
int err;
@ -262,6 +289,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
"%s PCM", tscm->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -383,7 +383,9 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
destroy_stream(tscm, &tscm->tx_stream);
}
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer)
{
unsigned int curr_rate;
int err;
@ -413,6 +415,14 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
fw_iso_resources_free(&tscm->tx_resources);
return err;
}
err = amdtp_domain_set_events_per_period(&tscm->domain,
frames_per_period, frames_per_buffer);
if (err < 0) {
fw_iso_resources_free(&tscm->tx_resources);
fw_iso_resources_free(&tscm->rx_resources);
return err;
}
}
return 0;
@ -463,7 +473,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
err = amdtp_domain_start(&tscm->domain);
err = amdtp_domain_start(&tscm->domain, 0);
if (err < 0)
return err;

View File

@ -168,7 +168,9 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate);
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
unsigned int frames_per_period,
unsigned int frames_per_buffer);
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);

View File

@ -34,6 +34,12 @@ config SND_HDA_PREALLOC_SIZE
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
config SND_INTEL_NHLT
tristate
bool
# this config should be selected only for Intel ACPI platforms.
# A fallback is provided so that the code compiles in all cases.
# A fallback is provided so that the code compiles in all cases.
config SND_INTEL_DSP_CONFIG
tristate
select SND_INTEL_NHLT if ACPI
# this config should be selected only for Intel DSP platforms.
# A fallback is provided so that the code compiles in all cases.

View File

@ -14,5 +14,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
#extended hda
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
snd-intel-nhlt-objs := intel-nhlt.o
obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o
snd-intel-dspcfg-objs := intel-dsp-config.o
snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o

View File

@ -363,6 +363,7 @@ static const struct regmap_config hda_regmap_cfg = {
.reg_write = hda_reg_write,
.use_single_read = true,
.use_single_write = true,
.disable_locking = true,
};
/**

View File

@ -0,0 +1,357 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/intel-dsp-config.h>
#include <sound/intel-nhlt.h>
static int dsp_driver;
module_param(dsp_driver, int, 0444);
MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
#define FLAG_SST BIT(0)
#define FLAG_SOF BIT(1)
#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
struct config_entry {
u32 flags;
u16 device;
const struct dmi_system_id *dmi_table;
};
/*
* configuration table
* - the order of similar PCI ID entries is important!
* - the first successful match will win
*/
static const struct config_entry config_table[] = {
/* Merrifield */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
{
.flags = FLAG_SOF,
.device = 0x119a,
},
#endif
/* Broxton-T */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
{
.flags = FLAG_SOF,
.device = 0x1a98,
},
#endif
/*
* Apollolake (Broxton-P)
* the legacy HDaudio driver is used except on Up Squared (SOF) and
* Chromebooks (SST)
*/
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
{
.flags = FLAG_SOF,
.device = 0x5a98,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Up Squared",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
}
},
{}
}
},
#endif
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
{
.flags = FLAG_SST,
.device = 0x5a98,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
#endif
/*
* Skylake and Kabylake use legacy HDaudio driver except for Google
* Chromebooks (SST)
*/
/* Sunrise Point-LP */
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
{
.flags = FLAG_SST,
.device = 0x9d70,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
#endif
/* Kabylake-LP */
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
{
.flags = FLAG_SST,
.device = 0x9d71,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
#endif
/*
* Geminilake uses legacy HDaudio driver except for Google
* Chromebooks
*/
/* Geminilake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
{
.flags = FLAG_SOF,
.device = 0x3198,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
#endif
/*
* CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
* HDaudio driver except for Google Chromebooks and when DMICs are
* present. Two cases are required since Coreboot does not expose NHLT
* tables.
*
* When the Chromebook quirk is not present, it's based on information
* that no such device exists. When the quirk is present, it could be
* either based on product information or a placeholder.
*/
/* Cannonlake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
{
.flags = FLAG_SOF,
.device = 0x9dc8,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0x9dc8,
},
#endif
/* Coffelake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
{
.flags = FLAG_SOF,
.device = 0xa348,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0xa348,
},
#endif
/* Cometlake-LP */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP)
{
.flags = FLAG_SOF,
.device = 0x02c8,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0x02c8,
},
#endif
/* Cometlake-H */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0x06c8,
},
#endif
/* Icelake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
{
.flags = FLAG_SOF,
.device = 0x34c8,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0x34c8,
},
#endif
/* Tigerlake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
{
.flags = FLAG_SOF,
.device = 0xa0c8,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
{}
}
},
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0xa0c8,
},
#endif
/* Elkhart Lake */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
.device = 0x4b55,
},
#endif
};
static const struct config_entry *snd_intel_dsp_find_config
(struct pci_dev *pci, const struct config_entry *table, u32 len)
{
u16 device;
device = pci->device;
for (; len > 0; len--, table++) {
if (table->device != device)
continue;
if (table->dmi_table && !dmi_check_system(table->dmi_table))
continue;
return table;
}
return NULL;
}
static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
{
struct nhlt_acpi_table *nhlt;
int ret = 0;
nhlt = intel_nhlt_init(&pci->dev);
if (nhlt) {
if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
ret = 1;
intel_nhlt_free(nhlt);
}
return ret;
}
int snd_intel_dsp_driver_probe(struct pci_dev *pci)
{
const struct config_entry *cfg;
/* Intel vendor only */
if (pci->vendor != 0x8086)
return SND_INTEL_DSP_DRIVER_ANY;
if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
return dsp_driver;
/*
* detect DSP by checking class/subclass/prog-id information
* class=04 subclass 03 prog-if 00: no DSP, use legacy driver
* class=04 subclass 01 prog-if 00: DSP is present
* (and may be required e.g. for DMIC or SSP support)
* class=04 subclass 03 prog-if 80: use DSP or legacy mode
*/
if (pci->class == 0x040300)
return SND_INTEL_DSP_DRIVER_LEGACY;
if (pci->class != 0x040100 && pci->class != 0x040380) {
dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDA legacy driver\n", pci->class);
return SND_INTEL_DSP_DRIVER_LEGACY;
}
dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
/* find the configuration for the specific device */
cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
if (!cfg)
return SND_INTEL_DSP_DRIVER_ANY;
if (cfg->flags & FLAG_SOF) {
if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) {
if (snd_intel_dsp_check_dmic(pci)) {
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
return SND_INTEL_DSP_DRIVER_SOF;
}
} else {
return SND_INTEL_DSP_DRIVER_SOF;
}
}
if (cfg->flags & FLAG_SST)
return SND_INTEL_DSP_DRIVER_SST;
return SND_INTEL_DSP_DRIVER_LEGACY;
}
EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel DSP config driver");

View File

@ -102,6 +102,3 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
return dmic_geo;
}
EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel NHLT driver");

View File

@ -2,22 +2,22 @@
# ALSA ISA drivers
config SND_WSS_LIB
tristate
select SND_PCM
tristate
select SND_PCM
select SND_TIMER
config SND_SB_COMMON
tristate
tristate
config SND_SB8_DSP
tristate
select SND_PCM
select SND_SB_COMMON
tristate
select SND_PCM
select SND_SB_COMMON
config SND_SB16_DSP
tristate
select SND_PCM
select SND_SB_COMMON
tristate
select SND_PCM
select SND_SB_COMMON
menuconfig SND_ISA
bool "ISA sound devices"

View File

@ -14,15 +14,15 @@ config SND_SGI_O2
tristate "SGI O2 Audio"
depends on SGI_IP32
select SND_PCM
help
Sound support for the SGI O2 Workstation.
help
Sound support for the SGI O2 Workstation.
config SND_SGI_HAL2
tristate "SGI HAL2 Audio"
depends on SGI_HAS_HAL2
tristate "SGI HAL2 Audio"
depends on SGI_HAS_HAL2
select SND_PCM
help
Sound support for the SGI Indy and Indigo2 Workstation.
help
Sound support for the SGI Indy and Indigo2 Workstation.
endif # SND_MIPS

View File

@ -741,8 +741,7 @@ static int hal2_pcm_create(struct snd_hal2 *hal2)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&hal2_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
0, 1024 * 1024);
NULL, 0, 1024 * 1024);
return 0;
}

View File

@ -582,14 +582,13 @@ static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}
/* hw_free callback */
static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_vmalloc_buffer(substream);
return snd_pcm_lib_free_pages(substream);
}
/* prepare callback */
@ -670,7 +669,6 @@ static const struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
@ -682,7 +680,6 @@ static const struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops snd_sgio2audio_capture_ops = {
@ -694,7 +691,6 @@ static const struct snd_pcm_ops snd_sgio2audio_capture_ops = {
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
/*
@ -720,6 +716,8 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
&snd_sgio2audio_playback1_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_sgio2audio_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
/* create second pcm device with one outputs and no input */
err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
@ -732,6 +730,8 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback2_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
return 0;
}

View File

@ -217,7 +217,7 @@ config SND_CMIPCI
will be called snd-cmipci.
config SND_OXYGEN_LIB
tristate
tristate
config SND_OXYGEN
tristate "C-Media 8786, 8787, 8788 (Oxygen)"

View File

@ -633,9 +633,9 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device)
chip->csubs = NULL;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
BUFFER_BYTES_MAX / 2,
BUFFER_BYTES_MAX);
&chip->pci->dev,
BUFFER_BYTES_MAX / 2,
BUFFER_BYTES_MAX);
return 0;
}

View File

@ -1672,7 +1672,7 @@ static int snd_ali_pcm(struct snd_ali *codec, int device,
desc->capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(codec->pci),
&codec->pci->dev,
64*1024, 128*1024);
pcm->info_flags = 0;

View File

@ -592,7 +592,8 @@ static int snd_als300_new_pcm(struct snd_als300 *chip)
/* pre-allocation of buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
&chip->pci->dev,
64*1024, 64*1024);
return 0;
}

View File

@ -693,7 +693,8 @@ static int snd_als4000_pcm(struct snd_sb *chip, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pci->dev,
64*1024, 64*1024);
chip->pcm = pcm;

View File

@ -1325,8 +1325,8 @@ static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
/*? do we want to emulate MMAP for non-BBM cards?
Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(asihpi->pci),
64*1024, BUFFER_BYTES_MAX);
&asihpi->pci->dev,
64*1024, BUFFER_BYTES_MAX);
return 0;
}

View File

@ -353,7 +353,7 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
ATI_DESC_LIST_SIZE,
&dma->desc_buf) < 0)
return -ENOMEM;
@ -1284,7 +1284,7 @@ static int snd_atiixp_pcm_new(struct atiixp *chip)
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64*1024, 128*1024);
err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@ -1317,7 +1317,7 @@ static int snd_atiixp_pcm_new(struct atiixp *chip)
chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64*1024, 128*1024);
/* pre-select AC97 SPDIF slots 10/11 */

View File

@ -321,7 +321,7 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
return -ENOMEM;
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0)
return -ENOMEM;
dma->period_bytes = dma->periods = 0; /* clear */
@ -995,7 +995,7 @@ static int snd_atiixp_pcm_new(struct atiixp_modem *chip)
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64*1024, 128*1024);
return 0;

View File

@ -436,7 +436,6 @@ static const struct snd_pcm_ops snd_vortex_playback_ops = {
.prepare = snd_vortex_pcm_prepare,
.trigger = snd_vortex_pcm_trigger,
.pointer = snd_vortex_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
/*
@ -638,7 +637,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
/* pre-allocation of Scatter-Gather buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci_dev),
&chip->pci_dev->dev,
0x10000, 0x10000);
switch (VORTEX_PCM_TYPE(pcm)) {

View File

@ -613,7 +613,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
/* Preallocate continuous pages. */
snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
@ -645,7 +645,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
/* Preallocate continuous pages. */
snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
@ -678,7 +678,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
/* Preallocate continuous pages. */
snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
64 * 1024, 64 * 1024);
/* Create control */

View File

@ -2135,8 +2135,8 @@ snd_azf3328_pcm(struct snd_azf3328 *chip)
chip->pcm[AZF_CODEC_CAPTURE] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024, 64*1024);
&chip->pci->dev,
64*1024, 64*1024);
err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
1, 0, &pcm);
@ -2151,8 +2151,8 @@ snd_azf3328_pcm(struct snd_azf3328 *chip)
chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024, 64*1024);
&chip->pci->dev,
64*1024, 64*1024);
return 0;
}

View File

@ -217,7 +217,7 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea
__le32 *risc;
if (chip->dma_risc.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0)
return -ENOMEM;
}
@ -545,7 +545,6 @@ static const struct snd_pcm_ops snd_bt87x_pcm_ops = {
.prepare = snd_bt87x_prepare,
.trigger = snd_bt87x_trigger,
.pointer = snd_bt87x_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static int snd_bt87x_capture_volume_info(struct snd_kcontrol *kcontrol,
@ -701,7 +700,7 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
strcpy(pcm->name, name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
128 * 1024,
ALIGN(255 * 4092, 1024));
return 0;

View File

@ -1389,7 +1389,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
substream;
substream = substream->next) {
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
}
@ -1397,7 +1397,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
substream;
substream = substream->next) {
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
}
@ -1692,7 +1692,7 @@ static int snd_ca0106_create(int dev, struct snd_card *card,
chip->irq = pci->irq;
/* This stores the periods table. */
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
1024, &chip->buffer) < 0) {
snd_ca0106_free(chip);
return -ENOMEM;

View File

@ -1902,7 +1902,7 @@ static int snd_cmipci_pcm_new(struct cmipci *cm, int device)
cm->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
&cm->pci->dev, 64*1024, 128*1024);
return 0;
}
@ -1924,7 +1924,7 @@ static int snd_cmipci_pcm2_new(struct cmipci *cm, int device)
cm->pcm2 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
&cm->pci->dev, 64*1024, 128*1024);
return 0;
}
@ -1947,7 +1947,7 @@ static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
cm->pcm_spdif = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
&cm->pci->dev, 64*1024, 128*1024);
err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_alt_chmaps, cm->max_channels, 0,

View File

@ -975,7 +975,8 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device)
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 512*1024);
&chip->pci->dev,
64*1024, 512*1024);
return 0;
}

View File

@ -1494,7 +1494,7 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL);
if (cpcm == NULL)
return -ENOMEM;
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &cpcm->hw_buf) < 0) {
kfree(cpcm);
return -ENOMEM;
@ -1582,7 +1582,7 @@ static int snd_cs46xx_capture_open(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &chip->capt.hw_buf) < 0)
return -ENOMEM;
chip->capt.substream = substream;
@ -1784,7 +1784,8 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
&chip->pci->dev,
64*1024, 256*1024);
return 0;
}
@ -1809,7 +1810,8 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
&chip->pci->dev,
64*1024, 256*1024);
return 0;
}
@ -1832,7 +1834,8 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
chip->pcm_center_lfe = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
&chip->pci->dev,
64*1024, 256*1024);
return 0;
}
@ -1855,7 +1858,8 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
chip->pcm_iec958 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
&chip->pci->dev,
64*1024, 256*1024);
return 0;
}

View File

@ -117,7 +117,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cs5535au->pci),
&cs5535au->pci->dev,
CS5535AUDIO_DESC_LIST_SIZE+1,
&dma->desc_buf) < 0)
return -ENOMEM;
@ -432,8 +432,8 @@ int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
strcpy(pcm->name, "CS5535 Audio");
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cs5535au->pci),
64*1024, 128*1024);
&cs5535au->pci->dev,
64*1024, 128*1024);
cs5535au->pcm = pcm;
return 0;

View File

@ -379,7 +379,6 @@ static const struct snd_pcm_ops ct_pcm_playback_ops = {
.prepare = ct_pcm_playback_prepare,
.trigger = ct_pcm_playback_trigger,
.pointer = ct_pcm_playback_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
/* PCM operators for capture */
@ -392,7 +391,6 @@ static const struct snd_pcm_ops ct_pcm_capture_ops = {
.prepare = ct_pcm_capture_prepare,
.trigger = ct_pcm_capture_trigger,
.pointer = ct_pcm_capture_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static const struct snd_pcm_chmap_elem surround_map[] = {
@ -452,7 +450,8 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
&atc->pci->dev,
128*1024, 128*1024);
chs = 2;
switch (device) {

View File

@ -183,7 +183,7 @@ int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
/* Allocate page table pages */
for (i = 0; i < CT_PTP_NUM; i++) {
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(pci),
&pci->dev,
PAGE_SIZE, &vm->ptp[i]);
if (err < 0)
break;

View File

@ -324,7 +324,7 @@ static int pcm_open(struct snd_pcm_substream *substream,
/* Finally allocate a page for the scatter-gather list */
if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
&chip->pci->dev,
PAGE_SIZE, &pipe->sgpage)) < 0) {
dev_err(chip->card->dev, "s-g list allocation failed\n");
return err;
@ -824,7 +824,6 @@ static const struct snd_pcm_ops analog_playback_ops = {
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static const struct snd_pcm_ops analog_capture_ops = {
.open = pcm_analog_in_open,
@ -835,7 +834,6 @@ static const struct snd_pcm_ops analog_capture_ops = {
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
#ifdef ECHOCARD_HAS_DIGITAL_IO
#ifndef ECHOCARD_HAS_VMIXER
@ -848,7 +846,6 @@ static const struct snd_pcm_ops digital_playback_ops = {
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
#endif /* !ECHOCARD_HAS_VMIXER */
static const struct snd_pcm_ops digital_capture_ops = {
@ -860,7 +857,6 @@ static const struct snd_pcm_ops digital_capture_ops = {
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
#endif /* ECHOCARD_HAS_DIGITAL_IO */
@ -869,7 +865,7 @@ static const struct snd_pcm_ops digital_capture_ops = {
/* Preallocate memory only for the first substream because it's the most
* used one
*/
static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
static void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
{
struct snd_pcm_substream *ss;
int stream;
@ -880,8 +876,6 @@ static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
dev,
ss->number ? 0 : 128<<10,
256<<10);
return 0;
}
@ -908,8 +902,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip)
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
return err;
snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital inputs, no outputs */
@ -920,8 +913,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip)
chip->digital_pcm = pcm;
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
return err;
snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#else /* ECHOCARD_HAS_VMIXER */
@ -941,8 +933,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip)
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
return err;
snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital i/o */
@ -955,8 +946,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip)
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
return err;
snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#endif /* ECHOCARD_HAS_VMIXER */
@ -1958,7 +1948,7 @@ static int snd_echo_create(struct snd_card *card,
/* Create the DSP comm page - this is the area of memory used for most
of the communication with the DSP, which accesses it via bus mastering */
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
sizeof(struct comm_page),
&chip->commpage_dma_buf) < 0) {
dev_err(chip->card->dev, "cannot allocate the comm page\n");

View File

@ -124,8 +124,9 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
goto error;
/* This stores the periods table. */
if (emu->card_capabilities->ca0151_chip) { /* P16V */
if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
1024, &emu->p16v_buffer)) < 0)
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
1024, &emu->p16v_buffer);
if (err < 0)
goto error;
}

View File

@ -877,7 +877,7 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
emu->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
32*1024, 32*1024);
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
@ -936,8 +936,8 @@ static int snd_emu10k1x_create(struct snd_card *card,
}
chip->irq = pci->irq;
if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
4 * 1024, &chip->dma_buffer) < 0) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
4 * 1024, &chip->dma_buffer) < 0) {
snd_emu10k1x_free(chip);
return -ENOMEM;
}

View File

@ -2464,7 +2464,7 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
}
if (size > 0) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
size * 2, &emu->fx8010.etram_pages) < 0)
return -ENOMEM;
memset(emu->fx8010.etram_pages.area, 0, size * 2);

View File

@ -1366,7 +1366,6 @@ static const struct snd_pcm_ops snd_emu10k1_playback_ops = {
.prepare = snd_emu10k1_playback_prepare,
.trigger = snd_emu10k1_playback_trigger,
.pointer = snd_emu10k1_playback_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static const struct snd_pcm_ops snd_emu10k1_capture_ops = {
@ -1390,7 +1389,6 @@ static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
.prepare = snd_emu10k1_efx_playback_prepare,
.trigger = snd_emu10k1_efx_playback_trigger,
.pointer = snd_emu10k1_efx_playback_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
@ -1414,12 +1412,12 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
return 0;
@ -1445,7 +1443,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
return 0;
@ -1480,7 +1478,7 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
emu->pcm_mic = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
return 0;
@ -1855,7 +1853,7 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
return err;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci),
&emu->pci->dev,
64*1024, 64*1024);
return 0;

View File

@ -387,7 +387,7 @@ int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
}
return snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(emu->pci), size, dmab);
&emu->pci->dev, size, dmab);
}
/*
@ -477,7 +477,7 @@ static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
int page;
dmab.dev.type = SNDRV_DMA_TYPE_DEV;
dmab.dev.dev = snd_dma_pci_data(emu->pci);
dmab.dev.dev = &emu->pci->dev;
for (page = first_page; page <= last_page; page++) {
if (emu->page_ptr_table[page] == NULL)

Some files were not shown because too many files have changed in this diff Show More