Merge branch 'tag/merge-20191121' into merge/upstream-merge-20191121

This commit is contained in:
Pierre-Louis Bossart 2019-11-21 13:15:15 -06:00
commit 2a38242a90
29 changed files with 1553 additions and 365 deletions

View File

@ -25,6 +25,13 @@ Required properties:
For required properties on SPI/I2C, consult SPI/I2C device tree documentation
Optional properties:
- reset-gpios : Optional reset gpio line connected to RST pin of the codec.
The RST line is low active:
RST = low: device power-down
RST = high: device is enabled
Examples:
i2c0: i2c0@0 {
@ -34,6 +41,7 @@ i2c0: i2c0@0 {
pcm3168a: audio-codec@44 {
compatible = "ti,pcm3168a";
reg = <0x44>;
reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
clocks = <&clk_core CLK_AUDIO>;
clock-names = "scki";
VDD1-supply = <&supply3v3>;

View File

@ -29,6 +29,11 @@ Optional properties:
3 or MICBIAS_AVDD - MICBIAS output is connected to AVDD
If this node is not mentioned or if the value is unknown, then
micbias is set to 2.0V.
- ai31xx-ocmv - output common-mode voltage setting
0 - 1.35V,
1 - 1.5V,
2 - 1.65V,
3 - 1.8V
Deprecated properties:

View File

@ -805,6 +805,7 @@ destructor and PCI entries. Example code is shown first, below.
return -EBUSY;
}
chip->irq = pci->irq;
card->sync_irq = chip->irq;
/* (2) initialization of the chip hardware */
.... /* (not implemented in this document) */
@ -965,6 +966,15 @@ usually like the following:
return IRQ_HANDLED;
}
After requesting the IRQ, you can passed it to ``card->sync_irq``
field:
::
card->irq = chip->irq;
This allows PCM core automatically performing
:c:func:`synchronize_irq()` at the necessary timing like ``hw_free``.
See the later section `sync_stop callback`_ for details.
Now let's write the corresponding destructor for the resources above.
The role of destructor is simple: disable the hardware (if already
@ -1270,21 +1280,23 @@ shows only the skeleton, how to build up the PCM interfaces.
/* the hardware-specific codes will be here */
....
return 0;
}
/* hw_params callback */
static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
/* the hardware-specific codes will be here */
....
return 0;
}
/* hw_free callback */
static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
/* the hardware-specific codes will be here */
....
return 0;
}
/* prepare callback */
@ -1339,7 +1351,6 @@ shows only the skeleton, how to build up the PCM interfaces.
static struct snd_pcm_ops snd_mychip_playback_ops = {
.open = snd_mychip_playback_open,
.close = snd_mychip_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_mychip_pcm_hw_params,
.hw_free = snd_mychip_pcm_hw_free,
.prepare = snd_mychip_pcm_prepare,
@ -1351,7 +1362,6 @@ shows only the skeleton, how to build up the PCM interfaces.
static struct snd_pcm_ops snd_mychip_capture_ops = {
.open = snd_mychip_capture_open,
.close = snd_mychip_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_mychip_pcm_hw_params,
.hw_free = snd_mychip_pcm_hw_free,
.prepare = snd_mychip_pcm_prepare,
@ -1382,9 +1392,9 @@ shows only the skeleton, how to build up the PCM interfaces.
&snd_mychip_capture_ops);
/* pre-allocation of buffers */
/* NOTE: this may fail */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pci->dev,
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,
&chip->pci->dev,
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
~~~~~~~~~~~~~~~~
@ -3543,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
-------------------------
@ -3697,8 +3768,8 @@ 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 ``&pci->dev``, where pci is
the :c:type:`struct pci_dev <pci_dev>` pointer of the chip as
@ -3706,8 +3777,8 @@ well.
::
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
&pci->dev, size, max);
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:
@ -3716,8 +3787,7 @@ The ``struct snd_sg_buf`` instance is created as
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
@ -3726,8 +3796,8 @@ 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()`.
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
------------------
@ -3740,8 +3810,8 @@ buffer preallocation with ``SNDRV_DMA_TYPE_VMALLOC`` type.
::
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
NULL, 0, 0);
The NULL is passed to the device pointer argument, which indicates
that the default pages (GFP_KERNEL and GFP_HIGHMEM) will be
@ -3758,7 +3828,7 @@ argument.
::
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
snd_dma_continuous_data(GFP_KERNEL | __GFP_DMA32), 0, 0);
Proc Interface

View File

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

View File

@ -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);
@ -1328,8 +1338,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
(IEC958_AES1_CON_PCM_CODER<<8)|\
(IEC958_AES3_CON_FS_48000<<24))
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
const char *snd_pcm_format_name(snd_pcm_format_t format);
/**

View File

@ -412,7 +412,7 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
unsigned long offset);
int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
int snd_soc_pcm_component_new(struct snd_pcm *pcm);
void snd_soc_pcm_component_free(struct snd_pcm *pcm);
int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd);
void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd);
#endif /* __SOC_COMPONENT_H */

View File

@ -299,6 +299,12 @@
.put = snd_soc_bytes_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info, .get = xhandler_get, \
.put = xhandler_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@ -982,6 +988,7 @@ struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
const char *components;
char dmi_longname[80];
char topology_shortname[32];

View File

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

View File

@ -72,4 +72,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned long offset);
#endif
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
#endif /* __SOUND_CORE_PCM_LOCAL_H */

View File

@ -15,6 +15,7 @@
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/initval.h>
#include "pcm_local.h"
static int preallocate_dma = 1;
module_param(preallocate_dma, int, 0444);
@ -193,9 +194,15 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
/*
* pre-allocate the buffer and create a proc file for the substream
*/
static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
size_t size, size_t max)
static void preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max, bool managed)
{
if (snd_BUG_ON(substream->dma_buffer.dev.type))
return;
substream->dma_buffer.dev.type = type;
substream->dma_buffer.dev.dev = data;
if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
preallocate_pcm_pages(substream, size);
@ -205,8 +212,23 @@ static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
substream->dma_max = max;
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
@ -222,11 +244,7 @@ void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max)
{
if (snd_BUG_ON(substream->dma_buffer.dev.type))
return;
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);
@ -245,15 +263,55 @@ 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);
/**
* 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

View File

@ -178,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;
@ -448,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;
}
@ -557,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
@ -649,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)
@ -662,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)
@ -723,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;
}
@ -767,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;
@ -959,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,
@ -1290,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);
}
@ -1566,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 = {
@ -1635,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;
@ -1686,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;

View File

@ -28,6 +28,7 @@
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/timer.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("A loopback soundcard");
@ -41,6 +42,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
static int pcm_notify[SNDRV_CARDS];
static char *timer_source[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
@ -52,11 +54,48 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
module_param_array(pcm_notify, int, NULL, 0444);
MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
module_param_array(timer_source, charp, NULL, 0444);
MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default].");
#define NO_PITCH 100000
#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE)
struct loopback_cable;
struct loopback_pcm;
struct loopback_ops {
/* optional
* call in loopback->cable_lock
*/
int (*open)(struct loopback_pcm *dpcm);
/* required
* call in cable->lock
*/
int (*start)(struct loopback_pcm *dpcm);
/* required
* call in cable->lock
*/
int (*stop)(struct loopback_pcm *dpcm);
/* optional */
int (*stop_sync)(struct loopback_pcm *dpcm);
/* optional */
int (*close_substream)(struct loopback_pcm *dpcm);
/* optional
* call in loopback->cable_lock
*/
int (*close_cable)(struct loopback_pcm *dpcm);
/* optional
* call in cable->lock
*/
unsigned int (*pos_update)(struct loopback_cable *cable);
/* optional */
void (*dpcm_info)(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer);
};
struct loopback_cable {
spinlock_t lock;
struct loopback_pcm *streams[2];
@ -65,6 +104,15 @@ struct loopback_cable {
unsigned int valid;
unsigned int running;
unsigned int pause;
/* timer specific */
struct loopback_ops *ops;
/* If sound timer is used */
struct {
int stream;
struct snd_timer_id id;
struct tasklet_struct event_tasklet;
struct snd_timer_instance *instance;
} snd_timer;
};
struct loopback_setup {
@ -85,6 +133,7 @@ struct loopback {
struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
struct snd_pcm *pcm[2];
struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
const char *timer_source;
};
struct loopback_pcm {
@ -102,10 +151,13 @@ struct loopback_pcm {
/* flags */
unsigned int period_update_pending :1;
/* timer stuff */
unsigned int irq_pos; /* fractional IRQ position */
unsigned int period_size_frac;
unsigned int irq_pos; /* fractional IRQ position in jiffies
* ticks
*/
unsigned int period_size_frac; /* period size in jiffies ticks */
unsigned int last_drift;
unsigned long last_jiffies;
/* If jiffies timer is used */
struct timer_list timer;
};
@ -153,7 +205,7 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
}
/* call in cable->lock */
static void loopback_timer_start(struct loopback_pcm *dpcm)
static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
{
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
@ -169,23 +221,101 @@ static void loopback_timer_start(struct loopback_pcm *dpcm)
tick = dpcm->period_size_frac - dpcm->irq_pos;
tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
mod_timer(&dpcm->timer, jiffies + tick);
return 0;
}
/* call in cable->lock */
static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
int err;
/* Loopback device has to use same period as timer card. Therefore
* wake up for each snd_pcm_period_elapsed() call of timer card.
*/
err = snd_timer_start(cable->snd_timer.instance, 1);
if (err < 0) {
/* do not report error if trying to start but already
* running. For example called by opposite substream
* of the same cable
*/
if (err == -EBUSY)
return 0;
pcm_err(dpcm->substream->pcm,
"snd_timer_start(%d,%d,%d) failed with %d",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
err);
}
return err;
}
/* call in cable->lock */
static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
{
del_timer(&dpcm->timer);
dpcm->timer.expires = 0;
return 0;
}
static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
/* call in cable->lock */
static int loopback_snd_timer_stop(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
int err;
/* only stop if both devices (playback and capture) are not running */
if (cable->running ^ cable->pause)
return 0;
err = snd_timer_stop(cable->snd_timer.instance);
if (err < 0) {
pcm_err(dpcm->substream->pcm,
"snd_timer_stop(%d,%d,%d) failed with %d",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
err);
}
return err;
}
static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
{
del_timer_sync(&dpcm->timer);
return 0;
}
#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
/* call in loopback->cable_lock */
static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
{
struct loopback_cable *cable = dpcm->cable;
/* snd_timer was not opened */
if (!cable->snd_timer.instance)
return 0;
/* wait till drain tasklet has finished if requested */
tasklet_kill(&cable->snd_timer.event_tasklet);
/* will only be called from free_cable() when other stream was
* already closed. Other stream cannot be reopened as long as
* loopback->cable_lock is locked. Therefore no need to lock
* cable->lock;
*/
snd_timer_close(cable->snd_timer.instance);
snd_timer_instance_free(cable->snd_timer.instance);
memset(&cable->snd_timer, 0, sizeof(cable->snd_timer));
return 0;
}
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
@ -249,7 +379,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
int err, stream = 1 << substream->stream;
int err = 0, stream = 1 << substream->stream;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@ -262,7 +392,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
cable->running |= stream;
cable->pause &= ~stream;
loopback_timer_start(dpcm);
err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -271,7 +401,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
cable->running &= ~stream;
cable->pause &= ~stream;
loopback_timer_stop(dpcm);
err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -280,7 +410,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&cable->lock);
cable->pause |= stream;
loopback_timer_stop(dpcm);
err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -290,7 +420,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
dpcm->last_jiffies = jiffies;
cable->pause &= ~stream;
loopback_timer_start(dpcm);
err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
@ -298,7 +428,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
default:
return -EINVAL;
}
return 0;
return err;
}
static void params_change(struct snd_pcm_substream *substream)
@ -312,6 +442,13 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
if (cable->snd_timer.instance) {
cable->hw.period_bytes_min =
frames_to_bytes(runtime, runtime->period_size);
cable->hw.period_bytes_max = cable->hw.period_bytes_min;
}
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@ -319,9 +456,13 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
int bps, salign;
int err, bps, salign;
loopback_timer_stop_sync(dpcm);
if (cable->ops->stop_sync) {
err = cable->ops->stop_sync(dpcm);
if (err < 0)
return err;
}
salign = (snd_pcm_format_physical_width(runtime->format) *
runtime->channels) / 8;
@ -457,7 +598,8 @@ static inline void bytepos_finish(struct loopback_pcm *dpcm,
}
/* call in cable->lock */
static unsigned int loopback_pos_update(struct loopback_cable *cable)
static unsigned int loopback_jiffies_timer_pos_update
(struct loopback_cable *cable)
{
struct loopback_pcm *dpcm_play =
cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
@ -510,14 +652,15 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
return running;
}
static void loopback_timer_function(struct timer_list *t)
static void loopback_jiffies_timer_function(struct timer_list *t)
{
struct loopback_pcm *dpcm = from_timer(dpcm, t, timer);
unsigned long flags;
spin_lock_irqsave(&dpcm->cable->lock, flags);
if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
loopback_timer_start(dpcm);
if (loopback_jiffies_timer_pos_update(dpcm->cable) &
(1 << dpcm->substream->stream)) {
loopback_jiffies_timer_start(dpcm);
if (dpcm->period_update_pending) {
dpcm->period_update_pending = 0;
spin_unlock_irqrestore(&dpcm->cable->lock, flags);
@ -529,6 +672,193 @@ static void loopback_timer_function(struct timer_list *t)
spin_unlock_irqrestore(&dpcm->cable->lock, flags);
}
/* call in cable->lock */
static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime,
unsigned long resolution)
{
if (resolution != runtime->timer_resolution) {
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
/* Worst case estimation of possible values for resolution
* resolution <= (512 * 1024) frames / 8kHz in nsec
* resolution <= 65.536.000.000 nsec
*
* period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz +
* 500.000
* period_size <= 12.582.912.000.000 <64bit
* / 1.000.000 usec/sec
*/
snd_pcm_uframes_t period_size_usec =
resolution / 1000 * runtime->rate;
/* round to nearest sample rate */
snd_pcm_uframes_t period_size =
(period_size_usec + 500 * 1000) / (1000 * 1000);
pcm_err(dpcm->substream->pcm,
"Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card timer %d,%d,%d. Use period size of %lu frames for loopback device.",
runtime->period_size, resolution, period_size,
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice,
period_size);
return -EINVAL;
}
return 0;
}
static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
int event,
unsigned long resolution)
{
struct loopback_pcm *dpcm_play, *dpcm_capt;
struct snd_pcm_substream *substream_play, *substream_capt;
struct snd_pcm_runtime *valid_runtime;
unsigned int running, elapsed_bytes;
unsigned long flags;
spin_lock_irqsave(&cable->lock, flags);
running = cable->running ^ cable->pause;
/* no need to do anything if no stream is running */
if (!running) {
spin_unlock_irqrestore(&cable->lock, flags);
return;
}
dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
dpcm_play->substream : NULL;
substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
dpcm_capt->substream : NULL;
if (event == SNDRV_TIMER_EVENT_MSTOP) {
if (!dpcm_play ||
dpcm_play->substream->runtime->status->state !=
SNDRV_PCM_STATE_DRAINING) {
spin_unlock_irqrestore(&cable->lock, flags);
return;
}
}
valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
dpcm_play->substream->runtime :
dpcm_capt->substream->runtime;
/* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */
if (event == SNDRV_TIMER_EVENT_TICK) {
/* The hardware rules guarantee that playback and capture period
* are the same. Therefore only one device has to be checked
* here.
*/
if (loopback_snd_timer_check_resolution(valid_runtime,
resolution) < 0) {
spin_unlock_irqrestore(&cable->lock, flags);
if (substream_play)
snd_pcm_stop_xrun(substream_play);
if (substream_capt)
snd_pcm_stop_xrun(substream_capt);
return;
}
}
elapsed_bytes = frames_to_bytes(valid_runtime,
valid_runtime->period_size);
/* The same timer interrupt is used for playback and capture device */
if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
(running & (1 << SNDRV_PCM_STREAM_CAPTURE))) {
copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes);
bytepos_finish(dpcm_play, elapsed_bytes);
bytepos_finish(dpcm_capt, elapsed_bytes);
} else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
bytepos_finish(dpcm_play, elapsed_bytes);
} else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
clear_capture_buf(dpcm_capt, elapsed_bytes);
bytepos_finish(dpcm_capt, elapsed_bytes);
}
spin_unlock_irqrestore(&cable->lock, flags);
if (substream_play)
snd_pcm_period_elapsed(substream_play);
if (substream_capt)
snd_pcm_period_elapsed(substream_capt);
}
static void loopback_snd_timer_function(struct snd_timer_instance *timeri,
unsigned long resolution,
unsigned long ticks)
{
struct loopback_cable *cable = timeri->callback_data;
loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK,
resolution);
}
static void loopback_snd_timer_tasklet(unsigned long arg)
{
struct snd_timer_instance *timeri = (struct snd_timer_instance *)arg;
struct loopback_cable *cable = timeri->callback_data;
loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0);
}
static void loopback_snd_timer_event(struct snd_timer_instance *timeri,
int event,
struct timespec *tstamp,
unsigned long resolution)
{
/* Do not lock cable->lock here because timer->lock is already hold.
* There are other functions which first lock cable->lock and than
* timer->lock e.g.
* loopback_trigger()
* spin_lock(&cable->lock)
* loopback_snd_timer_start()
* snd_timer_start()
* spin_lock(&timer->lock)
* Therefore when using the oposit order of locks here it could result
* in a deadlock.
*/
if (event == SNDRV_TIMER_EVENT_MSTOP) {
struct loopback_cable *cable = timeri->callback_data;
/* sound card of the timer was stopped. Therefore there will not
* be any further timer callbacks. Due to this forward audio
* data from here if in draining state. When still in running
* state the streaming will be aborted by the usual timeout. It
* should not be aborted here because may be the timer sound
* card does only a recovery and the timer is back soon.
* This tasklet triggers loopback_snd_timer_tasklet()
*/
tasklet_schedule(&cable->snd_timer.event_tasklet);
}
}
static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer)
{
snd_iprintf(buffer, " update_pending:\t%u\n",
dpcm->period_update_pending);
snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
dpcm->last_jiffies, jiffies);
snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
}
static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer)
{
struct loopback_cable *cable = dpcm->cable;
snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n",
cable->snd_timer.id.card,
cable->snd_timer.id.device,
cable->snd_timer.id.subdevice);
snd_iprintf(buffer, " timer open:\t\t%s\n",
(cable->snd_timer.stream == SNDRV_PCM_STREAM_CAPTURE) ?
"capture" : "playback");
}
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@ -536,7 +866,8 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t pos;
spin_lock(&dpcm->cable->lock);
loopback_pos_update(dpcm->cable);
if (dpcm->cable->ops->pos_update)
dpcm->cable->ops->pos_update(dpcm->cable);
pos = dpcm->buf_pos;
spin_unlock(&dpcm->cable->lock);
return bytes_to_frames(runtime, pos);
@ -646,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;
@ -661,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;
@ -684,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) {
@ -695,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);
@ -723,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;
@ -747,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 = {
@ -1076,13 +1629,8 @@ static void print_dpcm_info(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps);
snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign);
snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift);
snd_iprintf(buffer, " update_pending:\t%u\n",
dpcm->period_update_pending);
snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
dpcm->last_jiffies, jiffies);
snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
if (dpcm->cable->ops->dpcm_info)
dpcm->cable->ops->dpcm_info(dpcm, buffer);
}
static void print_substream_info(struct snd_info_buffer *buffer,
@ -1118,7 +1666,7 @@ static void print_cable_info(struct snd_info_entry *entry,
mutex_unlock(&loopback->cable_lock);
}
static int loopback_proc_new(struct loopback *loopback, int cidx)
static int loopback_cable_proc_new(struct loopback *loopback, int cidx)
{
char name[32];
@ -1127,6 +1675,48 @@ static int loopback_proc_new(struct loopback *loopback, int cidx)
print_cable_info);
}
static void loopback_set_timer_source(struct loopback *loopback,
const char *value)
{
if (loopback->timer_source) {
devm_kfree(loopback->card->dev, loopback->timer_source);
loopback->timer_source = NULL;
}
if (value && *value)
loopback->timer_source = devm_kstrdup(loopback->card->dev,
value, GFP_KERNEL);
}
static void print_timer_source_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct loopback *loopback = entry->private_data;
mutex_lock(&loopback->cable_lock);
snd_iprintf(buffer, "%s\n",
loopback->timer_source ? loopback->timer_source : "");
mutex_unlock(&loopback->cable_lock);
}
static void change_timer_source_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct loopback *loopback = entry->private_data;
char line[64];
mutex_lock(&loopback->cable_lock);
if (!snd_info_get_line(buffer, line, sizeof(line)))
loopback_set_timer_source(loopback, strim(line));
mutex_unlock(&loopback->cable_lock);
}
static int loopback_timer_source_proc_new(struct loopback *loopback)
{
return snd_card_rw_proc_new(loopback->card, "timer_source", loopback,
print_timer_source_info,
change_timer_source_info);
}
static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;
@ -1146,6 +1736,8 @@ static int loopback_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
loopback->card = card;
loopback_set_timer_source(loopback, timer_source[dev]);
mutex_init(&loopback->cable_lock);
err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
@ -1157,8 +1749,9 @@ static int loopback_probe(struct platform_device *devptr)
err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
if (err < 0)
goto __nodev;
loopback_proc_new(loopback, 0);
loopback_proc_new(loopback, 1);
loopback_cable_proc_new(loopback, 0);
loopback_cable_proc_new(loopback, 1);
loopback_timer_source_proc_new(loopback);
strcpy(card->driver, "Loopback");
strcpy(card->shortname, "Loopback");
sprintf(card->longname, "Loopback %i", dev + 1);

View File

@ -43,7 +43,7 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
EXPORT_SYMBOL_GPL(is_jack_detectable);
/* execute pin sense measurement */
static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
u32 pincap;
u32 val;
@ -55,19 +55,57 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
AC_VERB_SET_PIN_SENSE, 0);
}
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
AC_VERB_GET_PIN_SENSE, dev_id);
if (codec->inv_jack_detect)
val ^= AC_PINSENSE_PRESENCE;
return val;
}
/**
* snd_hda_jack_tbl_get - query the jack-table entry for the given NID
* snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
* @codec: the HDA codec
* @nid: pin NID to refer to
* @dev_id: pin device entry id
*/
struct hda_jack_tbl *
snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!nid || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid == nid && jack->dev_id == dev_id)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
/**
* snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
* @codec: the HDA codec
* @tag: tag value to refer to
* @dev_id: pin device entry id
*/
struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
unsigned char tag, int dev_id)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!tag || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->tag == tag && jack->dev_id == dev_id)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
static struct hda_jack_tbl *
any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
@ -79,27 +117,6 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get);
/**
* snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
* @codec: the HDA codec
* @tag: tag value to refer to
*/
struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!tag || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->tag == tag)
return jack;
return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
/**
* snd_hda_jack_tbl_new - create a jack-table entry for the given NID
@ -107,17 +124,36 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
* @nid: pin NID to assign
*/
static struct hda_jack_tbl *
snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
struct hda_jack_tbl *jack =
snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
struct hda_jack_tbl *existing_nid_jack =
any_jack_tbl_get_from_nid(codec, nid);
WARN_ON(dev_id != 0 && !codec->dp_mst);
if (jack)
return jack;
jack = snd_array_new(&codec->jacktbl);
if (!jack)
return NULL;
jack->nid = nid;
jack->dev_id = dev_id;
jack->jack_dirty = 1;
jack->tag = codec->jacktbl.used;
if (existing_nid_jack) {
jack->tag = existing_nid_jack->tag;
/*
* Copy jack_detect from existing_nid_jack to avoid
* snd_hda_jack_detect_enable_callback_mst() making multiple
* SET_UNSOLICITED_ENABLE calls on the same pin.
*/
jack->jack_detect = existing_nid_jack->jack_detect;
} else {
jack->tag = codec->jacktbl.used;
}
return jack;
}
@ -153,10 +189,12 @@ static void jack_detect_update(struct hda_codec *codec,
if (jack->phantom_jack)
jack->pin_sense = AC_PINSENSE_PRESENCE;
else
jack->pin_sense = read_pin_sense(codec, jack->nid);
jack->pin_sense = read_pin_sense(codec, jack->nid,
jack->dev_id);
/* A gating jack indicates the jack is invalid if gating is unplugged */
if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
if (jack->gating_jack &&
!snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
jack->jack_dirty = 0;
@ -164,7 +202,8 @@ static void jack_detect_update(struct hda_codec *codec,
/* If a jack is gated by this one update it. */
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
jack->dev_id);
if (gated) {
gated->jack_dirty = 1;
jack_detect_update(codec, gated);
@ -191,63 +230,69 @@ void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
/**
* snd_hda_pin_sense - execute pin sense measurement
* snd_hda_jack_pin_sense - execute pin sense measurement
* @codec: the CODEC to sense
* @nid: the pin NID to sense
*
* Execute necessary pin sense measurement and return its Presence Detect,
* Impedance, ELD Valid etc. status bits.
*/
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
struct hda_jack_tbl *jack =
snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
if (jack) {
jack_detect_update(codec, jack);
return jack->pin_sense;
}
return read_pin_sense(codec, nid);
return read_pin_sense(codec, nid, dev_id);
}
EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
/**
* snd_hda_jack_detect_state - query pin Presence Detect status
* snd_hda_jack_detect_state_mst - query pin Presence Detect status
* @codec: the CODEC to sense
* @nid: the pin NID to sense
* @dev_id: pin device entry id
*
* Query and return the pin's Presence Detect status, as either
* HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
*/
int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_jack_detect_state_mst(struct hda_codec *codec,
hda_nid_t nid, int dev_id)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
struct hda_jack_tbl *jack =
snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
if (jack && jack->phantom_jack)
return HDA_JACK_PHANTOM;
else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
AC_PINSENSE_PRESENCE)
return HDA_JACK_PRESENT;
else
return HDA_JACK_NOT_PRESENT;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state);
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
/**
* snd_hda_jack_detect_enable - enable the jack-detection
* snd_hda_jack_detect_enable_mst - enable the jack-detection
* @codec: the HDA codec
* @nid: pin NID to enable
* @func: callback function to register
* @dev_id: pin device entry id
*
* In the case of error, the return value will be a pointer embedded with
* errno. Check and handle the return value appropriately with standard
* macros such as @IS_ERR() and @PTR_ERR().
*/
struct hda_jack_callback *
snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
hda_jack_callback_fn func)
snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id, hda_jack_callback_fn func)
{
struct hda_jack_tbl *jack;
struct hda_jack_callback *callback = NULL;
int err;
jack = snd_hda_jack_tbl_new(codec, nid);
jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
if (!jack)
return ERR_PTR(-ENOMEM);
if (func) {
@ -256,6 +301,7 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
return ERR_PTR(-ENOMEM);
callback->func = func;
callback->nid = jack->nid;
callback->dev_id = jack->dev_id;
callback->next = jack->callback;
jack->callback = callback;
}
@ -272,19 +318,24 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
return ERR_PTR(err);
return callback;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback);
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
/**
* snd_hda_jack_detect_enable - Enable the jack detection on the given pin
* @codec: the HDA codec
* @nid: pin NID to enable jack detection
* @dev_id: pin device entry id
*
* Enable the jack detection with the default callback. Returns zero if
* successful or a negative error code.
*/
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
int dev_id)
{
return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL));
return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
nid,
dev_id,
NULL));
}
EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
@ -299,8 +350,11 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid)
{
struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
struct hda_jack_tbl *gating =
snd_hda_jack_tbl_new(codec, gating_nid, 0);
WARN_ON(codec->dp_mst);
if (!gated || !gating)
return -EINVAL;
@ -376,9 +430,10 @@ static void hda_free_jack_priv(struct snd_jack *jack)
}
/**
* snd_hda_jack_add_kctl - Add a kctl for the given pin
* snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
* @codec: the HDA codec
* @nid: pin NID to assign
* @dev_id : pin device entry id
* @name: string name for the jack
* @phantom_jack: flag to deal as a phantom jack
* @type: jack type bits to be reported, 0 for guessing from pincfg
@ -387,15 +442,15 @@ static void hda_free_jack_priv(struct snd_jack *jack)
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, bool phantom_jack,
int type, const struct hda_jack_keymap *keymap)
int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id, const char *name, bool phantom_jack,
int type, const struct hda_jack_keymap *keymap)
{
struct hda_jack_tbl *jack;
const struct hda_jack_keymap *map;
int err, state, buttons;
jack = snd_hda_jack_tbl_new(codec, nid);
jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
if (!jack)
return 0;
if (jack->jack)
@ -425,12 +480,12 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
snd_jack_set_key(jack->jack, map->type, map->key);
}
state = snd_hda_jack_detect(codec, nid);
state = snd_hda_jack_detect_mst(codec, nid, dev_id);
snd_jack_report(jack->jack, state ? jack->type : 0);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl);
EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
@ -441,6 +496,8 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
int err;
bool phantom_jack;
WARN_ON(codec->dp_mst);
if (!nid)
return 0;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
@ -462,7 +519,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
return err;
if (!phantom_jack)
return snd_hda_jack_detect_enable(codec, nid);
return snd_hda_jack_detect_enable(codec, nid, 0);
return 0;
}
@ -540,7 +597,8 @@ static void call_jack_callback(struct hda_codec *codec, unsigned int res,
}
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
snd_hda_jack_tbl_get(codec, jack->gated_jack);
snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
jack->dev_id);
if (gated) {
for (cb = gated->callback; cb; cb = cb->next) {
cb->jack = gated;
@ -561,7 +619,14 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
struct hda_jack_tbl *event;
int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
event = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (codec->dp_mst) {
int dev_entry =
(res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
} else {
event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
}
if (!event)
return;
event->jack_dirty = 1;

View File

@ -19,6 +19,7 @@ typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callba
struct hda_jack_callback {
hda_nid_t nid;
int dev_id;
hda_jack_callback_fn func;
unsigned int private_data; /* arbitrary data */
unsigned int unsol_res; /* unsolicited event bits */
@ -28,6 +29,7 @@ struct hda_jack_callback {
struct hda_jack_tbl {
hda_nid_t nid;
int dev_id;
unsigned char tag; /* unsol event tag */
struct hda_jack_callback *callback;
/* jack-detection stuff */
@ -49,46 +51,129 @@ struct hda_jack_keymap {
};
struct hda_jack_tbl *
snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id);
/**
* snd_hda_jack_tbl_get - query the jack-table entry for the given NID
* @codec: the HDA codec
* @nid: pin NID to refer to
*/
static inline struct hda_jack_tbl *
snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_jack_tbl_get_mst(codec, nid, 0);
}
struct hda_jack_tbl *
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
unsigned char tag, int dev_id);
void snd_hda_jack_tbl_clear(struct hda_codec *codec);
void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
int dev_id);
struct hda_jack_callback *
snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id, hda_jack_callback_fn cb);
/**
* snd_hda_jack_detect_enable - enable the jack-detection
* @codec: the HDA codec
* @nid: pin NID to enable
* @func: callback function to register
*
* In the case of error, the return value will be a pointer embedded with
* errno. Check and handle the return value appropriately with standard
* macros such as @IS_ERR() and @PTR_ERR().
*/
static inline struct hda_jack_callback *
snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
hda_jack_callback_fn cb);
hda_jack_callback_fn cb)
{
return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb);
}
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
/* the jack state returned from snd_hda_jack_detect_state() */
enum {
HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
};
int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id);
/**
* snd_hda_jack_detect_state - query pin Presence Detect status
* @codec: the CODEC to sense
* @nid: the pin NID to sense
*
* Query and return the pin's Presence Detect status, as either
* HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
*/
static inline int
snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_jack_detect_state_mst(codec, nid, 0);
}
/**
* snd_hda_jack_detect_mst - Detect the jack
* @codec: the HDA codec
* @nid: pin NID to check jack detection
* @dev_id: pin device entry id
*/
static inline bool
snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
return snd_hda_jack_detect_state_mst(codec, nid, dev_id) !=
HDA_JACK_NOT_PRESENT;
}
/**
* snd_hda_jack_detect - Detect the jack
* @codec: the HDA codec
* @nid: pin NID to check jack detection
*/
static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
static inline bool
snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
return snd_hda_jack_detect_mst(codec, nid, 0);
}
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, bool phantom_jack,
int type, const struct hda_jack_keymap *keymap);
int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id, const char *name, bool phantom_jack,
int type, const struct hda_jack_keymap *keymap);
/**
* snd_hda_jack_add_kctl - Add a kctl for the given pin
* @codec: the HDA codec
* @nid: pin NID to assign
* @name: string name for the jack
* @phantom_jack: flag to deal as a phantom jack
* @type: jack type bits to be reported, 0 for guessing from pincfg
* @keymap: optional jack / key mapping
*
* This assigns a jack-detection kctl to the given pin. The kcontrol
* will have the given name and index.
*/
static inline int
snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *name, bool phantom_jack,
int type, const struct hda_jack_keymap *keymap)
{
return snd_hda_jack_add_kctl_mst(codec, nid, 0,
name, phantom_jack, type, keymap);
}
int snd_hda_jack_add_kctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg);

View File

@ -910,6 +910,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),

View File

@ -80,16 +80,19 @@ struct hdmi_spec_per_pin {
/* operations used by generic code that can be overridden by patches */
struct hdmi_ops {
int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
unsigned char *buf, int *eld_size);
int dev_id, unsigned char *buf, int *eld_size);
void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
int dev_id,
int ca, int active_channels, int conn_type);
/* enable/disable HBR (HD passthrough) */
int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid,
int dev_id, bool hbr);
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format);
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
int format);
void (*pin_cvt_fixup)(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
@ -636,8 +639,16 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return true;
}
static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
int dev_id, unsigned char *buf, int *eld_size)
{
snd_hda_set_dev_select(codec, nid, dev_id);
return snd_hdmi_get_eld(codec, nid, buf, eld_size);
}
static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid,
hda_nid_t pin_nid, int dev_id,
int ca, int active_channels,
int conn_type)
{
@ -667,6 +678,8 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
return;
}
snd_hda_set_dev_select(codec, pin_nid, dev_id);
/*
* sizeof(ai) is used instead of sizeof(*hdmi_ai) or
* sizeof(*dp_ai) to avoid partial match/update problems when
@ -692,6 +705,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
struct hdmi_spec *spec = codec->spec;
struct hdac_chmap *chmap = &spec->chmap;
hda_nid_t pin_nid = per_pin->pin_nid;
int dev_id = per_pin->dev_id;
int channels = per_pin->channels;
int active_channels;
struct hdmi_eld *eld;
@ -700,6 +714,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
if (!channels)
return;
snd_hda_set_dev_select(codec, pin_nid, dev_id);
/* some HW (e.g. HSW+) needs reprogramming the amp at each time */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin_nid, 0,
@ -725,8 +741,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
pin_nid, non_pcm, ca, channels,
per_pin->chmap, per_pin->chmap_set);
spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
eld->info.conn_type);
spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
ca, active_channels, eld->info.conn_type);
per_pin->non_pcm = non_pcm;
}
@ -758,34 +774,32 @@ static void jack_callback(struct hda_codec *codec,
if (codec_has_acomp(codec))
return;
/* hda_jack don't support DP MST */
check_presence_and_report(codec, jack->nid, 0);
check_presence_and_report(codec, jack->nid, jack->dev_id);
}
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
struct hda_jack_tbl *jack;
int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
/*
* assume DP MST uses dyn_pcm_assign and acomp and
* never comes here
* if DP MST supports unsol event, below code need
* consider dev_entry
*/
jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (codec->dp_mst) {
int dev_entry =
(res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
} else {
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
}
if (!jack)
return;
jack->jack_dirty = 1;
codec_dbg(codec,
"HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA),
!!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
/* hda_jack don't support DP MST */
check_presence_and_report(codec, jack->nid, 0);
check_presence_and_report(codec, jack->nid, jack->dev_id);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@ -815,11 +829,21 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
struct hda_jack_tbl *jack;
if (codec_has_acomp(codec))
return;
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
if (codec->dp_mst) {
int dev_entry =
(res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
} else {
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
}
if (!jack) {
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
@ -860,11 +884,12 @@ static void haswell_verify_D0(struct hda_codec *codec,
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
bool hbr)
int dev_id, bool hbr)
{
int pinctl, new_pinctl;
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
snd_hda_set_dev_select(codec, pin_nid, dev_id);
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@ -894,13 +919,15 @@ static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
}
static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format)
hda_nid_t pin_nid, int dev_id,
u32 stream_tag, int format)
{
struct hdmi_spec *spec = codec->spec;
unsigned int param;
int err;
err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id,
is_hbr_format(format));
if (err) {
codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
@ -1274,6 +1301,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
int dev_id = per_pin->dev_id;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
codec_warn(codec,
@ -1282,24 +1310,43 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
return -EINVAL;
}
snd_hda_set_dev_select(codec, pin_nid, dev_id);
/* all the device entries on the same pin have the same conn list */
per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
per_pin->mux_nids,
HDA_MAX_CONNECTIONS);
per_pin->num_mux_nids =
snd_hda_get_raw_connections(codec, pin_nid, per_pin->mux_nids,
HDA_MAX_CONNECTIONS);
return 0;
}
static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
struct hdmi_spec_per_pin *per_pin)
struct hdmi_spec_per_pin *per_pin)
{
int i;
/* try the prefer PCM */
if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
/*
* generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
* number of pcms.
*
* The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n
* if m==0. This guarantees that dynamic pcm assignments are compatible
* with the legacy static per_pin-pmc assignment that existed in the
* days before DP-MST.
*
* per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
*/
if (per_pin->dev_id == 0 &&
!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
return per_pin->pin_nid_idx;
/* have a second try; check the "reserved area" over num_pins */
if (per_pin->dev_id != 0 &&
!(test_bit(spec->num_nids + (per_pin->dev_id - 1),
&spec->pcm_bitmap))) {
return spec->num_nids + (per_pin->dev_id - 1);
}
/* have a second try; check the area over num_nids */
for (i = spec->num_nids; i < spec->pcm_used; i++) {
if (!test_bit(i, &spec->pcm_bitmap))
return i;
@ -1493,6 +1540,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld;
hda_nid_t pin_nid = per_pin->pin_nid;
int dev_id = per_pin->dev_id;
/*
* Always execute a GetPinSense verb here, even when called from
* hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
@ -1505,7 +1553,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
bool ret;
bool do_repoll = false;
present = snd_hda_pin_sense(codec, pin_nid);
present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
mutex_lock(&per_pin->lock);
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
@ -1519,8 +1567,8 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
if (eld->eld_valid) {
if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
&eld->eld_size) < 0)
if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
eld->eld_buffer, &eld->eld_size) < 0)
eld->eld_valid = false;
else {
if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
@ -1538,7 +1586,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
ret = !repoll || !eld->monitor_present || eld->eld_valid;
jack = snd_hda_jack_tbl_get(codec, pin_nid);
jack = snd_hda_jack_tbl_get_mst(codec, pin_nid, per_pin->dev_id);
if (jack) {
jack->block_report = !ret;
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
@ -1569,7 +1617,8 @@ static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
* DP MST will use dyn_pcm_assign,
* so DP MST will never come here
*/
jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
per_pin->dev_id);
if (jack_tbl)
jack = jack_tbl->jack;
}
@ -1650,7 +1699,8 @@ static void hdmi_repoll_eld(struct work_struct *work)
struct hdmi_spec *spec = codec->spec;
struct hda_jack_tbl *jack;
jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
per_pin->dev_id);
if (jack)
jack->jack_dirty = 1;
@ -1855,7 +1905,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hdmi_spec *spec = codec->spec;
int pin_idx;
struct hdmi_spec_per_pin *per_pin;
hda_nid_t pin_nid;
struct snd_pcm_runtime *runtime = substream->runtime;
bool non_pcm;
int pinctl, stripe;
@ -1879,7 +1928,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
goto unlock;
}
per_pin = get_pin(spec, pin_idx);
pin_nid = per_pin->pin_nid;
/* Verify pin:cvt selections to avoid silent audio after S3.
* After S3, the audio driver restores pin:cvt selections
@ -1894,8 +1942,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
/* Todo: add DP1.2 MST audio support later */
if (codec_has_acomp(codec))
snd_hdac_sync_audio_rate(&codec->core, pin_nid, per_pin->dev_id,
runtime->rate);
snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
per_pin->dev_id, runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
@ -1913,16 +1961,18 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
mutex_unlock(&per_pin->lock);
if (spec->dyn_pin_out) {
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
snd_hda_set_dev_select(codec, per_pin->pin_nid,
per_pin->dev_id);
pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_hda_codec_write(codec, pin_nid, 0,
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pinctl | PIN_OUT);
}
/* snd_hda_set_dev_select() has been called before */
err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
stream_tag, format);
err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
per_pin->dev_id, stream_tag, format);
unlock:
mutex_unlock(&spec->pcm_lock);
return err;
@ -1974,6 +2024,8 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
per_pin = get_pin(spec, pin_idx);
if (spec->dyn_pin_out) {
snd_hda_set_dev_select(codec, per_pin->pin_nid,
per_pin->dev_id);
pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
@ -2151,11 +2203,13 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
if (phantom_jack)
strncat(hdmi_str, " Phantom",
sizeof(hdmi_str) - strlen(hdmi_str) - 1);
ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
phantom_jack, 0, NULL);
ret = snd_hda_jack_add_kctl_mst(codec, per_pin->pin_nid,
per_pin->dev_id, hdmi_str, phantom_jack,
0, NULL);
if (ret < 0)
return ret;
jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
per_pin->dev_id);
if (jack == NULL)
return 0;
/* assign jack->jack to pcm_rec[].jack to
@ -2264,10 +2318,11 @@ static int generic_hdmi_init(struct hda_codec *codec)
if (codec_has_acomp(codec))
continue;
if (spec->use_jack_detect)
snd_hda_jack_detect_enable(codec, pin_nid);
snd_hda_jack_detect_enable(codec, pin_nid, dev_id);
else
snd_hda_jack_detect_enable_callback(codec, pin_nid,
jack_callback);
snd_hda_jack_detect_enable_callback_mst(codec, pin_nid,
dev_id,
jack_callback);
}
mutex_unlock(&spec->bind_lock);
return 0;
@ -2357,7 +2412,7 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
};
static const struct hdmi_ops generic_standard_hdmi_ops = {
.pin_get_eld = snd_hdmi_get_eld,
.pin_get_eld = hdmi_pin_get_eld,
.pin_setup_infoframe = hdmi_pin_setup_infoframe,
.pin_hbr_setup = hdmi_pin_hbr_setup,
.setup_stream = hdmi_setup_stream,
@ -2417,11 +2472,11 @@ static int patch_generic_hdmi(struct hda_codec *codec)
/* turn on / off the unsol event jack detection dynamically */
static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
bool use_acomp)
int dev_id, bool use_acomp)
{
struct hda_jack_tbl *tbl;
tbl = snd_hda_jack_tbl_get(codec, nid);
tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
if (tbl) {
/* clear unsol even if component notifier is used, or re-enable
* if notifier is cleared
@ -2434,7 +2489,7 @@ static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
* at need (i.e. only when notifier is cleared)
*/
if (!use_acomp)
snd_hda_jack_detect_enable(codec, nid);
snd_hda_jack_detect_enable(codec, nid, dev_id);
}
}
@ -2454,6 +2509,7 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
for (i = 0; i < spec->num_pins; i++)
reprogram_jack_detect(spec->codec,
get_pin(spec, i)->pin_nid,
get_pin(spec, i)->dev_id,
use_acomp);
}
mutex_unlock(&spec->bind_lock);
@ -2554,7 +2610,8 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
hda_nid_t conns[4];
int nconns;
nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
nconns = snd_hda_get_raw_connections(codec, nid, conns,
ARRAY_SIZE(conns));
if (nconns == spec->num_cvts &&
!memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
return;
@ -2730,10 +2787,12 @@ static void register_i915_notifier(struct hda_codec *codec)
/* setup_stream ops override for HSW+ */
static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format)
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
int format)
{
haswell_verify_D0(codec, cvt_nid, pin_nid);
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
stream_tag, format);
}
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
@ -2959,7 +3018,7 @@ static int simple_playback_init(struct hda_codec *codec)
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
snd_hda_jack_detect_enable(codec, pin);
snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id);
return 0;
}
@ -3468,6 +3527,40 @@ static int patch_nvhdmi(struct hda_codec *codec)
struct hdmi_spec *spec;
int err;
err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
codec->dp_mst = true;
spec = codec->spec;
spec->dyn_pcm_assign = true;
err = hdmi_parse_codec(codec);
if (err < 0) {
generic_spec_free(codec);
return err;
}
generic_hdmi_init_per_pins(codec);
spec->dyn_pin_out = true;
spec->chmap.ops.chmap_cea_alloc_validate_get_type =
nvhdmi_chmap_cea_alloc_validate_get_type;
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
codec->link_down_at_suspend = 1;
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
return 0;
}
static int patch_nvhdmi_legacy(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err;
err = patch_generic_hdmi(codec);
if (err)
return err;
@ -3699,16 +3792,19 @@ static int patch_tegra_hdmi(struct hda_codec *codec)
#define ATI_HBR_ENABLE 0x10
static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size)
int dev_id, unsigned char *buf, int *eld_size)
{
WARN_ON(dev_id != 0);
/* call hda_eld.c ATI/AMD-specific function */
return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
is_amdhdmi_rev3_or_later(codec));
}
static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
static void atihdmi_pin_setup_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id, int ca,
int active_channels, int conn_type)
{
WARN_ON(dev_id != 0);
snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
}
@ -3899,10 +3995,12 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
}
static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
bool hbr)
int dev_id, bool hbr)
{
int hbr_ctl, hbr_ctl_new;
WARN_ON(dev_id != 0);
hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
if (hbr)
@ -3928,9 +4026,9 @@ static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
}
static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format)
hda_nid_t pin_nid, int dev_id,
u32 stream_tag, int format)
{
if (is_amdhdmi_rev3_or_later(codec)) {
int ramp_rate = 180; /* default as per AMD spec */
/* disable ramp-up/down for non-pcm as per AMD spec */
@ -3940,7 +4038,8 @@ static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
}
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
stream_tag, format);
}
@ -4070,25 +4169,25 @@ HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy),
/* 17 is known to be absent */
HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),

View File

@ -7248,6 +7248,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
#if 0
@ -7512,20 +7513,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x19, 0x02a11020},
{0x1a, 0x02a11030},
{0x21, 0x0221101f}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60140},
{0x14, 0x90170110},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60140},
{0x14, 0x90170150},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x40000000},
{0x14, 0x90170110},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
{0x14, 0x90170110},
{0x21, 0x02211020}),
@ -7608,38 +7595,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x1b, 0x01011020},
{0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60130},
{0x14, 0x90170110},
{0x1b, 0x01011020},
{0x21, 0x0221101f}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60170},
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell Inspiron 5468", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60180},
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0xb7a60130},
{0x14, 0x90170110},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60130},
{0x14, 0x90170110},
{0x14, 0x01011020},
{0x21, 0x0221101f}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170110},
{0x1b, 0x01011020},
{0x21, 0x0221101f}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
{0x14, 0x90170110},
{0x1b, 0x90a70130},
@ -7852,6 +7807,12 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
{0x19, 0x40000000},
{0x1b, 0x40000000}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x19, 0x40000000},
{0x1a, 0x40000000}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x19, 0x40000000},
{0x1a, 0x40000000}),
{}
};

View File

@ -9,7 +9,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@ -59,6 +61,7 @@ struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
struct gpio_desc *gpio_rst;
unsigned long sysclk;
struct pcm3168a_io_params io_params[2];
@ -643,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@ -702,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
/*
* Request the reset (connected to RST pin) gpio line as non exclusive
* as the same reset line might be connected to multiple pcm3168a codec
*
* The RST is low active, we want the GPIO line to be high initially, so
* request the initial level to LOW which in practice means DEASSERTED:
* The deasserted level of GPIO_ACTIVE_LOW is HIGH.
*/
pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW |
GPIOD_FLAGS_BIT_NONEXCLUSIVE);
if (IS_ERR(pcm3168a->gpio_rst)) {
ret = PTR_ERR(pcm3168a->gpio_rst);
if (ret != -EPROBE_DEFER )
dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
return ret;
}
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
@ -743,10 +766,18 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
if (pcm3168a->gpio_rst) {
/*
* The device is taken out from reset via GPIO line, wait for
* 3846 SCKI clock cycles for the internal reset de-assertion
*/
msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
} else {
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
}
}
pm_runtime_set_active(dev);
@ -785,6 +816,15 @@ static void pcm3168a_disable(struct device *dev)
void pcm3168a_remove(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
/*
* The RST is low active, we want the GPIO line to be low when the
* driver is removed, so set level to 1 which in practice means
* ASSERTED:
* The asserted level of GPIO_ACTIVE_LOW is LOW.
*/
gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
pm_runtime_disable(dev);
#ifndef CONFIG_PM
pcm3168a_disable(dev);

View File

@ -171,6 +171,7 @@ struct aic31xx_priv {
int rate_div_line;
bool master_dapm_route_applied;
int irq;
u8 ocmv; /* output common-mode voltage */
};
struct aic31xx_rate_divs {
@ -1312,6 +1313,11 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
if (ret)
return ret;
/* set output common-mode voltage */
snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
AIC31XX_HPD_OCMV_MASK,
aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
return 0;
}
@ -1501,6 +1507,43 @@ static irqreturn_t aic31xx_irq(int irq, void *data)
return IRQ_NONE;
}
static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
{
struct device *dev = priv->dev;
int dvdd, avdd;
u32 value;
if (dev->fwnode &&
fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
/* OCMV setting is forced by DT */
if (value <= 3) {
priv->ocmv = value;
return;
}
}
avdd = regulator_get_voltage(priv->supplies[3].consumer);
dvdd = regulator_get_voltage(priv->supplies[5].consumer);
if (avdd > 3600000 || dvdd > 1950000) {
dev_warn(dev,
"Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
avdd, dvdd);
} else if (avdd == 3600000 && dvdd == 1950000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
} else if (avdd >= 3300000 && dvdd >= 1800000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
} else if (avdd >= 3000000 && dvdd >= 1650000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
} else if (avdd >= 2700000 && dvdd >= 1525000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
} else {
dev_warn(dev,
"Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
avdd, dvdd);
}
}
static int aic31xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -1570,6 +1613,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
return ret;
}
aic31xx_configure_ocmv(aic31xx);
if (aic31xx->irq > 0) {
regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
AIC31XX_GPIO1_FUNC_MASK,

View File

@ -232,6 +232,14 @@ struct aic31xx_pdata {
#define AIC31XX_HSD_HP 0x01
#define AIC31XX_HSD_HS 0x03
/* AIC31XX_HPDRIVER */
#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3)
#define AIC31XX_HPD_OCMV_SHIFT 3
#define AIC31XX_HPD_OCMV_1_35V 0x0
#define AIC31XX_HPD_OCMV_1_5V 0x1
#define AIC31XX_HPD_OCMV_1_65V 0x2
#define AIC31XX_HPD_OCMV_1_8V 0x3
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0

View File

@ -599,6 +599,9 @@ struct wm_coeff_ctl_ops {
struct wm_coeff_ctl {
const char *name;
const char *fw_name;
/* Subname is needed to match with firmware */
const char *subname;
unsigned int subname_len;
struct wm_adsp_alg_region alg_region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *dsp;
@ -1399,6 +1402,7 @@ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
{
kfree(ctl->cache);
kfree(ctl->name);
kfree(ctl->subname);
kfree(ctl);
}
@ -1472,6 +1476,15 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ret = -ENOMEM;
goto err_ctl;
}
if (subname) {
ctl->subname_len = subname_len;
ctl->subname = kmemdup(subname,
strlen(subname) + 1, GFP_KERNEL);
if (!ctl->subname) {
ret = -ENOMEM;
goto err_ctl_name;
}
}
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
@ -1485,7 +1498,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_name;
goto err_ctl_subname;
}
list_add(&ctl->list, &dsp->ctl_list);
@ -1508,6 +1521,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
err_ctl_cache:
kfree(ctl->cache);
err_ctl_subname:
kfree(ctl->subname);
err_ctl_name:
kfree(ctl->name);
err_ctl:
@ -1995,6 +2010,70 @@ static int wm_adsp_load(struct wm_adsp *dsp)
return ret;
}
/*
* Find wm_coeff_ctl with input name as its subname
* If not found, return NULL
*/
static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp,
const char *name, int type,
unsigned int alg)
{
struct wm_coeff_ctl *pos, *rslt = NULL;
list_for_each_entry(pos, &dsp->ctl_list, list) {
if (!pos->subname)
continue;
if (strncmp(pos->subname, name, pos->subname_len) == 0 &&
pos->alg_region.alg == alg &&
pos->alg_region.type == type) {
rslt = pos;
break;
}
}
return rslt;
}
int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len)
{
struct wm_coeff_ctl *ctl;
struct snd_kcontrol *kcontrol;
int ret;
ctl = wm_adsp_get_ctl(dsp, name, type, alg);
if (!ctl)
return -EINVAL;
if (len > ctl->len)
return -EINVAL;
ret = wm_coeff_write_control(ctl, buf, len);
kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name);
snd_ctl_notify(dsp->component->card->snd_card,
SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len)
{
struct wm_coeff_ctl *ctl;
ctl = wm_adsp_get_ctl(dsp, name, type, alg);
if (!ctl)
return -EINVAL;
if (len > ctl->len)
return -EINVAL;
return wm_coeff_read_control(ctl, buf, len);
}
EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region)
{

View File

@ -201,5 +201,9 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp);
int wm_adsp_compr_copy(struct snd_compr_stream *stream,
char __user *buf, size_t count);
int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len);
int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
unsigned int alg, void *buf, size_t len);
#endif

View File

@ -466,18 +466,18 @@ config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH
If unsure select "N".
config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH
tristate "CML with RT1011 and RT5682 in I2S Mode"
depends on I2C && ACPI
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT1011
select SND_SOC_RT5682
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
help
This adds support for ASoC machine driver for SOF platform with
RT1011 + RT5682 I2S codec.
Say Y if you have such a device.
If unsure select "N".
tristate "CML with RT1011 and RT5682 in I2S Mode"
depends on I2C && ACPI
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT1011
select SND_SOC_RT5682
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
help
This adds support for ASoC machine driver for SOF platform with
RT1011 + RT5682 I2S codec.
Say Y if you have such a device.
If unsure select "N".
endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK

View File

@ -405,10 +405,12 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
},
.driver_data = (void *)(BYT_RT5640_IN1_MAP |
BYT_RT5640_MCLK_EN |
BYT_RT5640_SSP0_AIF1),
.driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
BYT_RT5640_JD_SRC_JD2_IN4N |
BYT_RT5640_OVCD_TH_2000UA |
BYT_RT5640_OVCD_SF_0P75 |
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
{
.matches = {

View File

@ -498,9 +498,8 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
return -EINVAL;
}
int snd_soc_pcm_component_new(struct snd_pcm *pcm)
int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
int ret;
@ -516,13 +515,12 @@ int snd_soc_pcm_component_new(struct snd_pcm *pcm)
return 0;
}
void snd_soc_pcm_component_free(struct snd_pcm *pcm)
void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component;
for_each_rtd_components(rtd, rtdcom, component)
if (component->driver->pcm_destruct)
component->driver->pcm_destruct(component, pcm);
component->driver->pcm_destruct(component, rtd->pcm);
}

View File

@ -419,6 +419,9 @@ static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
list_del(&rtd->list);
flush_delayed_work(&rtd->delayed_work);
snd_soc_pcm_component_free(rtd);
/*
* we don't need to call kfree() for rtd->dev
* see
@ -1945,19 +1948,14 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card,
{
struct snd_soc_dai_link *link, *_link;
/* This should be called before snd_card_free() */
soc_remove_link_components(card);
/* free the ALSA card at first; this syncs with pending operations */
if (card->snd_card) {
snd_card_free(card->snd_card);
card->snd_card = NULL;
}
if (card->snd_card)
snd_card_disconnect_sync(card->snd_card);
snd_soc_dapm_shutdown(card);
/* remove and free each DAI */
soc_remove_link_dais(card);
soc_remove_link_components(card);
for_each_card_links_safe(card, link, _link)
snd_soc_remove_dai_link(card, link);
@ -1972,6 +1970,11 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card,
/* remove the card */
if (card_probed && card->remove)
card->remove(card);
if (card->snd_card) {
snd_card_free(card->snd_card);
card->snd_card = NULL;
}
}
static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
@ -2105,6 +2108,19 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
soc_setup_card_name(card->snd_card->driver,
card->driver_name, card->name, 1);
if (card->components) {
/* the current implementation of snd_component_add() accepts */
/* multiple components in the string separated by space, */
/* but the string collision (identical string) check might */
/* not work correctly */
ret = snd_component_add(card->snd_card, card->components);
if (ret < 0) {
dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n",
card->name, ret);
goto probe_end;
}
}
if (card->late_probe) {
ret = card->late_probe(card);
if (ret < 0) {

View File

@ -861,6 +861,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
int i, ret = 0;
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
ret = soc_pcm_params_symmetry(substream, params);
if (ret)
goto out;
if (rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
@ -940,9 +945,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
component = NULL;
ret = soc_pcm_params_symmetry(substream, params);
if (ret)
goto component_err;
out:
mutex_unlock(&rtd->card->pcm_mutex);
return ret;
@ -2892,15 +2894,6 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
return ret;
}
static void soc_pcm_private_free(struct snd_pcm *pcm)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
/* need to sync the delayed work before releasing resources */
flush_delayed_work(&rtd->delayed_work);
snd_soc_pcm_component_free(pcm);
}
/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
@ -3036,13 +3029,12 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
ret = snd_soc_pcm_component_new(pcm);
ret = snd_soc_pcm_component_new(rtd);
if (ret < 0) {
dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret);
return ret;
}
pcm->private_free = soc_pcm_private_free;
pcm->no_device_suspend = true;
out:
dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",

View File

@ -264,16 +264,16 @@ config SND_SOC_SOF_ELKHARTLAKE
config SND_SOC_SOF_JASPERLAKE_SUPPORT
bool "SOF support for JasperLake"
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the JasperLake processors.
Say Y if you have such a device.
If unsure select "N".
This adds support for Sound Open Firmware for Intel(R) platforms
using the JasperLake processors.
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_SOF_JASPERLAKE
tristate
select SND_SOC_SOF_HDA_COMMON
help
This option is not user-selectable but automagically handled by
This option is not user-selectable but automagically handled by
'select' statements at a higher level
config SND_SOC_SOF_HDA_COMMON

View File

@ -261,34 +261,34 @@ static const struct scarlett2_device_info s6i6_gen2_info = {
},
.ports = {
{
[SCARLETT2_PORT_TYPE_NONE] = {
.id = 0x000,
.num = { 1, 0, 8, 8, 8 },
.src_descr = "Off",
.src_num_offset = 0,
},
{
[SCARLETT2_PORT_TYPE_ANALOGUE] = {
.id = 0x080,
.num = { 4, 4, 4, 4, 4 },
.src_descr = "Analogue %d",
.src_num_offset = 1,
.dst_descr = "Analogue Output %02d Playback"
},
{
[SCARLETT2_PORT_TYPE_SPDIF] = {
.id = 0x180,
.num = { 2, 2, 2, 2, 2 },
.src_descr = "S/PDIF %d",
.src_num_offset = 1,
.dst_descr = "S/PDIF Output %d Playback"
},
{
[SCARLETT2_PORT_TYPE_MIX] = {
.id = 0x300,
.num = { 10, 18, 18, 18, 18 },
.src_descr = "Mix %c",
.src_num_offset = 65,
.dst_descr = "Mixer Input %02d Capture"
},
{
[SCARLETT2_PORT_TYPE_PCM] = {
.id = 0x600,
.num = { 6, 6, 6, 6, 6 },
.src_descr = "PCM %d",
@ -317,44 +317,44 @@ static const struct scarlett2_device_info s18i8_gen2_info = {
},
.ports = {
{
[SCARLETT2_PORT_TYPE_NONE] = {
.id = 0x000,
.num = { 1, 0, 8, 8, 4 },
.src_descr = "Off",
.src_num_offset = 0,
},
{
[SCARLETT2_PORT_TYPE_ANALOGUE] = {
.id = 0x080,
.num = { 8, 6, 6, 6, 6 },
.src_descr = "Analogue %d",
.src_num_offset = 1,
.dst_descr = "Analogue Output %02d Playback"
},
{
[SCARLETT2_PORT_TYPE_SPDIF] = {
.id = 0x180,
/* S/PDIF outputs aren't available at 192KHz
* but are included in the USB mux I/O
* assignment message anyway
*/
.id = 0x180,
.num = { 2, 2, 2, 2, 2 },
.src_descr = "S/PDIF %d",
.src_num_offset = 1,
.dst_descr = "S/PDIF Output %d Playback"
},
{
[SCARLETT2_PORT_TYPE_ADAT] = {
.id = 0x200,
.num = { 8, 0, 0, 0, 0 },
.src_descr = "ADAT %d",
.src_num_offset = 1,
},
{
[SCARLETT2_PORT_TYPE_MIX] = {
.id = 0x300,
.num = { 10, 18, 18, 18, 18 },
.src_descr = "Mix %c",
.src_num_offset = 65,
.dst_descr = "Mixer Input %02d Capture"
},
{
[SCARLETT2_PORT_TYPE_PCM] = {
.id = 0x600,
.num = { 20, 18, 18, 14, 10 },
.src_descr = "PCM %d",
@ -387,20 +387,20 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
},
.ports = {
{
[SCARLETT2_PORT_TYPE_NONE] = {
.id = 0x000,
.num = { 1, 0, 8, 8, 6 },
.src_descr = "Off",
.src_num_offset = 0,
},
{
[SCARLETT2_PORT_TYPE_ANALOGUE] = {
.id = 0x080,
.num = { 8, 10, 10, 10, 10 },
.src_descr = "Analogue %d",
.src_num_offset = 1,
.dst_descr = "Analogue Output %02d Playback"
},
{
[SCARLETT2_PORT_TYPE_SPDIF] = {
/* S/PDIF outputs aren't available at 192KHz
* but are included in the USB mux I/O
* assignment message anyway
@ -411,21 +411,21 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
.src_num_offset = 1,
.dst_descr = "S/PDIF Output %d Playback"
},
{
[SCARLETT2_PORT_TYPE_ADAT] = {
.id = 0x200,
.num = { 8, 8, 8, 4, 0 },
.src_descr = "ADAT %d",
.src_num_offset = 1,
.dst_descr = "ADAT Output %d Playback"
},
{
[SCARLETT2_PORT_TYPE_MIX] = {
.id = 0x300,
.num = { 10, 18, 18, 18, 18 },
.src_descr = "Mix %c",
.src_num_offset = 65,
.dst_descr = "Mixer Input %02d Capture"
},
{
[SCARLETT2_PORT_TYPE_PCM] = {
.id = 0x600,
.num = { 20, 18, 18, 14, 10 },
.src_descr = "PCM %d",