Merge: ALSA - update drivers for 9.7 - upstream 6.13

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6587

JIRA: https://issues.redhat.com/browse/RHEL-80681

This upstream patchset updates the ALSA driver code:

- ALSA core
- ALSA HDA
- ALSA USB
- ALSA PCI
- ALSA SoC (mainly SOF including SoundWire drivers)
- Soundwire bus

The other components are touched to get things in sync with the current upstream:

Some touched drivers are for hardware platforms which are not used in RHEL. The purpose to merge those upstream commits is to keep the future code sync more easy.

Omitted-fix: 704dbe97a681 # not supported platform

Omitted-fix: efa527f984a1 # code is not used

Omitted-fix: ec15e5043d0b # not supported platform

Omitted-fix: 3f0b8d367db5 # not supported platform

Omitted-fix: e9d2a2f49244 # not supported platform

Omitted-fix: be8cd366beb8 # in MR !6787

Omitted-fix: 38e94cefbf45 # in MR !6787

Signed-off-by: Jaroslav Kysela <jkysela@redhat.com>

Approved-by: Tony Camuso <tcamuso@redhat.com>
Approved-by: Eric Chanudet <echanude@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: Augusto Caringi <acaringi@redhat.com>
This commit is contained in:
Augusto Caringi 2025-05-06 11:46:15 -03:00
commit f8561e9b91
310 changed files with 15121 additions and 5210 deletions

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/adi,adau1373.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADAU1373 CODEC
maintainers:
- Nuno Sá <nuno.sa@analog.com>
description: |
Analog Devices ADAU1373 Low power codec with speaker and headphone amplifiers.
https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1373.pdf
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- adi,adau1373
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
powerdown-gpios:
description: GPIO used for hardware power-down.
maxItems: 1
adi,micbias1-microvolt:
description:
This property sets the microphone bias voltage for the first microphone.
enum: [1800000, 2200000, 2600000, 2900000]
default: 2900000
adi,micbias2-microvolt:
description:
This property sets the microphone bias voltage for the second microphone.
enum: [1800000, 2200000, 2600000, 2900000]
default: 2900000
adi,input1-differential:
description: This property sets the first analog input as differential.
type: boolean
adi,input2-differential:
description: This property sets the second analog input as differential.
type: boolean
adi,input3-differential:
description: This property sets the third analog input as differential.
type: boolean
adi,input4-differential:
description: This property sets the fourth analog input as differential.
type: boolean
adi,lineout-differential:
description: This property sets the line output as differential.
type: boolean
adi,lineout-gnd-sense:
description: This property enables the line output ground sense control.
type: boolean
adi,drc-settings:
description:
This setting is used to control the dynamic range of the signal. The
device provides a maximum of three full band DRCs with 13 entries each.
$ref: /schemas/types.yaml#/definitions/uint8-array
oneOf:
- minItems: 13
maxItems: 13
- minItems: 26
maxItems: 26
- minItems: 39
maxItems: 39
required:
- "#sound-dai-cells"
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@1a {
compatible = "adi,adau1373";
reg = <0x1a>;
#sound-dai-cells = <0>;
powerdown-gpios = <&gpio 100 GPIO_ACTIVE_LOW>;
adi,input2-differential;
adi,input1-differential;
adi,lineout-differential;
adi,micbias2-microvolt = <1800000>;
adi,drc-settings = /bits/ 8 <
0xff 0xff 0x1 0x2 0xa 0xa 0xd 0x1 0xff 0xff 0x5 0xd 0xff
>;
};
};
...

View File

@ -17,8 +17,9 @@ description:
properties:
compatible:
enum:
- awinic,aw88395
- awinic,aw88081
- awinic,aw88261
- awinic,aw88395
- awinic,aw88399
reg:
@ -56,6 +57,7 @@ allOf:
compatible:
contains:
enum:
- awinic,aw88081
- awinic,aw88261
then:
properties:

View File

@ -0,0 +1,53 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/irondevice,sma1307.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Iron Device SMA1307 Audio Amplifier
maintainers:
- Kiseok Jo <kiseok.jo@irondevice.com>
description:
SMA1307 boosted digital speaker amplifier with feedback-loop.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- irondevice,sma1307a
- irondevice,sma1307aq
description:
If a 'q' is added, it indicated the product is AEC-Q100
qualified for automotive applications. SMA1307A supports
both WLCSP and QFN packages. However, SMA1307AQ only
supports the QFN package.
reg:
maxItems: 1
'#sound-dai-cells':
const: 1
required:
- compatible
- reg
- '#sound-dai-cells'
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
amplifier@1e {
compatible = "irondevice,sma1307a";
reg = <0x1e>;
#sound-dai-cells = <1>;
};
};

View File

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/neofidelity,ntp8835.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NeoFidelity NTP8835/NTP8835C Amplifiers
maintainers:
- Igor Prusov <ivprusov@salutedevices.com>
description: |
The NTP8835 is a single chip full digital audio amplifier
including power stages for stereo amplifier systems.
NTP8835 is integrated with versatile digital audio signal
processing functions, high-performance, high-fidelity fully
digital PWM modulator and two high-power full-bridge MOSFET
power stages. NTP8835C has identical programming interface,
but has different output signal characteristics.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- neofidelity,ntp8835
- neofidelity,ntp8835c
reg:
enum:
- 0x2a
- 0x2b
- 0x2c
- 0x2d
reset-gpios:
maxItems: 1
'#sound-dai-cells':
const: 0
clocks:
maxItems: 4
clock-names:
items:
- const: wck
- const: bck
- const: scl
- const: mclk
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2b {
compatible = "neofidelity,ntp8835";
#sound-dai-cells = <0>;
reg = <0x2b>;
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
clocks = <&clkc 551>, <&clkc 552>, <&clkc 553>, <&clkc 554>;
clock-names = "wck", "bck", "scl", "mclk";
};
};

View File

@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/neofidelity,ntp8918.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NeoFidelity NTP8918 Amplifier
maintainers:
- Igor Prusov <ivprusov@salutedevices.com>
description:
The NTP8918 is a single chip full digital audio amplifier
including power stage for stereo amplifier system.
The NTP8918 is integrated with versatile digital audio signal
processing functions, high-performance, high-fidelity fully
digital PWM modulator and two high-power full-bridge MOSFET
power stages.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- neofidelity,ntp8918
reg:
enum:
- 0x2a
- 0x2b
- 0x2c
- 0x2d
reset-gpios:
maxItems: 1
'#sound-dai-cells':
const: 0
clocks:
maxItems: 3
clock-names:
items:
- const: wck
- const: scl
- const: bck
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2a {
compatible = "neofidelity,ntp8918";
#sound-dai-cells = <0>;
reg = <0x2a>;
clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>;
clock-names = "wck", "scl", "bck";
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
};
};

View File

@ -25,6 +25,7 @@ properties:
- enum:
- qcom,sm8550-sndcard
- qcom,sm8650-sndcard
- qcom,sm8750-sndcard
- const: qcom,sm8450-sndcard
- enum:
- qcom,apq8016-sbc-sndcard

View File

@ -29,6 +29,10 @@ properties:
$ref: /schemas/types.yaml#/definitions/string-array
maxItems: 2
idle-state:
description: If present specifies the state when the mux is powered down
$ref: /schemas/mux/mux-controller.yaml#/properties/idle-state
sound-name-prefix: true
required:
@ -43,4 +47,5 @@ examples:
compatible = "simple-audio-mux";
mux-gpios = <&gpio 3 0>;
state-labels = "Label_A", "Label_B";
idle-state = <0>;
};

View File

@ -776,6 +776,8 @@ patternProperties:
description: National Semiconductor
"^nec,.*":
description: NEC LCD Technologies, Ltd.
"^neofidelity,.*":
description: Neofidelity Inc.
"^neonode,.*":
description: Neonode Inc.
"^netgear,.*":

View File

@ -0,0 +1,292 @@
.. SPDX-License-Identifier: GPL-2.0-only
=====================================================================
Audio drivers for Cirrus Logic CS35L54/56/57 Boosted Smart Amplifiers
=====================================================================
:Copyright: 2025 Cirrus Logic, Inc. and
Cirrus Logic International Semiconductor Ltd.
Contact: patches@opensource.cirrus.com
Summary
=======
The high-level summary of this document is:
**If you have a laptop that uses CS35L54/56/57 amplifiers but audio is not
working, DO NOT ATTEMPT TO USE FIRMWARE AND SETTINGS FROM ANOTHER LAPTOP,
EVEN IF THAT LAPTOP SEEMS SIMILAR.**
The CS35L54/56/57 amplifiers must be correctly configured for the power
supply voltage, speaker impedance, maximum speaker voltage/current, and
other external hardware connections.
The amplifiers feature advanced boost technology that increases the voltage
used to drive the speakers, while proprietary speaker protection algorithms
allow these boosted amplifiers to push the limits of the speakers without
causing damage. These **must** be configured correctly.
Supported Cirrus Logic amplifiers
---------------------------------
The cs35l56 drivers support:
* CS35L54
* CS35L56
* CS35L57
There are two drivers in the kernel
*For systems using SoundWire*: sound/soc/codecs/cs35l56.c and associated files
*For systems using HDA*: sound/pci/hda/cs35l56_hda.c
Firmware
========
The amplifier is controlled and managed by firmware running on the internal
DSP. Firmware files are essential to enable the full capabilities of the
amplifier.
Firmware is distributed in the linux-firmware repository:
https://gitlab.com/kernel-firmware/linux-firmware.git
On most SoundWire systems the amplifier has a default minimum capability to
produce audio. However this will be
* at low volume, to protect the speakers, since the speaker specifications
and power supply voltages are unknown.
* a mono mix of left and right channels.
On some SoundWire systems that have both CS42L43 and CS35L56/57 the CS35L56/57
receive their audio from the CS42L43 instead of directly from the host
SoundWire interface. These systems can be identified by the CS42L43 showing
in dmesg as a SoundWire device, but the CS35L56/57 as SPI. On these systems
the firmware is *mandatory* to enable receiving the audio from the CS42L43.
On HDA systems the firmware is *mandatory* to enable HDA bridge mode. There
will not be any audio from the amplifiers without firmware.
Cirrus Logic firmware files
---------------------------
Each amplifier requires two firmware files. One file has a .wmfw suffix, the
other has a .bin suffix.
The firmware is customized by the OEM to match the hardware of each laptop,
and the firmware is specific to that laptop. Because of this, there are many
firmware files in linux-firmware for these amplifiers. Firmware files are
**not interchangeable between laptops**.
Cirrus Logic submits files for known laptops to the upstream linux-firmware
repository. Providing Cirrus Logic is aware of a particular laptop and has
permission from the manufacturer to publish the firmware, it will be pushed
to linux-firmware. You may need to upgrade to a newer release of
linux-firmware to obtain the firmware for your laptop.
**Important:** the Makefile for linux-firmware creates symlinks that are listed
in the WHENCE file. These symlinks are required for the CS35L56 driver to be
able to load the firmware.
How do I know which firmware file I should have?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All firmware file names are qualified with a unique "system ID". On normal
x86 PCs with PCI audio this is the Vendor Subsystem ID (SSID) of the host
PCI audio interface.
The SSID can be viewed using the lspci tool::
lspci -v -nn | grep -A2 -i audio
0000:00:1f.3 Audio device [0403]: Intel Corporation Meteor Lake-P HD Audio Controller [8086:7e28]
Subsystem: Dell Meteor Lake-P HD Audio Controller [1028:0c63]
In this example the SSID is 10280c63.
The format of the firmware file names is:
cs35lxx-b0-dsp1-misc-SSID[-spkidX]-ampN
Where:
* cs35lxx-b0 is the amplifier model and silicon revision. This information
is logged by the driver during initialization.
* SSID is the 8-digit hexadecimal SSID value.
* ampN is the amplifier number (for example amp1). This is the same as
the prefix on the ALSA control names except that it is always lower-case
in the file name.
* spkidX is an optional part, used for laptops that have firmware
configurations for different makes and models of internal speakers.
Sound Open Firmware and ALSA topology files
-------------------------------------------
All SoundWire systems will require a Sound Open Firmware (SOF) for the
host CPU audio DSP, together with an ALSA topology file (.tplg).
The SOF firmware will usually be provided by the manufacturer of the host
CPU (i.e. Intel or AMD). The .tplg file is normally part of the SOF firmware
release.
SOF binary builds are available from: https://github.com/thesofproject/sof-bin/releases
The main SOF source is here: https://github.com/thesofproject
ALSA-ucm configurations
-----------------------
Typically an appropriate ALSA-ucm configuration file is needed for
use-case managers and audio servers such as PipeWire.
Configuration files are available from the alsa-ucm-conf repository:
https://git.alsa-project.org/?p=alsa-ucm-conf.git
Kernel log messages
===================
SoundWire
---------
A successful initialization will look like this (this will be repeated for
each amplifier)::
[ 7.568374] cs35l56 sdw:0:0:01fa:3556:01:0: supply VDD_P not found, using dummy regulator
[ 7.605208] cs35l56 sdw:0:0:01fa:3556:01:0: supply VDD_IO not found, using dummy regulator
[ 7.605313] cs35l56 sdw:0:0:01fa:3556:01:0: supply VDD_A not found, using dummy regulator
[ 7.939279] cs35l56 sdw:0:0:01fa:3556:01:0: Cirrus Logic CS35L56 Rev B0 OTP3 fw:3.4.4 (patched=0)
[ 7.947844] cs35l56 sdw:0:0:01fa:3556:01:0: Slave 4 state check1: UNATTACHED, status was 1
[ 8.740280] cs35l56 sdw:0:0:01fa:3556:01:0: supply VDD_B not found, using dummy regulator
[ 8.740552] cs35l56 sdw:0:0:01fa:3556:01:0: supply VDD_AMP not found, using dummy regulator
[ 9.242164] cs35l56 sdw:0:0:01fa:3556:01:0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx.wmfw: format 3 timestamp 0x66b2b872
[ 9.242173] cs35l56 sdw:0:0:01fa:3556:01:0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx.wmfw: Tue 05 Dec 2023 21:37:21 GMT Standard Time
[ 9.991709] cs35l56 sdw:0:0:01fa:3556:01:0: DSP1: Firmware: 1a00d6 vendor: 0x2 v3.11.23, 41 algorithms
[10.039098] cs35l56 sdw:0:0:01fa:3556:01:0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx-amp1.bin: v3.11.23
[10.879235] cs35l56 sdw:0:0:01fa:3556:01:0: Slave 4 state check1: UNATTACHED, status was 1
[11.401536] cs35l56 sdw:0:0:01fa:3556:01:0: Calibration applied
HDA
---
A successful initialization will look like this (this will be repeated for
each amplifier)::
[ 6.306475] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: Cirrus Logic CS35L56 Rev B0 OTP3 fw:3.4.4 (patched=0)
[ 6.613892] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: DSP system name: 'xxxxxxxx', amp name: 'AMP1'
[ 8.266660] snd_hda_codec_cs8409 ehdaudio0D0: bound i2c-CSC3556:00-cs35l56-hda.0 (ops cs35l56_hda_comp_ops [snd_hda_scodec_cs35l56])
[ 8.287525] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx.wmfw: format 3 timestamp 0x66b2b872
[ 8.287528] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx.wmfw: Tue 05 Dec 2023 21:37:21 GMT Standard Time
[ 9.984335] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: DSP1: Firmware: 1a00d6 vendor: 0x2 v3.11.23, 41 algorithms
[10.085797] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx-amp1.bin: v3.11.23
[10.655237] cs35l56-hda i2c-CSC3556:00-cs35l56-hda.0: Calibration applied
Important messages
~~~~~~~~~~~~~~~~~~
Cirrus Logic CS35L56 Rev B0 OTP3 fw:3.4.4 (patched=0)
Shows that the driver has been able to read device ID registers from the
amplifier.
* The actual amplifier type and silicon revision (CS35L56 B0 in this
example) is shown, as read from the amplifier identification registers.
* (patched=0) is normal, and indicates that the amplifier has been hard
reset and is running default ROM firmware.
* (patched=1) means that something has previously downloaded firmware
to the amplifier and the driver does not have control of the RESET
signal to be able to replace this preloaded firmware. This is normal
for systems where the BIOS downloads firmware to the amplifiers
before OS boot.
This status can also be seen if the cs35l56 kernel module is unloaded
and reloaded on a system where the driver does not have control of
RESET. SoundWire systems typically do not give the driver control of
RESET and only a BIOS (re)boot can reset the amplifiers.
DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx.wmfw
Shows that a .wmfw firmware file was found and downloaded.
DSP1: cirrus/cs35l56-b0-dsp1-misc-xxxxxxxx-amp1.bin
Shows that a .bin firmware file was found and downloaded.
Calibration applied
Factory calibration data in EFI was written to the amplifier.
Error messages
==============
This section explains some of the error messages that the driver can log.
Algorithm coefficient version %d.%d.%d but expected %d.%d.%d
The version of the .bin file content does not match the loaded firmware.
Caused by mismatched .wmfw and .bin file, or .bin file was found but
.wmfw was not.
No %s for algorithm %x
The version of the .bin file content does not match the loaded firmware.
Caused by mismatched .wmfw and .bin file, or .bin file was found but
.wmfw was not.
.bin file required but not found
HDA driver did not find a .bin file that matches this hardware.
Calibration disabled due to missing firmware controls
Driver was not able to write EFI calibration data to firmware registers.
This typically means that either:
* The driver did not find a suitable wmfw for this hardware, or
* The amplifier has already been patched with firmware by something
previously, and the driver does not have control of a hard RESET line
to be able to reset the amplifier and download the firmware files it
found. This situation is indicated by the device identification
string in the kernel log shows "(patched=1)"
Failed to write calibration
Same meaning and cause as "Calibration disabled due to missing firmware
controls"
Failed to read calibration data from EFI
Factory calibration data in EFI is missing, empty or corrupt.
This is most likely to be cause by accidentally deleting the file from
the EFI filesystem.
No calibration for silicon ID
The factory calibration data in EFI does not match this hardware.
The most likely cause is that an amplifier has been replaced on the
motherboard without going through manufacturer calibration process to
generate calibration data for the new amplifier.
Did not find any buses for CSCxxxx
Only on HDA systems. The HDA codec driver found an ACPI entry for
Cirrus Logic companion amps, but could not enumerate the ACPI entries for
the I2C/SPI buses. The most likely cause of this is that:
* The relevant bus driver (I2C or SPI) is not part of the kernel.
* The HDA codec driver was built-in to the kernel but the I2C/SPI
bus driver is a module and so the HDA codec driver cannot call the
bus driver functions.
init_completion timed out
The SoundWire bus controller (host end) did not enumerate the amplifier.
In other words, the ACPI says there is an amplifier but for some reason
it was not detected on the bus.
No AF01 node
Indicates an error in ACPI. A SoundWire system should have a Device()
node named "AF01" but it was not found.
Failed to get spk-id-gpios
ACPI says that the driver should request a GPIO but the driver was not
able to get that GPIO. The most likely cause is that the kernel does not
include the correct GPIO or PINCTRL driver for this system.
Failed to read spk-id
ACPI says that the driver should request a GPIO but the driver was not
able to read that GPIO.
Unexpected spk-id element count
AF01 contains more speaker ID GPIO entries than the driver supports
Overtemp error
Amplifier overheat protection was triggered and the amplifier shut down
to protect itself.
Amp short error
Amplifier detected a short-circuit on the speaker output pins and shut
down for protection. This would normally indicate a damaged speaker.
Hibernate wake failed
The driver tried to wake the amplifier from its power-saving state but
did not see the expected responses from the amplifier. This can be caused
by using firmware that does not match the hardware.

View File

@ -0,0 +1,9 @@
.. SPDX-License-Identifier: GPL-2.0
Codec-Specific Information
==========================
.. toctree::
:maxdepth: 2
cs35l56

View File

@ -0,0 +1,134 @@
==================================
ALSA Co-processor Acceleration API
==================================
Jaroslav Kysela <perex@perex.cz>
Overview
========
There is a requirement to expose the audio hardware that accelerates various
tasks for user space such as sample rate converters, compressed
stream decoders, etc.
This is description for the API extension for the compress ALSA API which
is able to handle "tasks" that are not bound to real-time operations
and allows for the serialization of operations.
Requirements
============
The main requirements are:
- serialization of multiple tasks for user space to allow multiple
operations without user space intervention
- separate buffers (input + output) for each operation
- expose buffers using mmap to user space
- signal user space when the task is finished (standard poll mechanism)
Design
======
A new direction SND_COMPRESS_ACCEL is introduced to identify
the passthrough API.
The API extension shares device enumeration and parameters handling from
the main compressed API. All other realtime streaming ioctls are deactivated
and a new set of task related ioctls are introduced. The standard
read/write/mmap I/O operations are not supported in the passthrough device.
Device ("stream") state handling is reduced to OPEN/SETUP. All other
states are not available for the passthrough mode.
Data I/O mechanism is using standard dma-buf interface with all advantages
like mmap, standard I/O, buffer sharing etc. One buffer is used for the
input data and second (separate) buffer is used for the output data. Each task
have separate I/O buffers.
For the buffering parameters, the fragments means a limit of allocated tasks
for given device. The fragment_size limits the input buffer size for the given
device. The output buffer size is determined by the driver (may be different
from the input buffer size).
State Machine
=============
The passthrough audio stream state machine is described below::
+----------+
| |
| OPEN |
| |
+----------+
|
|
| compr_set_params()
|
v
all passthrough task ops +----------+
+------------------------------------| |
| | SETUP |
| |
| +----------+
| |
+------------------------------------------+
Passthrough operations (ioctls)
===============================
All operations are protected using stream->device->lock (mutex).
CREATE
------
Creates a set of input/output buffers. The input buffer size is
fragment_size. Allocates unique seqno.
The hardware drivers allocate internal 'struct dma_buf' for both input and
output buffers (using 'dma_buf_export()' function). The anonymous
file descriptors for those buffers are passed to user space.
FREE
----
Free a set of input/output buffers. If a task is active, the stop
operation is executed before. If seqno is zero, operation is executed for all
tasks.
START
-----
Starts (queues) a task. There are two cases of the task start - right after
the task is created. In this case, origin_seqno must be zero.
The second case is for reusing of already finished task. The origin_seqno
must identify the task to be reused. In both cases, a new seqno value
is allocated and returned to user space.
The prerequisite is that application filled input dma buffer with
new source data and set input_size to pass the real data size to the driver.
The order of data processing is preserved (first started job must be
finished at first).
If the multiple tasks require a state handling (e.g. resampling operation),
the user space may set SND_COMPRESS_TFLG_NEW_STREAM flag to mark the
start of the new stream data. It is useful to keep the allocated buffers
for the new operation rather using open/close mechanism.
STOP
----
Stop (dequeues) a task. If seqno is zero, operation is executed for all
tasks.
STATUS
------
Obtain the task status (active, finished). Also, the driver will set
the real output data size (valid area in the output buffer).
Credits
=======
- Shengjiu Wang <shengjiu.wang@gmail.com>
- Takashi Iwai <tiwai@suse.de>
- Vinod Koul <vkoul@kernel.org>

View File

@ -6,6 +6,7 @@ Designs and Implementations
control-names
channel-mapping-api
compress-accel
compress-offload
timestamping
jack-controls

View File

@ -42,7 +42,7 @@ If you are interested in the deep debugging of HD-audio, read the
HD-audio specification at first. The specification is found on
Intel's web page, for example:
* https://www.intel.com/standards/hdaudio/
* https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html
HD-Audio Controller

View File

@ -13,6 +13,7 @@ Sound Subsystem Documentation
alsa-configuration
hd-audio/index
cards/index
codecs/index
utimers
.. only:: subproject and html

View File

@ -42,5 +42,17 @@ rate, number of channels and word size) to save on power.
It is also desirable to use the codec (if possible) to drive (or master) the
audio clocks as it usually gives more accurate sample rates than the CPU.
ASoC provided clock APIs
------------------------
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_sysclk
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_clkdiv
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_pll
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_bclk_ratio

View File

@ -35,6 +35,9 @@ The graph for the STM32MP1-DK1 sound card is shown in picture:
:alt: Example DAPM graph
:align: center
You can also generate compatible graph for your sound card using
`tools/sound/dapm-graph` utility.
DAPM power domains
==================

View File

@ -157,15 +157,13 @@ FE DAI links are defined as follows :-
.codec_dai_name = "snd-soc-dummy-dai",
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
.....< other FE and BE DAI links here >
};
This FE DAI link is pretty similar to a regular DAI link except that we also
set the DAI link to a DPCM FE with the ``dynamic = 1``. The supported FE stream
directions should also be set with the ``dpcm_playback`` and ``dpcm_capture``
flags. There is also an option to specify the ordering of the trigger call for
set the DAI link to a DPCM FE with the ``dynamic = 1``.
There is also an option to specify the ordering of the trigger call for
each FE. This allows the ASoC core to trigger the DSP before or after the other
components (as some DSPs have strong requirements for the ordering DAI/DSP
start and stop sequences).
@ -189,15 +187,12 @@ The BE DAIs are configured as follows :-
.ignore_pmdown_time = 1,
.be_hw_params_fixup = hswult_ssp0_fixup,
.ops = &haswell_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
.....< other BE DAI links here >
};
This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
the ``no_pcm`` flag to mark it has a BE and sets flags for supported stream
directions using ``dpcm_playback`` and ``dpcm_capture`` above.
the ``no_pcm`` flag to mark it has a BE.
The BE has also flags set for ignoring suspend and PM down time. This allows
the BE to work in a hostless mode where the host CPU is not transferring data

View File

@ -71,6 +71,18 @@ struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
.ops = &corgi_ops,
};
In the above struct, dais are registered using names but you can pass
either dai name or device tree node but not both. Also, names used here
for cpu/codec/platform dais should be globally unique.
Additionaly below example macro can be used to register cpu, codec and
platform dai::
SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
struct snd_soc_card then sets up the machine with its DAIs. e.g.
::
@ -81,6 +93,10 @@ struct snd_soc_card then sets up the machine with its DAIs. e.g.
.num_links = 1,
};
Following this, ``devm_snd_soc_register_card`` can be used to register
the sound card. During the registration, the individual components
such as the codec, CPU, and platform are probed. If all these components
are successfully probed, the sound card gets registered.
Machine Power Map
-----------------
@ -95,3 +111,13 @@ Machine Controls
----------------
Machine specific audio mixer controls can be added in the DAI init function.
Clocking Controls
-----------------
As previously noted, clock configuration is handled within the machine driver.
For details on the clock APIs that the machine driver can utilize for
setup, please refer to Documentation/sound/soc/clocking.rst. However, the
callback needs to be registered by the CPU/Codec/Platform drivers to configure
the clocks that is needed for the corresponding device operation.

View File

@ -1242,6 +1242,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Supported
W: http://wiki.analog.com/
W: http://ez.analog.com/community/linux-device-drivers
F: Documentation/devicetree/bindings/sound/adi,*
F: sound/soc/codecs/ad1*
F: sound/soc/codecs/ad7*
F: sound/soc/codecs/adau*
@ -1807,6 +1808,7 @@ F: Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
F: Documentation/devicetree/bindings/sound/apple,*
F: sound/soc/apple/*
F: sound/soc/codecs/cs42l83-i2c.c
F: sound/soc/codecs/cs42l84.*
F: sound/soc/codecs/ssm3515.c
ARM/ARTPEC MACHINE SUPPORT
@ -4572,6 +4574,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: patches@opensource.cirrus.com
S: Maintained
F: Documentation/devicetree/bindings/sound/cirrus,cs*
F: Documentation/sound/codecs/cs*
F: drivers/mfd/cs42l43*
F: drivers/pinctrl/cirrus/pinctrl-cs42l43*
F: include/dt-bindings/sound/cs*

View File

@ -967,7 +967,6 @@ static void cs42l43_boot_work(struct work_struct *work)
err:
pm_runtime_put_sync(cs42l43->dev);
cs42l43_dev_remove(cs42l43);
}
static int cs42l43_power_up(struct cs42l43 *cs42l43)
@ -1101,6 +1100,8 @@ EXPORT_SYMBOL_NS_GPL(cs42l43_dev_probe, MFD_CS42L43);
void cs42l43_dev_remove(struct cs42l43 *cs42l43)
{
cancel_work_sync(&cs42l43->boot_work);
cs42l43_power_down(cs42l43);
}
EXPORT_SYMBOL_NS_GPL(cs42l43_dev_remove, MFD_CS42L43);
@ -1108,16 +1109,39 @@ EXPORT_SYMBOL_NS_GPL(cs42l43_dev_remove, MFD_CS42L43);
static int cs42l43_suspend(struct device *dev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
static const struct reg_sequence mask_all[] = {
{ CS42L43_DECIM_MASK, 0xFFFFFFFF, },
{ CS42L43_EQ_MIX_MASK, 0xFFFFFFFF, },
{ CS42L43_ASP_MASK, 0xFFFFFFFF, },
{ CS42L43_PLL_MASK, 0xFFFFFFFF, },
{ CS42L43_SOFT_MASK, 0xFFFFFFFF, },
{ CS42L43_SWIRE_MASK, 0xFFFFFFFF, },
{ CS42L43_MSM_MASK, 0xFFFFFFFF, },
{ CS42L43_ACC_DET_MASK, 0xFFFFFFFF, },
{ CS42L43_I2C_TGT_MASK, 0xFFFFFFFF, },
{ CS42L43_SPI_MSTR_MASK, 0xFFFFFFFF, },
{ CS42L43_SW_TO_SPI_BRIDGE_MASK, 0xFFFFFFFF, },
{ CS42L43_OTP_MASK, 0xFFFFFFFF, },
{ CS42L43_CLASS_D_AMP_MASK, 0xFFFFFFFF, },
{ CS42L43_GPIO_INT_MASK, 0xFFFFFFFF, },
{ CS42L43_ASRC_MASK, 0xFFFFFFFF, },
{ CS42L43_HPOUT_MASK, 0xFFFFFFFF, },
};
int ret;
/*
* Don't care about being resumed here, but the driver does want
* force_resume to always trigger an actual resume, so that register
* state for the MCU/GPIOs is returned as soon as possible after system
* resume. force_resume will resume if the reference count is resumed on
* suspend hence the get_noresume.
*/
pm_runtime_get_noresume(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(cs42l43->dev, "Failed to resume for suspend: %d\n", ret);
return ret;
}
/* The IRQs will be re-enabled on resume by the cache sync */
ret = regmap_multi_reg_write_bypassed(cs42l43->regmap,
mask_all, ARRAY_SIZE(mask_all));
if (ret) {
dev_err(cs42l43->dev, "Failed to mask IRQs: %d\n", ret);
return ret;
}
ret = pm_runtime_force_suspend(dev);
if (ret) {
@ -1132,6 +1156,26 @@ static int cs42l43_suspend(struct device *dev)
if (ret)
return ret;
disable_irq(cs42l43->irq);
return 0;
}
static int cs42l43_suspend_noirq(struct device *dev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
enable_irq(cs42l43->irq);
return 0;
}
static int cs42l43_resume_noirq(struct device *dev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
disable_irq(cs42l43->irq);
return 0;
}
@ -1144,6 +1188,8 @@ static int cs42l43_resume(struct device *dev)
if (ret)
return ret;
enable_irq(cs42l43->irq);
ret = pm_runtime_force_resume(dev);
if (ret) {
dev_err(cs42l43->dev, "Failed to force resume: %d\n", ret);
@ -1211,6 +1257,7 @@ err:
EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = {
SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume)
NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend_noirq, cs42l43_resume_noirq)
RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL)
};

View File

@ -6,6 +6,7 @@
menuconfig SOUNDWIRE
tristate "SoundWire support"
depends on ACPI || OF
depends on SND_SOC_SDCA_OPTIONAL
help
SoundWire is a 2-Pin interface with data and clock line ratified
by the MIPI Alliance. SoundWire is used for transporting data

View File

@ -121,6 +121,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
sdw_pdata[index].instance = index;
sdw_pdata[index].acp_sdw_lock = res->acp_lock;
sdw_pdata[index].acp_rev = res->acp_rev;
pdevinfo[index].name = "amd_sdw_manager";
pdevinfo[index].id = index;
pdevinfo[index].parent = res->parent;
@ -177,7 +178,7 @@ EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT);
void sdw_amd_exit(struct sdw_amd_ctx *ctx)
{
sdw_amd_cleanup(ctx);
kfree(ctx->ids);
kfree(ctx->peripherals);
kfree(ctx);
}
EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT);
@ -204,10 +205,11 @@ int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx)
num_slaves++;
}
ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
if (!ctx->ids)
ctx->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves),
GFP_KERNEL);
if (!ctx->peripherals)
return -ENOMEM;
ctx->num_slaves = num_slaves;
ctx->peripherals->num_peripherals = num_slaves;
for (index = 0; index < ctx->count; index++) {
if (!(ctx->link_mask & BIT(index)))
continue;
@ -215,8 +217,7 @@ int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx)
if (amd_manager) {
bus = &amd_manager->bus;
list_for_each_entry(slave, &bus->slaves, node) {
ctx->ids[i].id = slave->id;
ctx->ids[i].link_id = bus->link_id;
ctx->peripherals->array[i] = slave;
i++;
}
}

View File

@ -433,12 +433,18 @@ static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_pa
u32 frame_fmt_reg, dpn_frame_fmt;
dev_dbg(amd_manager->dev, "p_params->num:0x%x\n", p_params->num);
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = sdw0_manager_dp_reg[p_params->num].frame_fmt_reg;
break;
case ACP_SDW1:
frame_fmt_reg = sdw1_manager_dp_reg[p_params->num].frame_fmt_reg;
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = acp63_sdw0_dp_reg[p_params->num].frame_fmt_reg;
break;
case ACP_SDW1:
frame_fmt_reg = acp63_sdw1_dp_reg[p_params->num].frame_fmt_reg;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@ -465,20 +471,28 @@ static int amd_sdw_transport_params(struct sdw_bus *bus,
u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
u32 offset_reg, lane_ctrl_ch_en_reg;
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = sdw0_manager_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = sdw0_manager_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = sdw0_manager_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = sdw0_manager_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
frame_fmt_reg = sdw1_manager_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = sdw1_manager_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = sdw1_manager_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = sdw1_manager_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = acp63_sdw0_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = acp63_sdw0_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = acp63_sdw0_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = acp63_sdw0_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg =
acp63_sdw0_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
frame_fmt_reg = acp63_sdw1_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = acp63_sdw1_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = acp63_sdw1_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = acp63_sdw1_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg =
acp63_sdw1_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@ -520,12 +534,20 @@ static int amd_sdw_port_enable(struct sdw_bus *bus,
u32 dpn_ch_enable;
u32 lane_ctrl_ch_en_reg;
switch (amd_manager->instance) {
case ACP_SDW0:
lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
switch (amd_manager->instance) {
case ACP_SDW0:
lane_ctrl_ch_en_reg =
acp63_sdw0_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
lane_ctrl_ch_en_reg =
acp63_sdw1_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@ -910,6 +932,7 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
amd_manager->mmio = amd_manager->acp_mmio +
(amd_manager->instance * SDW_MANAGER_REG_OFFSET);
amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
amd_manager->acp_rev = pdata->acp_rev;
amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
amd_manager->dev = dev;
@ -926,15 +949,21 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
* information.
*/
amd_manager->bus.controller_id = 0;
switch (amd_manager->instance) {
case ACP_SDW0:
amd_manager->num_dout_ports = AMD_SDW0_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
break;
case ACP_SDW1:
amd_manager->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
switch (amd_manager->instance) {
case ACP_SDW0:
amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
break;
case ACP_SDW1:
amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;

View File

@ -155,12 +155,12 @@
#define AMD_SDW_IRQ_MASK_8TO11 0x000c7777
#define AMD_SDW_IRQ_ERROR_MASK 0xff
#define AMD_SDW_MAX_FREQ_NUM 1
#define AMD_SDW0_MAX_TX_PORTS 3
#define AMD_SDW0_MAX_RX_PORTS 3
#define AMD_SDW1_MAX_TX_PORTS 1
#define AMD_SDW1_MAX_RX_PORTS 1
#define AMD_SDW0_MAX_DAI 6
#define AMD_SDW1_MAX_DAI 2
#define AMD_ACP63_SDW0_MAX_TX_PORTS 3
#define AMD_ACP63_SDW0_MAX_RX_PORTS 3
#define AMD_ACP63_SDW1_MAX_TX_PORTS 1
#define AMD_ACP63_SDW1_MAX_RX_PORTS 1
#define AMD_ACP63_SDW0_MAX_DAI 6
#define AMD_ACP63_SDW1_MAX_DAI 2
#define AMD_SDW_SLAVE_0_ATTACHED 5
#define AMD_SDW_SSP_COUNTER_VAL 3
@ -222,7 +222,7 @@ struct sdw_manager_dp_reg {
* in SoundWire DMA driver.
*/
static struct sdw_manager_dp_reg sdw0_manager_dp_reg[AMD_SDW0_MAX_DAI] = {
static struct sdw_manager_dp_reg acp63_sdw0_dp_reg[AMD_ACP63_SDW0_MAX_DAI] = {
{ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
@ -237,7 +237,7 @@ static struct sdw_manager_dp_reg sdw0_manager_dp_reg[AMD_SDW0_MAX_DAI] = {
ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
};
static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] = {
static struct sdw_manager_dp_reg acp63_sdw1_dp_reg[AMD_ACP63_SDW1_MAX_DAI] = {
{ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,

View File

@ -112,7 +112,7 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
/* Set higher order bits */
*bus->assigned = ~GENMASK(SDW_BROADCAST_DEV_NUM, SDW_ENUM_DEV_NUM);
/* Set enumuration device number and broadcast device number */
/* Set enumeration device number and broadcast device number */
set_bit(SDW_ENUM_DEV_NUM, bus->assigned);
set_bit(SDW_BROADCAST_DEV_NUM, bus->assigned);

View File

@ -1377,6 +1377,31 @@ static void cdns_init_clock_ctrl(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval);
}
/**
* sdw_cdns_soft_reset() - Cadence soft-reset
* @cdns: Cadence instance
*/
int sdw_cdns_soft_reset(struct sdw_cdns *cdns)
{
int ret;
cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_SOFT_RST,
CDNS_MCP_CONTROL_SOFT_RST);
ret = cdns_config_update(cdns);
if (ret < 0) {
dev_err(cdns->dev, "%s: config update failed\n", __func__);
return ret;
}
ret = cdns_set_wait(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_SOFT_RST, 0);
if (ret < 0)
dev_err(cdns->dev, "%s: Soft Reset timed out\n", __func__);
return ret;
}
EXPORT_SYMBOL(sdw_cdns_soft_reset);
/**
* sdw_cdns_init() - Cadence initialization
* @cdns: Cadence instance
@ -1400,6 +1425,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT,
CDNS_IP_MCP_CONTROL_CMD_ACCEPT);
/* disable wakeup */
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL,
CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP,
0);
/* Configure mcp config */
val = cdns_readl(cdns, CDNS_MCP_CONFIG);

View File

@ -168,6 +168,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns);
irqreturn_t sdw_cdns_irq(int irq, void *dev_id);
irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
int sdw_cdns_soft_reset(struct sdw_cdns *cdns);
int sdw_cdns_init(struct sdw_cdns *cdns);
int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config);

View File

@ -175,6 +175,9 @@ static int intel_link_power_up(struct sdw_intel *sdw)
__func__, ret);
goto out;
}
hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true,
AZX_REG_ML_LEPTR_ID_SDW, true);
}
*shim_mask |= BIT(link_id);
@ -201,6 +204,10 @@ static int intel_link_power_down(struct sdw_intel *sdw)
*shim_mask &= ~BIT(link_id);
if (!*shim_mask)
hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true,
AZX_REG_ML_LEPTR_ID_SDW, false);
ret = hdac_bus_eml_sdw_power_down_unlocked(sdw->link_res->hbus, link_id);
if (ret < 0) {
dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_down failed: %d\n",

View File

@ -16,6 +16,12 @@ int intel_start_bus(struct sdw_intel *sdw)
struct sdw_bus *bus = &cdns->bus;
int ret;
ret = sdw_cdns_soft_reset(cdns);
if (ret < 0) {
dev_err(dev, "%s: unable to soft-reset Cadence IP: %d\n", __func__, ret);
return ret;
}
/*
* follow recommended programming flows to avoid timeouts when
* gsync is enabled

View File

@ -252,17 +252,16 @@ static struct sdw_intel_ctx
num_slaves++;
}
ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
if (!ctx->ids)
ctx->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves),
GFP_KERNEL);
if (!ctx->peripherals)
goto err;
ctx->num_slaves = num_slaves;
ctx->peripherals->num_peripherals = num_slaves;
i = 0;
list_for_each_entry(link, &ctx->link_list, list) {
bus = &link->cdns->bus;
list_for_each_entry(slave, &bus->slaves, node) {
ctx->ids[i].id = slave->id;
ctx->ids[i].link_id = bus->link_id;
ctx->peripherals->array[i] = slave;
i++;
}
}
@ -371,7 +370,7 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx)
}
sdw_intel_cleanup(ctx);
kfree(ctx->ids);
kfree(ctx->peripherals);
kfree(ctx->ldev);
kfree(ctx);
}

View File

@ -23,6 +23,26 @@
#include <linux/soundwire/sdw.h>
#include "bus.h"
static bool mipi_fwnode_property_read_bool(const struct fwnode_handle *fwnode,
const char *propname)
{
int ret;
u8 val;
if (!fwnode_property_present(fwnode, propname))
return false;
ret = fwnode_property_read_u8_array(fwnode, propname, &val, 1);
if (ret < 0)
return false;
return !!val;
}
static bool mipi_device_property_read_bool(const struct device *dev,
const char *propname)
{
return mipi_fwnode_property_read_bool(dev_fwnode(dev), propname);
}
/**
* sdw_master_read_prop() - Read Master properties
* @bus: SDW bus instance
@ -31,8 +51,11 @@ int sdw_master_read_prop(struct sdw_bus *bus)
{
struct sdw_master_prop *prop = &bus->prop;
struct fwnode_handle *link;
const char *scales_prop;
char name[32];
int nval, i;
int nval;
int ret;
int i;
device_property_read_u32(bus->dev,
"mipi-sdw-sw-interface-revision",
@ -48,11 +71,11 @@ int sdw_master_read_prop(struct sdw_bus *bus)
return -EIO;
}
if (fwnode_property_read_bool(link,
if (mipi_fwnode_property_read_bool(link,
"mipi-sdw-clock-stop-mode0-supported"))
prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE0);
if (fwnode_property_read_bool(link,
if (mipi_fwnode_property_read_bool(link,
"mipi-sdw-clock-stop-mode1-supported"))
prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE1);
@ -71,9 +94,11 @@ int sdw_master_read_prop(struct sdw_bus *bus)
return -ENOMEM;
}
fwnode_property_read_u32_array(link,
ret = fwnode_property_read_u32_array(link,
"mipi-sdw-clock-frequencies-supported",
prop->clk_freq, prop->num_clk_freq);
if (ret < 0)
return ret;
}
/*
@ -88,7 +113,12 @@ int sdw_master_read_prop(struct sdw_bus *bus)
}
}
nval = fwnode_property_count_u32(link, "mipi-sdw-supported-clock-gears");
scales_prop = "mipi-sdw-supported-clock-scales";
nval = fwnode_property_count_u32(link, scales_prop);
if (nval == 0) {
scales_prop = "mipi-sdw-supported-clock-gears";
nval = fwnode_property_count_u32(link, scales_prop);
}
if (nval > 0) {
prop->num_clk_gears = nval;
prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears,
@ -99,10 +129,12 @@ int sdw_master_read_prop(struct sdw_bus *bus)
return -ENOMEM;
}
fwnode_property_read_u32_array(link,
"mipi-sdw-supported-clock-gears",
ret = fwnode_property_read_u32_array(link,
scales_prop,
prop->clk_gears,
prop->num_clk_gears);
if (ret < 0)
return ret;
}
fwnode_property_read_u32(link, "mipi-sdw-default-frame-rate",
@ -114,7 +146,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
fwnode_property_read_u32(link, "mipi-sdw-default-frame-col-size",
&prop->default_col);
prop->dynamic_frame = fwnode_property_read_bool(link,
prop->dynamic_frame = mipi_fwnode_property_read_bool(link,
"mipi-sdw-dynamic-frame-shape");
fwnode_property_read_u32(link, "mipi-sdw-command-error-threshold",
@ -131,6 +163,7 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave,
struct sdw_dp0_prop *dp0)
{
int nval;
int ret;
fwnode_property_read_u32(port, "mipi-sdw-port-max-wordlength",
&dp0->max_word);
@ -148,20 +181,38 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave,
if (!dp0->words)
return -ENOMEM;
fwnode_property_read_u32_array(port,
ret = fwnode_property_read_u32_array(port,
"mipi-sdw-port-wordlength-configs",
dp0->words, dp0->num_words);
if (ret < 0)
return ret;
}
dp0->BRA_flow_controlled = fwnode_property_read_bool(port,
dp0->BRA_flow_controlled = mipi_fwnode_property_read_bool(port,
"mipi-sdw-bra-flow-controlled");
dp0->simple_ch_prep_sm = fwnode_property_read_bool(port,
dp0->simple_ch_prep_sm = mipi_fwnode_property_read_bool(port,
"mipi-sdw-simplified-channel-prepare-sm");
dp0->imp_def_interrupts = fwnode_property_read_bool(port,
dp0->imp_def_interrupts = mipi_fwnode_property_read_bool(port,
"mipi-sdw-imp-def-dp0-interrupts-supported");
nval = fwnode_property_count_u32(port, "mipi-sdw-lane-list");
if (nval > 0) {
dp0->num_lanes = nval;
dp0->lane_list = devm_kcalloc(&slave->dev,
dp0->num_lanes, sizeof(*dp0->lane_list),
GFP_KERNEL);
if (!dp0->lane_list)
return -ENOMEM;
ret = fwnode_property_read_u32_array(port,
"mipi-sdw-lane-list",
dp0->lane_list, dp0->num_lanes);
if (ret < 0)
return ret;
}
return 0;
}
@ -171,9 +222,10 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
{
struct fwnode_handle *node;
u32 bit, i = 0;
int nval;
unsigned long addr;
char name[40];
int nval;
int ret;
addr = ports;
/* valid ports are 1 to 14 so apply mask */
@ -208,9 +260,11 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
return -ENOMEM;
}
fwnode_property_read_u32_array(node,
ret = fwnode_property_read_u32_array(node,
"mipi-sdw-port-wordlength-configs",
dpn[i].words, dpn[i].num_words);
if (ret < 0)
return ret;
}
fwnode_property_read_u32(node, "mipi-sdw-data-port-type",
@ -220,7 +274,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
"mipi-sdw-max-grouping-supported",
&dpn[i].max_grouping);
dpn[i].simple_ch_prep_sm = fwnode_property_read_bool(node,
dpn[i].simple_ch_prep_sm = mipi_fwnode_property_read_bool(node,
"mipi-sdw-simplified-channelprepare-sm");
fwnode_property_read_u32(node,
@ -249,9 +303,11 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
return -ENOMEM;
}
fwnode_property_read_u32_array(node,
ret = fwnode_property_read_u32_array(node,
"mipi-sdw-channel-number-list",
dpn[i].channels, dpn[i].num_channels);
if (ret < 0)
return ret;
}
nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list");
@ -266,10 +322,12 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
return -ENOMEM;
}
fwnode_property_read_u32_array(node,
ret = fwnode_property_read_u32_array(node,
"mipi-sdw-channel-combination-list",
dpn[i].ch_combinations,
dpn[i].num_ch_combinations);
if (ret < 0)
return ret;
}
fwnode_property_read_u32(node,
@ -278,13 +336,27 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
fwnode_property_read_u32(node, "mipi-sdw-max-async-buffer",
&dpn[i].max_async_buffer);
dpn[i].block_pack_mode = fwnode_property_read_bool(node,
dpn[i].block_pack_mode = mipi_fwnode_property_read_bool(node,
"mipi-sdw-block-packing-mode");
fwnode_property_read_u32(node, "mipi-sdw-port-encoding-type",
&dpn[i].port_encoding);
/* TODO: Read audio mode */
nval = fwnode_property_count_u32(node, "mipi-sdw-lane-list");
if (nval > 0) {
dpn[i].num_lanes = nval;
dpn[i].lane_list = devm_kcalloc(&slave->dev,
dpn[i].num_lanes, sizeof(*dpn[i].lane_list),
GFP_KERNEL);
if (!dpn[i].lane_list)
return -ENOMEM;
ret = fwnode_property_read_u32_array(node,
"mipi-sdw-lane-list",
dpn[i].lane_list, dpn[i].num_lanes);
if (ret < 0)
return ret;
}
fwnode_handle_put(node);
@ -304,42 +376,46 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
struct device *dev = &slave->dev;
struct fwnode_handle *port;
int nval;
int ret;
device_property_read_u32(dev, "mipi-sdw-sw-interface-revision",
&prop->mipi_revision);
prop->wake_capable = device_property_read_bool(dev,
prop->wake_capable = mipi_device_property_read_bool(dev,
"mipi-sdw-wake-up-unavailable");
prop->wake_capable = !prop->wake_capable;
prop->test_mode_capable = device_property_read_bool(dev,
prop->test_mode_capable = mipi_device_property_read_bool(dev,
"mipi-sdw-test-mode-supported");
prop->clk_stop_mode1 = false;
if (device_property_read_bool(dev,
if (mipi_device_property_read_bool(dev,
"mipi-sdw-clock-stop-mode1-supported"))
prop->clk_stop_mode1 = true;
prop->simple_clk_stop_capable = device_property_read_bool(dev,
prop->simple_clk_stop_capable = mipi_device_property_read_bool(dev,
"mipi-sdw-simplified-clockstopprepare-sm-supported");
device_property_read_u32(dev, "mipi-sdw-clockstopprepare-timeout",
&prop->clk_stop_timeout);
device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout",
&prop->ch_prep_timeout);
ret = device_property_read_u32(dev, "mipi-sdw-peripheral-channelprepare-timeout",
&prop->ch_prep_timeout);
if (ret < 0)
device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout",
&prop->ch_prep_timeout);
device_property_read_u32(dev,
"mipi-sdw-clockstopprepare-hard-reset-behavior",
&prop->reset_behave);
prop->high_PHY_capable = device_property_read_bool(dev,
prop->high_PHY_capable = mipi_device_property_read_bool(dev,
"mipi-sdw-highPHY-capable");
prop->paging_support = device_property_read_bool(dev,
prop->paging_support = mipi_device_property_read_bool(dev,
"mipi-sdw-paging-support");
prop->bank_delay_support = device_property_read_bool(dev,
prop->bank_delay_support = mipi_device_property_read_bool(dev,
"mipi-sdw-bank-delay-support");
device_property_read_u32(dev,
@ -354,7 +430,17 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
device_property_read_u32(dev, "mipi-sdw-sink-port-list",
&prop->sink_ports);
/* Read dp0 properties */
device_property_read_u32(dev, "mipi-sdw-sdca-interrupt-register-list",
&prop->sdca_interrupt_register_list);
prop->commit_register_supported = mipi_device_property_read_bool(dev,
"mipi-sdw-commit-register-supported");
/*
* Read dp0 properties - we don't rely on the 'mipi-sdw-dp-0-supported'
* property since the 'mipi-sdw-dp0-subproperties' property is logically
* equivalent.
*/
port = device_get_named_child_node(dev, "mipi-sdw-dp-0-subproperties");
if (!port) {
dev_dbg(dev, "DP0 node not found!!\n");

View File

@ -1173,7 +1173,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl,
else
sconfig.direction = SDW_DATA_DIR_RX;
/* hw parameters wil be ignored as we only support PDM */
/* hw parameters will be ignored as we only support PDM */
sconfig.ch_count = 1;
sconfig.frame_rate = params_rate(params);
sconfig.type = stream->type;

View File

@ -5,6 +5,7 @@
#include <linux/of.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/sdca.h>
#include "bus.h"
#include "sysfs_local.h"
@ -70,6 +71,17 @@ int sdw_slave_add(struct sdw_bus *bus,
list_add_tail(&slave->node, &bus->slaves);
mutex_unlock(&bus->bus_lock);
/*
* The Soundwire driver probe may optionally register SDCA
* sub-devices, one per Function. This means the information
* on the SDCA revision and the number/type of Functions need
* to be extracted from platform firmware before the SoundWire
* driver probe, and as a consequence before the SoundWire
* device_register() below.
*/
sdca_lookup_interface_revision(slave);
sdca_lookup_functions(slave);
ret = device_register(&slave->dev);
if (ret) {
dev_err(bus->dev, "Failed to add slave: ret %d\n", ret);
@ -259,3 +271,5 @@ int sdw_of_find_slaves(struct sdw_bus *bus)
return 0;
}
MODULE_IMPORT_NS(SND_SOC_SDCA);

View File

@ -224,7 +224,7 @@ int sdw_slave_sysfs_init(struct sdw_slave *slave)
/*
* the status is shown in capital letters for UNATTACHED and RESERVED
* on purpose, to highligh users to the fact that these status values
* on purpose, to highlight users to the fact that these status values
* are not expected.
*/
static const char *const slave_status[] = {

View File

@ -4,12 +4,21 @@
#ifndef __SOUNDWIRE_H
#define __SOUNDWIRE_H
#include <linux/bitfield.h>
#include <linux/bug.h>
#include <linux/lockdep_types.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/lockdep_types.h>
#include <linux/mod_devicetable.h>
#include <linux/bitfield.h>
#include <sound/sdca.h>
#include <linux/mutex.h>
#include <linux/types.h>
struct dentry;
struct fwnode_handle;
struct sdw_bus;
struct sdw_slave;
@ -226,64 +235,36 @@ enum sdw_clk_stop_mode {
/**
* struct sdw_dp0_prop - DP0 properties
* @words: wordlengths supported
* @max_word: Maximum number of bits in a Payload Channel Sample, 1 to 64
* (inclusive)
* @min_word: Minimum number of bits in a Payload Channel Sample, 1 to 64
* (inclusive)
* @num_words: number of wordlengths supported
* @words: wordlengths supported
* @ch_prep_timeout: Port-specific timeout value, in milliseconds
* @BRA_flow_controlled: Slave implementation results in an OK_NotReady
* response
* @simple_ch_prep_sm: If channel prepare sequence is required
* @ch_prep_timeout: Port-specific timeout value, in milliseconds
* @imp_def_interrupts: If set, each bit corresponds to support for
* implementation-defined interrupts
* @num_lanes: array size of @lane_list
* @lane_list: indicates which Lanes can be used by DP0
*
* The wordlengths are specified by Spec as max, min AND number of
* discrete values, implementation can define based on the wordlengths they
* support
*/
struct sdw_dp0_prop {
u32 *words;
u32 max_word;
u32 min_word;
u32 num_words;
u32 *words;
u32 ch_prep_timeout;
bool BRA_flow_controlled;
bool simple_ch_prep_sm;
u32 ch_prep_timeout;
bool imp_def_interrupts;
};
/**
* struct sdw_dpn_audio_mode - Audio mode properties for DPn
* @bus_min_freq: Minimum bus frequency, in Hz
* @bus_max_freq: Maximum bus frequency, in Hz
* @bus_num_freq: Number of discrete frequencies supported
* @bus_freq: Discrete bus frequencies, in Hz
* @min_freq: Minimum sampling frequency, in Hz
* @max_freq: Maximum sampling bus frequency, in Hz
* @num_freq: Number of discrete sampling frequency supported
* @freq: Discrete sampling frequencies, in Hz
* @prep_ch_behave: Specifies the dependencies between Channel Prepare
* sequence and bus clock configuration
* If 0, Channel Prepare can happen at any Bus clock rate
* If 1, Channel Prepare sequence shall happen only after Bus clock is
* changed to a frequency supported by this mode or compatible modes
* described by the next field
* @glitchless: Bitmap describing possible glitchless transitions from this
* Audio Mode to other Audio Modes
*/
struct sdw_dpn_audio_mode {
u32 bus_min_freq;
u32 bus_max_freq;
u32 bus_num_freq;
u32 *bus_freq;
u32 max_freq;
u32 min_freq;
u32 num_freq;
u32 *freq;
u32 prep_ch_behave;
u32 glitchless;
int num_lanes;
u32 *lane_list;
};
/**
@ -298,24 +279,25 @@ struct sdw_dpn_audio_mode {
* @type: Data port type. Full, Simplified or Reduced
* @max_grouping: Maximum number of samples that can be grouped together for
* a full data port
* @simple_ch_prep_sm: If the port supports simplified channel prepare state
* machine
* @ch_prep_timeout: Port-specific timeout value, in milliseconds
* @imp_def_interrupts: If set, each bit corresponds to support for
* implementation-defined interrupts
* @max_ch: Maximum channels supported
* @min_ch: Minimum channels supported
* @num_channels: Number of discrete channels supported
* @channels: Discrete channels supported
* @num_ch_combinations: Number of channel combinations supported
* @channels: Discrete channels supported
* @ch_combinations: Channel combinations supported
* @lane_list: indicates which Lanes can be used by DPn
* @num_lanes: array size of @lane_list
* @modes: SDW mode supported
* @max_async_buffer: Number of samples that this port can buffer in
* asynchronous modes
* @port_encoding: Payload Channel Sample encoding schemes supported
* @block_pack_mode: Type of block port mode supported
* @read_only_wordlength: Read Only wordlength field in DPN_BlockCtrl1 register
* @port_encoding: Payload Channel Sample encoding schemes supported
* @audio_modes: Audio modes supported
* @simple_ch_prep_sm: If the port supports simplified channel prepare state
* machine
*/
struct sdw_dpn_prop {
u32 num;
@ -325,25 +307,29 @@ struct sdw_dpn_prop {
u32 *words;
enum sdw_dpn_type type;
u32 max_grouping;
bool simple_ch_prep_sm;
u32 ch_prep_timeout;
u32 imp_def_interrupts;
u32 max_ch;
u32 min_ch;
u32 num_channels;
u32 *channels;
u32 num_ch_combinations;
u32 *channels;
u32 *ch_combinations;
u32 *lane_list;
int num_lanes;
u32 modes;
u32 max_async_buffer;
u32 port_encoding;
bool block_pack_mode;
bool read_only_wordlength;
u32 port_encoding;
struct sdw_dpn_audio_mode *audio_modes;
bool simple_ch_prep_sm;
};
/**
* struct sdw_slave_prop - SoundWire Slave properties
* @dp0_prop: Data Port 0 properties
* @src_dpn_prop: Source Data Port N properties
* @sink_dpn_prop: Sink Data Port N properties
* @mipi_revision: Spec version of the implementation
* @wake_capable: Wake-up events are supported
* @test_mode_capable: If test mode is supported
@ -360,23 +346,26 @@ struct sdw_dpn_prop {
* SCP_AddrPage2
* @bank_delay_support: Slave implements bank delay/bridge support registers
* SCP_BankDelay and SCP_NextFrame
* @lane_control_support: Slave supports lane control
* @p15_behave: Slave behavior when the Master attempts a read to the Port15
* alias
* @lane_control_support: Slave supports lane control
* @master_count: Number of Masters present on this Slave
* @source_ports: Bitmap identifying source ports
* @sink_ports: Bitmap identifying sink ports
* @dp0_prop: Data Port 0 properties
* @src_dpn_prop: Source Data Port N properties
* @sink_dpn_prop: Sink Data Port N properties
* @scp_int1_mask: SCP_INT1_MASK desired settings
* @quirks: bitmask identifying deltas from the MIPI specification
* @sdca_interrupt_register_list: indicates which sets of SDCA interrupt status
* and masks are supported
* @commit_register_supported: is PCP_Commit register supported
* @scp_int1_mask: SCP_INT1_MASK desired settings
* @clock_reg_supported: the Peripheral implements the clock base and scale
* registers introduced with the SoundWire 1.2 specification. SDCA devices
* do not need to set this boolean property as the registers are required.
* @use_domain_irq: call actual IRQ handler on slave, as well as callback
*/
struct sdw_slave_prop {
struct sdw_dp0_prop *dp0_prop;
struct sdw_dpn_prop *src_dpn_prop;
struct sdw_dpn_prop *sink_dpn_prop;
u32 mipi_revision;
bool wake_capable;
bool test_mode_capable;
@ -388,16 +377,15 @@ struct sdw_slave_prop {
bool high_PHY_capable;
bool paging_support;
bool bank_delay_support;
enum sdw_p15_behave p15_behave;
bool lane_control_support;
enum sdw_p15_behave p15_behave;
u32 master_count;
u32 source_ports;
u32 sink_ports;
struct sdw_dp0_prop *dp0_prop;
struct sdw_dpn_prop *src_dpn_prop;
struct sdw_dpn_prop *sink_dpn_prop;
u8 scp_int1_mask;
u32 quirks;
u32 sdca_interrupt_register_list;
u8 commit_register_supported;
u8 scp_int1_mask;
bool clock_reg_supported;
bool use_domain_irq;
};
@ -406,13 +394,14 @@ struct sdw_slave_prop {
/**
* struct sdw_master_prop - Master properties
* @clk_gears: Clock gears supported
* @clk_freq: Clock frequencies supported, in Hz
* @quirks: bitmask identifying optional behavior beyond the scope of the MIPI specification
* @revision: MIPI spec version of the implementation
* @clk_stop_modes: Bitmap, bit N set when clock-stop-modeN supported
* @max_clk_freq: Maximum Bus clock frequency, in Hz
* @num_clk_gears: Number of clock gears supported
* @clk_gears: Clock gears supported
* @num_clk_freq: Number of clock frequencies supported, in Hz
* @clk_freq: Clock frequencies supported, in Hz
* @default_frame_rate: Controller default Frame rate, in Hz
* @default_row: Number of rows
* @default_col: Number of columns
@ -421,24 +410,23 @@ struct sdw_slave_prop {
* command
* @mclk_freq: clock reference passed to SoundWire Master, in Hz.
* @hw_disabled: if true, the Master is not functional, typically due to pin-mux
* @quirks: bitmask identifying optional behavior beyond the scope of the MIPI specification
*/
struct sdw_master_prop {
u32 *clk_gears;
u32 *clk_freq;
u64 quirks;
u32 revision;
u32 clk_stop_modes;
u32 max_clk_freq;
u32 num_clk_gears;
u32 *clk_gears;
u32 num_clk_freq;
u32 *clk_freq;
u32 default_frame_rate;
u32 default_row;
u32 default_col;
bool dynamic_frame;
u32 err_threshold;
u32 mclk_freq;
bool dynamic_frame;
bool hw_disabled;
u64 quirks;
};
/* Definitions for Master quirks */
@ -488,9 +476,9 @@ struct sdw_slave_id {
__u8 sdw_version:4;
};
struct sdw_extended_slave_id {
int link_id;
struct sdw_slave_id id;
struct sdw_peripherals {
int num_peripherals;
struct sdw_slave *array[];
};
/*
@ -630,7 +618,6 @@ struct sdw_slave_ops {
int (*clk_stop)(struct sdw_slave *slave,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type);
};
/**
@ -663,6 +650,7 @@ struct sdw_slave_ops {
* @is_mockup_device: status flag used to squelch errors in the command/control
* protocol for SoundWire mockup devices
* @sdw_dev_lock: mutex used to protect callbacks/remove races
* @sdca_data: structure containing all device data for SDCA helpers
*/
struct sdw_slave {
struct sdw_slave_id id;
@ -686,6 +674,7 @@ struct sdw_slave {
bool first_interrupt_done;
bool is_mockup_device;
struct mutex sdw_dev_lock; /* protect callbacks/remove races */
struct sdca_device_data sdca_data;
};
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
@ -704,8 +693,7 @@ struct sdw_master_device {
container_of(d, struct sdw_master_device, dev)
struct sdw_driver {
int (*probe)(struct sdw_slave *sdw,
const struct sdw_device_id *id);
int (*probe)(struct sdw_slave *sdw, const struct sdw_device_id *id);
int (*remove)(struct sdw_slave *sdw);
void (*shutdown)(struct sdw_slave *sdw);
@ -724,7 +712,7 @@ struct sdw_driver {
SDW_SLAVE_ENTRY_EXT((_mfg_id), (_part_id), 0, 0, (_drv_data))
int sdw_handle_slave_status(struct sdw_bus *bus,
enum sdw_slave_status status[]);
enum sdw_slave_status status[]);
/*
* SDW master structures and APIs
@ -806,29 +794,28 @@ struct sdw_enable_ch {
*/
struct sdw_master_port_ops {
int (*dpn_set_port_params)(struct sdw_bus *bus,
struct sdw_port_params *port_params,
unsigned int bank);
struct sdw_port_params *port_params,
unsigned int bank);
int (*dpn_set_port_transport_params)(struct sdw_bus *bus,
struct sdw_transport_params *transport_params,
enum sdw_reg_bank bank);
int (*dpn_port_prep)(struct sdw_bus *bus,
struct sdw_prepare_ch *prepare_ch);
struct sdw_transport_params *transport_params,
enum sdw_reg_bank bank);
int (*dpn_port_prep)(struct sdw_bus *bus, struct sdw_prepare_ch *prepare_ch);
int (*dpn_port_enable_ch)(struct sdw_bus *bus,
struct sdw_enable_ch *enable_ch, unsigned int bank);
struct sdw_enable_ch *enable_ch, unsigned int bank);
};
struct sdw_msg;
/**
* struct sdw_defer - SDW deffered message
* @length: message length
* struct sdw_defer - SDW deferred message
* @complete: message completion
* @msg: SDW message
* @length: message length
*/
struct sdw_defer {
struct sdw_msg *msg;
int length;
struct completion complete;
struct sdw_msg *msg;
};
/**
@ -849,14 +836,11 @@ struct sdw_defer {
*/
struct sdw_master_ops {
int (*read_prop)(struct sdw_bus *bus);
u64 (*override_adr)
(struct sdw_bus *bus, u64 addr);
enum sdw_command_response (*xfer_msg)
(struct sdw_bus *bus, struct sdw_msg *msg);
enum sdw_command_response (*xfer_msg_defer)
(struct sdw_bus *bus);
u64 (*override_adr)(struct sdw_bus *bus, u64 addr);
enum sdw_command_response (*xfer_msg)(struct sdw_bus *bus, struct sdw_msg *msg);
enum sdw_command_response (*xfer_msg_defer)(struct sdw_bus *bus);
int (*set_bus_conf)(struct sdw_bus *bus,
struct sdw_bus_params *params);
struct sdw_bus_params *params);
int (*pre_bank_switch)(struct sdw_bus *bus);
int (*post_bank_switch)(struct sdw_bus *bus);
u32 (*read_ping_status)(struct sdw_bus *bus);
@ -871,68 +855,71 @@ struct sdw_master_ops {
* struct sdw_bus - SoundWire bus
* @dev: Shortcut to &bus->md->dev to avoid changing the entire code.
* @md: Master device
* @controller_id: system-unique controller ID. If set to -1, the bus @id will be used.
* @link_id: Link id number, can be 0 to N, unique for each Controller
* @id: bus system-wide unique id
* @slaves: list of Slaves on this bus
* @assigned: Bitmap for Slave device numbers.
* Bit set implies used number, bit clear implies unused number.
* @bus_lock_key: bus lock key associated to @bus_lock
* @bus_lock: bus lock
* @slaves: list of Slaves on this bus
* @msg_lock_key: message lock key associated to @msg_lock
* @msg_lock: message lock
* @compute_params: points to Bus resource management implementation
* @ops: Master callback ops
* @port_ops: Master port callback ops
* @params: Current bus parameters
* @prop: Master properties
* @vendor_specific_prop: pointer to non-standard properties
* @m_rt_list: List of Master instance of all stream(s) running on Bus. This
* is used to compute and program bus bandwidth, clock, frame shape,
* transport and port parameters
* @debugfs: Bus debugfs
* @domain: IRQ domain
* @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed
* @multi_link: Store bus property that indicates if multi links
* are supported. This flag is populated by drivers after reading
* appropriate firmware (ACPI/DT).
* @params: Current bus parameters
* @stream_refcount: number of streams currently using this bus
* @ops: Master callback ops
* @port_ops: Master port callback ops
* @prop: Master properties
* @vendor_specific_prop: pointer to non-standard properties
* @hw_sync_min_links: Number of links used by a stream above which
* hardware-based synchronization is required. This value is only
* meaningful if multi_link is set. If set to 1, hardware-based
* synchronization will be used even if a stream only uses a single
* SoundWire segment.
* @stream_refcount: number of streams currently using this bus
* @controller_id: system-unique controller ID. If set to -1, the bus @id will be used.
* @link_id: Link id number, can be 0 to N, unique for each Controller
* @id: bus system-wide unique id
* @compute_params: points to Bus resource management implementation
* @assigned: Bitmap for Slave device numbers.
* Bit set implies used number, bit clear implies unused number.
* @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed
* @domain: IRQ domain
* @irq_chip: IRQ chip
* @debugfs: Bus debugfs (optional)
* @multi_link: Store bus property that indicates if multi links
* are supported. This flag is populated by drivers after reading
* appropriate firmware (ACPI/DT).
*/
struct sdw_bus {
struct device *dev;
struct sdw_master_device *md;
struct lock_class_key bus_lock_key;
struct mutex bus_lock;
struct list_head slaves;
struct lock_class_key msg_lock_key;
struct mutex msg_lock;
struct list_head m_rt_list;
struct sdw_defer defer_msg;
struct sdw_bus_params params;
int stream_refcount;
const struct sdw_master_ops *ops;
const struct sdw_master_port_ops *port_ops;
struct sdw_master_prop prop;
void *vendor_specific_prop;
int hw_sync_min_links;
int controller_id;
unsigned int link_id;
int id;
struct list_head slaves;
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
struct mutex bus_lock;
struct lock_class_key bus_lock_key;
struct mutex msg_lock;
struct lock_class_key msg_lock_key;
int (*compute_params)(struct sdw_bus *bus);
const struct sdw_master_ops *ops;
const struct sdw_master_port_ops *port_ops;
struct sdw_bus_params params;
struct sdw_master_prop prop;
void *vendor_specific_prop;
struct list_head m_rt_list;
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
unsigned int clk_stop_timeout;
u32 bank_switch_timeout;
struct irq_chip irq_chip;
struct irq_domain *domain;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct irq_chip irq_chip;
struct irq_domain *domain;
struct sdw_defer defer_msg;
unsigned int clk_stop_timeout;
u32 bank_switch_timeout;
bool multi_link;
int hw_sync_min_links;
int stream_refcount;
};
int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
@ -1010,18 +997,18 @@ struct sdw_stream_params {
* @params: Stream parameters
* @state: Current state of the stream
* @type: Stream type PCM or PDM
* @m_rt_count: Count of Master runtime(s) in this stream
* @master_list: List of Master runtime(s) in this stream.
* master_list can contain only one m_rt per Master instance
* for a stream
* @m_rt_count: Count of Master runtime(s) in this stream
*/
struct sdw_stream_runtime {
const char *name;
struct sdw_stream_params params;
enum sdw_stream_state state;
enum sdw_stream_type type;
struct list_head master_list;
int m_rt_count;
struct list_head master_list;
};
struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name);
@ -1030,12 +1017,12 @@ void sdw_release_stream(struct sdw_stream_runtime *stream);
int sdw_compute_params(struct sdw_bus *bus);
int sdw_stream_add_master(struct sdw_bus *bus,
struct sdw_stream_config *stream_config,
const struct sdw_port_config *port_config,
unsigned int num_ports,
struct sdw_stream_runtime *stream);
struct sdw_stream_config *stream_config,
const struct sdw_port_config *port_config,
unsigned int num_ports,
struct sdw_stream_runtime *stream);
int sdw_stream_remove_master(struct sdw_bus *bus,
struct sdw_stream_runtime *stream);
struct sdw_stream_runtime *stream);
int sdw_startup_stream(void *sdw_substream);
int sdw_prepare_stream(struct sdw_stream_runtime *stream);
int sdw_enable_stream(struct sdw_stream_runtime *stream);

View File

@ -27,9 +27,11 @@
#define ACP_SDW0 0
#define ACP_SDW1 1
#define AMD_SDW_MAX_MANAGER_COUNT 2
#define ACP63_PCI_REV_ID 0x63
struct acp_sdw_pdata {
u16 instance;
u32 acp_rev;
/* mutex to protect acp common register access */
struct mutex *acp_sdw_lock;
};
@ -66,6 +68,7 @@ struct sdw_amd_dai_runtime {
* @instance: SoundWire manager instance
* @quirks: SoundWire manager quirks
* @wake_en_mask: wake enable mask per SoundWire manager
* @acp_rev: acp pci device revision id
* @clk_stopped: flag set to true when clock is stopped
* @power_mode_mask: flag interprets amd SoundWire manager power mode
* @dai_runtime_array: dai runtime array
@ -94,6 +97,7 @@ struct amd_sdw_manager {
u32 quirks;
u32 wake_en_mask;
u32 power_mode_mask;
u32 acp_rev;
bool clk_stopped;
struct sdw_amd_dai_runtime **dai_runtime_array;
@ -115,25 +119,23 @@ struct sdw_amd_acpi_info {
* struct sdw_amd_ctx - context allocated by the controller driver probe
*
* @count: link count
* @num_slaves: total number of devices exposed across all enabled links
* @link_mask: bit-wise mask listing SoundWire links reported by the
* Controller
* @ids: array of slave_id, representing Slaves exposed across all enabled
* links
* @pdev: platform device structure
* @peripherals: array representing Peripherals exposed across all enabled links
*/
struct sdw_amd_ctx {
int count;
int num_slaves;
u32 link_mask;
struct sdw_extended_slave_id *ids;
struct platform_device *pdev[AMD_SDW_MAX_MANAGER_COUNT];
struct sdw_peripherals *peripherals;
};
/**
* struct sdw_amd_res - Soundwire AMD global resource structure,
* typically populated by the DSP driver/Legacy driver
*
* @acp_rev: acp pci device revision id
* @addr: acp pci device resource start address
* @reg_range: ACP register range
* @link_mask: bit-wise mask listing links selected by the DSP driver/
@ -146,6 +148,7 @@ struct sdw_amd_ctx {
* @acp_lock: mutex protecting acp common registers access
*/
struct sdw_amd_res {
u32 acp_rev;
u32 addr;
u32 reg_range;
u32 link_mask;

View File

@ -4,6 +4,7 @@
#ifndef __SDW_INTEL_H
#define __SDW_INTEL_H
#include <linux/acpi.h>
#include <linux/irqreturn.h>
#include <linux/soundwire/sdw.h>
@ -286,31 +287,28 @@ struct hdac_bus;
* hardware capabilities after all power dependencies are settled.
* @link_mask: bit-wise mask listing SoundWire links reported by the
* Controller
* @num_slaves: total number of devices exposed across all enabled links
* @handle: ACPI parent handle
* @ldev: information for each link (controller-specific and kept
* opaque here)
* @ids: array of slave_id, representing Slaves exposed across all enabled
* links
* @link_list: list to handle interrupts across all links
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
* @shim_mask: flags to track initialization of SHIM shared registers
* @shim_base: sdw shim base.
* @alh_base: sdw alh base.
* @peripherals: array representing Peripherals exposed across all enabled links
*/
struct sdw_intel_ctx {
int count;
void __iomem *mmio_base;
u32 link_mask;
int num_slaves;
acpi_handle handle;
struct sdw_intel_link_dev **ldev;
struct sdw_extended_slave_id *ids;
struct list_head link_list;
struct mutex shim_lock; /* lock for access to shared SHIM registers */
u32 shim_mask;
u32 shim_base;
u32 alh_base;
struct sdw_peripherals *peripherals;
};
/**

View File

@ -1,33 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Analog Devices ADAU1373 Audio Codec drive
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#ifndef __SOUND_ADAU1373_H__
#define __SOUND_ADAU1373_H__
enum adau1373_micbias_voltage {
ADAU1373_MICBIAS_2_9V = 0,
ADAU1373_MICBIAS_2_2V = 1,
ADAU1373_MICBIAS_2_6V = 2,
ADAU1373_MICBIAS_1_8V = 3,
};
#define ADAU1373_DRC_SIZE 13
struct adau1373_platform_data {
bool input_differential[4];
bool lineout_differential;
bool lineout_ground_sense;
unsigned int num_drc;
uint8_t drc_setting[3][ADAU1373_DRC_SIZE];
enum adau1373_micbias_voltage micbias1;
enum adau1373_micbias_voltage micbias2;
};
#endif

View File

@ -19,6 +19,30 @@
struct snd_compr_ops;
/**
* struct snd_compr_task_runtime: task runtime description
* @list: list of all managed tasks
* @input: input DMA buffer
* @output: output DMA buffer
* @seqno: sequence number
* @input_size: really used data in the input buffer
* @output_size: really used data in the output buffer
* @flags: see SND_COMPRESS_TFLG_*
* @state: actual task state
* @private_value: used by the lowlevel driver (opaque)
*/
struct snd_compr_task_runtime {
struct list_head list;
struct dma_buf *input;
struct dma_buf *output;
u64 seqno;
u64 input_size;
u64 output_size;
u32 flags;
u8 state;
void *private_value;
};
/**
* struct snd_compr_runtime: runtime stream description
* @state: stream state
@ -37,6 +61,10 @@ struct snd_compr_ops;
* @dma_addr: physical buffer address (not accessible from main CPU)
* @dma_bytes: size of DMA area
* @dma_buffer_p: runtime dma buffer pointer
* @active_tasks: count of active tasks
* @total_tasks: count of all tasks
* @task_seqno: last task sequence number (!= 0)
* @tasks: list of all tasks
*/
struct snd_compr_runtime {
snd_pcm_state_t state;
@ -54,6 +82,13 @@ struct snd_compr_runtime {
dma_addr_t dma_addr;
size_t dma_bytes;
struct snd_dma_buffer *dma_buffer_p;
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
u32 active_tasks;
u32 total_tasks;
u64 task_seqno;
struct list_head tasks;
#endif
};
/**
@ -108,6 +143,10 @@ struct snd_compr_stream {
* Not valid if copy is implemented
* @get_caps: Retrieve DSP capabilities, mandatory
* @get_codec_caps: Retrieve capabilities for a specific codec, mandatory
* @task_create: Create a set of input/output buffers for accel operations
* @task_start: Start (queue) a task for accel operations
* @task_stop: Stop (dequeue) a task for accel operations
* @task_free: Free a set of input/output buffers for accel operations
*/
struct snd_compr_ops {
int (*open)(struct snd_compr_stream *stream);
@ -132,6 +171,12 @@ struct snd_compr_ops {
struct snd_compr_caps *caps);
int (*get_codec_caps) (struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec);
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
int (*task_create) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task);
int (*task_start) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task);
int (*task_stop) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task);
int (*task_free) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task);
#endif
};
/**
@ -242,4 +287,9 @@ int snd_compr_free_pages(struct snd_compr_stream *stream);
int snd_compr_stop_error(struct snd_compr_stream *stream,
snd_pcm_state_t state);
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
void snd_compr_task_finished(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task);
#endif
#endif

View File

@ -15,6 +15,7 @@ int hda_bus_ml_init(struct hdac_bus *bus);
void hda_bus_ml_free(struct hdac_bus *bus);
int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid);
void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable);
void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable);
bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
@ -71,6 +72,9 @@ static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
static inline int
hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; }
static inline void
hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable) { }
static inline void
hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) { }

View File

@ -180,7 +180,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
#define AZX_INT_ALL_STREAM 0x3fffffff /* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */

View File

@ -97,11 +97,11 @@ struct snd_pcm_ops {
#define SNDRV_PCM_TRIGGER_STOP 0
#define SNDRV_PCM_TRIGGER_START 1
#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3
#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4
#define SNDRV_PCM_TRIGGER_SUSPEND 5
#define SNDRV_PCM_TRIGGER_RESUME 6
#define SNDRV_PCM_TRIGGER_DRAIN 7
#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 2
#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 3
#define SNDRV_PCM_TRIGGER_SUSPEND 4
#define SNDRV_PCM_TRIGGER_RESUME 5
#define SNDRV_PCM_TRIGGER_DRAIN 6
#define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1)
@ -1393,30 +1393,6 @@ snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size);
}
/**
* snd_pcm_mmap_data_open - increase the mmap counter
* @area: VMA
*
* PCM mmap callback should handle this counter properly
*/
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
atomic_inc(&substream->mmap_count);
}
/**
* snd_pcm_mmap_data_close - decrease the mmap counter
* @area: VMA
*
* PCM mmap callback should handle this counter properly
*/
static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
atomic_dec(&substream->mmap_count);
}
int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area);
/* mmap for io-memory area */

62
include/sound/sdca.h Normal file
View File

@ -0,0 +1,62 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright(c) 2024 Intel Corporation
*/
#ifndef __SDCA_H__
#define __SDCA_H__
struct sdw_slave;
#define SDCA_MAX_FUNCTION_COUNT 8
/**
* sdca_device_desc - short descriptor for an SDCA Function
* @adr: ACPI address (used for SDCA register access)
* @type: Function topology type
* @name: human-readable string
*/
struct sdca_function_desc {
u64 adr;
u32 type;
const char *name;
};
/**
* sdca_device_data - structure containing all SDCA related information
* @sdca_interface_revision: value read from _DSD property, mainly to check
* for changes between silicon versions
* @num_functions: total number of supported SDCA functions. Invalid/unsupported
* functions will be skipped.
* @sdca_func: array of function descriptors
*/
struct sdca_device_data {
u32 interface_revision;
int num_functions;
struct sdca_function_desc sdca_func[SDCA_MAX_FUNCTION_COUNT];
};
enum sdca_quirk {
SDCA_QUIRKS_RT712_VB,
};
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_interface_revision(struct sdw_slave *slave);
bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk);
#else
static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
{
return false;
}
#endif
#endif

View File

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright(c) 2024 Intel Corporation
*/
#ifndef __SDCA_FUNCTION_H__
#define __SDCA_FUNCTION_H__
/*
* SDCA Function Types from SDCA specification v1.0a Section 5.1.2
* all Function types not described are reserved
* Note that SIMPLE_AMP, SIMPLE_MIC and SIMPLE_JACK Function Types
* are NOT defined in SDCA 1.0a, but they were defined in earlier
* drafts and are planned for 1.1.
*/
enum sdca_function_type {
SDCA_FUNCTION_TYPE_SMART_AMP = 0x01, /* Amplifier with protection features */
SDCA_FUNCTION_TYPE_SIMPLE_AMP = 0x02, /* subset of SmartAmp */
SDCA_FUNCTION_TYPE_SMART_MIC = 0x03, /* Smart microphone with acoustic triggers */
SDCA_FUNCTION_TYPE_SIMPLE_MIC = 0x04, /* subset of SmartMic */
SDCA_FUNCTION_TYPE_SPEAKER_MIC = 0x05, /* Combination of SmartMic and SmartAmp */
SDCA_FUNCTION_TYPE_UAJ = 0x06, /* 3.5mm Universal Audio jack */
SDCA_FUNCTION_TYPE_RJ = 0x07, /* Retaskable jack */
SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08, /* Subset of UAJ */
SDCA_FUNCTION_TYPE_HID = 0x0A, /* Human Interface Device, for e.g. buttons */
SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F, /* Implementation-defined function */
};
/* Human-readable names used for kernel logs and Function device registration/bind */
#define SDCA_FUNCTION_TYPE_SMART_AMP_NAME "SmartAmp"
#define SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME "SimpleAmp"
#define SDCA_FUNCTION_TYPE_SMART_MIC_NAME "SmartMic"
#define SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME "SimpleMic"
#define SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME "SpeakerMic"
#define SDCA_FUNCTION_TYPE_UAJ_NAME "UAJ"
#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
enum sdca_entity0_controls {
SDCA_CONTROL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,
SDCA_CONTROL_ENTITY_0_INTSTAT_CLEAR = 0x02,
SDCA_CONTROL_ENTITY_0_INT_ENABLE = 0x03,
SDCA_CONTROL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04,
SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY = 0x05,
SDCA_CONTROL_ENTITY_0_FUNCTION_MANUFACTURER_ID = 0x06,
SDCA_CONTROL_ENTITY_0_FUNCTION_ID = 0x07,
SDCA_CONTROL_ENTITY_0_FUNCTION_VERSION = 0x08
};
#endif

View File

@ -89,6 +89,13 @@ struct simple_util_priv {
#define simple_props_to_dai_codec(props, i) ((props)->codec_dai + i)
#define simple_props_to_codec_conf(props, i) ((props)->codec_conf + i)
/* has the same effect as simple_priv_to_props(). Preferred over
* simple_priv_to_props() when dealing with PCM runtime data as
* the ID stored in rtd->id may not be a valid array index.
*/
#define runtime_simple_priv_to_props(priv, rtd) \
((priv)->dai_props + ((rtd)->dai_link - (priv)->dai_link))
#define for_each_prop_dlc_cpus(props, i, cpu) \
for ((i) = 0; \
((i) < (props)->num.cpus) && \

View File

@ -187,6 +187,10 @@ struct snd_soc_acpi_link_adr {
* ACPI ID alone is not sufficient, wrong or misleading
* @quirk_data: data used to uniquely identify a machine, usually a list of
* audio codecs whose presence if checked with ACPI
* @machine_check: pointer to quirk function. The functionality is similar to
* the use of @machine_quirk, except that the return value is a boolean: the intent
* is to skip a machine if the additional hardware/firmware verification invalidates
* the initial selection in the snd_soc_acpi_mach table.
* @pdata: intended for platform data or machine specific-ops. This structure
* is not constant since this field may be updated at run-time
* @sof_tplg_filename: Sound Open Firmware topology file name, if enabled
@ -205,6 +209,7 @@ struct snd_soc_acpi_mach {
const char *board;
struct snd_soc_acpi_mach * (*machine_quirk)(void *arg);
const void *quirk_data;
bool (*machine_check)(void *arg);
void *pdata;
struct snd_soc_acpi_mach_params mach_params;
const char *sof_tplg_filename;
@ -235,7 +240,6 @@ static inline bool snd_soc_acpi_sof_parent(struct device *dev)
bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev,
const struct snd_soc_acpi_link_adr *link,
struct sdw_extended_slave_id *ids,
int num_slaves);
struct sdw_peripherals *peripherals);
#endif

View File

@ -216,8 +216,7 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream, int rollback);
void snd_soc_dai_suspend(struct snd_soc_dai *dai);
void snd_soc_dai_resume(struct snd_soc_dai *dai);
int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
struct snd_soc_pcm_runtime *rtd, int num);
int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd);
bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int stream);
void snd_soc_dai_action(struct snd_soc_dai *dai,
int stream, int action);
@ -275,7 +274,7 @@ struct snd_soc_dai_ops {
int (*probe)(struct snd_soc_dai *dai);
int (*remove)(struct snd_soc_dai *dai);
/* compress dai */
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
int (*compress_new)(struct snd_soc_pcm_runtime *rtd);
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);

View File

@ -476,11 +476,11 @@ struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev,
struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
const char *driver_name);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd);
#ifdef CONFIG_SND_SOC_COMPRESS
int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num);
int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd);
#else
static inline int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
static inline int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd)
{
return 0;
}
@ -531,8 +531,13 @@ int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params,
int tdm_width, int tdm_slots, int slot_multiple);
/* set runtime hw params */
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw);
static inline int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw)
{
substream->runtime->hw = *hw;
return 0;
}
struct snd_ac97 *snd_soc_alloc_ac97_component(struct snd_soc_component *component);
struct snd_ac97 *snd_soc_new_ac97_component(struct snd_soc_component *component,
@ -805,11 +810,6 @@ struct snd_soc_dai_link {
/* This DAI link can route to other DAI links at runtime (Frontend)*/
unsigned int dynamic:1;
/* REMOVE ME */
/* DPCM capture and Playback support */
unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* DPCM used FE & BE merged format */
unsigned int dpcm_merged_format:1;
/* DPCM used FE & BE merged channel */
@ -1185,7 +1185,7 @@ struct snd_soc_pcm_runtime {
struct dentry *debugfs_dpcm_root;
#endif
unsigned int num; /* 0-based and monotonic increasing */
unsigned int id; /* 0-based and monotonic increasing */
struct list_head list; /* rtd list of the soc card */
/* function mark */
@ -1428,10 +1428,6 @@ struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_a
struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
bool legacy_dai_naming);
struct snd_soc_dai *devm_snd_soc_register_dai(struct device *dev,
struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
bool legacy_dai_naming);
void snd_soc_unregister_dai(struct snd_soc_dai *dai);
struct snd_soc_dai *snd_soc_find_dai(

View File

@ -152,14 +152,15 @@ void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_lin
struct snd_soc_dai_link_component *cpus, int cpus_num,
struct snd_soc_dai_link_component *platform_component,
int num_platforms, struct snd_soc_dai_link_component *codecs,
int codecs_num, int (*init)(struct snd_soc_pcm_runtime *rtd),
int codecs_num, int no_pcm,
int (*init)(struct snd_soc_pcm_runtime *rtd),
const struct snd_soc_ops *ops);
int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
int *be_id, char *name, int playback, int capture,
const char *cpu_dai_name, const char *platform_comp_name,
int num_platforms, const char *codec_name,
const char *codec_dai_name,
const char *codec_dai_name, int no_pcm,
int (*init)(struct snd_soc_pcm_runtime *rtd),
const struct snd_soc_ops *ops);
@ -236,8 +237,7 @@ int asoc_sdw_rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_s
int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt700_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt722_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);

View File

@ -60,6 +60,7 @@ enum sof_ext_man_elem_type {
SOF_EXT_MAN_ELEM_FW_VERSION = 0,
SOF_EXT_MAN_ELEM_WINDOW = 1,
SOF_EXT_MAN_ELEM_CC_VERSION = 2,
SOF_EXT_MAN_ELEM_PROBE_INFO = 3,
SOF_EXT_MAN_ELEM_DBG_ABI = 4,
SOF_EXT_MAN_ELEM_CONFIG_DATA = 5, /**< ABI3.17 */
SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA = 6,

View File

@ -156,6 +156,7 @@ struct tasdevice_priv {
struct tasdevice_rca rcabin;
struct calidata cali_data;
struct tasdevice_fw *fmw;
struct gpio_desc *speaker_id;
struct gpio_desc *reset;
struct mutex codec_lock;
struct regmap *regmap;

View File

@ -14,7 +14,7 @@
#include <sound/compress_params.h>
#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 2, 0)
#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 3, 0)
/**
* struct snd_compressed_buffer - compressed buffer
* @fragment_size: size of buffer fragment in bytes
@ -68,7 +68,8 @@ struct snd_compr_avail {
enum snd_compr_direction {
SND_COMPRESS_PLAYBACK = 0,
SND_COMPRESS_CAPTURE
SND_COMPRESS_CAPTURE,
SND_COMPRESS_ACCEL
};
/**
@ -127,6 +128,59 @@ struct snd_compr_metadata {
__u32 value[8];
} __attribute__((packed, aligned(4)));
/* flags for struct snd_compr_task */
#define SND_COMPRESS_TFLG_NEW_STREAM (1<<0) /* mark for the new stream data */
/**
* struct snd_compr_task - task primitive for non-realtime operation
* @seqno: sequence number (task identifier)
* @origin_seqno: previous sequence number (task identifier) - for reuse
* @input_fd: data input file descriptor (dma-buf)
* @output_fd: data output file descriptor (dma-buf)
* @input_size: filled data in bytes (from caller, must not exceed fragment size)
* @flags: see SND_COMPRESS_TFLG_* defines
* @reserved: reserved for future extension
*/
struct snd_compr_task {
__u64 seqno;
__u64 origin_seqno;
int input_fd;
int output_fd;
__u64 input_size;
__u32 flags;
__u8 reserved[16];
} __attribute__((packed, aligned(4)));
/**
* enum snd_compr_state - task state
* @SND_COMPRESS_TASK_STATE_IDLE: task is not queued
* @SND_COMPRESS_TASK_STATE_ACTIVE: task is in the queue
* @SND_COMPRESS_TASK_STATE_FINISHED: task was processed, output is available
*/
enum snd_compr_state {
SND_COMPRESS_TASK_STATE_IDLE = 0,
SND_COMPRESS_TASK_STATE_ACTIVE,
SND_COMPRESS_TASK_STATE_FINISHED
};
/**
* struct snd_compr_task_status - task status
* @seqno: sequence number (task identifier)
* @input_size: filled data in bytes (from user space)
* @output_size: filled data in bytes (from driver)
* @output_flags: reserved for future (all zeros - from driver)
* @state: actual task state (SND_COMPRESS_TASK_STATE_*)
* @reserved: reserved for future extension
*/
struct snd_compr_task_status {
__u64 seqno;
__u64 input_size;
__u64 output_size;
__u32 output_flags;
__u8 state;
__u8 reserved[15];
} __attribute__((packed, aligned(4)));
/*
* compress path ioctl definitions
* SNDRV_COMPRESS_GET_CAPS: Query capability of DSP
@ -164,6 +218,14 @@ struct snd_compr_metadata {
#define SNDRV_COMPRESS_DRAIN _IO('C', 0x34)
#define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35)
#define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36)
#define SNDRV_COMPRESS_TASK_CREATE _IOWR('C', 0x60, struct snd_compr_task)
#define SNDRV_COMPRESS_TASK_FREE _IOW('C', 0x61, __u64)
#define SNDRV_COMPRESS_TASK_START _IOWR('C', 0x62, struct snd_compr_task)
#define SNDRV_COMPRESS_TASK_STOP _IOW('C', 0x63, __u64)
#define SNDRV_COMPRESS_TASK_STATUS _IOWR('C', 0x68, struct snd_compr_task_status)
/*
* TODO
* 1. add mmap support

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_ADAU1373 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_AW88081 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_CS42L84 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_ES8323 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_NTP8835 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_NTP8918 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_SMA1307 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_UDA1342 is not set

View File

@ -0,0 +1 @@
# CONFIG_SND_SOC_AMD_LEGACY_SDW_MACH is not set

View File

@ -0,0 +1 @@
CONFIG_SND_SOC_RT721_SDCA_SDW=m

View File

@ -180,7 +180,7 @@ static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
/**
* snd_ac97_codec_driver_register - register an AC97 codec driver
* @dev: AC97 driver codec to register
* @drv: AC97 driver codec to register
*
* Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
* controller.
@ -196,7 +196,7 @@ EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
/**
* snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
* @dev: AC97 codec driver to unregister
* @drv: AC97 codec driver to unregister
*
* Unregister a previously registered ac97 codec driver.
*/
@ -338,6 +338,7 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
* @dev: the device providing the ac97 DC function
* @slots_available: mask of the ac97 codecs that can be scanned and probed
* bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
* @codecs_pdata: codec platform data
*
* Register a digital controller which can control up to 4 ac97 codecs. This is
* the controller side of the AC97 AC-link, while the slave side are the codecs.

View File

@ -59,6 +59,9 @@ config SND_CORE_TEST
config SND_COMPRESS_OFFLOAD
tristate
config SND_COMPRESS_ACCEL
bool
config SND_JACK
bool

View File

@ -24,6 +24,7 @@
#include <linux/types.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/compat.h>
#include <sound/core.h>
@ -54,6 +55,12 @@ struct snd_compr_file {
static void error_delayed_work(struct work_struct *work);
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
static void snd_compr_task_free_all(struct snd_compr_stream *stream);
#else
static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { }
#endif
/*
* a note on stream states used:
* we use following states in the compressed core
@ -85,6 +92,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
dirn = SND_COMPRESS_PLAYBACK;
else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
dirn = SND_COMPRESS_CAPTURE;
else if ((f->f_flags & O_ACCMODE) == O_RDWR)
dirn = SND_COMPRESS_ACCEL;
else
return -EINVAL;
@ -125,6 +134,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
}
runtime->state = SNDRV_PCM_STATE_OPEN;
init_waitqueue_head(&runtime->sleep);
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
INIT_LIST_HEAD(&runtime->tasks);
#endif
data->stream.runtime = runtime;
f->private_data = (void *)data;
scoped_guard(mutex, &compr->lock)
@ -154,6 +166,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
break;
}
snd_compr_task_free_all(&data->stream);
data->stream.ops->free(&data->stream);
if (!data->stream.runtime->dma_buffer_p)
kfree(data->stream.runtime->buffer);
@ -226,6 +240,9 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
struct snd_compr_avail ioctl_avail;
size_t avail;
if (stream->direction == SND_COMPRESS_ACCEL)
return -EBADFD;
avail = snd_compr_calc_avail(stream, &ioctl_avail);
ioctl_avail.avail = avail;
@ -287,6 +304,8 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
return -EFAULT;
stream = &data->stream;
if (stream->direction == SND_COMPRESS_ACCEL)
return -EBADFD;
guard(mutex)(&stream->device->lock);
/* write is allowed when stream is running or has been setup */
switch (stream->runtime->state) {
@ -336,6 +355,8 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
return -EFAULT;
stream = &data->stream;
if (stream->direction == SND_COMPRESS_ACCEL)
return -EBADFD;
guard(mutex)(&stream->device->lock);
/* read is allowed when stream is running, paused, draining and setup
@ -385,6 +406,7 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
{
struct snd_compr_file *data = f->private_data;
struct snd_compr_stream *stream;
struct snd_compr_runtime *runtime;
size_t avail;
__poll_t retval = 0;
@ -392,10 +414,11 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
return EPOLLERR;
stream = &data->stream;
runtime = stream->runtime;
guard(mutex)(&stream->device->lock);
switch (stream->runtime->state) {
switch (runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_XRUN:
return snd_compr_get_poll(stream) | EPOLLERR;
@ -403,23 +426,37 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
break;
}
poll_wait(f, &stream->runtime->sleep, wait);
poll_wait(f, &runtime->sleep, wait);
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
if (stream->direction == SND_COMPRESS_ACCEL) {
struct snd_compr_task_runtime *task;
if (runtime->fragments > runtime->active_tasks)
retval |= EPOLLOUT | EPOLLWRNORM;
task = list_first_entry_or_null(&runtime->tasks,
struct snd_compr_task_runtime,
list);
if (task && task->state == SND_COMPRESS_TASK_STATE_FINISHED)
retval |= EPOLLIN | EPOLLRDNORM;
return retval;
}
#endif
avail = snd_compr_get_avail(stream);
pr_debug("avail is %ld\n", (unsigned long)avail);
/* check if we have at least one fragment to fill */
switch (stream->runtime->state) {
switch (runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
/* stream has been woken up after drain is complete
* draining done so set stream state to stopped
*/
retval = snd_compr_get_poll(stream);
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
runtime->state = SNDRV_PCM_STATE_SETUP;
break;
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
if (avail >= stream->runtime->fragment_size)
if (avail >= runtime->fragment_size)
retval = snd_compr_get_poll(stream);
break;
default:
@ -521,6 +558,9 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
unsigned int buffer_size;
void *buffer = NULL;
if (stream->direction == SND_COMPRESS_ACCEL)
goto params;
buffer_size = params->buffer.fragment_size * params->buffer.fragments;
if (stream->ops->copy) {
buffer = NULL;
@ -543,18 +583,30 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
if (!buffer)
return -ENOMEM;
}
stream->runtime->fragment_size = params->buffer.fragment_size;
stream->runtime->fragments = params->buffer.fragments;
stream->runtime->buffer = buffer;
stream->runtime->buffer_size = buffer_size;
params:
stream->runtime->fragment_size = params->buffer.fragment_size;
stream->runtime->fragments = params->buffer.fragments;
return 0;
}
static int snd_compress_check_input(struct snd_compr_params *params)
static int
snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_params *params)
{
u32 max_fragments;
/* first let's check the buffer parameter's */
if (params->buffer.fragment_size == 0 ||
params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
if (params->buffer.fragment_size == 0)
return -EINVAL;
if (stream->direction == SND_COMPRESS_ACCEL)
max_fragments = 64; /* safe value */
else
max_fragments = U32_MAX / params->buffer.fragment_size;
if (params->buffer.fragments > max_fragments ||
params->buffer.fragments == 0)
return -EINVAL;
@ -583,7 +635,7 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
if (IS_ERR(params))
return PTR_ERR(params);
retval = snd_compress_check_input(params);
retval = snd_compress_check_input(stream, params);
if (retval)
return retval;
@ -939,6 +991,273 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
return snd_compress_wait_for_drain(stream);
}
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
static struct snd_compr_task_runtime *
snd_compr_find_task(struct snd_compr_stream *stream, __u64 seqno)
{
struct snd_compr_task_runtime *task;
list_for_each_entry(task, &stream->runtime->tasks, list) {
if (task->seqno == seqno)
return task;
}
return NULL;
}
static void snd_compr_task_free(struct snd_compr_task_runtime *task)
{
if (task->output)
dma_buf_put(task->output);
if (task->input)
dma_buf_put(task->input);
kfree(task);
}
static u64 snd_compr_seqno_next(struct snd_compr_stream *stream)
{
u64 seqno = ++stream->runtime->task_seqno;
if (seqno == 0)
seqno = ++stream->runtime->task_seqno;
return seqno;
}
static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask)
{
struct snd_compr_task_runtime *task;
int retval, fd_i, fd_o;
if (stream->runtime->total_tasks >= stream->runtime->fragments)
return -EBUSY;
if (utask->origin_seqno != 0 || utask->input_size != 0)
return -EINVAL;
task = kzalloc(sizeof(*task), GFP_KERNEL);
if (task == NULL)
return -ENOMEM;
task->seqno = utask->seqno = snd_compr_seqno_next(stream);
task->input_size = utask->input_size;
retval = stream->ops->task_create(stream, task);
if (retval < 0)
goto cleanup;
/* similar functionality as in dma_buf_fd(), but ensure that both
file descriptors are allocated before fd_install() */
if (!task->input || !task->input->file || !task->output || !task->output->file) {
retval = -EINVAL;
goto cleanup;
}
fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC);
if (fd_i < 0)
goto cleanup;
fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC);
if (fd_o < 0) {
put_unused_fd(fd_i);
goto cleanup;
}
/* keep dmabuf reference until freed with task free ioctl */
get_dma_buf(task->input);
get_dma_buf(task->output);
fd_install(fd_i, task->input->file);
fd_install(fd_o, task->output->file);
utask->input_fd = fd_i;
utask->output_fd = fd_o;
list_add_tail(&task->list, &stream->runtime->tasks);
stream->runtime->total_tasks++;
return 0;
cleanup:
snd_compr_task_free(task);
return retval;
}
static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_task *task __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
task = memdup_user((void __user *)arg, sizeof(*task));
if (IS_ERR(task))
return PTR_ERR(task);
retval = snd_compr_task_new(stream, task);
if (retval >= 0)
if (copy_to_user((void __user *)arg, task, sizeof(*task)))
retval = -EFAULT;
return retval;
}
static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task,
struct snd_compr_task *utask)
{
if (task == NULL)
return -EINVAL;
if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED)
return -EBUSY;
if (utask->input_size > task->input->size)
return -EINVAL;
task->flags = utask->flags;
task->input_size = utask->input_size;
task->state = SND_COMPRESS_TASK_STATE_IDLE;
return 0;
}
static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_compr_task *utask)
{
struct snd_compr_task_runtime *task;
int retval;
if (utask->origin_seqno > 0) {
task = snd_compr_find_task(stream, utask->origin_seqno);
retval = snd_compr_task_start_prepare(task, utask);
if (retval < 0)
return retval;
task->seqno = utask->seqno = snd_compr_seqno_next(stream);
utask->origin_seqno = 0;
list_move_tail(&task->list, &stream->runtime->tasks);
} else {
task = snd_compr_find_task(stream, utask->seqno);
if (task && task->state != SND_COMPRESS_TASK_STATE_IDLE)
return -EBUSY;
retval = snd_compr_task_start_prepare(task, utask);
if (retval < 0)
return retval;
}
retval = stream->ops->task_start(stream, task);
if (retval >= 0) {
task->state = SND_COMPRESS_TASK_STATE_ACTIVE;
stream->runtime->active_tasks++;
}
return retval;
}
static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_task *task __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
task = memdup_user((void __user *)arg, sizeof(*task));
if (IS_ERR(task))
return PTR_ERR(task);
retval = snd_compr_task_start(stream, task);
if (retval >= 0)
if (copy_to_user((void __user *)arg, task, sizeof(*task)))
retval = -EFAULT;
return retval;
}
static void snd_compr_task_stop_one(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
if (task->state != SND_COMPRESS_TASK_STATE_ACTIVE)
return;
stream->ops->task_stop(stream, task);
if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
stream->runtime->active_tasks--;
list_move_tail(&task->list, &stream->runtime->tasks);
task->state = SND_COMPRESS_TASK_STATE_IDLE;
}
static void snd_compr_task_free_one(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
snd_compr_task_stop_one(stream, task);
stream->ops->task_free(stream, task);
list_del(&task->list);
snd_compr_task_free(task);
stream->runtime->total_tasks--;
}
static void snd_compr_task_free_all(struct snd_compr_stream *stream)
{
struct snd_compr_task_runtime *task, *temp;
list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
snd_compr_task_free_one(stream, task);
}
typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task);
static int snd_compr_task_seq(struct snd_compr_stream *stream, unsigned long arg,
snd_compr_seq_func_t fcn)
{
struct snd_compr_task_runtime *task, *temp;
__u64 seqno;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
retval = copy_from_user(&seqno, (__u64 __user *)arg, sizeof(seqno));
if (retval)
return -EFAULT;
retval = 0;
if (seqno == 0) {
list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
fcn(stream, task);
} else {
task = snd_compr_find_task(stream, seqno);
if (task == NULL) {
retval = -EINVAL;
} else {
fcn(stream, task);
}
}
return retval;
}
static int snd_compr_task_status(struct snd_compr_stream *stream,
struct snd_compr_task_status *status)
{
struct snd_compr_task_runtime *task;
task = snd_compr_find_task(stream, status->seqno);
if (task == NULL)
return -EINVAL;
status->input_size = task->input_size;
status->output_size = task->output_size;
status->state = task->state;
return 0;
}
static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_task_status *status __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
status = memdup_user((void __user *)arg, sizeof(*status));
if (IS_ERR(status))
return PTR_ERR(status);
retval = snd_compr_task_status(stream, status);
if (retval >= 0)
if (copy_to_user((void __user *)arg, status, sizeof(*status)))
retval = -EFAULT;
return retval;
}
/**
* snd_compr_task_finished: Notify that the task was finished
* @stream: pointer to stream
* @task: runtime task structure
*
* Set the finished task state and notify waiters.
*/
void snd_compr_task_finished(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
guard(mutex)(&stream->device->lock);
if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
stream->runtime->active_tasks--;
task->state = SND_COMPRESS_TASK_STATE_FINISHED;
wake_up(&stream->runtime->sleep);
}
EXPORT_SYMBOL_GPL(snd_compr_task_finished);
MODULE_IMPORT_NS("DMA_BUF");
#endif /* CONFIG_SND_COMPRESS_ACCEL */
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
struct snd_compr_file *data = f->private_data;
@ -968,6 +1287,27 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return snd_compr_set_metadata(stream, arg);
case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
return snd_compr_get_metadata(stream, arg);
}
if (stream->direction == SND_COMPRESS_ACCEL) {
#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_COMPRESS_TASK_CREATE):
return snd_compr_task_create(stream, arg);
case _IOC_NR(SNDRV_COMPRESS_TASK_FREE):
return snd_compr_task_seq(stream, arg, snd_compr_task_free_one);
case _IOC_NR(SNDRV_COMPRESS_TASK_START):
return snd_compr_task_start_ioctl(stream, arg);
case _IOC_NR(SNDRV_COMPRESS_TASK_STOP):
return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one);
case _IOC_NR(SNDRV_COMPRESS_TASK_STATUS):
return snd_compr_task_status_ioctl(stream, arg);
}
#endif
return -ENOTTY;
}
switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
return snd_compr_tstamp(stream, arg);
case _IOC_NR(SNDRV_COMPRESS_AVAIL):
@ -1140,6 +1480,11 @@ int snd_compress_new(struct snd_card *card, int device,
};
int ret;
#if !IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
if (snd_BUG_ON(dirn == SND_COMPRESS_ACCEL))
return -EINVAL;
#endif
compr->card = card;
compr->device = device;
compr->direction = dirn;

View File

@ -3773,6 +3773,26 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
/*
* snd_pcm_mmap_data_open - increase the mmap counter
*/
static void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = area->vm_private_data;
atomic_inc(&substream->mmap_count);
}
/*
* snd_pcm_mmap_data_close - decrease the mmap counter
*/
static void snd_pcm_mmap_data_close(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = area->vm_private_data;
atomic_dec(&substream->mmap_count);
}
/*
* fault callback for mmapping a RAM page
*/

View File

@ -370,7 +370,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
{
struct snd_ump_block *fb, *p;
if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS)
if (blk >= SNDRV_UMP_MAX_BLOCKS)
return -EINVAL;
if (snd_ump_get_block(ump, blk))
@ -391,7 +391,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
fb->info.first_group = first_group;
fb->info.num_groups = num_groups;
/* fill the default name, may be overwritten to a better name */
snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d",
snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u",
first_group + 1, first_group + num_groups);
/* put the entry in the ordered list */

View File

@ -333,53 +333,6 @@ retry_after_bus_reset:
}
EXPORT_SYMBOL(cmp_connection_establish);
/**
* cmp_connection_update - update the connection after a bus reset
* @c: the connection manager
*
* This function must be called from the driver's .update handler to
* reestablish any connection that might have been active.
*
* Returns zero on success, or a negative error code. On an error, the
* connection is broken and the caller must stop transmitting iso packets.
*/
int cmp_connection_update(struct cmp_connection *c)
{
int err;
mutex_lock(&c->mutex);
if (!c->connected) {
mutex_unlock(&c->mutex);
return 0;
}
err = fw_iso_resources_update(&c->resources);
if (err < 0)
goto err_unconnect;
if (c->direction == CMP_OUTPUT)
err = pcr_modify(c, opcr_set_modify, pcr_set_check,
SUCCEED_ON_BUS_RESET);
else
err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
SUCCEED_ON_BUS_RESET);
if (err < 0)
goto err_unconnect;
mutex_unlock(&c->mutex);
return 0;
err_unconnect:
c->connected = false;
mutex_unlock(&c->mutex);
return err;
}
EXPORT_SYMBOL(cmp_connection_update);
static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
{
return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);

View File

@ -47,7 +47,6 @@ int cmp_connection_reserve(struct cmp_connection *connection,
void cmp_connection_release(struct cmp_connection *connection);
int cmp_connection_establish(struct cmp_connection *connection);
int cmp_connection_update(struct cmp_connection *connection);
void cmp_connection_break(struct cmp_connection *connection);
#endif

View File

@ -464,7 +464,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
return -ENOMEM;
}
err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
hpi_stream_get_info_ex(dpcm->h_stream, NULL,
&dpcm->hpi_buffer_attached, NULL, NULL, NULL);
}
bytes_per_sec = params_rate(params) * params_channels(params);

View File

@ -933,6 +933,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
bool match_all_pins)
{
const struct snd_hda_pin_quirk *pq;
const char *name = NULL;
if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
return;
@ -946,9 +947,10 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
codec->fixup_id = pq->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
codec->fixup_name = pq->name;
codec_dbg(codec, "%s: picked fixup %s (pin match)\n",
codec->core.chip_name, codec->fixup_name);
name = pq->name;
#endif
codec_info(codec, "%s: picked fixup %s (pin match)\n",
codec->core.chip_name, name ? name : "");
codec->fixup_list = fixlist;
return;
}
@ -1015,8 +1017,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
id = HDA_FIXUP_ID_NO_FIXUP;
fixlist = NULL;
codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n",
codec->core.chip_name);
codec_info(codec, "%s: picked no fixup (nofixup specified)\n",
codec->core.chip_name);
goto found;
}
@ -1026,8 +1028,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
if (!strcmp(codec->modelname, models->name)) {
id = models->id;
name = models->name;
codec_dbg(codec, "%s: picked fixup %s (model specified)\n",
codec->core.chip_name, codec->fixup_name);
codec_info(codec, "%s: picked fixup %s (model specified)\n",
codec->core.chip_name, name);
goto found;
}
models++;
@ -1085,9 +1087,9 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
#ifdef CONFIG_SND_DEBUG_VERBOSE
name = q->name;
#endif
codec_dbg(codec, "%s: picked fixup %s for %s %04x:%04x\n",
codec->core.chip_name, name ? name : "",
type, q->subvendor, q->subdevice);
codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n",
codec->core.chip_name, name ? name : "",
type, q->subvendor, q->subdevice);
found:
codec->fixup_id = id;
codec->fixup_list = fixlist;

View File

@ -772,6 +772,14 @@ static void azx_clear_irq_pending(struct azx *chip)
static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
struct hdac_bus *bus = azx_bus(chip);
int ret;
if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) {
ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX);
if (ret < 0)
return ret;
chip->msi = 0;
}
if (request_irq(chip->pci->irq, azx_interrupt,
chip->msi ? 0 : IRQF_SHARED,
@ -785,7 +793,6 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
}
bus->irq = chip->pci->irq;
chip->card->sync_irq = bus->irq;
pci_intx(chip->pci, !chip->msi);
return 0;
}
@ -1031,22 +1038,12 @@ static int azx_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hdac_bus *bus;
if (!azx_is_pm_ready(card))
return 0;
chip = card->private_data;
bus = azx_bus(chip);
azx_shutdown_chip(chip);
if (bus->irq >= 0) {
free_irq(bus->irq, chip);
bus->irq = -1;
chip->card->sync_irq = -1;
}
if (chip->msi)
pci_disable_msi(chip->pci);
trace_azx_suspend(chip);
return 0;
@ -1061,11 +1058,6 @@ static int __maybe_unused azx_resume(struct device *dev)
return 0;
chip = card->private_data;
if (chip->msi)
if (pci_enable_msi(chip->pci) < 0)
chip->msi = 0;
if (azx_acquire_irq(chip, 1) < 0)
return -EIO;
__azx_runtime_resume(chip);
@ -1866,6 +1858,8 @@ static int azx_first_init(struct azx *chip)
bus->polling_mode = 1;
bus->not_use_interrupts = 1;
bus->access_sdnctl_in_dword = 1;
if (!chip->jackpoll_interval)
chip->jackpoll_interval = msecs_to_jiffies(1500);
}
err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio");
@ -1891,13 +1885,9 @@ static int azx_first_init(struct azx *chip)
chip->gts_present = true;
#endif
if (chip->msi) {
if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
dev_dbg(card->dev, "Disabling 64bit MSI\n");
pci->no_64bit_msi = true;
}
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) {
dev_dbg(card->dev, "Disabling 64bit MSI\n");
pci->no_64bit_msi = true;
}
pci_set_master(pci);
@ -2049,7 +2039,7 @@ static int disable_msi_reset_irq(struct azx *chip)
free_irq(bus->irq, chip);
bus->irq = -1;
chip->card->sync_irq = -1;
pci_disable_msi(chip->pci);
pci_free_irq_vectors(chip->pci);
chip->msi = 0;
err = azx_acquire_irq(chip, 1);
if (err < 0)

View File

@ -6491,6 +6491,16 @@ static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
}
}
/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */
static void alc294_fixup_bass_speaker_15(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
static const hda_nid_t conn[] = { 0x02, 0x03 };
snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
}
}
/* Hook to update amp GPIO4 for automute */
static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
struct hda_jack_callback *jack)
@ -7780,6 +7790,7 @@ enum {
ALC245_FIXUP_CLEVO_NOISY_MIC,
ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE,
ALC233_FIXUP_MEDION_MTL_SPK,
ALC294_FIXUP_BASS_SPEAKER_15,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@ -10114,6 +10125,10 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
},
[ALC294_FIXUP_BASS_SPEAKER_15] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc294_fixup_bass_speaker_15,
},
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@ -10633,6 +10648,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE),
SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15),
SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502),
SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2),
@ -10930,8 +10946,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),

View File

@ -16,6 +16,7 @@
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pci_ids.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/hda_codec.h>
@ -110,10 +111,20 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
return 1;
}
static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false };
static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = {
{ "speakerid-gpios", &speakerid_gpios, 1 },
{ }
};
static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
{
struct acpi_device *adev;
struct device *physdev;
LIST_HEAD(resources);
const char *sub;
uint32_t subid;
int ret;
adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
@ -123,18 +134,50 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
return -ENODEV;
}
physdev = get_device(acpi_get_first_physical_node(adev));
ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p);
if (ret < 0)
if (ret < 0) {
dev_err(p->dev, "Failed to get ACPI resource.\n");
goto err;
}
sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
if (IS_ERR(sub)) {
/* No subsys id in older tas2563 projects. */
if (!strncmp(hid, "INT8866", sizeof("INT8866")))
goto end_2563;
dev_err(p->dev, "Failed to get SUBSYS ID.\n");
ret = PTR_ERR(sub);
goto err;
}
/* Speaker id was needed for ASUS projects. */
ret = kstrtou32(sub, 16, &subid);
if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) {
ret = devm_acpi_dev_add_driver_gpios(p->dev,
tas2781_speaker_id_gpios);
if (ret < 0)
dev_err(p->dev, "Failed to add driver gpio %d.\n",
ret);
p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN);
if (IS_ERR(p->speaker_id)) {
dev_err(p->dev, "Failed to get Speaker id.\n");
ret = PTR_ERR(p->speaker_id);
goto err;
}
} else {
p->speaker_id = NULL;
}
end_2563:
acpi_dev_free_resource_list(&resources);
strscpy(p->dev_name, hid, sizeof(p->dev_name));
put_device(physdev);
acpi_dev_put(adev);
return 0;
err:
dev_err(p->dev, "read acpi error, ret: %d\n", ret);
put_device(physdev);
acpi_dev_put(adev);
return ret;
@ -615,7 +658,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
struct tasdevice_priv *tas_priv = context;
struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
struct hda_codec *codec = tas_priv->codec;
int i, ret;
int i, ret, spk_id;
pm_runtime_get_sync(tas_priv->dev);
mutex_lock(&tas_priv->codec_lock);
@ -648,8 +691,25 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
tasdevice_dsp_remove(tas_priv);
tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin",
codec->core.subsystem_id & 0xffff);
if (tas_priv->speaker_id != NULL) {
// Speaker id need to be checked for ASUS only.
spk_id = gpiod_get_value(tas_priv->speaker_id);
if (spk_id < 0) {
// Speaker id is not valid, use default.
dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id);
spk_id = 0;
}
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),
"TAS2XXX%04X%d.bin",
lower_16_bits(codec->core.subsystem_id),
spk_id);
} else {
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),
"TAS2XXX%04X.bin",
lower_16_bits(codec->core.subsystem_id));
}
ret = tasdevice_dsp_parser(tas_priv);
if (ret) {
dev_err(tas_priv->dev, "dspfw load %s error\n",

View File

@ -170,14 +170,9 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
ovol = (0x7f - nvol) | (tmp & 0x80);
/*
dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n",
idx, ovol);
*/
if (change)
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
return change;
}

View File

@ -312,8 +312,6 @@ static void aica_period_elapsed(struct timer_list *t)
static void spu_begin_dma(struct snd_pcm_substream *substream)
{
struct snd_card_aica *dreamcastcard;
struct snd_pcm_runtime *runtime;
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
schedule_work(&(dreamcastcard->spu_dma_work));

View File

@ -95,6 +95,7 @@ source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/sdca/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sof/Kconfig"
source "sound/soc/spear/Kconfig"

View File

@ -59,6 +59,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sdca/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sof/
obj-$(CONFIG_SND_SOC) += spear/

View File

@ -163,6 +163,7 @@ config SND_SOC_AMD_SOUNDWIRE
config SND_SOC_AMD_PS
tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
select SND_SOC_ACPI_AMD_MATCH
depends on X86 && PCI && ACPI
help
This option enables Audio Coprocessor i.e ACP v6.3 support on

View File

@ -542,7 +542,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.init = cz_da7219_init,
.dpcm_playback = 1,
.playback_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_play_ops,
SND_SOC_DAILINK_REG(designware1, dlgs, platform),
@ -552,7 +552,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.stream_name = "Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_cap_ops,
SND_SOC_DAILINK_REG(designware2, dlgs, platform),
@ -562,7 +562,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
.playback_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_max_play_ops,
SND_SOC_DAILINK_REG(designware3, mx, platform),
@ -573,7 +573,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.stream_name = "DMIC0 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic0_cap_ops,
SND_SOC_DAILINK_REG(designware3, adau, platform),
@ -584,7 +584,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.stream_name = "DMIC1 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic1_cap_ops,
SND_SOC_DAILINK_REG(designware2, adau, platform),
@ -598,7 +598,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.init = cz_rt5682_init,
.dpcm_playback = 1,
.playback_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_play_ops,
SND_SOC_DAILINK_REG(designware1, rt5682, platform),
@ -608,7 +608,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.stream_name = "Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_cap_ops,
SND_SOC_DAILINK_REG(designware2, rt5682, platform),
@ -618,7 +618,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
.playback_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_max_play_ops,
SND_SOC_DAILINK_REG(designware3, mx, platform),
@ -629,7 +629,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.stream_name = "DMIC0 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_dmic0_cap_ops,
SND_SOC_DAILINK_REG(designware3, adau, platform),
@ -640,7 +640,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.stream_name = "DMIC1 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
.capture_only = 1,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_dmic1_cap_ops,
SND_SOC_DAILINK_REG(designware2, adau, platform),

View File

@ -150,8 +150,6 @@ static struct snd_soc_dai_link st_dai_es8336[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.dpcm_capture = 1,
.dpcm_playback = 1,
.init = st_es8336_init,
.ops = &st_es8336_ops,
SND_SOC_DAILINK_REG(designware1, codec, platform),

View File

@ -119,10 +119,17 @@ config SND_SOC_AMD_SOF_MACH
help
This option enables SOF sound card support for ACP audio.
config SND_SOC_AMD_SDW_MACH_COMMON
tristate
help
This option enables common SoundWire Machine driver module for
AMD platforms.
config SND_SOC_AMD_SOF_SDW_MACH
tristate "AMD SOF Soundwire Machine Driver Support"
depends on X86 && PCI && ACPI
depends on SOUNDWIRE
select SND_SOC_AMD_SDW_MACH_COMMON
select SND_SOC_SDW_UTILS
select SND_SOC_DMIC
select SND_SOC_RT711_SDW
@ -137,6 +144,28 @@ config SND_SOC_AMD_SOF_SDW_MACH
on AMD platform.
If unsure select "N".
config SND_SOC_AMD_LEGACY_SDW_MACH
tristate "AMD Legacy(No DSP) Soundwire Machine Driver Support"
depends on X86 && PCI && ACPI
depends on SOUNDWIRE
select SND_SOC_AMD_SDW_MACH_COMMON
select SND_SOC_SDW_UTILS
select SND_SOC_DMIC
select SND_SOC_RT711_SDW
select SND_SOC_RT711_SDCA_SDW
select SND_SOC_RT712_SDCA_SDW
select SND_SOC_RT712_SDCA_DMIC_SDW
select SND_SOC_RT1316_SDW
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT722_SDCA_SDW
help
This option enables Legacy(No DSP) sound card support for SoundWire
enabled AMD platforms along with ACP PDM controller.
Say Y if you want to enable SoundWire based machine driver support
on AMD platform.
If unsure select "N".
endif # SND_SOC_AMD_ACP_COMMON
config SND_AMD_SOUNDWIRE_ACPI

View File

@ -23,7 +23,9 @@ snd-acp-mach-y := acp-mach-common.o
snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
snd-acp-sof-mach-y := acp-sof-mach.o
snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
@ -41,4 +43,6 @@ obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
obj-$(CONFIG_SND_SOC_ACPI_AMD_MATCH) += snd-soc-acpi-amd-match.o
obj-$(CONFIG_SND_SOC_AMD_SDW_MACH_COMMON) += snd-acp-sdw-mach.o
obj-$(CONFIG_SND_SOC_AMD_SOF_SDW_MACH) += snd-acp-sdw-sof-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_SDW_MACH) += snd-acp-sdw-legacy-mach.o

View File

@ -59,9 +59,9 @@ static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
val |= BIT(1);
switch (chip->acp_rev) {
case ACP63_DEV:
case ACP70_DEV:
case ACP71_DEV:
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, adata->lrclk_div);
val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, adata->bclk_div);
break;
@ -121,8 +121,8 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
}
switch (chip->acp_rev) {
case ACP3X_DEV:
case ACP6X_DEV:
case ACP_RN_PCI_ID:
case ACP_RMB_PCI_ID:
switch (slots) {
case 1 ... 7:
no_of_slots = slots;
@ -135,9 +135,9 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
return -EINVAL;
}
break;
case ACP63_DEV:
case ACP70_DEV:
case ACP71_DEV:
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
switch (slots) {
case 1 ... 31:
no_of_slots = slots;
@ -160,8 +160,8 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
spin_lock_irq(&adata->acp_lock);
list_for_each_entry(stream, &adata->stream_list, list) {
switch (chip->acp_rev) {
case ACP3X_DEV:
case ACP6X_DEV:
case ACP_RN_PCI_ID:
case ACP_RMB_PCI_ID:
if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
adata->tdm_tx_fmt[stream->dai_id - 1] =
FRM_LEN | (slots << 15) | (slot_len << 18);
@ -169,9 +169,9 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
adata->tdm_rx_fmt[stream->dai_id - 1] =
FRM_LEN | (slots << 15) | (slot_len << 18);
break;
case ACP63_DEV:
case ACP70_DEV:
case ACP71_DEV:
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
adata->tdm_tx_fmt[stream->dai_id - 1] =
FRM_LEN | (slots << 13) | (slot_len << 18);
@ -534,7 +534,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_I2S_TX_FIFOADDR(adata);
reg_fifo_size = ACP_I2S_TX_FIFOSIZE(adata);
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START;
else
phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
@ -546,7 +546,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_I2S_RX_FIFOADDR(adata);
reg_fifo_size = ACP_I2S_RX_FIFOSIZE(adata);
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START;
else
phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
@ -561,7 +561,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_BT_TX_FIFOADDR(adata);
reg_fifo_size = ACP_BT_TX_FIFOSIZE(adata);
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START;
else
phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
@ -573,7 +573,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_BT_RX_FIFOADDR(adata);
reg_fifo_size = ACP_BT_RX_FIFOSIZE(adata);
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START;
else
phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
@ -588,7 +588,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_HS_TX_FIFOADDR;
reg_fifo_size = ACP_HS_TX_FIFOSIZE;
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START;
else
phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
@ -600,7 +600,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
reg_fifo_addr = ACP_HS_RX_FIFOADDR;
reg_fifo_size = ACP_HS_RX_FIFOSIZE;
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START;
else
phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;

View File

@ -257,20 +257,20 @@ static int acp_power_on(struct acp_chip_info *chip)
base = chip->base;
switch (chip->acp_rev) {
case ACP3X_DEV:
case ACP_RN_PCI_ID:
acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;
acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;
break;
case ACP6X_DEV:
case ACP_RMB_PCI_ID:
acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;
acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;
break;
case ACP63_DEV:
case ACP63_PCI_ID:
acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS;
acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL;
break;
case ACP70_DEV:
case ACP71_DEV:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;
acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;
break;
@ -322,7 +322,7 @@ int acp_init(struct acp_chip_info *chip)
pr_err("ACP reset failed\n");
return ret;
}
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
writel(0, chip->base + ACP_ZSC_DSP_CTRL);
return 0;
}
@ -337,7 +337,7 @@ int acp_deinit(struct acp_chip_info *chip)
if (ret)
return ret;
if (chip->acp_rev < ACP70_DEV)
if (chip->acp_rev < ACP70_PCI_ID)
writel(0, chip->base + ACP_CONTROL);
else
writel(0x01, chip->base + ACP_ZSC_DSP_CTRL);
@ -448,20 +448,20 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
u32 pdm_addr;
switch (chip->acp_rev) {
case ACP3X_DEV:
case ACP_RN_PCI_ID:
pdm_addr = ACP_RENOIR_PDM_ADDR;
check_acp3x_config(chip);
break;
case ACP6X_DEV:
case ACP_RMB_PCI_ID:
pdm_addr = ACP_REMBRANDT_PDM_ADDR;
check_acp6x_config(chip);
break;
case ACP63_DEV:
case ACP63_PCI_ID:
pdm_addr = ACP63_PDM_ADDR;
check_acp6x_config(chip);
break;
case ACP70_DEV:
case ACP71_DEV:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
pdm_addr = ACP70_PDM_ADDR;
check_acp70_config(chip);
break;

View File

@ -57,7 +57,6 @@ static struct acp_card_drvdata es83xx_rn_data = {
.dmic_cpu_id = DMIC,
.hs_codec_id = ES83XX,
.dmic_codec_id = DMIC,
.platform = RENOIR,
};
static struct acp_card_drvdata max_nau8825_data = {
@ -68,7 +67,6 @@ static struct acp_card_drvdata max_nau8825_data = {
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
.tdm_mode = false,
};
@ -80,7 +78,6 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
.tdm_mode = false,
};
@ -126,6 +123,7 @@ static int acp_asoc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
const struct dmi_system_id *dmi_id;
struct acp_card_drvdata *acp_card_drvdata;
int ret;
@ -171,7 +169,9 @@ static int acp_asoc_probe(struct platform_device *pdev)
goto out;
}
if (!strcmp(pdev->name, "acp-pdm-mach"))
acp_card_drvdata->platform = *((int *)dev->platform_data);
acp_card_drvdata->acp_rev = *((int *)dev->platform_data);
else
acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
dmi_id = dmi_first_match(acp_quirk_table);
if (dmi_id && dmi_id->driver_data)

View File

@ -1407,8 +1407,6 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(sof_sp);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
if (!drv_data->hs_codec_id) {
@ -1444,8 +1442,6 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(sof_hs);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
if (!drv_data->hs_codec_id) {
@ -1471,7 +1467,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
if (drv_data->amp_cpu_id == I2S_SP) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
if (drv_data->platform == RENOIR) {
if (drv_data->acp_rev == ACP_RN_PCI_ID) {
links[i].cpus = sof_sp;
links[i].num_cpus = ARRAY_SIZE(sof_sp);
} else {
@ -1480,7 +1476,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
}
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
links[i].playback_only = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
if (!drv_data->amp_codec_id) {
@ -1512,7 +1508,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
links[i].playback_only = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
if (!drv_data->amp_codec_id) {
@ -1527,7 +1523,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].init = acp_card_maxim_init;
}
if (drv_data->amp_codec_id == MAX98388) {
links[i].dpcm_capture = 1;
links[i].playback_only = 0;
links[i].codecs = max98388;
links[i].num_codecs = ARRAY_SIZE(max98388);
links[i].ops = &acp_max98388_ops;
@ -1553,8 +1549,6 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(sof_bt);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
if (!drv_data->bt_codec_id) {
@ -1574,7 +1568,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(sof_dmic);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_capture = 1;
links[i].capture_only = 1;
links[i].nonatomic = true;
links[i].no_pcm = 1;
}
@ -1613,8 +1607,6 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(i2s_sp);
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = &snd_soc_dummy_dlc;
@ -1647,18 +1639,21 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].id = HEADSET_BE_ID;
links[i].cpus = i2s_hs;
links[i].num_cpus = ARRAY_SIZE(i2s_hs);
if (drv_data->platform == REMBRANDT) {
switch (drv_data->acp_rev) {
case ACP_RMB_PCI_ID:
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else if (drv_data->platform == ACP63) {
break;
case ACP63_PCI_ID:
links[i].platforms = platform_acp63_component;
links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
} else {
break;
default:
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
break;
}
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = &snd_soc_dummy_dlc;
@ -1686,7 +1681,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].num_cpus = ARRAY_SIZE(i2s_sp);
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
links[i].dpcm_playback = 1;
links[i].playback_only = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = &snd_soc_dummy_dlc;
@ -1714,17 +1709,22 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].id = AMP_BE_ID;
links[i].cpus = i2s_hs;
links[i].num_cpus = ARRAY_SIZE(i2s_hs);
if (drv_data->platform == REMBRANDT) {
switch (drv_data->acp_rev) {
case ACP_RMB_PCI_ID:
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else if (drv_data->platform == ACP63) {
break;
case ACP63_PCI_ID:
links[i].platforms = platform_acp63_component;
links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
} else {
break;
default:
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
break;
}
links[i].dpcm_playback = 1;
links[i].playback_only = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = &snd_soc_dummy_dlc;
@ -1749,6 +1749,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
if (drv_data->dmic_cpu_id == DMIC) {
links[i].name = "acp-dmic-codec";
links[i].stream_name = "DMIC capture";
links[i].id = DMIC_BE_ID;
if (drv_data->dmic_codec_id == DMIC) {
links[i].codecs = dmic_codec;
@ -1760,21 +1761,27 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
}
links[i].cpus = pdm_dmic;
links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
if (drv_data->platform == REMBRANDT) {
switch (drv_data->acp_rev) {
case ACP_RMB_PCI_ID:
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else if (drv_data->platform == ACP63) {
break;
case ACP63_PCI_ID:
links[i].platforms = platform_acp63_component;
links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
} else if ((drv_data->platform == ACP70) || (drv_data->platform == ACP71)) {
break;
case ACP70_PCI_ID:
case ACP71_PCI_ID:
links[i].platforms = platform_acp70_component;
links[i].num_platforms = ARRAY_SIZE(platform_acp70_component);
} else {
break;
default:
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
break;
}
links[i].ops = &acp_card_dmic_ops;
links[i].dpcm_capture = 1;
links[i].capture_only = 1;
}
card->dai_link = links;

View File

@ -18,6 +18,8 @@
#include <linux/module.h>
#include <sound/soc.h>
#include "acp_common.h"
#define TDM_CHANNELS 8
#define ACP_OPS(priv, cb) ((priv)->ops.cb)
@ -51,14 +53,6 @@ enum codec_endpoints {
ES83XX,
};
enum platform_end_point {
RENOIR = 0,
REMBRANDT,
ACP63,
ACP70,
ACP71,
};
struct acp_mach_ops {
int (*probe)(struct snd_soc_card *card);
int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
@ -77,7 +71,7 @@ struct acp_card_drvdata {
unsigned int bt_codec_id;
unsigned int dmic_codec_id;
unsigned int dai_fmt;
unsigned int platform;
unsigned int acp_rev;
struct clk *wclk;
struct clk *bclk;
struct acp_mach_ops ops;

View File

@ -77,27 +77,22 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
res_acp = acp_res;
num_res = ARRAY_SIZE(acp_res);
chip->acp_rev = pci->revision;
switch (pci->revision) {
case 0x01:
chip->name = "acp_asoc_renoir";
chip->acp_rev = ACP3X_DEV;
break;
case 0x6f:
chip->name = "acp_asoc_rembrandt";
chip->acp_rev = ACP6X_DEV;
break;
case 0x63:
chip->name = "acp_asoc_acp63";
chip->acp_rev = ACP63_DEV;
break;
case 0x70:
chip->name = "acp_asoc_acp70";
chip->acp_rev = ACP70_DEV;
break;
case 0x71:
chip->name = "acp_asoc_acp70";
chip->acp_rev = ACP71_DEV;
break;
default:
dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);

View File

@ -47,7 +47,7 @@ static int acp_dmic_prepare(struct snd_pcm_substream *substream,
size_dmic = frames_to_bytes(substream->runtime,
substream->runtime->buffer_size);
if (chip->acp_rev >= ACP70_DEV)
if (chip->acp_rev >= ACP70_PCI_ID)
physical_addr = ACP7x_DMIC_MEM_WINDOW_START;
else
physical_addr = stream->reg_offset + MEM_WINDOW_START;

View File

@ -114,7 +114,7 @@ int acp_machine_select(struct acp_dev_data *adata)
int size, platform;
if (adata->flag == FLAG_AMD_LEGACY_ONLY_DMIC) {
platform = adata->platform;
platform = adata->acp_rev;
adata->mach_dev = platform_device_register_data(adata->dev, "acp-pdm-mach",
PLATFORM_DEVID_NONE, &platform,
sizeof(platform));
@ -125,6 +125,7 @@ int acp_machine_select(struct acp_dev_data *adata)
dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
return -EINVAL;
}
mach->mach_params.subsystem_rev = adata->acp_rev;
adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
PLATFORM_DEVID_NONE, mach, size);
}
@ -142,9 +143,6 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
u16 i2s_flag = 0;
u32 ext_intr_stat, ext_intr_stat1;
if (!adata)
return IRQ_NONE;
if (adata->rsrc->no_of_ctrls == 2)
ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
@ -204,9 +202,9 @@ void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int s
u32 low, high, val;
u16 page_idx;
switch (adata->platform) {
case ACP70:
case ACP71:
switch (adata->acp_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
switch (stream->dai_id) {
case I2S_SP_INSTANCE:
if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
@ -270,9 +268,9 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs
stream->substream = substream;
chip = dev_get_platdata(dev);
switch (chip->acp_rev) {
case ACP63_DEV:
case ACP70_DEV:
case ACP71_DEV:
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = acp6x_pcm_hardware_playback;
else

View File

@ -197,7 +197,7 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
return -ENODEV;
}
if (chip->acp_rev != ACP6X_DEV) {
if (chip->acp_rev != ACP_RMB_PCI_ID) {
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
return -ENODEV;
}
@ -227,7 +227,7 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
adata->dai_driver = acp_rmb_dai;
adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
adata->rsrc = &rsrc;
adata->platform = REMBRANDT;
adata->acp_rev = chip->acp_rev;
adata->flag = chip->flag;
adata->is_i2s_config = chip->is_i2s_config;
adata->machines = snd_soc_acpi_amd_rmb_acp_machines;

View File

@ -157,7 +157,7 @@ static int renoir_audio_probe(struct platform_device *pdev)
return -ENODEV;
}
if (chip->acp_rev != ACP3X_DEV) {
if (chip->acp_rev != ACP_RN_PCI_ID) {
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
return -ENODEV;
}
@ -185,7 +185,7 @@ static int renoir_audio_probe(struct platform_device *pdev)
adata->dai_driver = acp_renoir_dai;
adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
adata->rsrc = &rsrc;
adata->platform = RENOIR;
adata->acp_rev = chip->acp_rev;
adata->flag = chip->flag;
adata->machines = snd_soc_acpi_amd_acp_machines;

View File

@ -0,0 +1,486 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2024 Advanced Micro Devices, Inc.
/*
* acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms
*/
#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "soc_amd_sdw_common.h"
#include "../../codecs/rt711.h"
static unsigned long soc_sdw_quirk = RT711_JD1;
static int quirk_override = -1;
module_param_named(quirk, quirk_override, int, 0444);
MODULE_PARM_DESC(quirk, "Board-specific quirk override");
static void log_quirks(struct device *dev)
{
if (SOC_JACK_JDSRC(soc_sdw_quirk))
dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
SOC_JACK_JDSRC(soc_sdw_quirk));
if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
}
static int soc_sdw_quirk_cb(const struct dmi_system_id *id)
{
soc_sdw_quirk = (unsigned long)id->driver_data;
return 1;
}
static const struct dmi_system_id soc_sdw_quirk_table[] = {
{
.callback = soc_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
},
.driver_data = (void *)RT711_JD2,
},
{}
};
static const struct snd_soc_ops sdw_ops = {
.startup = asoc_sdw_startup,
.prepare = asoc_sdw_prepare,
.trigger = asoc_sdw_trigger,
.hw_params = asoc_sdw_hw_params,
.hw_free = asoc_sdw_hw_free,
.shutdown = asoc_sdw_shutdown,
};
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
static int create_sdw_dailink(struct snd_soc_card *card,
struct asoc_sdw_dailink *soc_dai,
struct snd_soc_dai_link **dai_links,
int *be_id, struct snd_soc_codec_conf **codec_conf,
struct snd_soc_dai_link_component *sdw_platform_component)
{
struct device *dev = card->dev;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
struct asoc_sdw_endpoint *soc_end;
int cpu_pin_id;
int stream;
int ret;
list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
if (soc_end->name_prefix) {
(*codec_conf)->dlc.name = soc_end->codec_name;
(*codec_conf)->name_prefix = soc_end->name_prefix;
(*codec_conf)++;
}
if (soc_end->include_sidecar) {
ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf);
if (ret)
return ret;
}
}
for_each_pcm_streams(stream) {
static const char * const sdw_stream_name[] = {
"SDW%d-PIN%d-PLAYBACK",
"SDW%d-PIN%d-CAPTURE",
"SDW%d-PIN%d-PLAYBACK-%s",
"SDW%d-PIN%d-CAPTURE-%s",
};
struct snd_soc_dai_link_ch_map *codec_maps;
struct snd_soc_dai_link_component *codecs;
struct snd_soc_dai_link_component *cpus;
int num_cpus = hweight32(soc_dai->link_mask[stream]);
int num_codecs = soc_dai->num_devs[stream];
int playback, capture;
int j = 0;
char *name;
if (!soc_dai->num_devs[stream])
continue;
soc_end = list_first_entry(&soc_dai->endpoints,
struct asoc_sdw_endpoint, list);
*be_id = soc_end->dai_info->dailink[stream];
if (*be_id < 0) {
dev_err(dev, "Invalid dailink id %d\n", *be_id);
return -EINVAL;
}
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
/* create stream name according to first link id */
if (ctx->append_dai_type) {
name = devm_kasprintf(dev, GFP_KERNEL,
sdw_stream_name[stream + 2],
ffs(soc_end->link_mask) - 1,
cpu_pin_id,
type_strings[soc_end->dai_info->dai_type]);
} else {
name = devm_kasprintf(dev, GFP_KERNEL,
sdw_stream_name[stream],
ffs(soc_end->link_mask) - 1,
cpu_pin_id);
}
if (!name)
return -ENOMEM;
cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
if (!cpus)
return -ENOMEM;
codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
if (!codecs)
return -ENOMEM;
codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
if (!codec_maps)
return -ENOMEM;
list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
if (!soc_end->dai_info->direction[stream])
continue;
int link_num = ffs(soc_end->link_mask) - 1;
cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SDW%d Pin%d",
link_num, cpu_pin_id);
dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
if (!cpus->dai_name)
return -ENOMEM;
codec_maps[j].cpu = 0;
codec_maps[j].codec = j;
codecs[j].name = soc_end->codec_name;
codecs[j].dai_name = soc_end->dai_info->dai_name;
j++;
}
WARN_ON(j != num_codecs);
playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
cpus, num_cpus, sdw_platform_component,
1, codecs, num_codecs,
0, asoc_sdw_rtd_init, &sdw_ops);
/*
* SoundWire DAILINKs use 'stream' functions and Bank Switch operations
* based on wait_for_completion(), tag them as 'nonatomic'.
*/
(*dai_links)->nonatomic = true;
(*dai_links)->ch_maps = codec_maps;
list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
if (soc_end->dai_info->init)
soc_end->dai_info->init(card, *dai_links,
soc_end->codec_info,
playback);
}
(*dai_links)++;
}
return 0;
}
static int create_sdw_dailinks(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links, int *be_id,
struct asoc_sdw_dailink *soc_dais,
struct snd_soc_codec_conf **codec_conf)
{
struct device *dev = card->dev;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
struct snd_soc_dai_link_component *sdw_platform_component;
int ret;
sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
GFP_KERNEL);
if (!sdw_platform_component)
return -ENOMEM;
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
sdw_platform_component->name = "amd_ps_sdw_dma.0";
break;
default:
return -EINVAL;
}
/* generate DAI links by each sdw link */
while (soc_dais->initialised) {
int current_be_id;
ret = create_sdw_dailink(card, soc_dais, dai_links,
&current_be_id, codec_conf, sdw_platform_component);
if (ret)
return ret;
/* Update the be_id to match the highest ID used for SDW link */
if (*be_id < current_be_id)
*be_id = current_be_id;
soc_dais++;
}
return 0;
}
static int create_dmic_dailinks(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
{
struct device *dev = card->dev;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
struct snd_soc_dai_link_component *pdm_cpu;
struct snd_soc_dai_link_component *pdm_platform;
int ret;
pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
if (!pdm_cpu)
return -ENOMEM;
pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
if (!pdm_platform)
return -ENOMEM;
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
pdm_cpu->name = "acp_ps_pdm_dma.0";
pdm_platform->name = "acp_ps_pdm_dma.0";
break;
default:
return -EINVAL;
}
*be_id = ACP_DMIC_BE_ID;
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
0, 1, // DMIC only supports capture
pdm_cpu->name, pdm_platform->name, 1,
"dmic-codec.0", "dmic-hifi", no_pcm,
asoc_sdw_dmic_init, NULL);
if (ret)
return ret;
(*dai_links)++;
return 0;
}
static int soc_card_dai_links_create(struct snd_soc_card *card)
{
struct device *dev = card->dev;
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
int num_devs = 0;
int num_ends = 0;
int num_links;
int be_id = 0;
int ret;
ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
if (ret < 0) {
dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
return ret;
}
/* One per DAI link, worst case is a DAI link for every endpoint */
soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
if (!soc_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
if (!soc_ends)
return -ENOMEM;
ret = asoc_sdw_parse_sdw_endpoints(card, soc_dais, soc_ends, &num_devs);
if (ret < 0)
return ret;
sdw_be_num = ret;
/* enable dmic */
if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
dmic_num = 1;
dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
if (!codec_conf)
return -ENOMEM;
/* allocate BE dailinks */
num_links = sdw_be_num + dmic_num;
dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
if (!dai_links)
return -ENOMEM;
card->codec_conf = codec_conf;
card->num_configs = num_devs;
card->dai_link = dai_links;
card->num_links = num_links;
/* SDW */
if (sdw_be_num) {
ret = create_sdw_dailinks(card, &dai_links, &be_id,
soc_dais, &codec_conf);
if (ret)
return ret;
}
/* dmic */
if (dmic_num > 0) {
if (ctx->ignore_internal_dmic) {
dev_warn(dev, "Ignoring ACP DMIC\n");
} else {
ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);
if (ret)
return ret;
}
}
WARN_ON(codec_conf != card->codec_conf + card->num_configs);
WARN_ON(dai_links != card->dai_link + card->num_links);
return ret;
}
static int mc_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
struct snd_soc_card *card;
struct amd_mc_ctx *amd_ctx;
struct asoc_sdw_mc_private *ctx;
int amp_num = 0, i;
int ret;
amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
if (!amd_ctx)
return -ENOMEM;
amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
ctx->private = amd_ctx;
card = &ctx->card;
card->dev = &pdev->dev;
card->name = "amd-soundwire";
card->owner = THIS_MODULE;
card->late_probe = asoc_sdw_card_late_probe;
snd_soc_card_set_drvdata(card, ctx);
dmi_check_system(soc_sdw_quirk_table);
if (quirk_override != -1) {
dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
soc_sdw_quirk, quirk_override);
soc_sdw_quirk = quirk_override;
}
log_quirks(card->dev);
ctx->mc_quirk = soc_sdw_quirk;
dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);
/* reset amp_num to ensure amp_num++ starts from 0 in each probe */
for (i = 0; i < ctx->codec_info_list_count; i++)
codec_info_list[i].amp_num = 0;
ret = soc_card_dai_links_create(card);
if (ret < 0)
return ret;
/*
* the default amp_num is zero for each codec and
* amp_num will only be increased for active amp
* codecs on used platform
*/
for (i = 0; i < ctx->codec_info_list_count; i++)
amp_num += codec_info_list[i].amp_num;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
" cfg-amp:%d", amp_num);
if (!card->components)
return -ENOMEM;
if (mach->mach_params.dmic_num) {
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:dmic cfg-mics:%d",
card->components,
mach->mach_params.dmic_num);
if (!card->components)
return -ENOMEM;
}
/* Register the card */
ret = devm_snd_soc_register_card(card->dev, card);
if (ret) {
dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
asoc_sdw_mc_dailink_exit_loop(card);
return ret;
}
platform_set_drvdata(pdev, card);
return ret;
}
static void mc_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
asoc_sdw_mc_dailink_exit_loop(card);
}
static const struct platform_device_id mc_id_table[] = {
{ "amd_sdw", },
{}
};
MODULE_DEVICE_TABLE(platform, mc_id_table);
static struct platform_driver soc_sdw_driver = {
.driver = {
.name = "amd_sdw",
.pm = &snd_soc_pm_ops,
},
.probe = mc_probe,
.remove_new = mc_remove,
.id_table = mc_id_table,
};
module_platform_driver(soc_sdw_driver);
MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");
MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);
MODULE_IMPORT_NS(SND_SOC_AMD_SDW_MACH);

View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2024 Advanced Micro Devices, Inc.
/*
* acp-sdw-mach-common - Common machine driver helper functions for
* legacy(No DSP) stack and SOF stack.
*/
#include <linux/device.h>
#include <linux/module.h>
#include "soc_amd_sdw_common.h"
int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
{
switch (sdw_link_id) {
case AMD_SDW0:
switch (be_id) {
case SOC_SDW_JACK_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO0_TX;
break;
case SOC_SDW_JACK_IN_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO0_RX;
break;
case SOC_SDW_AMP_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO1_TX;
break;
case SOC_SDW_AMP_IN_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO1_RX;
break;
case SOC_SDW_DMIC_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO2_RX;
break;
default:
dev_err(dev, "Invalid be id:%d\n", be_id);
return -EINVAL;
}
break;
case AMD_SDW1:
switch (be_id) {
case SOC_SDW_JACK_OUT_DAI_ID:
case SOC_SDW_AMP_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW1_AUDIO0_TX;
break;
case SOC_SDW_JACK_IN_DAI_ID:
case SOC_SDW_AMP_IN_DAI_ID:
case SOC_SDW_DMIC_DAI_ID:
*cpu_pin_id = ACP63_SW1_AUDIO0_RX;
break;
default:
dev_err(dev, "invalid be_id:%d\n", be_id);
return -EINVAL;
}
break;
default:
dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, SND_SOC_AMD_SDW_MACH);
MODULE_DESCRIPTION("AMD SoundWire Common Machine driver");
MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
MODULE_LICENSE("GPL");

View File

@ -64,54 +64,6 @@ static const struct snd_soc_ops sdw_ops = {
.shutdown = asoc_sdw_shutdown,
};
static int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
{
switch (sdw_link_id) {
case AMD_SDW0:
switch (be_id) {
case SOC_SDW_JACK_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO0_TX;
break;
case SOC_SDW_JACK_IN_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO0_RX;
break;
case SOC_SDW_AMP_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO1_TX;
break;
case SOC_SDW_AMP_IN_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO1_RX;
break;
case SOC_SDW_DMIC_DAI_ID:
*cpu_pin_id = ACP63_SW0_AUDIO2_RX;
break;
default:
dev_err(dev, "Invalid be id:%d\n", be_id);
return -EINVAL;
}
break;
case AMD_SDW1:
switch (be_id) {
case SOC_SDW_JACK_OUT_DAI_ID:
case SOC_SDW_AMP_OUT_DAI_ID:
*cpu_pin_id = ACP63_SW1_AUDIO0_TX;
break;
case SOC_SDW_JACK_IN_DAI_ID:
case SOC_SDW_AMP_IN_DAI_ID:
case SOC_SDW_DMIC_DAI_ID:
*cpu_pin_id = ACP63_SW1_AUDIO0_RX;
break;
default:
dev_err(dev, "invalid be_id:%d\n", be_id);
return -EINVAL;
}
break;
default:
dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
return -EINVAL;
}
return 0;
}
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
static int create_sdw_dailink(struct snd_soc_card *card,
@ -236,7 +188,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
cpus, num_cpus, platform_component,
ARRAY_SIZE(platform_component), codecs, num_codecs,
asoc_sdw_rtd_init, &sdw_ops);
1, asoc_sdw_rtd_init, &sdw_ops);
/*
* SoundWire DAILINKs use 'stream' functions and Bank Switch operations
@ -285,7 +237,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
}
static int create_dmic_dailinks(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links, int *be_id)
struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
{
struct device *dev = card->dev;
int ret;
@ -294,7 +246,7 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
0, 1, // DMIC only supports capture
"acp-sof-dmic", platform_component->name,
ARRAY_SIZE(platform_component),
"dmic-codec", "dmic-hifi",
"dmic-codec", "dmic-hifi", no_pcm,
asoc_sdw_dmic_init, NULL);
if (ret)
return ret;
@ -377,7 +329,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
if (ctx->ignore_internal_dmic) {
dev_warn(dev, "Ignoring ACP DMIC\n");
} else {
ret = create_dmic_dailinks(card, &dai_links, &be_id);
ret = create_dmic_dailinks(card, &dai_links, &be_id, 1);
if (ret)
return ret;
}
@ -491,3 +443,4 @@ MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);
MODULE_IMPORT_NS(SND_SOC_AMD_SDW_MACH);

View File

@ -46,7 +46,6 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
.platform = RENOIR,
};
static struct acp_card_drvdata sof_rt5682s_max_data = {
@ -56,7 +55,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
.platform = RENOIR,
};
static struct acp_card_drvdata sof_nau8825_data = {
@ -66,7 +64,6 @@ static struct acp_card_drvdata sof_nau8825_data = {
.hs_codec_id = NAU8825,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
.platform = REMBRANDT,
.soc_mclk = true,
};
@ -77,7 +74,6 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
.platform = REMBRANDT,
.soc_mclk = true,
};
@ -94,6 +90,7 @@ static int acp_sof_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
const struct dmi_system_id *dmi_id;
struct acp_card_drvdata *acp_card_drvdata;
int ret;
@ -116,6 +113,7 @@ static int acp_sof_probe(struct platform_device *pdev)
if (dmi_id && dmi_id->driver_data)
acp_card_drvdata->tdm_mode = dmi_id->driver_data;
acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
ret = acp_sofdsp_dai_links_create(card);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n");

View File

@ -207,7 +207,7 @@ static int acp63_audio_probe(struct platform_device *pdev)
return -ENODEV;
}
if (chip->acp_rev != ACP63_DEV) {
if (chip->acp_rev != ACP63_PCI_ID) {
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
return -ENODEV;
}
@ -237,7 +237,7 @@ static int acp63_audio_probe(struct platform_device *pdev)
adata->dai_driver = acp63_dai;
adata->num_dai = ARRAY_SIZE(acp63_dai);
adata->rsrc = &rsrc;
adata->platform = ACP63;
adata->acp_rev = chip->acp_rev;
adata->flag = chip->flag;
adata->is_i2s_config = chip->is_i2s_config;
adata->machines = snd_soc_acpi_amd_acp63_acp_machines;

View File

@ -142,9 +142,9 @@ static int acp70_i2s_master_clock_generate(struct acp_dev_data *adata)
struct pci_dev *smn_dev;
u32 device_id;
if (adata->platform == ACP70)
if (adata->acp_rev == ACP70_PCI_ID)
device_id = 0x1507;
else if (adata->platform == ACP71)
else if (adata->acp_rev == ACP71_PCI_ID)
device_id = 0x1122;
else
return -ENODEV;
@ -175,8 +175,8 @@ static int acp_acp70_audio_probe(struct platform_device *pdev)
}
switch (chip->acp_rev) {
case ACP70_DEV:
case ACP71_DEV:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
break;
default:
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
@ -209,11 +209,7 @@ static int acp_acp70_audio_probe(struct platform_device *pdev)
adata->num_dai = ARRAY_SIZE(acp70_dai);
adata->rsrc = &rsrc;
adata->machines = snd_soc_acpi_amd_acp70_acp_machines;
if (chip->acp_rev == ACP70_DEV)
adata->platform = ACP70;
else
adata->platform = ACP71;
adata->acp_rev = chip->acp_rev;
adata->flag = chip->flag;
acp_machine_select(adata);

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
*/
/*
* acp_common.h - acp common header file
*/
#ifndef __ACP_COMMON_H
#define __ACP_COMMON_H
#define ACP_RN_PCI_ID 0x01
#define ACP_VANGOGH_PCI_ID 0x50
#define ACP_RMB_PCI_ID 0x6F
#define ACP63_PCI_ID 0x63
#define ACP70_PCI_ID 0x70
#define ACP71_PCI_ID 0x71
#endif

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