mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 04:23:35 +02:00
Merge remote-tracking branch 'takashi/for-next' into tag/merge-20191121
This commit is contained in:
commit
a07cc202ba
|
|
@ -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
|
||||
==============
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
34
include/sound/intel-dsp-config.h
Normal file
34
include/sound/intel-dsp-config.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(®ister_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(®ister_mutex);
|
||||
err = snd_timer_close_locked(timeri, &card_dev_to_put);
|
||||
snd_timer_close_locked(timeri, &card_dev_to_put);
|
||||
mutex_unlock(®ister_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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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, ¶ms);
|
||||
} else {
|
||||
bool sched_irq = false;
|
||||
|
||||
params.header_length = 0;
|
||||
params.payload_length = 0;
|
||||
err = queue_out_packet(s, ¶ms);
|
||||
|
||||
if (is_irq_target) {
|
||||
sched_irq = !((s->packet_index + 1) %
|
||||
idle_irq_interval);
|
||||
}
|
||||
|
||||
err = queue_out_packet(s, ¶ms, 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,
|
||||
®, 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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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, ®,
|
||||
|
|
@ -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, ®,
|
||||
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, ®,
|
||||
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, ®,
|
||||
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,
|
||||
®, 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, ®,
|
||||
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,
|
||||
®, 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, ®,
|
||||
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,
|
||||
®, 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, ®,
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
357
sound/hda/intel-dsp-config.c
Normal file
357
sound/hda/intel-dsp-config.c
Normal 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");
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue
Block a user