Merge remote-tracking branches 'asoc/topic/fsl-spdif', 'asoc/topic/img' and 'asoc/topic/intel' into asoc-next
This commit is contained in:
commit
b9546d09b1
@ -27,6 +27,11 @@ Required properties:
|
||||
Transceiver Clock Diagram" of SoC reference manual.
|
||||
It can also be referred to TxClk_Source bit of
|
||||
register SPDIF_STC.
|
||||
"spba" The spba clock is required when SPDIF is placed as a
|
||||
bus slave of the Shared Peripheral Bus and when two
|
||||
or more bus masters (CPU, DMA or DSP) try to access
|
||||
it. This property is optional depending on the SoC
|
||||
design.
|
||||
|
||||
- big-endian : If this property is absent, the native endian mode
|
||||
will be in use as default, or the big endian mode
|
||||
|
47
Documentation/devicetree/bindings/sound/img,i2s-in.txt
Normal file
47
Documentation/devicetree/bindings/sound/img,i2s-in.txt
Normal file
@ -0,0 +1,47 @@
|
||||
Imagination Technologies I2S Input Controller
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : Compatible list, must contain "img,i2s-in"
|
||||
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names
|
||||
|
||||
- clock-names : Must include the following entry:
|
||||
"sys" The system clock
|
||||
|
||||
- dmas: Contains an entry for each entry in dma-names.
|
||||
|
||||
- dma-names: Must include the following entry:
|
||||
"rx" Single DMA channel used by all active I2S channels
|
||||
|
||||
- img,i2s-channels : Number of I2S channels instantiated in the I2S in block
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- interrupts : Contains the I2S in interrupts. Depending on
|
||||
the configuration, there may be no interrupts, one interrupt,
|
||||
or an interrupt per I2S channel. For the case where there is
|
||||
one interrupt per channel, the interrupts should be listed
|
||||
in ascending channel order
|
||||
|
||||
- resets: Contains a phandle to the I2S in reset signal
|
||||
|
||||
- reset-names: Contains the reset signal name "rst"
|
||||
|
||||
Example:
|
||||
|
||||
i2s_in: i2s-in@18100800 {
|
||||
compatible = "img,i2s-in";
|
||||
reg = <0x18100800 0x200>;
|
||||
interrupts = <GIC_SHARED 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdc 30 0xffffffff 0>;
|
||||
dma-names = "rx";
|
||||
clocks = <&cr_periph SYS_CLK_I2S_IN>;
|
||||
clock-names = "sys";
|
||||
img,i2s-channels = <6>;
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
51
Documentation/devicetree/bindings/sound/img,i2s-out.txt
Normal file
51
Documentation/devicetree/bindings/sound/img,i2s-out.txt
Normal file
@ -0,0 +1,51 @@
|
||||
Imagination Technologies I2S Output Controller
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : Compatible list, must contain "img,i2s-out"
|
||||
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names
|
||||
|
||||
- clock-names : Must include the following entries:
|
||||
"sys" The system clock
|
||||
"ref" The reference clock
|
||||
|
||||
- dmas: Contains an entry for each entry in dma-names.
|
||||
|
||||
- dma-names: Must include the following entry:
|
||||
"tx" Single DMA channel used by all active I2S channels
|
||||
|
||||
- img,i2s-channels : Number of I2S channels instantiated in the I2S out block
|
||||
|
||||
- resets: Contains a phandle to the I2S out reset signal
|
||||
|
||||
- reset-names: Contains the reset signal name "rst"
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- interrupts : Contains the I2S out interrupts. Depending on
|
||||
the configuration, there may be no interrupts, one interrupt,
|
||||
or an interrupt per I2S channel. For the case where there is
|
||||
one interrupt per channel, the interrupts should be listed
|
||||
in ascending channel order
|
||||
|
||||
Example:
|
||||
|
||||
i2s_out: i2s-out@18100A00 {
|
||||
compatible = "img,i2s-out";
|
||||
reg = <0x18100A00 0x200>;
|
||||
interrupts = <GIC_SHARED 13 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdc 23 0xffffffff 0>;
|
||||
dma-names = "tx";
|
||||
clocks = <&cr_periph SYS_CLK_I2S_OUT>,
|
||||
<&clk_core CLK_I2S>;
|
||||
clock-names = "sys", "ref";
|
||||
img,i2s-channels = <6>;
|
||||
resets = <&pistachio_reset PISTACHIO_RESET_I2S_OUT>;
|
||||
reset-names = "rst";
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
44
Documentation/devicetree/bindings/sound/img,parallel-out.txt
Normal file
44
Documentation/devicetree/bindings/sound/img,parallel-out.txt
Normal file
@ -0,0 +1,44 @@
|
||||
Imagination Technologies Parallel Output Controller
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : Compatible list, must contain "img,parallel-out".
|
||||
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
- dmas: Contains an entry for each entry in dma-names.
|
||||
|
||||
- dma-names: Must include the following entry:
|
||||
"tx"
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Includes the following entries:
|
||||
"sys" The system clock
|
||||
"ref" The reference clock
|
||||
|
||||
- resets: Contains a phandle to the parallel out reset signal
|
||||
|
||||
- reset-names: Contains the reset signal name "rst"
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- interrupts : Contains the parallel out interrupt, if present
|
||||
|
||||
Example:
|
||||
|
||||
parallel_out: parallel-out@18100C00 {
|
||||
compatible = "img,parallel-out";
|
||||
reg = <0x18100C00 0x100>;
|
||||
interrupts = <GIC_SHARED 19 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdc 16 0xffffffff 0>;
|
||||
dma-names = "tx";
|
||||
clocks = <&cr_periph SYS_CLK_PAUD_OUT>,
|
||||
<&clk_core CLK_AUDIO_DAC>;
|
||||
clock-names = "sys", "ref";
|
||||
resets = <&pistachio_reset PISTACHIO_RESET_PRL_OUT>;
|
||||
reset-names = "rst";
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
Pistachio internal DAC DT bindings
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "img,pistachio-internal-dac"
|
||||
|
||||
- img,cr-top : Must contain a phandle to the top level control syscon
|
||||
node which contains the internal dac control registers
|
||||
|
||||
- VDD-supply : Digital power supply regulator (+1.8V or +3.3V)
|
||||
|
||||
Examples:
|
||||
|
||||
internal_dac: internal-dac {
|
||||
compatible = "img,pistachio-internal-dac";
|
||||
img,cr-top = <&cr_top>;
|
||||
VDD-supply = <&supply3v3>;
|
||||
};
|
41
Documentation/devicetree/bindings/sound/img,spdif-in.txt
Normal file
41
Documentation/devicetree/bindings/sound/img,spdif-in.txt
Normal file
@ -0,0 +1,41 @@
|
||||
Imagination Technologies SPDIF Input Controller
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : Compatible list, must contain "img,spdif-in"
|
||||
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
- dmas: Contains an entry for each entry in dma-names.
|
||||
|
||||
- dma-names: Must include the following entry:
|
||||
"rx"
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names
|
||||
|
||||
- clock-names : Includes the following entries:
|
||||
"sys" The system clock
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- resets: Should contain a phandle to the spdif in reset signal, if any
|
||||
|
||||
- reset-names: Should contain the reset signal name "rst", if a
|
||||
reset phandle is given
|
||||
|
||||
- interrupts : Contains the spdif in interrupt, if present
|
||||
|
||||
Example:
|
||||
|
||||
spdif_in: spdif-in@18100E00 {
|
||||
compatible = "img,spdif-in";
|
||||
reg = <0x18100E00 0x100>;
|
||||
interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdc 15 0xffffffff 0>;
|
||||
dma-names = "rx";
|
||||
clocks = <&cr_periph SYS_CLK_SPDIF_IN>;
|
||||
clock-names = "sys";
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
44
Documentation/devicetree/bindings/sound/img,spdif-out.txt
Normal file
44
Documentation/devicetree/bindings/sound/img,spdif-out.txt
Normal file
@ -0,0 +1,44 @@
|
||||
Imagination Technologies SPDIF Output Controller
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : Compatible list, must contain "img,spdif-out"
|
||||
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
- dmas: Contains an entry for each entry in dma-names.
|
||||
|
||||
- dma-names: Must include the following entry:
|
||||
"tx"
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Includes the following entries:
|
||||
"sys" The system clock
|
||||
"ref" The reference clock
|
||||
|
||||
- resets: Contains a phandle to the spdif out reset signal
|
||||
|
||||
- reset-names: Contains the reset signal name "rst"
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- interrupts : Contains the parallel out interrupt, if present
|
||||
|
||||
Example:
|
||||
|
||||
spdif_out: spdif-out@18100D00 {
|
||||
compatible = "img,spdif-out";
|
||||
reg = <0x18100D00 0x100>;
|
||||
interrupts = <GIC_SHARED 21 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdc 14 0xffffffff 0>;
|
||||
dma-names = "tx";
|
||||
clocks = <&cr_periph SYS_CLK_SPDIF_OUT>,
|
||||
<&clk_core CLK_SPDIF>;
|
||||
clock-names = "sys", "ref";
|
||||
resets = <&pistachio_reset PISTACHIO_RESET_SPDIF_OUT>;
|
||||
reset-names = "rst";
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
49
Documentation/sound/alsa/img,spdif-in.txt
Normal file
49
Documentation/sound/alsa/img,spdif-in.txt
Normal file
@ -0,0 +1,49 @@
|
||||
The Imagination Technologies SPDIF Input controller contains the following
|
||||
controls:
|
||||
|
||||
name='IEC958 Capture Mask',index=0
|
||||
|
||||
This control returns a mask that shows which of the IEC958 status bits
|
||||
can be read using the 'IEC958 Capture Default' control.
|
||||
|
||||
name='IEC958 Capture Default',index=0
|
||||
|
||||
This control returns the status bits contained within the SPDIF stream that
|
||||
is being received. The 'IEC958 Capture Mask' shows which bits can be read
|
||||
from this control.
|
||||
|
||||
name='SPDIF In Multi Frequency Acquire',index=0
|
||||
name='SPDIF In Multi Frequency Acquire',index=1
|
||||
name='SPDIF In Multi Frequency Acquire',index=2
|
||||
name='SPDIF In Multi Frequency Acquire',index=3
|
||||
|
||||
This control is used to attempt acquisition of up to four different sample
|
||||
rates. The active rate can be obtained by reading the 'SPDIF In Lock Frequency'
|
||||
control.
|
||||
|
||||
When the value of this control is set to {0,0,0,0}, the rate given to hw_params
|
||||
will determine the single rate the block will capture. Else, the rate given to
|
||||
hw_params will be ignored, and the block will attempt capture for each of the
|
||||
four sample rates set here.
|
||||
|
||||
If less than four rates are required, the same rate can be specified more than
|
||||
once
|
||||
|
||||
name='SPDIF In Lock Frequency',index=0
|
||||
|
||||
This control returns the active capture rate, or 0 if a lock has not been
|
||||
acquired
|
||||
|
||||
name='SPDIF In Lock TRK',index=0
|
||||
|
||||
This control is used to modify the locking/jitter rejection characteristics
|
||||
of the block. Larger values increase the locking range, but reduce jitter
|
||||
rejection.
|
||||
|
||||
name='SPDIF In Lock Acquire Threshold',index=0
|
||||
|
||||
This control is used to change the threshold at which a lock is acquired.
|
||||
|
||||
name='SPDIF In Lock Release Threshold',index=0
|
||||
|
||||
This control is used to change the threshold at which a lock is released.
|
@ -55,6 +55,7 @@ enum sst_audio_device_id_mrfld {
|
||||
PIPE_MEDIA0_IN = 0x8F,
|
||||
PIPE_MEDIA1_IN = 0x90,
|
||||
PIPE_MEDIA2_IN = 0x91,
|
||||
PIPE_MEDIA3_IN = 0x9C,
|
||||
PIPE_RSVD = 0xFF,
|
||||
};
|
||||
|
||||
|
@ -186,9 +186,15 @@ struct hdac_ext_device {
|
||||
/* codec ops */
|
||||
struct hdac_ext_codec_ops ops;
|
||||
|
||||
struct snd_card *card;
|
||||
void *scodec;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct hdac_ext_dma_params {
|
||||
u32 format;
|
||||
u8 stream_tag;
|
||||
};
|
||||
#define to_ehdac_device(dev) (container_of((dev), \
|
||||
struct hdac_ext_device, hdac))
|
||||
/*
|
||||
|
@ -50,6 +50,7 @@ source "sound/soc/jz4740/Kconfig"
|
||||
source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
source "sound/soc/mediatek/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
|
@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += davinci/
|
||||
obj-$(CONFIG_SND_SOC) += dwc/
|
||||
obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += img/
|
||||
obj-$(CONFIG_SND_SOC) += intel/
|
||||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
|
@ -68,6 +68,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_GTM601
|
||||
select SND_SOC_HDAC_HDMI
|
||||
select SND_SOC_ICS43432
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
@ -483,6 +484,11 @@ config SND_SOC_ES8328_SPI
|
||||
config SND_SOC_GTM601
|
||||
tristate 'GTM601 UMTS modem audio codec'
|
||||
|
||||
config SND_SOC_HDAC_HDMI
|
||||
tristate
|
||||
select SND_HDA_EXT_CORE
|
||||
select HDMI
|
||||
|
||||
config SND_SOC_ICS43432
|
||||
tristate
|
||||
|
||||
|
@ -61,6 +61,7 @@ snd-soc-es8328-objs := es8328.o
|
||||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-ics43432-objs := ics43432.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
@ -261,6 +262,7 @@ obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
|
659
sound/soc/codecs/hdac_hdmi.c
Normal file
659
sound/soc/codecs/hdac_hdmi.c
Normal file
@ -0,0 +1,659 @@
|
||||
/*
|
||||
* hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
|
||||
*
|
||||
* Copyright (C) 2014-2015 Intel Corp
|
||||
* Author: Samreen Nilofer <samreen.nilofer@intel.com>
|
||||
* Subhransu S. Prusty <subhransu.s.prusty@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include "../../hda/local.h"
|
||||
|
||||
#define AMP_OUT_MUTE 0xb080
|
||||
#define AMP_OUT_UNMUTE 0xb000
|
||||
#define PIN_OUT (AC_PINCTL_OUT_EN)
|
||||
|
||||
#define HDA_MAX_CONNECTIONS 32
|
||||
|
||||
struct hdac_hdmi_cvt_params {
|
||||
unsigned int channels_min;
|
||||
unsigned int channels_max;
|
||||
u32 rates;
|
||||
u64 formats;
|
||||
unsigned int maxbps;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_cvt {
|
||||
hda_nid_t nid;
|
||||
struct hdac_hdmi_cvt_params params;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pin {
|
||||
hda_nid_t nid;
|
||||
int num_mux_nids;
|
||||
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
||||
};
|
||||
|
||||
struct hdac_hdmi_dai_pin_map {
|
||||
int dai_id;
|
||||
struct hdac_hdmi_pin pin;
|
||||
struct hdac_hdmi_cvt cvt;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_priv {
|
||||
hda_nid_t pin_nid[3];
|
||||
hda_nid_t cvt_nid[3];
|
||||
struct hdac_hdmi_dai_pin_map dai_map[3];
|
||||
};
|
||||
|
||||
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
|
||||
{
|
||||
struct hdac_device *hdac = container_of(dev, struct hdac_device, dev);
|
||||
|
||||
return container_of(hdac, struct hdac_ext_device, hdac);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
|
||||
cvt_nid, pin_nid, stream_tag, format);
|
||||
|
||||
val = (stream_tag << 4);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, val);
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid)
|
||||
{
|
||||
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
|
||||
struct hdmi_audio_infoframe frame;
|
||||
u8 *dip = (u8 *)&frame;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hdmi_audio_infoframe_init(&frame);
|
||||
|
||||
/* Default stereo for now */
|
||||
frame.channels = 2;
|
||||
|
||||
/* setup channel count */
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
|
||||
|
||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* stop infoframe transmission */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
|
||||
|
||||
|
||||
/* Fill infoframe. Index auto-incremented */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(frame); i++)
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
|
||||
|
||||
/* Start infoframe */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
|
||||
{
|
||||
/* Power up pin widget */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin.nid, pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->pin.nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
|
||||
/* Power up converter */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt.nid, pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
struct hdac_ext_dma_params *dd;
|
||||
int ret;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
||||
dd->stream_tag, dd->format);
|
||||
|
||||
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt.nid,
|
||||
dai_map->pin.nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return hdac_hdmi_setup_stream(hdac, dai_map->cvt.nid, dai_map->pin.nid,
|
||||
dd->stream_tag, dd->format);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
|
||||
params_channels(hparams), params_format(hparams),
|
||||
24, 0);
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
|
||||
kfree(dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
int val;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin.nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
|
||||
|
||||
if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
|
||||
dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Only stereo supported as of now */
|
||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
||||
|
||||
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
||||
&cvt->params.rates,
|
||||
&cvt->params.formats,
|
||||
&cvt->params.maxbps);
|
||||
if (err < 0)
|
||||
dev_err(&hdac->dev,
|
||||
"Failed to query pcm params for nid %d: %d\n",
|
||||
cvt->nid, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
|
||||
struct hdac_hdmi_pin *pin)
|
||||
{
|
||||
if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
|
||||
dev_warn(&hdac->hdac.dev,
|
||||
"HDMI: pin %d wcaps %#x does not support connection list\n",
|
||||
pin->nid, get_wcaps(&hdac->hdac, pin->nid));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
|
||||
pin->mux_nids, HDA_MAX_CONNECTIONS);
|
||||
if (pin->num_mux_nids == 0) {
|
||||
dev_err(&hdac->hdac.dev, "No connections found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return pin->num_mux_nids;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
|
||||
enum snd_soc_dapm_type id,
|
||||
const char *wname, const char *stream)
|
||||
{
|
||||
w->id = id;
|
||||
w->name = wname;
|
||||
w->sname = stream;
|
||||
w->reg = SND_SOC_NOPM;
|
||||
w->shift = 0;
|
||||
w->kcontrol_news = NULL;
|
||||
w->num_kcontrols = 0;
|
||||
w->priv = NULL;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
|
||||
const char *sink, const char *control, const char *src)
|
||||
{
|
||||
route->sink = sink;
|
||||
route->source = src;
|
||||
route->control = control;
|
||||
route->connected = NULL;
|
||||
}
|
||||
|
||||
static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map)
|
||||
{
|
||||
struct snd_soc_dapm_route route[1];
|
||||
struct snd_soc_dapm_widget widgets[2] = { {0} };
|
||||
|
||||
memset(&route, 0, sizeof(route));
|
||||
|
||||
hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
|
||||
"hif1 Output", NULL);
|
||||
hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
|
||||
"Coverter 1", "hif1");
|
||||
|
||||
hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
|
||||
snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map,
|
||||
hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dai_map->dai_id = dai_id;
|
||||
dai_map->pin.nid = pin_nid;
|
||||
|
||||
ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin);
|
||||
if (ret < 0) {
|
||||
dev_err(&edev->hdac.dev,
|
||||
"Error querying connection list: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dai_map->cvt.nid = cvt_nid;
|
||||
|
||||
/* Enable out path for this pin widget */
|
||||
snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
|
||||
/* Enable transmission */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, 1);
|
||||
|
||||
/* Category Code (CC) to zero */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, 0);
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, 0);
|
||||
|
||||
return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all nodes and store the cvt/pin nids in array
|
||||
* Add one time initialization for pin and cvt widgets
|
||||
*/
|
||||
static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, num_nodes;
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
int cvt_nid = 0, pin_nid = 0;
|
||||
|
||||
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
|
||||
if (!nid || num_nodes < 0) {
|
||||
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdac->num_nodes = num_nodes;
|
||||
hdac->start_nid = nid;
|
||||
|
||||
for (i = 0; i < hdac->num_nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = get_wcaps(hdac, nid);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case AC_WID_AUD_OUT:
|
||||
hdmi->cvt_nid[cvt_nid] = nid;
|
||||
cvt_nid++;
|
||||
break;
|
||||
|
||||
case AC_WID_PIN:
|
||||
hdmi->pin_nid[pin_nid] = nid;
|
||||
pin_nid++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hdac->end_nid = nid;
|
||||
|
||||
if (!pin_nid || !cvt_nid)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Currently on board only 1 pin and 1 converter is enabled for
|
||||
* simplification, more will be added eventually
|
||||
* So using fixed map for dai_id:pin:cvt
|
||||
*/
|
||||
return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0],
|
||||
hdmi->cvt_nid[0], 0);
|
||||
}
|
||||
|
||||
static int hdmi_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(&codec->component);
|
||||
|
||||
edev->scodec = codec;
|
||||
|
||||
create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
|
||||
|
||||
/* Imp: Store the card pointer in hda_codec */
|
||||
edev->card = dapm->card->snd_card;
|
||||
|
||||
/*
|
||||
* hdac_device core already sets the state to active and calls
|
||||
* get_noresume. So enable runtime and set the device to suspend.
|
||||
*/
|
||||
pm_runtime_enable(&edev->hdac.dev);
|
||||
pm_runtime_put(&edev->hdac.dev);
|
||||
pm_runtime_suspend(&edev->hdac.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
pm_runtime_disable(&edev->hdac.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver hdmi_hda_codec = {
|
||||
.probe = hdmi_codec_probe,
|
||||
.remove = hdmi_codec_remove,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops hdmi_dai_ops = {
|
||||
.startup = hdac_hdmi_pcm_open,
|
||||
.shutdown = hdac_hdmi_pcm_close,
|
||||
.hw_params = hdac_hdmi_set_hw_params,
|
||||
.prepare = hdac_hdmi_playback_prepare,
|
||||
.hw_free = hdac_hdmi_playback_cleanup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver hdmi_dais[] = {
|
||||
{ .name = "intel-hdmi-hif1",
|
||||
.playback = {
|
||||
.stream_name = "hif1",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
|
||||
},
|
||||
.ops = &hdmi_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_device *codec = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi_priv;
|
||||
int ret = 0;
|
||||
|
||||
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
|
||||
if (hdmi_priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
edev->private_data = hdmi_priv;
|
||||
|
||||
dev_set_drvdata(&codec->dev, edev);
|
||||
|
||||
ret = hdac_hdmi_parse_and_map_nid(edev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ASoC specific initialization */
|
||||
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
|
||||
hdmi_dais, ARRAY_SIZE(hdmi_dais));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
|
||||
{
|
||||
snd_soc_unregister_codec(&edev->hdac.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
/* Power down afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
|
||||
|
||||
err = snd_hdac_display_power(bus, false);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
err = snd_hdac_display_power(bus, true);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power up afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define hdac_hdmi_runtime_suspend NULL
|
||||
#define hdac_hdmi_runtime_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops hdac_hdmi_pm = {
|
||||
SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct hda_device_id hdmi_list[] = {
|
||||
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
|
||||
|
||||
static struct hdac_ext_driver hdmi_driver = {
|
||||
. hdac = {
|
||||
.driver = {
|
||||
.name = "HDMI HDA Codec",
|
||||
.pm = &hdac_hdmi_pm,
|
||||
},
|
||||
.id_table = hdmi_list,
|
||||
},
|
||||
.probe = hdac_hdmi_dev_probe,
|
||||
.remove = hdac_hdmi_dev_remove,
|
||||
};
|
||||
|
||||
static int __init hdmi_init(void)
|
||||
{
|
||||
return snd_hda_ext_driver_register(&hdmi_driver);
|
||||
}
|
||||
|
||||
static void __exit hdmi_exit(void)
|
||||
{
|
||||
snd_hda_ext_driver_unregister(&hdmi_driver);
|
||||
}
|
||||
|
||||
module_init(hdmi_init);
|
||||
module_exit(hdmi_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("HDMI HD codec");
|
||||
MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
|
@ -88,6 +88,7 @@ struct spdif_mixer_control {
|
||||
* @rxclk: rx clock sources for capture
|
||||
* @coreclk: core clock for register access via DMA
|
||||
* @sysclk: system clock for rx clock rate measurement
|
||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
*/
|
||||
@ -106,6 +107,7 @@ struct fsl_spdif_priv {
|
||||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct clk *spbaclk;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
/* regcache for SRPC */
|
||||
@ -474,6 +476,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable spba clock\n");
|
||||
goto err_spbaclk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
@ -515,6 +525,9 @@ disable_txclk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
err:
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
err_spbaclk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
@ -548,6 +561,8 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
||||
spdif_intr_status_clear(spdif_priv);
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR,
|
||||
SCR_LOW_POWER, SCR_LOW_POWER);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
@ -1261,6 +1276,10 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(spdif_priv->coreclk);
|
||||
}
|
||||
|
||||
spdif_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
|
||||
if (IS_ERR(spdif_priv->spbaclk))
|
||||
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
|
52
sound/soc/img/Kconfig
Normal file
52
sound/soc/img/Kconfig
Normal file
@ -0,0 +1,52 @@
|
||||
config SND_SOC_IMG
|
||||
bool "Audio support for Imagination Technologies designs"
|
||||
help
|
||||
Audio support for Imagination Technologies audio hardware
|
||||
|
||||
config SND_SOC_IMG_I2S_IN
|
||||
tristate "Imagination I2S Input Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for I2S in driver for
|
||||
Imagination Technologies I2S in device.
|
||||
|
||||
config SND_SOC_IMG_I2S_OUT
|
||||
tristate "Imagination I2S Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for I2S out driver for
|
||||
Imagination Technologies I2S out device.
|
||||
|
||||
config SND_SOC_IMG_PARALLEL_OUT
|
||||
tristate "Imagination Parallel Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for parallel out driver for
|
||||
Imagination Technologies parallel out device.
|
||||
|
||||
config SND_SOC_IMG_SPDIF_IN
|
||||
tristate "Imagination SPDIF Input Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for SPDIF input driver for
|
||||
Imagination Technologies SPDIF input device.
|
||||
|
||||
config SND_SOC_IMG_SPDIF_OUT
|
||||
tristate "Imagination SPDIF Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for SPDIF out driver for
|
||||
Imagination Technologies SPDIF out device.
|
||||
|
||||
|
||||
config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC
|
||||
tristate "Support for Pistachio SoC Internal DAC Driver"
|
||||
depends on SND_SOC_IMG
|
||||
help
|
||||
Say Y or M if you want to add support for Pistachio internal DAC
|
||||
driver for Imagination Technologies Pistachio internal DAC device.
|
7
sound/soc/img/Makefile
Normal file
7
sound/soc/img/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_SPDIF_OUT) += img-spdif-out.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC) += pistachio-internal-dac.o
|
516
sound/soc/img/img-i2s-in.c
Normal file
516
sound/soc/img/img-i2s-in.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* IMG I2S input controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_I2S_IN_RX_FIFO 0x0
|
||||
|
||||
#define IMG_I2S_IN_CTL 0x4
|
||||
#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK 0xfffffffc
|
||||
#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT 2
|
||||
#define IMG_I2S_IN_CTL_16PACK_MASK BIT(1)
|
||||
#define IMG_I2S_IN_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_IN_CH_CTL 0x4
|
||||
#define IMG_I2S_IN_CH_CTL_CCDEL_MASK 0x38000
|
||||
#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT 15
|
||||
#define IMG_I2S_IN_CH_CTL_FEN_MASK BIT(14)
|
||||
#define IMG_I2S_IN_CH_CTL_FMODE_MASK BIT(13)
|
||||
#define IMG_I2S_IN_CH_CTL_16PACK_MASK BIT(12)
|
||||
#define IMG_I2S_IN_CH_CTL_JUST_MASK BIT(10)
|
||||
#define IMG_I2S_IN_CH_CTL_PACKH_MASK BIT(9)
|
||||
#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK BIT(8)
|
||||
#define IMG_I2S_IN_CH_CTL_BLKP_MASK BIT(7)
|
||||
#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK BIT(6)
|
||||
#define IMG_I2S_IN_CH_CTL_LRD_MASK BIT(3)
|
||||
#define IMG_I2S_IN_CH_CTL_FW_MASK BIT(2)
|
||||
#define IMG_I2S_IN_CH_CTL_SW_MASK BIT(1)
|
||||
#define IMG_I2S_IN_CH_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_IN_CH_STRIDE 0x20
|
||||
|
||||
struct img_i2s_in {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int max_i2s_chan;
|
||||
void __iomem *channel_base;
|
||||
unsigned int active_channels;
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
};
|
||||
|
||||
static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
|
||||
{
|
||||
return readl(i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
|
||||
u32 reg)
|
||||
{
|
||||
return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
|
||||
reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
|
||||
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
|
||||
reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
|
||||
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg &= ~IMG_I2S_IN_CTL_ME_MASK;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg |= IMG_I2S_IN_CTL_ME_MASK;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
|
||||
{
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
}
|
||||
|
||||
static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
img_i2s_in_enable(i2s);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_i2s_in_disable(i2s);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
|
||||
unsigned int sample_rate, unsigned int frame_size,
|
||||
unsigned int *bclk_filter_enable,
|
||||
unsigned int *bclk_filter_value)
|
||||
{
|
||||
unsigned int bclk_freq, cur_freq;
|
||||
|
||||
bclk_freq = sample_rate * frame_size;
|
||||
|
||||
cur_freq = clk_get_rate(i2s->clk_sys);
|
||||
|
||||
if (cur_freq >= bclk_freq * 8) {
|
||||
*bclk_filter_enable = 1;
|
||||
*bclk_filter_value = 0;
|
||||
} else if (cur_freq >= bclk_freq * 7) {
|
||||
*bclk_filter_enable = 1;
|
||||
*bclk_filter_value = 1;
|
||||
} else if (cur_freq >= bclk_freq * 6) {
|
||||
*bclk_filter_enable = 0;
|
||||
*bclk_filter_value = 0;
|
||||
} else {
|
||||
dev_err(i2s->dev,
|
||||
"Sys clock rate %u insufficient for sample rate %u\n",
|
||||
cur_freq, sample_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels, i2s_channels, frame_size;
|
||||
unsigned int bclk_filter_enable, bclk_filter_value;
|
||||
int i, ret = 0;
|
||||
u32 reg, control_mask, chan_control_mask;
|
||||
u32 control_set = 0, chan_control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
i2s_channels = channels / 2;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
frame_size = 64;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
frame_size = 64;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
frame_size = 32;
|
||||
control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((channels < 2) ||
|
||||
(channels > (i2s->max_i2s_chan * 2)) ||
|
||||
(channels % 2))
|
||||
return -EINVAL;
|
||||
|
||||
control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
|
||||
|
||||
ret = img_i2s_in_check_rate(i2s, rate, frame_size,
|
||||
&bclk_filter_enable, &bclk_filter_value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (bclk_filter_enable)
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
|
||||
|
||||
if (bclk_filter_value)
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
|
||||
|
||||
control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
|
||||
IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FEN_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FMODE_MASK |
|
||||
IMG_I2S_IN_CH_CTL_SW_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FW_MASK |
|
||||
IMG_I2S_IN_CH_CTL_PACKH_MASK;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
i2s->active_channels = i2s_channels;
|
||||
|
||||
img_i2s_in_flush(i2s);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_enable(i2s, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
int i;
|
||||
u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
|
||||
u32 reg;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
|
||||
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
|
||||
/*
|
||||
* BLKP and LRD must be set during separate register writes
|
||||
*/
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_enable(i2s, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
|
||||
.trigger = img_i2s_in_trigger,
|
||||
.hw_params = img_i2s_in_hw_params,
|
||||
.set_fmt = img_i2s_in_set_fmt
|
||||
};
|
||||
|
||||
static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver img_i2s_in_component = {
|
||||
.name = "img-i2s-in"
|
||||
};
|
||||
|
||||
static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
|
||||
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
|
||||
{
|
||||
unsigned int i2s_channels = params_channels(params) / 2;
|
||||
struct snd_soc_pcm_runtime *rtd = st->private_data;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int ret;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sc->src_addr = dma_data->addr;
|
||||
sc->src_addr_width = dma_data->addr_width;
|
||||
sc->src_maxburst = 4 * i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
|
||||
.prepare_slave_config = img_i2s_in_dma_prepare_slave_config
|
||||
};
|
||||
|
||||
static int img_i2s_in_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_in *i2s;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
struct reset_control *rst;
|
||||
unsigned int max_i2s_chan_pow_2;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
|
||||
if (!i2s)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
i2s->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
i2s->base = base;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
|
||||
&i2s->max_i2s_chan)) {
|
||||
dev_err(dev, "No img,i2s-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
|
||||
|
||||
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
|
||||
|
||||
i2s->clk_sys = devm_clk_get(dev, "sys");
|
||||
if (IS_ERR(i2s->clk_sys)) {
|
||||
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(i2s->clk_sys);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2s->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2s->active_channels = 1;
|
||||
i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
|
||||
i2s->dma_data.addr_width = 4;
|
||||
|
||||
i2s->dai_driver.probe = img_i2s_in_dai_probe;
|
||||
i2s->dai_driver.capture.channels_min = 2;
|
||||
i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
|
||||
i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
|
||||
i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
|
||||
i2s->dai_driver.ops = &img_i2s_in_dai_ops;
|
||||
|
||||
rst = devm_reset_control_get(dev, "rst");
|
||||
if (IS_ERR(rst)) {
|
||||
if (PTR_ERR(rst) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "No top level reset found\n");
|
||||
|
||||
img_i2s_in_disable(i2s);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
} else {
|
||||
reset_control_assert(rst);
|
||||
reset_control_deassert(rst);
|
||||
}
|
||||
|
||||
img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_in_ch_writel(i2s, i,
|
||||
(4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
|
||||
IMG_I2S_IN_CH_CTL_JUST_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
|
||||
&i2s->dai_driver, 1);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_i2s_in_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_in *i2s = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_i2s_in_of_match[] = {
|
||||
{ .compatible = "img,i2s-in" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
|
||||
|
||||
static struct platform_driver img_i2s_in_driver = {
|
||||
.driver = {
|
||||
.name = "img-i2s-in",
|
||||
.of_match_table = img_i2s_in_of_match
|
||||
},
|
||||
.probe = img_i2s_in_probe,
|
||||
.remove = img_i2s_in_dev_remove
|
||||
};
|
||||
module_platform_driver(img_i2s_in_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_DESCRIPTION("IMG I2S Input Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
565
sound/soc/img/img-i2s-out.c
Normal file
565
sound/soc/img/img-i2s-out.c
Normal file
@ -0,0 +1,565 @@
|
||||
/*
|
||||
* IMG I2S output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_I2S_OUT_TX_FIFO 0x0
|
||||
|
||||
#define IMG_I2S_OUT_CTL 0x4
|
||||
#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24)
|
||||
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000
|
||||
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13
|
||||
#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8)
|
||||
#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6)
|
||||
#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5)
|
||||
#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4)
|
||||
#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3)
|
||||
#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2)
|
||||
#define IMG_I2S_OUT_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_OUT_CH_CTL 0x4
|
||||
#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0
|
||||
#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4
|
||||
#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_OUT_CH_STRIDE 0x20
|
||||
|
||||
struct img_i2s_out {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int max_i2s_chan;
|
||||
void __iomem *channel_base;
|
||||
bool force_clk_active;
|
||||
unsigned int active_channels;
|
||||
struct reset_control *rst;
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
};
|
||||
|
||||
static int img_i2s_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_i2s_out *i2s = dev_get_drvdata(dev);
|
||||
|
||||
if (!i2s->force_clk_active)
|
||||
clk_disable_unprepare(i2s->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_i2s_out *i2s = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!i2s->force_clk_active) {
|
||||
ret = clk_prepare_enable(i2s->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
|
||||
u32 reg)
|
||||
{
|
||||
writel(val, i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
|
||||
{
|
||||
return readl(i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
|
||||
u32 chan, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
|
||||
u32 reg)
|
||||
{
|
||||
return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
|
||||
reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
|
||||
reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg |= IMG_I2S_OUT_CTL_ME_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
}
|
||||
|
||||
static void img_i2s_out_reset(struct img_i2s_out *i2s)
|
||||
{
|
||||
int i;
|
||||
u32 core_ctl, chan_ctl;
|
||||
|
||||
core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
|
||||
~IMG_I2S_OUT_CTL_ME_MASK &
|
||||
~IMG_I2S_OUT_CTL_DATA_EN_MASK;
|
||||
|
||||
if (!i2s->force_clk_active)
|
||||
core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
|
||||
chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
|
||||
~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
|
||||
reset_control_assert(i2s->rst);
|
||||
reset_control_deassert(i2s->rst);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
|
||||
img_i2s_out_enable(i2s);
|
||||
}
|
||||
|
||||
static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
if (!i2s->force_clk_active)
|
||||
reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_i2s_out_reset(i2s);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int channels, i2s_channels;
|
||||
long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
|
||||
int i;
|
||||
u32 reg, control_mask, control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
i2s_channels = channels / 2;
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if ((channels < 2) ||
|
||||
(channels > (i2s->max_i2s_chan * 2)) ||
|
||||
(channels % 2))
|
||||
return -EINVAL;
|
||||
|
||||
pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
|
||||
if (pre_div_a < 0)
|
||||
return pre_div_a;
|
||||
pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
|
||||
if (pre_div_b < 0)
|
||||
return pre_div_b;
|
||||
|
||||
diff_a = abs((pre_div_a / 256) - rate);
|
||||
diff_b = abs((pre_div_b / 384) - rate);
|
||||
|
||||
/* If diffs are equal, use lower clock rate */
|
||||
if (diff_a > diff_b)
|
||||
clk_set_rate(i2s->clk_ref, pre_div_b);
|
||||
else
|
||||
clk_set_rate(i2s->clk_ref, pre_div_a);
|
||||
|
||||
/*
|
||||
* Another driver (eg alsa machine driver) may have rejected the above
|
||||
* change. Get the current rate and set the register bit according to
|
||||
* the new minimum diff
|
||||
*/
|
||||
clk_rate = clk_get_rate(i2s->clk_ref);
|
||||
|
||||
diff_a = abs((clk_rate / 256) - rate);
|
||||
diff_b = abs((clk_rate / 384) - rate);
|
||||
|
||||
if (diff_a > diff_b)
|
||||
control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
|
||||
|
||||
control_set |= ((i2s_channels - 1) <<
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
img_i2s_out_disable(i2s);
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
for (i = 0; i < i2s_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
for (; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_disable(i2s, i);
|
||||
|
||||
img_i2s_out_enable(i2s);
|
||||
|
||||
i2s->active_channels = i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
int i;
|
||||
bool force_clk_active;
|
||||
u32 chan_control_mask, control_mask, chan_control_set = 0;
|
||||
u32 reg, control_set = 0;
|
||||
|
||||
force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
|
||||
SND_SOC_DAIFMT_CONT);
|
||||
|
||||
if (force_clk_active)
|
||||
control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
|
||||
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
|
||||
IMG_I2S_OUT_CTL_MASTER_MASK |
|
||||
IMG_I2S_OUT_CTL_BCLK_POL_MASK |
|
||||
IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
|
||||
chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
|
||||
|
||||
img_i2s_out_disable(i2s);
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_disable(i2s, i);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
img_i2s_out_enable(i2s);
|
||||
|
||||
i2s->force_clk_active = force_clk_active;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
|
||||
.trigger = img_i2s_out_trigger,
|
||||
.hw_params = img_i2s_out_hw_params,
|
||||
.set_fmt = img_i2s_out_set_fmt
|
||||
};
|
||||
|
||||
static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver img_i2s_out_component = {
|
||||
.name = "img-i2s-out"
|
||||
};
|
||||
|
||||
static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
|
||||
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
|
||||
{
|
||||
unsigned int i2s_channels = params_channels(params) / 2;
|
||||
struct snd_soc_pcm_runtime *rtd = st->private_data;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int ret;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sc->dst_addr = dma_data->addr;
|
||||
sc->dst_addr_width = dma_data->addr_width;
|
||||
sc->dst_maxburst = 4 * i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
|
||||
.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
|
||||
};
|
||||
|
||||
static int img_i2s_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_out *i2s;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int i, ret;
|
||||
unsigned int max_i2s_chan_pow_2;
|
||||
u32 reg;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
|
||||
if (!i2s)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
i2s->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
i2s->base = base;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
|
||||
&i2s->max_i2s_chan)) {
|
||||
dev_err(&pdev->dev, "No img,i2s-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
|
||||
|
||||
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
|
||||
|
||||
i2s->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(i2s->rst)) {
|
||||
if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(i2s->rst);
|
||||
}
|
||||
|
||||
i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(i2s->clk_sys)) {
|
||||
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(i2s->clk_sys);
|
||||
}
|
||||
|
||||
i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(i2s->clk_ref)) {
|
||||
if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(i2s->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2s->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
|
||||
IMG_I2S_OUT_CHAN_CTL_LT_MASK |
|
||||
IMG_I2S_OUT_CHAN_CTL_CH_MASK |
|
||||
(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
|
||||
|
||||
img_i2s_out_reset(i2s);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_i2s_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
i2s->active_channels = 1;
|
||||
i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
|
||||
i2s->dma_data.addr_width = 4;
|
||||
i2s->dma_data.maxburst = 4;
|
||||
|
||||
i2s->dai_driver.probe = img_i2s_out_dai_probe;
|
||||
i2s->dai_driver.playback.channels_min = 2;
|
||||
i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
|
||||
i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
|
||||
i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
i2s->dai_driver.ops = &img_i2s_out_dai_ops;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_i2s_out_component, &i2s->dai_driver, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
|
||||
&img_i2s_out_dma_config, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_i2s_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_i2s_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_out *i2s = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_i2s_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_i2s_out_of_match[] = {
|
||||
{ .compatible = "img,i2s-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_i2s_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_i2s_out_suspend,
|
||||
img_i2s_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_i2s_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-i2s-out",
|
||||
.of_match_table = img_i2s_out_of_match,
|
||||
.pm = &img_i2s_out_pm_ops
|
||||
},
|
||||
.probe = img_i2s_out_probe,
|
||||
.remove = img_i2s_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_i2s_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_DESCRIPTION("IMG I2S Output Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
327
sound/soc/img/img-parallel-out.c
Normal file
327
sound/soc/img/img-parallel-out.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* IMG parallel output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_PRL_OUT_TX_FIFO 0
|
||||
|
||||
#define IMG_PRL_OUT_CTL 0x4
|
||||
#define IMG_PRL_OUT_CTL_CH_MASK BIT(4)
|
||||
#define IMG_PRL_OUT_CTL_PACKH_MASK BIT(3)
|
||||
#define IMG_PRL_OUT_CTL_EDGE_MASK BIT(2)
|
||||
#define IMG_PRL_OUT_CTL_ME_MASK BIT(1)
|
||||
#define IMG_PRL_OUT_CTL_SRST_MASK BIT(0)
|
||||
|
||||
struct img_prl_out {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
static int img_prl_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_prl_out *prl = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(prl->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_prl_out *prl = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(prl->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_prl_out_writel(struct img_prl_out *prl,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, prl->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg)
|
||||
{
|
||||
return readl(prl->base + reg);
|
||||
}
|
||||
|
||||
static void img_prl_out_reset(struct img_prl_out *prl)
|
||||
{
|
||||
u32 ctl;
|
||||
|
||||
ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) &
|
||||
~IMG_PRL_OUT_CTL_ME_MASK;
|
||||
|
||||
reset_control_assert(prl->rst);
|
||||
reset_control_deassert(prl->rst);
|
||||
|
||||
img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL);
|
||||
}
|
||||
|
||||
static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg |= IMG_PRL_OUT_CTL_ME_MASK;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_prl_out_reset(prl);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels;
|
||||
u32 reg, control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
control_set |= IMG_PRL_OUT_CTL_PACKH_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
clk_set_rate(prl->clk_ref, rate * 256);
|
||||
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg, control_set = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
control_set |= IMG_PRL_OUT_CTL_EDGE_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
|
||||
.trigger = img_prl_out_trigger,
|
||||
.hw_params = img_prl_out_hw_params,
|
||||
.set_fmt = img_prl_out_set_fmt
|
||||
};
|
||||
|
||||
static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_prl_out_dai = {
|
||||
.probe = img_prl_out_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE
|
||||
},
|
||||
.ops = &img_prl_out_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_prl_out_component = {
|
||||
.name = "img-prl-out"
|
||||
};
|
||||
|
||||
static int img_prl_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_prl_out *prl;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);
|
||||
if (!prl)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, prl);
|
||||
|
||||
prl->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
prl->base = base;
|
||||
|
||||
prl->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(prl->rst)) {
|
||||
if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(prl->rst);
|
||||
}
|
||||
|
||||
prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(prl->clk_sys)) {
|
||||
if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(prl->clk_sys);
|
||||
}
|
||||
|
||||
prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(prl->clk_ref)) {
|
||||
if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(prl->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(prl->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);
|
||||
img_prl_out_reset(prl);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_prl_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;
|
||||
prl->dma_data.addr_width = 4;
|
||||
prl->dma_data.maxburst = 4;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_prl_out_component,
|
||||
&img_prl_out_dai, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_prl_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(prl->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_prl_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_prl_out *prl = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_prl_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(prl->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_prl_out_of_match[] = {
|
||||
{ .compatible = "img,parallel-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_prl_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_prl_out_suspend,
|
||||
img_prl_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_prl_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-parallel-out",
|
||||
.of_match_table = img_prl_out_of_match,
|
||||
.pm = &img_prl_out_pm_ops
|
||||
},
|
||||
.probe = img_prl_out_probe,
|
||||
.remove = img_prl_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_prl_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_DESCRIPTION("IMG Parallel Output Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
806
sound/soc/img/img-spdif-in.c
Normal file
806
sound/soc/img/img-spdif-in.c
Normal file
@ -0,0 +1,806 @@
|
||||
/*
|
||||
* IMG SPDIF input controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_SPDIF_IN_RX_FIFO_OFFSET 0
|
||||
|
||||
#define IMG_SPDIF_IN_CTL 0x4
|
||||
#define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff
|
||||
#define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0
|
||||
#define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00
|
||||
#define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8
|
||||
#define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000
|
||||
#define IMG_SPDIF_IN_CTL_TRK_SHIFT 16
|
||||
#define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000
|
||||
#define IMG_SPDIF_IN_CTL_SRD_SHIFT 28
|
||||
#define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31)
|
||||
|
||||
#define IMG_SPDIF_IN_STATUS 0x8
|
||||
#define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000
|
||||
#define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12
|
||||
#define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15)
|
||||
#define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15
|
||||
|
||||
#define IMG_SPDIF_IN_CLKGEN 0x1c
|
||||
#define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff
|
||||
#define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0
|
||||
#define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000
|
||||
#define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16
|
||||
|
||||
#define IMG_SPDIF_IN_CSL 0x20
|
||||
|
||||
#define IMG_SPDIF_IN_CSH 0x24
|
||||
#define IMG_SPDIF_IN_CSH_MASK 0xff
|
||||
#define IMG_SPDIF_IN_CSH_SHIFT 0
|
||||
|
||||
#define IMG_SPDIF_IN_SOFT_RESET 0x28
|
||||
#define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0)
|
||||
|
||||
#define IMG_SPDIF_IN_ACLKGEN_START 0x2c
|
||||
#define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff
|
||||
#define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0
|
||||
#define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00
|
||||
#define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10
|
||||
#define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000
|
||||
#define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20
|
||||
|
||||
#define IMG_SPDIF_IN_NUM_ACLKGEN 4
|
||||
|
||||
struct img_spdif_in {
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int trk;
|
||||
bool multi_freq;
|
||||
int lock_acquire;
|
||||
int lock_release;
|
||||
unsigned int single_freq;
|
||||
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
bool active;
|
||||
|
||||
/* Write-only registers */
|
||||
unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
};
|
||||
|
||||
static inline void img_spdif_in_writel(struct img_spdif_in *spdif,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg)
|
||||
{
|
||||
return readl(spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif,
|
||||
u32 index)
|
||||
{
|
||||
img_spdif_in_writel(spdif, spdif->aclkgen_regs[index],
|
||||
IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4));
|
||||
}
|
||||
|
||||
static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif,
|
||||
unsigned int sample_rate, unsigned long *actual_freq)
|
||||
{
|
||||
unsigned long min_freq, freq_t;
|
||||
|
||||
/* Clock rate must be at least 24x the bit rate */
|
||||
min_freq = sample_rate * 2 * 32 * 24;
|
||||
|
||||
freq_t = clk_get_rate(spdif->clk_sys);
|
||||
|
||||
if (freq_t < min_freq)
|
||||
return -EINVAL;
|
||||
|
||||
*actual_freq = freq_t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom,
|
||||
unsigned int *phld, unsigned long clk_rate)
|
||||
{
|
||||
unsigned int ori, nom, hld;
|
||||
|
||||
/*
|
||||
* Calculate oversampling ratio, nominal phase increment and hold
|
||||
* increment for the given rate / frequency
|
||||
*/
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
ori = clk_rate / (rate * 64);
|
||||
|
||||
if (!ori)
|
||||
return -EINVAL;
|
||||
|
||||
nom = (4096 / ori) + 1;
|
||||
do
|
||||
hld = 4096 - (--nom * (ori - 1));
|
||||
while (hld < 120);
|
||||
|
||||
*pnom = nom;
|
||||
*phld = hld;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif,
|
||||
unsigned int rate)
|
||||
{
|
||||
unsigned int nom, hld;
|
||||
unsigned long flags, clk_rate;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) &
|
||||
IMG_SPDIF_IN_CLKGEN_NOM_MASK;
|
||||
reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) &
|
||||
IMG_SPDIF_IN_CLKGEN_HLD_MASK;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN);
|
||||
|
||||
spdif->single_freq = rate;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif,
|
||||
unsigned int multi_freqs[])
|
||||
{
|
||||
unsigned int nom, hld, rate, max_rate = 0;
|
||||
unsigned long flags, clk_rate;
|
||||
int i, ret = 0;
|
||||
u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
|
||||
if (multi_freqs[i] > max_rate)
|
||||
max_rate = multi_freqs[i];
|
||||
|
||||
ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
rate = multi_freqs[i];
|
||||
|
||||
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) &
|
||||
IMG_SPDIF_IN_ACLKGEN_NOM_MASK;
|
||||
reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) &
|
||||
IMG_SPDIF_IN_ACLKGEN_HLD_MASK;
|
||||
temp_regs[i] = reg;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT;
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg;
|
||||
img_spdif_in_aclkgen_writel(spdif, i);
|
||||
}
|
||||
|
||||
spdif->multi_freq = true;
|
||||
spdif->multi_freqs[0] = multi_freqs[0];
|
||||
spdif->multi_freqs[1] = multi_freqs[1];
|
||||
spdif->multi_freqs[2] = multi_freqs[2];
|
||||
spdif->multi_freqs[3] = multi_freqs[3];
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.iec958.status[0] = 0xff;
|
||||
ucontrol->value.iec958.status[1] = 0xff;
|
||||
ucontrol->value.iec958.status[2] = 0xff;
|
||||
ucontrol->value.iec958.status[3] = 0xff;
|
||||
ucontrol->value.iec958.status[4] = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL);
|
||||
ucontrol->value.iec958.status[0] = reg & 0xff;
|
||||
ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
|
||||
ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
|
||||
ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH);
|
||||
ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK)
|
||||
>> IMG_SPDIF_IN_CSH_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = LONG_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
if (spdif->multi_freq) {
|
||||
ucontrol->value.integer.value[0] = spdif->multi_freqs[0];
|
||||
ucontrol->value.integer.value[1] = spdif->multi_freqs[1];
|
||||
ucontrol->value.integer.value[2] = spdif->multi_freqs[2];
|
||||
ucontrol->value.integer.value[3] = spdif->multi_freqs[3];
|
||||
} else {
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
ucontrol->value.integer.value[1] = 0;
|
||||
ucontrol->value.integer.value[2] = 0;
|
||||
ucontrol->value.integer.value[3] = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
bool multi_freq;
|
||||
unsigned long flags;
|
||||
|
||||
if ((ucontrol->value.integer.value[0] == 0) &&
|
||||
(ucontrol->value.integer.value[1] == 0) &&
|
||||
(ucontrol->value.integer.value[2] == 0) &&
|
||||
(ucontrol->value.integer.value[3] == 0)) {
|
||||
multi_freq = false;
|
||||
} else {
|
||||
multi_freqs[0] = ucontrol->value.integer.value[0];
|
||||
multi_freqs[1] = ucontrol->value.integer.value[1];
|
||||
multi_freqs[2] = ucontrol->value.integer.value[2];
|
||||
multi_freqs[3] = ucontrol->value.integer.value[3];
|
||||
multi_freq = true;
|
||||
}
|
||||
|
||||
if (multi_freq)
|
||||
return img_spdif_in_do_clkgen_multi(spdif, multi_freqs);
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->multi_freq = false;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = LONG_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uc)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS);
|
||||
if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) {
|
||||
if (spdif->multi_freq) {
|
||||
i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >>
|
||||
IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1;
|
||||
uc->value.integer.value[0] = spdif->multi_freqs[i];
|
||||
} else {
|
||||
uc->value.integer.value[0] = spdif->single_freq;
|
||||
}
|
||||
} else {
|
||||
uc->value.integer.value[0] = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 255;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->trk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->trk = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK;
|
||||
reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] &
|
||||
~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) |
|
||||
(spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT);
|
||||
|
||||
img_spdif_in_aclkgen_writel(spdif, i);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = -128;
|
||||
uinfo->value.integer.max = 127;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->lock_acquire;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->lock_acquire = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->lock_release;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->lock_release = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new img_spdif_in_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
|
||||
.info = img_spdif_in_iec958_info,
|
||||
.get = img_spdif_in_get_status_mask
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
|
||||
.info = img_spdif_in_iec958_info,
|
||||
.get = img_spdif_in_get_status
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Multi Frequency Acquire",
|
||||
.info = img_spdif_in_info_multi_freq,
|
||||
.get = img_spdif_in_get_multi_freq,
|
||||
.put = img_spdif_in_set_multi_freq
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Frequency",
|
||||
.info = img_spdif_in_info_lock_freq,
|
||||
.get = img_spdif_in_get_lock_freq
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock TRK",
|
||||
.info = img_spdif_in_info_trk,
|
||||
.get = img_spdif_in_get_trk,
|
||||
.put = img_spdif_in_set_trk
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Acquire Threshold",
|
||||
.info = img_spdif_in_info_lock,
|
||||
.get = img_spdif_in_get_lock_acquire,
|
||||
.put = img_spdif_in_set_lock_acquire
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Release Threshold",
|
||||
.info = img_spdif_in_info_lock,
|
||||
.get = img_spdif_in_get_lock_release,
|
||||
.put = img_spdif_in_set_lock_release
|
||||
}
|
||||
};
|
||||
|
||||
static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
if (spdif->multi_freq)
|
||||
reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK;
|
||||
else
|
||||
reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT);
|
||||
reg |= IMG_SPDIF_IN_CTL_SRT_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
spdif->active = true;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
spdif->active = false;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_in_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
channels = params_channels(params);
|
||||
format = params_format(params);
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
return img_spdif_in_do_clkgen_single(spdif, rate);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
|
||||
.trigger = img_spdif_in_trigger,
|
||||
.hw_params = img_spdif_in_hw_params
|
||||
};
|
||||
|
||||
static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data);
|
||||
|
||||
snd_soc_add_dai_controls(dai, img_spdif_in_controls,
|
||||
ARRAY_SIZE(img_spdif_in_controls));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_spdif_in_dai = {
|
||||
.probe = img_spdif_in_dai_probe,
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.ops = &img_spdif_in_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_spdif_in_component = {
|
||||
.name = "img-spdif-in"
|
||||
};
|
||||
|
||||
static int img_spdif_in_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_in *spdif;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct reset_control *rst;
|
||||
u32 reg;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
|
||||
if (!spdif)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, spdif);
|
||||
|
||||
spdif->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
spdif->base = base;
|
||||
|
||||
spdif->clk_sys = devm_clk_get(dev, "sys");
|
||||
if (IS_ERR(spdif->clk_sys)) {
|
||||
if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(spdif->clk_sys);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(rst)) {
|
||||
if (PTR_ERR(rst) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
dev_dbg(dev, "No top level reset found\n");
|
||||
img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK,
|
||||
IMG_SPDIF_IN_SOFT_RESET);
|
||||
img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET);
|
||||
} else {
|
||||
reset_control_assert(rst);
|
||||
reset_control_deassert(rst);
|
||||
}
|
||||
|
||||
spin_lock_init(&spdif->lock);
|
||||
|
||||
spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET;
|
||||
spdif->dma_data.addr_width = 4;
|
||||
spdif->dma_data.maxburst = 4;
|
||||
spdif->trk = 0x80;
|
||||
spdif->lock_acquire = 4;
|
||||
spdif->lock_release = -128;
|
||||
|
||||
reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_TRK_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_spdif_in_component, &img_spdif_in_dai, 1);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_in_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_in *spdif = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_spdif_in_of_match[] = {
|
||||
{ .compatible = "img,spdif-in" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_spdif_in_of_match);
|
||||
|
||||
static struct platform_driver img_spdif_in_driver = {
|
||||
.driver = {
|
||||
.name = "img-spdif-in",
|
||||
.of_match_table = img_spdif_in_of_match
|
||||
},
|
||||
.probe = img_spdif_in_probe,
|
||||
.remove = img_spdif_in_dev_remove
|
||||
};
|
||||
module_platform_driver(img_spdif_in_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_DESCRIPTION("IMG SPDIF Input driver");
|
||||
MODULE_LICENSE("GPL v2");
|
441
sound/soc/img/img-spdif-out.c
Normal file
441
sound/soc/img/img-spdif-out.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
* IMG SPDIF output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_SPDIF_OUT_TX_FIFO 0x0
|
||||
|
||||
#define IMG_SPDIF_OUT_CTL 0x4
|
||||
#define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4)
|
||||
#define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2)
|
||||
#define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0)
|
||||
|
||||
#define IMG_SPDIF_OUT_CSL 0x14
|
||||
|
||||
#define IMG_SPDIF_OUT_CSH_UV 0x18
|
||||
#define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0
|
||||
#define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff
|
||||
|
||||
struct img_spdif_out {
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
static int img_spdif_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_spdif_out *spdif = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_spdif_out *spdif = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val,
|
||||
u32 reg)
|
||||
{
|
||||
writel(val, spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg)
|
||||
{
|
||||
return readl(spdif->base + reg);
|
||||
}
|
||||
|
||||
static void img_spdif_out_reset(struct img_spdif_out *spdif)
|
||||
{
|
||||
u32 ctl, status_low, status_high;
|
||||
|
||||
ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) &
|
||||
~IMG_SPDIF_OUT_CTL_SRT_MASK;
|
||||
status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
|
||||
status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
|
||||
reset_control_assert(spdif->rst);
|
||||
reset_control_deassert(spdif->rst);
|
||||
|
||||
img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL);
|
||||
img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL);
|
||||
img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV);
|
||||
}
|
||||
|
||||
static int img_spdif_out_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.iec958.status[0] = 0xff;
|
||||
ucontrol->value.iec958.status[1] = 0xff;
|
||||
ucontrol->value.iec958.status[2] = 0xff;
|
||||
ucontrol->value.iec958.status[3] = 0xff;
|
||||
ucontrol->value.iec958.status[4] = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
|
||||
ucontrol->value.iec958.status[0] = reg & 0xff;
|
||||
ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
|
||||
ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
|
||||
ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
ucontrol->value.iec958.status[4] =
|
||||
(reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >>
|
||||
IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
reg = ((u32)ucontrol->value.iec958.status[3] << 24);
|
||||
reg |= ((u32)ucontrol->value.iec958.status[2] << 16);
|
||||
reg |= ((u32)ucontrol->value.iec958.status[1] << 8);
|
||||
reg |= (u32)ucontrol->value.iec958.status[0];
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK;
|
||||
reg |= (u32)ucontrol->value.iec958.status[4] <<
|
||||
IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new img_spdif_out_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
|
||||
.info = img_spdif_out_info,
|
||||
.get = img_spdif_out_get_status_mask
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
.info = img_spdif_out_info,
|
||||
.get = img_spdif_out_get_status,
|
||||
.put = img_spdif_out_set_status
|
||||
}
|
||||
};
|
||||
|
||||
static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
|
||||
reg |= IMG_SPDIF_OUT_CTL_SRT_MASK;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
img_spdif_out_reset(spdif);
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int channels;
|
||||
long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
|
||||
u32 reg;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n",
|
||||
rate, channels, format);
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256);
|
||||
if (pre_div_a < 0)
|
||||
return pre_div_a;
|
||||
pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384);
|
||||
if (pre_div_b < 0)
|
||||
return pre_div_b;
|
||||
|
||||
diff_a = abs((pre_div_a / 256) - rate);
|
||||
diff_b = abs((pre_div_b / 384) - rate);
|
||||
|
||||
/* If diffs are equal, use lower clock rate */
|
||||
if (diff_a > diff_b)
|
||||
clk_set_rate(spdif->clk_ref, pre_div_b);
|
||||
else
|
||||
clk_set_rate(spdif->clk_ref, pre_div_a);
|
||||
|
||||
/*
|
||||
* Another driver (eg machine driver) may have rejected the above
|
||||
* change. Get the current rate and set the register bit according to
|
||||
* the new min diff
|
||||
*/
|
||||
clk_rate = clk_get_rate(spdif->clk_ref);
|
||||
|
||||
diff_a = abs((clk_rate / 256) - rate);
|
||||
diff_b = abs((clk_rate / 384) - rate);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
|
||||
if (diff_a <= diff_b)
|
||||
reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK;
|
||||
else
|
||||
reg |= IMG_SPDIF_OUT_CTL_CLK_MASK;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_spdif_out_dai_ops = {
|
||||
.trigger = img_spdif_out_trigger,
|
||||
.hw_params = img_spdif_out_hw_params
|
||||
};
|
||||
|
||||
static int img_spdif_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
|
||||
|
||||
snd_soc_add_dai_controls(dai, img_spdif_out_controls,
|
||||
ARRAY_SIZE(img_spdif_out_controls));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_spdif_out_dai = {
|
||||
.probe = img_spdif_out_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.ops = &img_spdif_out_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_spdif_out_component = {
|
||||
.name = "img-spdif-out"
|
||||
};
|
||||
|
||||
static int img_spdif_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_out *spdif;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
|
||||
if (!spdif)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, spdif);
|
||||
|
||||
spdif->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
spdif->base = base;
|
||||
|
||||
spdif->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(spdif->rst)) {
|
||||
if (PTR_ERR(spdif->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(spdif->rst);
|
||||
}
|
||||
|
||||
spdif->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(spdif->clk_sys)) {
|
||||
if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(spdif->clk_sys);
|
||||
}
|
||||
|
||||
spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(spdif->clk_ref)) {
|
||||
if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(spdif->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
|
||||
IMG_SPDIF_OUT_CTL);
|
||||
|
||||
img_spdif_out_reset(spdif);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_spdif_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
spin_lock_init(&spdif->lock);
|
||||
|
||||
spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO;
|
||||
spdif->dma_data.addr_width = 4;
|
||||
spdif->dma_data.maxburst = 4;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_spdif_out_component,
|
||||
&img_spdif_out_dai, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
dev_dbg(&pdev->dev, "Probe successful\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_spdif_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_out *spdif = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_spdif_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_spdif_out_of_match[] = {
|
||||
{ .compatible = "img,spdif-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_spdif_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_spdif_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_spdif_out_suspend,
|
||||
img_spdif_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_spdif_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-spdif-out",
|
||||
.of_match_table = img_spdif_out_of_match,
|
||||
.pm = &img_spdif_out_pm_ops
|
||||
},
|
||||
.probe = img_spdif_out_probe,
|
||||
.remove = img_spdif_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_spdif_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_DESCRIPTION("IMG SPDIF Output driver");
|
||||
MODULE_LICENSE("GPL v2");
|
287
sound/soc/img/pistachio-internal-dac.c
Normal file
287
sound/soc/img/pistachio-internal-dac.c
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Pistachio internal dac driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL 0x40
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK 0x2
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_SRST 0x44
|
||||
#define PISTACHIO_INTERNAL_DAC_SRST_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL 0x48
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT 0
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK 0xFFF
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK 0x1000
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT 13
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK 0x1FE000
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_PWR 0x1
|
||||
#define PISTACHIO_INTERNAL_DAC_PWR_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
/* codec private data */
|
||||
struct pistachio_internal_dac {
|
||||
struct regmap *regmap;
|
||||
struct regulator *supply;
|
||||
bool mute;
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = {
|
||||
SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1)
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_OUTPUT("AOUTL"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUTR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = {
|
||||
{ "AOUTL", NULL, "DAC" },
|
||||
{ "AOUTR", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static void pistachio_internal_dac_reg_writel(struct regmap *top_regs,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK,
|
||||
reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK,
|
||||
val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0);
|
||||
}
|
||||
|
||||
static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac)
|
||||
{
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK);
|
||||
|
||||
pistachio_internal_dac_reg_writel(dac->regmap, 0,
|
||||
PISTACHIO_INTERNAL_DAC_PWR);
|
||||
}
|
||||
|
||||
static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac)
|
||||
{
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK);
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK, 0);
|
||||
|
||||
pistachio_internal_dac_reg_writel(dac->regmap,
|
||||
PISTACHIO_INTERNAL_DAC_PWR_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_PWR);
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
|
||||
{
|
||||
.name = "pistachio_internal_dac",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = PISTACHIO_INTERNAL_DAC_FORMATS,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_codec_init_regmap(codec, dac->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_codec_driver pistachio_internal_dac_driver = {
|
||||
.probe = pistachio_internal_dac_codec_probe,
|
||||
.idle_bias_off = true,
|
||||
.controls = pistachio_internal_dac_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
|
||||
.dapm_widgets = pistachio_internal_dac_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
|
||||
.dapm_routes = pistachio_internal_dac_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
|
||||
};
|
||||
|
||||
static int pistachio_internal_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac;
|
||||
int ret, voltage;
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 reg;
|
||||
|
||||
dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
|
||||
|
||||
if (!dac)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, dac);
|
||||
|
||||
dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"img,cr-top");
|
||||
if (IS_ERR(dac->regmap))
|
||||
return PTR_ERR(dac->regmap);
|
||||
|
||||
dac->supply = devm_regulator_get(dev, "VDD");
|
||||
if (IS_ERR(dac->supply)) {
|
||||
ret = PTR_ERR(dac->supply);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(dac->supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
voltage = regulator_get_voltage(dac->supply);
|
||||
|
||||
switch (voltage) {
|
||||
case 1800000:
|
||||
reg = 0;
|
||||
break;
|
||||
case 3300000:
|
||||
reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid voltage: %d\n", voltage);
|
||||
ret = -EINVAL;
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
|
||||
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
pistachio_internal_dac_pwr_on(dac);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver,
|
||||
pistachio_internal_dac_dais,
|
||||
ARRAY_SIZE(pistachio_internal_dac_dais));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register codec: %d\n", ret);
|
||||
goto err_pwr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pwr:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
err_regulator:
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pistachio_internal_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pistachio_internal_dac_rt_resume(struct device *dev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(dac->supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pistachio_internal_dac_pwr_on(dac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pistachio_internal_dac_rt_suspend(struct device *dev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
|
||||
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
|
||||
pistachio_internal_dac_rt_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id pistachio_internal_dac_of_match[] = {
|
||||
{ .compatible = "img,pistachio-internal-dac" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
|
||||
|
||||
static struct platform_driver pistachio_internal_dac_plat_driver = {
|
||||
.driver = {
|
||||
.name = "img-pistachio-internal-dac",
|
||||
.of_match_table = pistachio_internal_dac_of_match,
|
||||
.pm = &pistachio_internal_dac_pm_ops
|
||||
},
|
||||
.probe = pistachio_internal_dac_probe,
|
||||
.remove = pistachio_internal_dac_remove
|
||||
};
|
||||
module_platform_driver(pistachio_internal_dac_plat_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Pistachio Internal DAC driver");
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -24,6 +24,7 @@ config SND_SST_IPC_PCI
|
||||
config SND_SST_IPC_ACPI
|
||||
tristate
|
||||
select SND_SST_IPC
|
||||
select SND_SOC_INTEL_SST
|
||||
depends on ACPI
|
||||
|
||||
config SND_SOC_INTEL_SST
|
||||
@ -43,7 +44,7 @@ config SND_SOC_INTEL_BAYTRAIL
|
||||
config SND_SOC_INTEL_HASWELL_MACH
|
||||
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
|
||||
depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_HASWELL
|
||||
select SND_SOC_RT5640
|
||||
@ -56,18 +57,19 @@ config SND_SOC_INTEL_HASWELL_MACH
|
||||
config SND_SOC_INTEL_BYT_RT5640_MACH
|
||||
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n)
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_BAYTRAIL
|
||||
select SND_SOC_RT5640
|
||||
help
|
||||
This adds audio driver for Intel Baytrail platform based boards
|
||||
with the RT5640 audio codec.
|
||||
with the RT5640 audio codec. This driver is deprecated, use
|
||||
SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
|
||||
|
||||
config SND_SOC_INTEL_BYT_MAX98090_MACH
|
||||
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_BAYTRAIL
|
||||
select SND_SOC_MAX98090
|
||||
@ -79,7 +81,7 @@ config SND_SOC_INTEL_BROADWELL_MACH
|
||||
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
|
||||
depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
|
||||
I2C_DESIGNWARE_PLATFORM
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_HASWELL
|
||||
select SND_SOC_RT286
|
||||
@ -90,14 +92,14 @@ config SND_SOC_INTEL_BROADWELL_MACH
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_BYTCR_RT5640_MACH
|
||||
tristate "ASoC Audio DSP Support for MID BYT Platform"
|
||||
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
|
||||
depends on X86 && I2C
|
||||
select SND_SOC_RT5640
|
||||
select SND_SST_MFLD_PLATFORM
|
||||
select SND_SST_IPC_ACPI
|
||||
help
|
||||
This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
|
||||
used as alsa device in audio substem in Intel(R) MID devices
|
||||
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
|
||||
platforms with RT5640 audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
@ -154,3 +156,31 @@ config SND_SOC_INTEL_SKL_RT286_MACH
|
||||
with RT286 I2S audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
|
||||
tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_SKYLAKE
|
||||
select SND_SOC_NAU8825
|
||||
select SND_SOC_SSM4567
|
||||
select SND_SOC_DMIC
|
||||
help
|
||||
This adds support for ASoC Onboard Codec I2S machine driver. This will
|
||||
create an alsa sound card for NAU88L25 + SSM4567.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
|
||||
tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_SKYLAKE
|
||||
select SND_SOC_NAU8825
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_DMIC
|
||||
help
|
||||
This adds support for ASoC Onboard Codec I2S machine driver. This will
|
||||
create an alsa sound card for NAU88L25 + MAX98357A.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
@ -443,7 +443,7 @@ static int sst_gain_get(struct snd_kcontrol *kcontrol,
|
||||
break;
|
||||
|
||||
case SST_GAIN_MUTE:
|
||||
ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
|
||||
ucontrol->value.integer.value[0] = gv->mute ? 0 : 1;
|
||||
break;
|
||||
|
||||
case SST_GAIN_RAMP_DURATION:
|
||||
@ -479,7 +479,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol,
|
||||
break;
|
||||
|
||||
case SST_GAIN_MUTE:
|
||||
gv->mute = !!ucontrol->value.integer.value[0];
|
||||
gv->mute = !ucontrol->value.integer.value[0];
|
||||
dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
|
||||
break;
|
||||
|
||||
@ -1109,6 +1109,7 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{"media0_in", NULL, "Compress Playback"},
|
||||
{"media1_in", NULL, "Headset Playback"},
|
||||
{"media2_in", NULL, "pcm0_out"},
|
||||
{"media3_in", NULL, "Deepbuffer Playback"},
|
||||
|
||||
{"media0_out mix 0", "media0_in Switch", "media0_in"},
|
||||
{"media0_out mix 0", "media1_in Switch", "media1_in"},
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
enum {
|
||||
MERR_DPCM_AUDIO = 0,
|
||||
MERR_DPCM_DEEP_BUFFER,
|
||||
MERR_DPCM_COMPR,
|
||||
};
|
||||
|
||||
|
@ -98,6 +98,7 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
|
||||
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_DEEP_BUFFER, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, 0},
|
||||
};
|
||||
|
||||
static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
|
||||
@ -500,14 +501,25 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
|
||||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Headset Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "deepbuffer-cpu-dai",
|
||||
.ops = &sst_media_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "Deepbuffer Playback",
|
||||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -516,10 +528,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
|
||||
.ops = &sst_compr_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "Compress Playback",
|
||||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
/* BE CPU Dais */
|
||||
|
@ -40,18 +40,9 @@
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
#include "../../common/sst-acpi.h"
|
||||
#include "sst.h"
|
||||
|
||||
struct sst_machines {
|
||||
char *codec_id;
|
||||
char board[32];
|
||||
char machine[32];
|
||||
void (*machine_quirk)(void);
|
||||
char firmware[FW_NAME_SIZE];
|
||||
struct sst_platform_info *pdata;
|
||||
|
||||
};
|
||||
|
||||
/* LPE viewpoint addresses */
|
||||
#define SST_BYT_IRAM_PHY_START 0xff2c0000
|
||||
#define SST_BYT_IRAM_PHY_END 0xff2d4000
|
||||
@ -223,37 +214,16 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static struct sst_machines *sst_acpi_find_machine(
|
||||
struct sst_machines *machines)
|
||||
{
|
||||
struct sst_machines *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->codec_id; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sst_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0;
|
||||
struct intel_sst_drv *ctx;
|
||||
const struct acpi_device_id *id;
|
||||
struct sst_machines *mach;
|
||||
struct sst_acpi_mach *mach;
|
||||
struct platform_device *mdev;
|
||||
struct platform_device *plat_dev;
|
||||
struct sst_platform_info *pdata;
|
||||
unsigned int dev_id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
@ -261,12 +231,13 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
dev_dbg(dev, "for %s", id->id);
|
||||
|
||||
mach = (struct sst_machines *)id->driver_data;
|
||||
mach = (struct sst_acpi_mach *)id->driver_data;
|
||||
mach = sst_acpi_find_machine(mach);
|
||||
if (mach == NULL) {
|
||||
dev_err(dev, "No matching machine driver found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pdata = mach->pdata;
|
||||
|
||||
ret = kstrtouint(id->id, 16, &dev_id);
|
||||
if (ret < 0) {
|
||||
@ -276,16 +247,16 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
||||
|
||||
dev_dbg(dev, "ACPI device id: %x\n", dev_id);
|
||||
|
||||
plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
|
||||
plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0);
|
||||
if (IS_ERR(plat_dev)) {
|
||||
dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
|
||||
dev_err(dev, "Failed to create machine device: %s\n", pdata->platform);
|
||||
return PTR_ERR(plat_dev);
|
||||
}
|
||||
|
||||
/* Create platform device for sst machine driver */
|
||||
mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
|
||||
mdev = platform_device_register_data(dev, mach->drv_name, -1, NULL, 0);
|
||||
if (IS_ERR(mdev)) {
|
||||
dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
|
||||
dev_err(dev, "Failed to create machine device: %s\n", mach->drv_name);
|
||||
return PTR_ERR(mdev);
|
||||
}
|
||||
|
||||
@ -294,8 +265,8 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
/* Fill sst platform data */
|
||||
ctx->pdata = mach->pdata;
|
||||
strcpy(ctx->firmware_name, mach->firmware);
|
||||
ctx->pdata = pdata;
|
||||
strcpy(ctx->firmware_name, mach->fw_filename);
|
||||
|
||||
ret = sst_platform_get_resources(ctx);
|
||||
if (ret)
|
||||
@ -342,22 +313,22 @@ static int sst_acpi_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sst_machines sst_acpi_bytcr[] = {
|
||||
{"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin",
|
||||
static struct sst_acpi_mach sst_acpi_bytcr[] = {
|
||||
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
|
||||
&byt_rvp_platform_data },
|
||||
{},
|
||||
};
|
||||
|
||||
/* Cherryview-based platforms: CherryTrail and Braswell */
|
||||
static struct sst_machines sst_acpi_chv[] = {
|
||||
{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
|
||||
static struct sst_acpi_mach sst_acpi_chv[] = {
|
||||
{"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
|
||||
{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
|
||||
{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"193C9890", "cht-bsw", "cht-bsw-max98090", NULL,
|
||||
"intel/fw_sst_22a8.bin", &chv_platform_data },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -108,7 +108,7 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
|
||||
str_id, pipe_id);
|
||||
ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
|
||||
IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
|
||||
&alloc_param, data, true, true, false, true);
|
||||
&alloc_param, &data, true, true, false, true);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
|
||||
|
@ -7,6 +7,8 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
|
||||
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
|
||||
snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
|
||||
snd-soc-skl_rt286-objs := skl_rt286.o
|
||||
snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
|
||||
snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
|
||||
@ -17,3 +19,5 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
|
||||
|
@ -20,51 +20,75 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../../codecs/rt5640.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
|
||||
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("Internal Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_audio_map[] = {
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"IN2N", NULL, "Headset Mic"},
|
||||
{"Headset Mic", NULL, "MICBIAS1"},
|
||||
{"IN1P", NULL, "MICBIAS1"},
|
||||
{"LDO2", NULL, "Int Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Ext Spk", NULL, "SPOLP"},
|
||||
{"Ext Spk", NULL, "SPOLN"},
|
||||
{"Ext Spk", NULL, "SPORP"},
|
||||
{"Ext Spk", NULL, "SPORN"},
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
|
||||
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
||||
{"ssp2 Tx", NULL, "codec_out0"},
|
||||
{"ssp2 Tx", NULL, "codec_out1"},
|
||||
{"codec_in0", NULL, "ssp2 Rx"},
|
||||
{"codec_in1", NULL, "ssp2 Rx"},
|
||||
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
||||
|
||||
{"Headset Mic", NULL, "MICBIAS1"},
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Speaker", NULL, "SPOLP"},
|
||||
{"Speaker", NULL, "SPOLN"},
|
||||
{"Speaker", NULL, "SPORP"},
|
||||
{"Speaker", NULL, "SPORN"},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new byt_mc_controls[] = {
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
|
||||
{"DMIC1", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
|
||||
{"DMIC2", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
|
||||
{"Internal Mic", NULL, "MICBIAS1"},
|
||||
{"IN1P", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
enum {
|
||||
BYT_RT5640_DMIC1_MAP,
|
||||
BYT_RT5640_DMIC2_MAP,
|
||||
BYT_RT5640_IN1_MAP,
|
||||
};
|
||||
|
||||
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
|
||||
#define BYT_RT5640_DMIC_EN BIT(16)
|
||||
|
||||
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
|
||||
BYT_RT5640_DMIC_EN;
|
||||
|
||||
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Int Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Ext Spk"),
|
||||
SOC_DAPM_PIN_SWITCH("Internal Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
@ -92,7 +116,82 @@ static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_pcm_stream byt_dai_params = {
|
||||
static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
byt_rt5640_quirk = (unsigned long)id->driver_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
|
||||
},
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
|
||||
},
|
||||
.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
|
||||
BYT_RT5640_DMIC_EN),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = runtime->codec;
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
const struct snd_soc_dapm_route *custom_map;
|
||||
int num_routes;
|
||||
|
||||
card->dapm.idle_bias_off = true;
|
||||
|
||||
ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
|
||||
ARRAY_SIZE(byt_rt5640_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "unable to add card controls\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dmi_check_system(byt_rt5640_quirk_table);
|
||||
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
|
||||
case BYT_RT5640_IN1_MAP:
|
||||
custom_map = byt_rt5640_intmic_in1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
|
||||
break;
|
||||
case BYT_RT5640_DMIC2_MAP:
|
||||
custom_map = byt_rt5640_intmic_dmic2_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
|
||||
break;
|
||||
default:
|
||||
custom_map = byt_rt5640_intmic_dmic1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
|
||||
ret = rt5640_dmic_enable(codec, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
@ -100,13 +199,14 @@ static const struct snd_soc_pcm_stream byt_dai_params = {
|
||||
.channels_max = 2,
|
||||
};
|
||||
|
||||
static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
int ret;
|
||||
|
||||
/* The DSP will covert the FE rate to 48k, stereo, 24bits */
|
||||
rate->min = rate->max = 48000;
|
||||
@ -114,24 +214,46 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
|
||||
/* set SSP2 to 24-bit */
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
||||
|
||||
/*
|
||||
* Default mode for SSP configuration is TDM 4 slot, override config
|
||||
* with explicit setting to I2S 2ch 24-bit. The word length is set with
|
||||
* dai_set_tdm_slot() since there is no other API exposed
|
||||
*/
|
||||
ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_IF |
|
||||
SND_SOC_DAIFMT_CBS_CFS
|
||||
);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int byt_aif1_startup(struct snd_pcm_substream *substream)
|
||||
static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_single(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_RATE, 48000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops byt_aif1_ops = {
|
||||
.startup = byt_aif1_startup,
|
||||
static struct snd_soc_ops byt_rt5640_aif1_ops = {
|
||||
.startup = byt_rt5640_aif1_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_ops byt_be_ssp2_ops = {
|
||||
.hw_params = byt_aif1_hw_params,
|
||||
static struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
|
||||
.hw_params = byt_rt5640_aif1_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link byt_dailink[] = {
|
||||
static struct snd_soc_dai_link byt_rt5640_dais[] = {
|
||||
[MERR_DPCM_AUDIO] = {
|
||||
.name = "Baytrail Audio Port",
|
||||
.stream_name = "Baytrail Audio",
|
||||
@ -143,7 +265,20 @@ static struct snd_soc_dai_link byt_dailink[] = {
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &byt_aif1_ops,
|
||||
.ops = &byt_rt5640_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &byt_rt5640_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Baytrail Compressed Port",
|
||||
@ -164,55 +299,57 @@ static struct snd_soc_dai_link byt_dailink[] = {
|
||||
.codec_name = "i2c-10EC5640:00",
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS,
|
||||
.be_hw_params_fixup = byt_codec_fixup,
|
||||
.be_hw_params_fixup = byt_rt5640_codec_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &byt_be_ssp2_ops,
|
||||
.init = byt_rt5640_init,
|
||||
.ops = &byt_rt5640_be_ssp2_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* SoC card */
|
||||
static struct snd_soc_card snd_soc_card_byt = {
|
||||
.name = "baytrailcraudio",
|
||||
static struct snd_soc_card byt_rt5640_card = {
|
||||
.name = "bytcr-rt5640",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = byt_dailink,
|
||||
.num_links = ARRAY_SIZE(byt_dailink),
|
||||
.dapm_widgets = byt_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
|
||||
.dapm_routes = byt_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_audio_map),
|
||||
.controls = byt_mc_controls,
|
||||
.num_controls = ARRAY_SIZE(byt_mc_controls),
|
||||
.dai_link = byt_rt5640_dais,
|
||||
.num_links = ARRAY_SIZE(byt_rt5640_dais),
|
||||
.dapm_widgets = byt_rt5640_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
|
||||
.dapm_routes = byt_rt5640_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int snd_byt_mc_probe(struct platform_device *pdev)
|
||||
static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
/* register the soc card */
|
||||
snd_soc_card_byt.dev = &pdev->dev;
|
||||
byt_rt5640_card.dev = &pdev->dev;
|
||||
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
|
||||
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
|
||||
if (ret_val) {
|
||||
dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
|
||||
dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
|
||||
ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
platform_set_drvdata(pdev, &snd_soc_card_byt);
|
||||
platform_set_drvdata(pdev, &byt_rt5640_card);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static struct platform_driver snd_byt_mc_driver = {
|
||||
static struct platform_driver snd_byt_rt5640_mc_driver = {
|
||||
.driver = {
|
||||
.name = "bytt100_rt5640",
|
||||
.name = "bytcr_rt5640",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = snd_byt_mc_probe,
|
||||
.probe = snd_byt_rt5640_mc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(snd_byt_mc_driver);
|
||||
module_platform_driver(snd_byt_rt5640_mc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bytt100_rt5640");
|
||||
MODULE_ALIAS("platform:bytcr_rt5640");
|
||||
|
@ -232,6 +232,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
@ -260,6 +260,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
@ -248,6 +248,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
485
sound/soc/intel/boards/skl_nau88l25_max98357a.c
Normal file
485
sound/soc/intel/boards/skl_nau88l25_max98357a.c
Normal file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Intel Skylake I2S Machine Driver with MAXIM98357A
|
||||
* and NAU88L25
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "../../codecs/nau8825.h"
|
||||
|
||||
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
|
||||
#define SKL_MAXIM_CODEC_DAI "HiFi"
|
||||
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
|
||||
strlen(SKL_NUVOTON_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = skl_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new skylake_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Spk"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
SND_SOC_DAPM_SPK("DP", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI", NULL),
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_map[] = {
|
||||
/* HP jack connectors - unknown if we have jack detection */
|
||||
{ "Headphone Jack", NULL, "HPOL" },
|
||||
{ "Headphone Jack", NULL, "HPOR" },
|
||||
|
||||
/* speaker */
|
||||
{ "Spk", NULL, "Speaker" },
|
||||
|
||||
/* other jacks */
|
||||
{ "MIC", NULL, "Headset Mic" },
|
||||
{ "DMic", NULL, "SoC DMIC" },
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
{"HDMI", NULL, "hif5 Output"},
|
||||
{"DP", NULL, "hif6 Output"},
|
||||
|
||||
/* CODEC BE connections */
|
||||
{ "HiFi Playback", NULL, "ssp0 Tx" },
|
||||
{ "ssp0 Tx", NULL, "codec0_out" },
|
||||
|
||||
{ "Playback", NULL, "ssp1 Tx" },
|
||||
{ "ssp1 Tx", NULL, "codec1_out" },
|
||||
|
||||
{ "codec0_in", NULL, "ssp1 Rx" },
|
||||
{ "ssp1 Rx", NULL, "Capture" },
|
||||
|
||||
/* DMIC */
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
{ "hifi1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
{ "Headphone Jack", NULL, "Platform Clock" },
|
||||
{ "Headset Mic", NULL, "Platform Clock" },
|
||||
};
|
||||
|
||||
static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The ADSP will covert the FE rate to 48k, stereo */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
||||
/*
|
||||
* Headset buttons map to the google Reference headset.
|
||||
* These can be configured by userspace.
|
||||
*/
|
||||
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nau8825_enable_jack_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* On this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_nau8825_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_nau8825_ops = {
|
||||
.hw_params = skylake_nau8825_hw_params,
|
||||
};
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
|
||||
if (params_channels(params) == 2)
|
||||
channels->min = channels->max = 2;
|
||||
else
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
static unsigned int rates_16000[] = {
|
||||
16000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_16000 = {
|
||||
.count = ARRAY_SIZE(rates_16000),
|
||||
.list = rates_16000,
|
||||
};
|
||||
|
||||
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_16000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylaye_refcap_ops = {
|
||||
.startup = skylake_refcap_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_dais[] = {
|
||||
/* Front End DAI links */
|
||||
{
|
||||
.name = "Skl Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.init = skylake_nau8825_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
.stream_name = "Audio Record",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
.stream_name = "Wake on Voice",
|
||||
.cpu_dai_name = "Reference Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl HDMI Port",
|
||||
.stream_name = "Hdmi",
|
||||
.cpu_dai_name = "HDMI Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.init = NULL,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "MX98357A:00",
|
||||
.codec_dai_name = SKL_MAXIM_CODEC_DAI,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "i2c-10508825:00",
|
||||
.codec_dai_name = SKL_NUVOTON_CODEC_DAI,
|
||||
.init = skylake_nau8825_codec_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.ops = &skylake_nau8825_ops,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
{
|
||||
.name = "iDisp",
|
||||
.be_id = 3,
|
||||
.cpu_dai_name = "iDisp Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825max",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = skylake_dais,
|
||||
.num_links = ARRAY_SIZE(skylake_dais),
|
||||
.controls = skylake_controls,
|
||||
.num_controls = ARRAY_SIZE(skylake_controls),
|
||||
.dapm_widgets = skylake_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
|
||||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
||||
static struct platform_driver skylake_audio = {
|
||||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_nau88l25_max98357a_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(skylake_audio)
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
|
||||
MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
|
536
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
Normal file
536
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Intel Skylake I2S Machine Driver for NAU88L25+SSM4567
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Modified from:
|
||||
* Intel Skylake I2S Machine Driver for NAU88L25 and SSM4567
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "../../codecs/nau8825.h"
|
||||
|
||||
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
|
||||
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
|
||||
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
|
||||
strlen(SKL_NUVOTON_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new skylake_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Left Speaker"),
|
||||
SOC_DAPM_PIN_SWITCH("Right Speaker"),
|
||||
};
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = skl_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
dev_err(card->dev, "Codec dai not found\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Left Speaker", NULL),
|
||||
SND_SOC_DAPM_SPK("Right Speaker", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
SND_SOC_DAPM_SPK("DP", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI", NULL),
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_map[] = {
|
||||
/* HP jack connectors - unknown if we have jack detection */
|
||||
{"Headphone Jack", NULL, "HPOL"},
|
||||
{"Headphone Jack", NULL, "HPOR"},
|
||||
|
||||
/* speaker */
|
||||
{"Left Speaker", NULL, "Left OUT"},
|
||||
{"Right Speaker", NULL, "Right OUT"},
|
||||
|
||||
/* other jacks */
|
||||
{"MIC", NULL, "Headset Mic"},
|
||||
{"DMic", NULL, "SoC DMIC"},
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
|
||||
{"HDMI", NULL, "hif5 Output"},
|
||||
{"DP", NULL, "hif6 Output"},
|
||||
/* CODEC BE connections */
|
||||
{ "Left Playback", NULL, "ssp0 Tx"},
|
||||
{ "Right Playback", NULL, "ssp0 Tx"},
|
||||
{ "ssp0 Tx", NULL, "codec0_out"},
|
||||
|
||||
{ "Playback", NULL, "ssp1 Tx"},
|
||||
{ "ssp1 Tx", NULL, "codec1_out"},
|
||||
|
||||
{ "codec0_in", NULL, "ssp1 Rx" },
|
||||
{ "ssp1 Rx", NULL, "Capture" },
|
||||
|
||||
/* DMIC */
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
{ "hifi1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
{ "Headphone Jack", NULL, "Platform Clock" },
|
||||
{ "Headset Mic", NULL, "Platform Clock" },
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_conf ssm4567_codec_conf[] = {
|
||||
{
|
||||
.dev_name = "i2c-INT343B:00",
|
||||
.name_prefix = "Left",
|
||||
},
|
||||
{
|
||||
.dev_name = "i2c-INT343B:01",
|
||||
.name_prefix = "Right",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link_component ssm4567_codec_components[] = {
|
||||
{ /* Left */
|
||||
.name = "i2c-INT343B:00",
|
||||
.dai_name = SKL_SSM_CODEC_DAI,
|
||||
},
|
||||
{ /* Right */
|
||||
.name = "i2c-INT343B:01",
|
||||
.dai_name = SKL_SSM_CODEC_DAI,
|
||||
},
|
||||
};
|
||||
|
||||
static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Slot 1 for left */
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x01, 0x01, 2, 48);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Slot 2 for right */
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x02, 0x02, 2, 48);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
||||
/*
|
||||
* 4 buttons here map to the google Reference headset
|
||||
* The use of these buttons can be decided by the user space.
|
||||
*/
|
||||
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nau8825_enable_jack_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* on this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_nau8825_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The ADSP will covert the FE rate to 48k, stereo */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
if (params_channels(params) == 2)
|
||||
channels->min = channels->max = 2;
|
||||
else
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_nau8825_ops = {
|
||||
.hw_params = skylake_nau8825_hw_params,
|
||||
};
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
static unsigned int rates_16000[] = {
|
||||
16000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_16000 = {
|
||||
.count = ARRAY_SIZE(rates_16000),
|
||||
.list = rates_16000,
|
||||
};
|
||||
|
||||
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_16000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylaye_refcap_ops = {
|
||||
.startup = skylake_refcap_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_dais[] = {
|
||||
/* Front End DAI links */
|
||||
{
|
||||
.name = "Skl Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.init = skylake_nau8825_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
.stream_name = "Audio Record",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
.stream_name = "Wake on Voice",
|
||||
.cpu_dai_name = "Reference Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl HDMI Port",
|
||||
.stream_name = "Hdmi",
|
||||
.cpu_dai_name = "HDMI Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.init = NULL,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codecs = ssm4567_codec_components,
|
||||
.num_codecs = ARRAY_SIZE(ssm4567_codec_components),
|
||||
.dai_fmt = SND_SOC_DAIFMT_DSP_A |
|
||||
SND_SOC_DAIFMT_IB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.init = skylake_ssm4567_codec_init,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "i2c-10508825:00",
|
||||
.codec_dai_name = SKL_NUVOTON_CODEC_DAI,
|
||||
.init = skylake_nau8825_codec_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.ops = &skylake_nau8825_ops,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.ignore_suspend = 1,
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
{
|
||||
.name = "iDisp",
|
||||
.be_id = 3,
|
||||
.cpu_dai_name = "iDisp Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825adi",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = skylake_dais,
|
||||
.num_links = ARRAY_SIZE(skylake_dais),
|
||||
.controls = skylake_controls,
|
||||
.num_controls = ARRAY_SIZE(skylake_controls),
|
||||
.dapm_widgets = skylake_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
|
||||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.codec_conf = ssm4567_codec_conf,
|
||||
.num_configs = ARRAY_SIZE(ssm4567_codec_conf),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
||||
static struct platform_driver skylake_audio = {
|
||||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_nau88l25_ssm4567_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(skylake_audio)
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
|
||||
MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
|
||||
MODULE_AUTHOR("Naveen M <naveen.m@intel.com>");
|
||||
MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
|
||||
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
|
@ -52,6 +52,7 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("DMIC2", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
||||
@ -67,7 +68,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
||||
|
||||
/* digital mics */
|
||||
{"DMIC1 Pin", NULL, "DMIC2"},
|
||||
{"DMIC AIF", NULL, "SoC DMIC"},
|
||||
{"DMic", NULL, "SoC DMIC"},
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
|
||||
/* CODEC BE connections */
|
||||
{ "AIF1 Playback", NULL, "ssp0 Tx"},
|
||||
@ -79,13 +82,24 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
||||
{ "ssp0 Rx", NULL, "AIF1 Capture" },
|
||||
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "Capture" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
|
||||
{ "hif1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
|
||||
};
|
||||
|
||||
static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
@ -101,9 +115,59 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
|
||||
rt286_mic_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* on this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_rt286_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
@ -112,12 +176,15 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The output is 48KHz, stereo, 16bits */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -140,6 +207,42 @@ static struct snd_soc_ops skylake_rt286_ops = {
|
||||
.hw_params = skylake_rt286_hw_params,
|
||||
};
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
/* Front End DAI links */
|
||||
@ -152,11 +255,13 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.init = skylake_rt286_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST,
|
||||
SND_SOC_DPCM_TRIGGER_POST
|
||||
},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_rt286_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
@ -172,6 +277,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
SND_SOC_DPCM_TRIGGER_POST
|
||||
},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_rt286_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
@ -186,6 +292,19 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
@ -201,7 +320,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_suspend = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp0_fixup,
|
||||
.ops = &skylake_rt286_ops,
|
||||
@ -215,6 +333,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
@ -247,6 +366,7 @@ static struct platform_driver skylake_audio = {
|
||||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_alc286s_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
snd-soc-sst-dsp-objs := sst-dsp.o
|
||||
snd-soc-sst-acpi-objs := sst-acpi.o
|
||||
snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o
|
||||
snd-soc-sst-ipc-objs := sst-ipc.o
|
||||
|
||||
ifneq ($(CONFIG_DW_DMAC_CORE),)
|
||||
snd-soc-sst-dsp-objs += sst-firmware.o
|
||||
endif
|
||||
snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
|
||||
|
||||
|
@ -21,21 +21,12 @@
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-acpi.h"
|
||||
|
||||
#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000
|
||||
#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000
|
||||
#define SST_LPT_DSP_DMA_SIZE (1024 - 1)
|
||||
|
||||
/* Descriptor for SST ASoC machine driver */
|
||||
struct sst_acpi_mach {
|
||||
/* ACPI ID for the matching machine driver. Audio codec for instance */
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
/* machine driver name */
|
||||
const char *drv_name;
|
||||
/* firmware file name */
|
||||
const char *fw_filename;
|
||||
};
|
||||
|
||||
/* Descriptor for setting up SST platform data */
|
||||
struct sst_acpi_desc {
|
||||
const char *drv_name;
|
||||
@ -88,28 +79,6 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
|
||||
return;
|
||||
}
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static struct sst_acpi_mach *sst_acpi_find_machine(
|
||||
struct sst_acpi_mach *machines)
|
||||
{
|
||||
struct sst_acpi_mach *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->id[0]; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sst_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
@ -211,7 +180,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static struct sst_acpi_mach haswell_machines[] = {
|
||||
{ "INT33CA", "haswell-audio", "intel/IntcSST1.bin" },
|
||||
{ "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -229,7 +198,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = {
|
||||
};
|
||||
|
||||
static struct sst_acpi_mach broadwell_machines[] = {
|
||||
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin" },
|
||||
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -247,8 +216,8 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = {
|
||||
};
|
||||
|
||||
static struct sst_acpi_mach baytrail_machines[] = {
|
||||
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
|
||||
{ "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
|
||||
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
|
||||
{ "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
|
33
sound/soc/intel/common/sst-acpi.h
Normal file
33
sound/soc/intel/common/sst-acpi.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2013-15, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/* acpi match */
|
||||
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
|
||||
|
||||
/* Descriptor for SST ASoC machine driver */
|
||||
struct sst_acpi_mach {
|
||||
/* ACPI ID for the matching machine driver. Audio codec for instance */
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
/* machine driver name */
|
||||
const char *drv_name;
|
||||
/* firmware file name */
|
||||
const char *fw_filename;
|
||||
|
||||
/* board name */
|
||||
const char *board;
|
||||
void (*machine_quirk)(void);
|
||||
void *pdata;
|
||||
};
|
@ -243,7 +243,7 @@ struct sst_mem_block {
|
||||
u32 size; /* block size */
|
||||
u32 index; /* block index 0..N */
|
||||
enum sst_mem_type type; /* block memory type IRAM/DRAM */
|
||||
struct sst_block_ops *ops; /* block operations, if any */
|
||||
const struct sst_block_ops *ops;/* block operations, if any */
|
||||
|
||||
/* block status */
|
||||
u32 bytes_used; /* bytes in use by modules */
|
||||
@ -308,6 +308,8 @@ struct sst_dsp {
|
||||
|
||||
/* SKL data */
|
||||
|
||||
const char *fw_name;
|
||||
|
||||
/* To allocate CL dma buffers */
|
||||
struct skl_dsp_loader_ops dsp_ops;
|
||||
struct skl_dsp_fw_ops fw_ops;
|
||||
@ -376,8 +378,8 @@ void sst_block_free_scratch(struct sst_dsp *dsp);
|
||||
|
||||
/* Register the DSPs memory blocks - would be nice to read from ACPI */
|
||||
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
|
||||
u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
|
||||
void *private);
|
||||
u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
|
||||
u32 index, void *private);
|
||||
void sst_mem_block_unregister_all(struct sst_dsp *dsp);
|
||||
|
||||
/* Create/Free DMA resources */
|
||||
|
@ -420,7 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
|
||||
#ifdef CONFIG_DW_DMAC_CORE
|
||||
struct sst_dsp *sst_dsp_new(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
|
||||
{
|
||||
|
@ -216,7 +216,7 @@ struct sst_pdata {
|
||||
void *dsp;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
|
||||
#ifdef CONFIG_DW_DMAC_CORE
|
||||
/* Initialization */
|
||||
struct sst_dsp *sst_dsp_new(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
|
||||
|
@ -51,8 +51,22 @@ struct sst_dma {
|
||||
|
||||
static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
|
||||
{
|
||||
u32 tmp = 0;
|
||||
int i, m, n;
|
||||
const u8 *src_byte = src;
|
||||
|
||||
m = bytes / 4;
|
||||
n = bytes % 4;
|
||||
|
||||
/* __iowrite32_copy use 32bit size values so divide by 4 */
|
||||
__iowrite32_copy((void *)dest, src, bytes/4);
|
||||
__iowrite32_copy((void *)dest, src, m);
|
||||
|
||||
if (n) {
|
||||
for (i = 0; i < n; i++)
|
||||
tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8);
|
||||
__iowrite32_copy((void *)(dest + m * 4), &tmp, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void sst_dma_transfer_complete(void *arg)
|
||||
@ -1014,8 +1028,8 @@ EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
|
||||
|
||||
/* register a DSP memory block for use with FW based modules */
|
||||
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
|
||||
u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
|
||||
void *private)
|
||||
u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
|
||||
u32 index, void *private)
|
||||
{
|
||||
struct sst_mem_block *block;
|
||||
|
||||
|
43
sound/soc/intel/common/sst-match-acpi.c
Normal file
43
sound/soc/intel/common/sst-match-acpi.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* sst_match_apci.c - SST (LPE) match for ACPI enumeration.
|
||||
*
|
||||
* Copyright (c) 2013-15, Intel Corporation.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sst-acpi.h"
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
|
||||
{
|
||||
struct sst_acpi_mach *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->id[0]; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
|
@ -607,7 +607,7 @@ static int hsw_block_disable(struct sst_mem_block *block)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sst_block_ops sst_hsw_ops = {
|
||||
static const struct sst_block_ops sst_hsw_ops = {
|
||||
.enable = hsw_block_enable,
|
||||
.disable = hsw_block_disable,
|
||||
};
|
||||
|
@ -778,7 +778,6 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
|
||||
struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
|
||||
struct sst_generic_ipc *ipc = &hsw->ipc;
|
||||
u32 ipcx, ipcd;
|
||||
int handled;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
@ -790,34 +789,30 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
|
||||
if (ipcx & SST_IPCX_DONE) {
|
||||
|
||||
/* Handle Immediate reply from DSP Core */
|
||||
handled = hsw_process_reply(hsw, ipcx);
|
||||
hsw_process_reply(hsw, ipcx);
|
||||
|
||||
if (handled > 0) {
|
||||
/* clear DONE bit - tell DSP we have completed */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
|
||||
SST_IPCX_DONE, 0);
|
||||
/* clear DONE bit - tell DSP we have completed */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
|
||||
SST_IPCX_DONE, 0);
|
||||
|
||||
/* unmask Done interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_DONE, 0);
|
||||
}
|
||||
/* unmask Done interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_DONE, 0);
|
||||
}
|
||||
|
||||
/* new message from DSP */
|
||||
if (ipcd & SST_IPCD_BUSY) {
|
||||
|
||||
/* Handle Notification and Delayed reply from DSP Core */
|
||||
handled = hsw_process_notification(hsw);
|
||||
hsw_process_notification(hsw);
|
||||
|
||||
/* clear BUSY bit and set DONE bit - accept new messages */
|
||||
if (handled > 0) {
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
|
||||
SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
|
||||
SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
|
||||
|
||||
/* unmask busy interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_BUSY, 0);
|
||||
}
|
||||
/* unmask busy interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_BUSY, 0);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
@ -96,7 +96,7 @@ int skl_init_dsp(struct skl *skl)
|
||||
}
|
||||
|
||||
ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
|
||||
loader_ops, &skl->skl_sst);
|
||||
skl->fw_name, loader_ops, &skl->skl_sst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -182,94 +182,6 @@ enum skl_bitdepth skl_get_bit_depth(int params)
|
||||
}
|
||||
}
|
||||
|
||||
static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg)
|
||||
{
|
||||
u32 config;
|
||||
|
||||
switch (ch_cfg) {
|
||||
case SKL_CH_CFG_MONO:
|
||||
config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT);
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_STEREO:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_2_1:
|
||||
config = (0xFFFFF000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4)
|
||||
| (SKL_CHANNEL_LFE << 8));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_3_0:
|
||||
config = (0xFFFFF000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_3_1:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LFE << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_QUATRO:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 8)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_4_0:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_CENTER_SURROUND << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_5_0:
|
||||
config = (0xFFF00000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 12)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 16));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_5_1:
|
||||
config = (0xFF000000 | SKL_CHANNEL_CENTER
|
||||
| (SKL_CHANNEL_LEFT << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 12)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 16)
|
||||
| (SKL_CHANNEL_LFE << 20));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_DUAL_MONO:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_LEFT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_I2S_DUAL_STEREO_0:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_I2S_DUAL_STEREO_1:
|
||||
config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8)
|
||||
| (SKL_CHANNEL_RIGHT << 12));
|
||||
break;
|
||||
|
||||
default:
|
||||
config = 0xFFFFFFFF;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each module in DSP expects a base module configuration, which consists of
|
||||
* PCM format information, which we calculate in driver and resource values
|
||||
@ -280,7 +192,7 @@ static void skl_set_base_module_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_base_cfg *base_cfg)
|
||||
{
|
||||
struct skl_module_fmt *format = &mconfig->in_fmt;
|
||||
struct skl_module_fmt *format = &mconfig->in_fmt[0];
|
||||
|
||||
base_cfg->audio_fmt.number_of_channels = (u8)format->channels;
|
||||
|
||||
@ -293,14 +205,14 @@ static void skl_set_base_module_format(struct skl_sst *ctx,
|
||||
format->bit_depth, format->valid_bit_depth,
|
||||
format->ch_cfg);
|
||||
|
||||
base_cfg->audio_fmt.channel_map = skl_create_channel_map(
|
||||
base_cfg->audio_fmt.ch_cfg);
|
||||
base_cfg->audio_fmt.channel_map = format->ch_map;
|
||||
|
||||
base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL;
|
||||
base_cfg->audio_fmt.interleaving = format->interleaving_style;
|
||||
|
||||
base_cfg->cps = mconfig->mcps;
|
||||
base_cfg->ibs = mconfig->ibs;
|
||||
base_cfg->obs = mconfig->obs;
|
||||
base_cfg->is_pages = mconfig->mem_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -399,7 +311,7 @@ static void skl_setup_out_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_audio_data_format *out_fmt)
|
||||
{
|
||||
struct skl_module_fmt *format = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *format = &mconfig->out_fmt[0];
|
||||
|
||||
out_fmt->number_of_channels = (u8)format->channels;
|
||||
out_fmt->s_freq = format->s_freq;
|
||||
@ -407,8 +319,9 @@ static void skl_setup_out_format(struct skl_sst *ctx,
|
||||
out_fmt->valid_bit_depth = format->valid_bit_depth;
|
||||
out_fmt->ch_cfg = format->ch_cfg;
|
||||
|
||||
out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg);
|
||||
out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL;
|
||||
out_fmt->channel_map = format->ch_map;
|
||||
out_fmt->interleaving = format->interleaving_style;
|
||||
out_fmt->sample_type = format->sample_type;
|
||||
|
||||
dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n",
|
||||
out_fmt->number_of_channels, format->s_freq, format->bit_depth);
|
||||
@ -423,7 +336,7 @@ static void skl_set_src_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_src_module_cfg *src_mconfig)
|
||||
{
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig,
|
||||
(struct skl_base_cfg *)src_mconfig);
|
||||
@ -440,7 +353,7 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_up_down_mixer_cfg *mixer_mconfig)
|
||||
{
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
|
||||
int i = 0;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig,
|
||||
@ -475,6 +388,47 @@ static void skl_set_copier_format(struct skl_sst *ctx,
|
||||
skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Algo module are DSP pre processing modules. Algo module take base module
|
||||
* configuration and params
|
||||
*/
|
||||
|
||||
static void skl_set_algo_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_algo_cfg *algo_mcfg)
|
||||
{
|
||||
struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)algo_mcfg;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig, base_cfg);
|
||||
|
||||
if (mconfig->formats_config.caps_size == 0)
|
||||
return;
|
||||
|
||||
memcpy(algo_mcfg->params,
|
||||
mconfig->formats_config.caps,
|
||||
mconfig->formats_config.caps_size);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Mic select module allows selecting one or many input channels, thus
|
||||
* acting as a demux.
|
||||
*
|
||||
* Mic select module take base module configuration and out-format
|
||||
* configuration
|
||||
*/
|
||||
static void skl_set_base_outfmt_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_base_outfmt_cfg *base_outfmt_mcfg)
|
||||
{
|
||||
struct skl_audio_data_format *out_fmt = &base_outfmt_mcfg->out_fmt;
|
||||
struct skl_base_cfg *base_cfg =
|
||||
(struct skl_base_cfg *)base_outfmt_mcfg;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig, base_cfg);
|
||||
skl_setup_out_format(ctx, mconfig, out_fmt);
|
||||
}
|
||||
|
||||
static u16 skl_get_module_param_size(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig)
|
||||
{
|
||||
@ -492,6 +446,14 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx,
|
||||
case SKL_MODULE_TYPE_UPDWMIX:
|
||||
return sizeof(struct skl_up_down_mixer_cfg);
|
||||
|
||||
case SKL_MODULE_TYPE_ALGO:
|
||||
param_size = sizeof(struct skl_base_cfg);
|
||||
param_size += mconfig->formats_config.caps_size;
|
||||
return param_size;
|
||||
|
||||
case SKL_MODULE_TYPE_BASE_OUTFMT:
|
||||
return sizeof(struct skl_base_outfmt_cfg);
|
||||
|
||||
default:
|
||||
/*
|
||||
* return only base cfg when no specific module type is
|
||||
@ -538,6 +500,14 @@ static int skl_set_module_format(struct skl_sst *ctx,
|
||||
skl_set_updown_mixer_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
case SKL_MODULE_TYPE_ALGO:
|
||||
skl_set_algo_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
case SKL_MODULE_TYPE_BASE_OUTFMT:
|
||||
skl_set_base_outfmt_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
default:
|
||||
skl_set_base_module_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
@ -571,10 +541,10 @@ static int skl_get_queue_index(struct skl_module_pin *mpin,
|
||||
* In static, the pin_index is fixed based on module_id and instance id
|
||||
*/
|
||||
static int skl_alloc_queue(struct skl_module_pin *mpin,
|
||||
struct skl_module_inst_id id, int max)
|
||||
struct skl_module_cfg *tgt_cfg, int max)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct skl_module_inst_id id = tgt_cfg->id;
|
||||
/*
|
||||
* if pin in dynamic, find first free pin
|
||||
* otherwise find match module and instance id pin as topology will
|
||||
@ -583,16 +553,23 @@ static int skl_alloc_queue(struct skl_module_pin *mpin,
|
||||
*/
|
||||
for (i = 0; i < max; i++) {
|
||||
if (mpin[i].is_dynamic) {
|
||||
if (!mpin[i].in_use) {
|
||||
if (!mpin[i].in_use &&
|
||||
mpin[i].pin_state == SKL_PIN_UNBIND) {
|
||||
|
||||
mpin[i].in_use = true;
|
||||
mpin[i].id.module_id = id.module_id;
|
||||
mpin[i].id.instance_id = id.instance_id;
|
||||
mpin[i].tgt_mcfg = tgt_cfg;
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
if (mpin[i].id.module_id == id.module_id &&
|
||||
mpin[i].id.instance_id == id.instance_id)
|
||||
mpin[i].id.instance_id == id.instance_id &&
|
||||
mpin[i].pin_state == SKL_PIN_UNBIND) {
|
||||
|
||||
mpin[i].tgt_mcfg = tgt_cfg;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,6 +583,28 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
|
||||
mpin[q_index].id.module_id = 0;
|
||||
mpin[q_index].id.instance_id = 0;
|
||||
}
|
||||
mpin[q_index].pin_state = SKL_PIN_UNBIND;
|
||||
mpin[q_index].tgt_mcfg = NULL;
|
||||
}
|
||||
|
||||
/* Module state will be set to unint, if all the out pin state is UNBIND */
|
||||
|
||||
static void skl_clear_module_state(struct skl_module_pin *mpin, int max,
|
||||
struct skl_module_cfg *mcfg)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
if (mpin[i].pin_state == SKL_PIN_UNBIND)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
mcfg->m_state = SKL_MODULE_UNINIT;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -615,7 +614,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
|
||||
* invoke the DSP by sending IPC INIT_INSTANCE using ipc helper
|
||||
*/
|
||||
int skl_init_module(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig, char *param)
|
||||
struct skl_module_cfg *mconfig)
|
||||
{
|
||||
u16 module_config_size = 0;
|
||||
void *param_data = NULL;
|
||||
@ -682,37 +681,30 @@ int skl_unbind_modules(struct skl_sst *ctx,
|
||||
struct skl_module_inst_id dst_id = dst_mcfg->id;
|
||||
int in_max = dst_mcfg->max_in_queue;
|
||||
int out_max = src_mcfg->max_out_queue;
|
||||
int src_index, dst_index;
|
||||
int src_index, dst_index, src_pin_state, dst_pin_state;
|
||||
|
||||
skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
|
||||
|
||||
if (src_mcfg->m_state != SKL_MODULE_BIND_DONE)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* if intra module unbind, check if both modules are BIND,
|
||||
* then send unbind
|
||||
*/
|
||||
if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) &&
|
||||
dst_mcfg->m_state != SKL_MODULE_BIND_DONE)
|
||||
return 0;
|
||||
else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE &&
|
||||
dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
|
||||
return 0;
|
||||
|
||||
/* get src queue index */
|
||||
src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max);
|
||||
if (src_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
|
||||
msg.src_queue = src_index;
|
||||
|
||||
/* get dst queue index */
|
||||
dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max);
|
||||
if (dst_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
|
||||
msg.dst_queue = dst_index;
|
||||
|
||||
src_pin_state = src_mcfg->m_out_pin[src_index].pin_state;
|
||||
dst_pin_state = dst_mcfg->m_in_pin[dst_index].pin_state;
|
||||
|
||||
if (src_pin_state != SKL_PIN_BIND_DONE ||
|
||||
dst_pin_state != SKL_PIN_BIND_DONE)
|
||||
return 0;
|
||||
|
||||
msg.module_id = src_mcfg->id.module_id;
|
||||
msg.instance_id = src_mcfg->id.instance_id;
|
||||
@ -722,10 +714,15 @@ int skl_unbind_modules(struct skl_sst *ctx,
|
||||
|
||||
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
|
||||
if (!ret) {
|
||||
src_mcfg->m_state = SKL_MODULE_UNINIT;
|
||||
/* free queue only if unbind is success */
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
skl_free_queue(dst_mcfg->m_in_pin, dst_index);
|
||||
|
||||
/*
|
||||
* check only if src module bind state, bind is
|
||||
* always from src -> sink
|
||||
*/
|
||||
skl_clear_module_state(src_mcfg->m_out_pin, out_max, src_mcfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -744,8 +741,6 @@ int skl_bind_modules(struct skl_sst *ctx,
|
||||
{
|
||||
int ret;
|
||||
struct skl_ipc_bind_unbind_msg msg;
|
||||
struct skl_module_inst_id src_id = src_mcfg->id;
|
||||
struct skl_module_inst_id dst_id = dst_mcfg->id;
|
||||
int in_max = dst_mcfg->max_in_queue;
|
||||
int out_max = src_mcfg->max_out_queue;
|
||||
int src_index, dst_index;
|
||||
@ -756,18 +751,18 @@ int skl_bind_modules(struct skl_sst *ctx,
|
||||
dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
|
||||
return 0;
|
||||
|
||||
src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max);
|
||||
src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_mcfg, out_max);
|
||||
if (src_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
|
||||
dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max);
|
||||
msg.src_queue = src_index;
|
||||
dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_mcfg, in_max);
|
||||
if (dst_index < 0) {
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
|
||||
msg.dst_queue = dst_index;
|
||||
|
||||
dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n",
|
||||
msg.src_queue, msg.dst_queue);
|
||||
@ -782,6 +777,8 @@ int skl_bind_modules(struct skl_sst *ctx,
|
||||
|
||||
if (!ret) {
|
||||
src_mcfg->m_state = SKL_MODULE_BIND_DONE;
|
||||
src_mcfg->m_out_pin[src_index].pin_state = SKL_PIN_BIND_DONE;
|
||||
dst_mcfg->m_in_pin[dst_index].pin_state = SKL_PIN_BIND_DONE;
|
||||
} else {
|
||||
/* error case , if IPC fails, clear the queue index */
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
@ -852,6 +849,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
|
||||
ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "Failed to delete pipeline\n");
|
||||
|
||||
pipe->state = SKL_PIPE_INVALID;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -916,3 +915,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Algo parameter set helper function */
|
||||
int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg)
|
||||
{
|
||||
struct skl_ipc_large_config_msg msg;
|
||||
|
||||
msg.module_id = mcfg->id.module_id;
|
||||
msg.instance_id = mcfg->id.instance_id;
|
||||
msg.param_data_size = size;
|
||||
msg.large_param_id = param_id;
|
||||
|
||||
return skl_ipc_set_large_config(&ctx->ipc, &msg, params);
|
||||
}
|
||||
|
||||
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg)
|
||||
{
|
||||
struct skl_ipc_large_config_msg msg;
|
||||
|
||||
msg.module_id = mcfg->id.module_id;
|
||||
msg.instance_id = mcfg->id.instance_id;
|
||||
msg.param_data_size = size;
|
||||
msg.large_param_id = param_id;
|
||||
|
||||
return skl_ipc_get_large_config(&ctx->ipc, &msg, params);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ void skl_nhlt_free(void *addr)
|
||||
|
||||
static struct nhlt_specific_cfg *skl_get_specific_cfg(
|
||||
struct device *dev, struct nhlt_fmt *fmt,
|
||||
u8 no_ch, u32 rate, u16 bps)
|
||||
u8 no_ch, u32 rate, u16 bps, u8 linktype)
|
||||
{
|
||||
struct nhlt_specific_cfg *sp_config;
|
||||
struct wav_fmt *wfmt;
|
||||
@ -68,11 +68,17 @@ static struct nhlt_specific_cfg *skl_get_specific_cfg(
|
||||
wfmt = &fmt_config->fmt_ext.fmt;
|
||||
dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
|
||||
wfmt->bits_per_sample, wfmt->samples_per_sec);
|
||||
if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate &&
|
||||
wfmt->bits_per_sample == bps) {
|
||||
if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) {
|
||||
/*
|
||||
* if link type is dmic ignore rate check as the blob is
|
||||
* generic for all rates
|
||||
*/
|
||||
sp_config = &fmt_config->config;
|
||||
if (linktype == NHLT_LINK_DMIC)
|
||||
return sp_config;
|
||||
|
||||
return sp_config;
|
||||
if (wfmt->samples_per_sec == rate)
|
||||
return sp_config;
|
||||
}
|
||||
|
||||
fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
|
||||
@ -115,7 +121,7 @@ struct nhlt_specific_cfg
|
||||
struct device *dev = bus->dev;
|
||||
struct nhlt_specific_cfg *sp_config;
|
||||
struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
|
||||
u16 bps = num_ch * s_fmt;
|
||||
u16 bps = (s_fmt == 16) ? 16 : 32;
|
||||
u8 j;
|
||||
|
||||
dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
|
||||
@ -128,7 +134,8 @@ struct nhlt_specific_cfg
|
||||
if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) {
|
||||
fmt = (struct nhlt_fmt *)(epnt->config.caps +
|
||||
epnt->config.size);
|
||||
sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps);
|
||||
sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
|
||||
s_rate, bps, link_type);
|
||||
if (sp_config)
|
||||
return sp_config;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#define HDA_MONO 1
|
||||
#define HDA_STEREO 2
|
||||
#define HDA_QUAD 4
|
||||
|
||||
static struct snd_pcm_hardware azx_pcm_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
@ -39,12 +40,15 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
||||
SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
|
||||
SNDRV_PCM_INFO_HAS_LINK_ATIME |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_8000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = HDA_QUAD,
|
||||
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
|
||||
@ -105,6 +109,31 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *e
|
||||
return HDAC_EXT_STREAM_TYPE_COUPLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if the stream opened is marked as ignore_suspend by machine, if so
|
||||
* then enable suspend_active refcount
|
||||
*
|
||||
* The count supend_active does not need lock as it is used in open/close
|
||||
* and suspend context
|
||||
*/
|
||||
static void skl_set_suspend_active(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai, bool enable)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
|
||||
struct snd_soc_dapm_widget *w;
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
w = dai->playback_widget;
|
||||
else
|
||||
w = dai->capture_widget;
|
||||
|
||||
if (w->ignore_suspend && enable)
|
||||
skl->supend_active++;
|
||||
else if (w->ignore_suspend && !enable)
|
||||
skl->supend_active--;
|
||||
}
|
||||
|
||||
static int skl_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
@ -112,12 +141,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct hdac_ext_stream *stream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct skl_dma_params *dma_params;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
|
||||
ret = pm_runtime_get_sync(dai->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
stream = snd_hdac_ext_stream_assign(ebus, substream,
|
||||
skl_get_host_stream_type(ebus));
|
||||
@ -146,6 +171,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
|
||||
|
||||
dev_dbg(dai->dev, "stream tag set in dma params=%d\n",
|
||||
dma_params->stream_tag);
|
||||
skl_set_suspend_active(substream, dai, true);
|
||||
snd_pcm_set_sync(substream);
|
||||
|
||||
return 0;
|
||||
@ -185,10 +211,6 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
int err;
|
||||
|
||||
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
|
||||
if (hdac_stream(stream)->prepared) {
|
||||
dev_dbg(dai->dev, "already stream is prepared - returning\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
format_val = skl_get_format(substream, dai);
|
||||
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
|
||||
@ -261,9 +283,8 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
|
||||
* dma_params
|
||||
*/
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
skl_set_suspend_active(substream, dai, false);
|
||||
|
||||
pm_runtime_mark_last_busy(dai->dev);
|
||||
pm_runtime_put_autosuspend(dai->dev);
|
||||
kfree(dma_params);
|
||||
}
|
||||
|
||||
@ -291,7 +312,53 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
|
||||
p_params.ch = params_channels(params);
|
||||
p_params.s_freq = params_rate(params);
|
||||
p_params.stream = substream->stream;
|
||||
skl_tplg_be_update_params(dai, &p_params);
|
||||
|
||||
return skl_tplg_be_update_params(dai, &p_params);
|
||||
}
|
||||
|
||||
static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct hdac_ext_stream *stream;
|
||||
int start;
|
||||
unsigned long cookie;
|
||||
struct hdac_stream *hstr;
|
||||
|
||||
stream = get_hdac_ext_stream(substream);
|
||||
hstr = hdac_stream(stream);
|
||||
|
||||
if (!hstr->prepared)
|
||||
return -EPIPE;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
start = 1;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
start = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
|
||||
if (start) {
|
||||
snd_hdac_stream_start(hdac_stream(stream), true);
|
||||
snd_hdac_stream_timecounter_init(hstr, 0);
|
||||
} else {
|
||||
snd_hdac_stream_stop(hdac_stream(stream));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -302,23 +369,54 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct skl *skl = get_skl_ctx(dai->dev);
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
struct skl_module_cfg *mconfig;
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
|
||||
int ret;
|
||||
|
||||
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
|
||||
if (!mconfig)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
skl_pcm_prepare(substream, dai);
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
/*
|
||||
* Start HOST DMA and Start FE Pipe.This is to make sure that
|
||||
* there are no underrun/overrun in the case when the FE
|
||||
* pipeline is started but there is a delay in starting the
|
||||
* DMA channel on the host.
|
||||
*/
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, true);
|
||||
ret = skl_decoupled_trigger(substream, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return skl_run_pipe(ctx, mconfig->pipe);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
return skl_stop_pipe(ctx, mconfig->pipe);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
/*
|
||||
* Stop FE Pipe first and stop DMA. This is to make sure that
|
||||
* there are no underrun/overrun in the case if there is a delay
|
||||
* between the two operations.
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_decoupled_trigger(substream, cmd);
|
||||
if (cmd == SNDRV_PCM_TRIGGER_SUSPEND)
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_link_hw_params(struct snd_pcm_substream *substream,
|
||||
@ -352,9 +450,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
|
||||
p_params.stream = substream->stream;
|
||||
p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
|
||||
|
||||
skl_tplg_be_update_params(dai, &p_params);
|
||||
|
||||
return 0;
|
||||
return skl_tplg_be_update_params(dai, &p_params);
|
||||
}
|
||||
|
||||
static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
@ -443,19 +539,6 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_be_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
return pm_runtime_get_sync(dai->dev);
|
||||
}
|
||||
|
||||
static void skl_be_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
pm_runtime_mark_last_busy(dai->dev);
|
||||
pm_runtime_put_autosuspend(dai->dev);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops skl_pcm_dai_ops = {
|
||||
.startup = skl_pcm_open,
|
||||
.shutdown = skl_pcm_close,
|
||||
@ -466,24 +549,18 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_dmic_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.hw_params = skl_be_hw_params,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.hw_params = skl_be_hw_params,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_link_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.prepare = skl_link_pcm_prepare,
|
||||
.hw_params = skl_link_hw_params,
|
||||
.hw_free = skl_link_hw_free,
|
||||
.trigger = skl_link_pcm_trigger,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
@ -511,7 +588,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.capture = {
|
||||
.stream_name = "Reference Capture",
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
@ -538,6 +615,18 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "DMIC Pin",
|
||||
.ops = &skl_pcm_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "DMIC Capture",
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
|
||||
/* BE CPU Dais */
|
||||
{
|
||||
.name = "SSP0 Pin",
|
||||
@ -557,6 +646,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP1 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp1 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp1 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "iDisp Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
@ -573,8 +680,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.ops = &skl_dmic_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "DMIC01 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
@ -688,66 +795,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct hdac_ext_stream *stream;
|
||||
int start;
|
||||
unsigned long cookie;
|
||||
struct hdac_stream *hstr;
|
||||
|
||||
dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name);
|
||||
|
||||
stream = get_hdac_ext_stream(substream);
|
||||
hstr = hdac_stream(stream);
|
||||
|
||||
if (!hstr->prepared)
|
||||
return -EPIPE;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
start = 1;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
start = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
|
||||
if (start)
|
||||
snd_hdac_stream_start(hdac_stream(stream), true);
|
||||
else
|
||||
snd_hdac_stream_stop(hdac_stream(stream));
|
||||
|
||||
if (start)
|
||||
snd_hdac_stream_timecounter_init(hstr, 0);
|
||||
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
|
||||
if (ebus->ppcap)
|
||||
return skl_decoupled_trigger(substream, cmd);
|
||||
else
|
||||
if (!ebus->ppcap)
|
||||
return skl_coupled_trigger(substream, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate runtime delay from LPIB */
|
||||
@ -789,7 +845,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
|
||||
{
|
||||
struct hdac_stream *hstr = hdac_stream(hstream);
|
||||
struct snd_pcm_substream *substream = hstr->substream;
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_bus *ebus;
|
||||
unsigned int pos;
|
||||
int delay;
|
||||
|
||||
@ -800,6 +856,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
|
||||
pos = 0;
|
||||
|
||||
if (substream->runtime) {
|
||||
ebus = get_bus_ctx(substream);
|
||||
delay = skl_get_delay_from_lpib(ebus, hstream, pos)
|
||||
+ codec_delay;
|
||||
substream->runtime->delay += delay;
|
||||
@ -941,7 +998,6 @@ int skl_platform_register(struct device *dev)
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
INIT_LIST_HEAD(&skl->ppl_list);
|
||||
INIT_LIST_HEAD(&skl->dapm_path_list);
|
||||
|
||||
ret = snd_soc_register_platform(dev, &skl_platform_drv);
|
||||
if (ret) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
|
||||
@ -33,6 +34,53 @@ void skl_cldma_int_disable(struct sst_dsp *ctx)
|
||||
SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0);
|
||||
}
|
||||
|
||||
static void skl_cldma_stream_run(struct sst_dsp *ctx, bool enable)
|
||||
{
|
||||
unsigned char val;
|
||||
int timeout;
|
||||
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(enable));
|
||||
|
||||
udelay(3);
|
||||
timeout = 300;
|
||||
do {
|
||||
/* waiting for hardware to report that the stream Run bit set */
|
||||
val = sst_dsp_shim_read(ctx, SKL_ADSP_REG_CL_SD_CTL) &
|
||||
CL_SD_CTL_RUN_MASK;
|
||||
if (enable && val)
|
||||
break;
|
||||
else if (!enable && !val)
|
||||
break;
|
||||
udelay(3);
|
||||
} while (--timeout);
|
||||
|
||||
if (timeout == 0)
|
||||
dev_err(ctx->dev, "Failed to set Run bit=%d enable=%d\n", val, enable);
|
||||
}
|
||||
|
||||
static void skl_cldma_stream_clear(struct sst_dsp *ctx)
|
||||
{
|
||||
/* make sure Run bit is cleared before setting stream register */
|
||||
skl_cldma_stream_run(ctx, 0);
|
||||
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
|
||||
}
|
||||
|
||||
/* Code loader helper APIs */
|
||||
static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
|
||||
struct snd_dma_buffer *dmab_data,
|
||||
@ -68,6 +116,7 @@ static void skl_cldma_setup_controller(struct sst_dsp *ctx,
|
||||
struct snd_dma_buffer *dmab_bdl, unsigned int max_size,
|
||||
u32 count)
|
||||
{
|
||||
skl_cldma_stream_clear(ctx);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL,
|
||||
CL_SD_BDLPLBA(dmab_bdl->addr));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU,
|
||||
@ -107,36 +156,13 @@ static void skl_cldma_cleanup_spb(struct sst_dsp *ctx)
|
||||
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0);
|
||||
}
|
||||
|
||||
static void skl_cldma_trigger(struct sst_dsp *ctx, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(1));
|
||||
else
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(0));
|
||||
}
|
||||
|
||||
static void skl_cldma_cleanup(struct sst_dsp *ctx)
|
||||
{
|
||||
skl_cldma_cleanup_spb(ctx);
|
||||
skl_cldma_stream_clear(ctx);
|
||||
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
|
||||
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
|
||||
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl);
|
||||
}
|
||||
|
||||
static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
|
||||
@ -164,7 +190,7 @@ cleanup:
|
||||
|
||||
static void skl_cldma_stop(struct sst_dsp *ctx)
|
||||
{
|
||||
ctx->cl_dev.ops.cl_trigger(ctx, false);
|
||||
skl_cldma_stream_run(ctx, false);
|
||||
}
|
||||
|
||||
static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
|
||||
@ -175,6 +201,21 @@ static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
|
||||
ctx->cl_dev.dma_buffer_offset, trigger);
|
||||
dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos);
|
||||
|
||||
/*
|
||||
* Check if the size exceeds buffer boundary. If it exceeds
|
||||
* max_buffer size, then copy till buffer size and then copy
|
||||
* remaining buffer from the start of ring buffer.
|
||||
*/
|
||||
if (ctx->cl_dev.dma_buffer_offset + size > ctx->cl_dev.bufsize) {
|
||||
unsigned int size_b = ctx->cl_dev.bufsize -
|
||||
ctx->cl_dev.dma_buffer_offset;
|
||||
memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
|
||||
curr_pos, size_b);
|
||||
size -= size_b;
|
||||
curr_pos += size_b;
|
||||
ctx->cl_dev.dma_buffer_offset = 0;
|
||||
}
|
||||
|
||||
memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
|
||||
curr_pos, size);
|
||||
|
||||
@ -291,7 +332,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
|
||||
ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller;
|
||||
ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb;
|
||||
ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb;
|
||||
ctx->cl_dev.ops.cl_trigger = skl_cldma_trigger;
|
||||
ctx->cl_dev.ops.cl_trigger = skl_cldma_stream_run;
|
||||
ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup;
|
||||
ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf;
|
||||
ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
|
||||
|
@ -58,9 +58,9 @@ struct sst_dsp_device;
|
||||
|
||||
#define SKL_ADSP_MMIO_LEN 0x10000
|
||||
|
||||
#define SKL_ADSP_W0_STAT_SZ 0x800
|
||||
#define SKL_ADSP_W0_STAT_SZ 0x1000
|
||||
|
||||
#define SKL_ADSP_W0_UP_SZ 0x800
|
||||
#define SKL_ADSP_W0_UP_SZ 0x1000
|
||||
|
||||
#define SKL_ADSP_W1_SZ 0x1000
|
||||
|
||||
@ -114,6 +114,9 @@ struct skl_dsp_fw_ops {
|
||||
int (*set_state_D0)(struct sst_dsp *ctx);
|
||||
int (*set_state_D3)(struct sst_dsp *ctx);
|
||||
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
|
||||
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
|
||||
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
|
||||
|
||||
};
|
||||
|
||||
struct skl_dsp_loader_ops {
|
||||
@ -123,6 +126,17 @@ struct skl_dsp_loader_ops {
|
||||
struct snd_dma_buffer *dmab);
|
||||
};
|
||||
|
||||
struct skl_load_module_info {
|
||||
u16 mod_id;
|
||||
const struct firmware *fw;
|
||||
};
|
||||
|
||||
struct skl_module_table {
|
||||
struct skl_load_module_info *mod_info;
|
||||
unsigned int usage_cnt;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
void skl_cldma_process_intr(struct sst_dsp *ctx);
|
||||
void skl_cldma_int_disable(struct sst_dsp *ctx);
|
||||
int skl_cldma_prepare(struct sst_dsp *ctx);
|
||||
@ -139,7 +153,8 @@ void skl_dsp_free(struct sst_dsp *dsp);
|
||||
|
||||
int skl_dsp_boot(struct sst_dsp *ctx);
|
||||
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp);
|
||||
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
|
||||
struct skl_sst **dsp);
|
||||
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
|
||||
|
||||
#endif /*__SKL_SST_DSP_H__*/
|
||||
|
@ -130,6 +130,11 @@
|
||||
#define IPC_SRC_QUEUE_MASK 0x7
|
||||
#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \
|
||||
<< IPC_SRC_QUEUE_SHIFT)
|
||||
/* Load Module count */
|
||||
#define IPC_LOAD_MODULE_SHIFT 0
|
||||
#define IPC_LOAD_MODULE_MASK 0xFF
|
||||
#define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \
|
||||
<< IPC_LOAD_MODULE_SHIFT)
|
||||
|
||||
/* Save pipeline messgae extension register */
|
||||
#define IPC_DMA_ID_SHIFT 0
|
||||
@ -344,6 +349,8 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
|
||||
switch (reply) {
|
||||
case IPC_GLB_REPLY_SUCCESS:
|
||||
dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
|
||||
/* copy the rx data from the mailbox */
|
||||
sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
|
||||
break;
|
||||
|
||||
case IPC_GLB_REPLY_OUT_OF_MEMORY:
|
||||
@ -650,7 +657,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id,
|
||||
dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
|
||||
header.primary, header.extension);
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
|
||||
dx, sizeof(dx), NULL, 0);
|
||||
dx, sizeof(*dx), NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret);
|
||||
return ret;
|
||||
@ -728,6 +735,54 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind);
|
||||
|
||||
/*
|
||||
* In order to load a module we need to send IPC to initiate that. DMA will
|
||||
* performed to load the module memory. The FW supports multiple module load
|
||||
* at single shot, so we can send IPC with N modules represented by
|
||||
* module_cnt
|
||||
*/
|
||||
int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS);
|
||||
header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
|
||||
(sizeof(u16) * module_cnt), NULL, 0);
|
||||
if (ret < 0)
|
||||
dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_load_modules);
|
||||
|
||||
int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt,
|
||||
void *data)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS);
|
||||
header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
|
||||
(sizeof(u16) * module_cnt), NULL, 0);
|
||||
if (ret < 0)
|
||||
dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_unload_modules);
|
||||
|
||||
int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param)
|
||||
{
|
||||
@ -781,3 +836,54 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_set_large_config);
|
||||
|
||||
int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret = 0;
|
||||
size_t sz_remaining, rx_size, data_offset;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_GET);
|
||||
header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
|
||||
header.primary |= IPC_MOD_ID(msg->module_id);
|
||||
|
||||
header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size);
|
||||
header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id);
|
||||
header.extension |= IPC_FINAL_BLOCK(1);
|
||||
header.extension |= IPC_INITIAL_BLOCK(1);
|
||||
|
||||
sz_remaining = msg->param_data_size;
|
||||
data_offset = 0;
|
||||
|
||||
while (sz_remaining != 0) {
|
||||
rx_size = sz_remaining > SKL_ADSP_W1_SZ
|
||||
? SKL_ADSP_W1_SZ : sz_remaining;
|
||||
if (rx_size == sz_remaining)
|
||||
header.extension |= IPC_FINAL_BLOCK(1);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0,
|
||||
((char *)param) + data_offset,
|
||||
msg->param_data_size);
|
||||
if (ret < 0) {
|
||||
dev_err(ipc->dev,
|
||||
"ipc: get large config fail, err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
sz_remaining -= rx_size;
|
||||
data_offset = msg->param_data_size - sz_remaining;
|
||||
|
||||
/* clear the fields */
|
||||
header.extension &= IPC_INITIAL_BLOCK_CLEAR;
|
||||
header.extension &= IPC_DATA_OFFSET_SZ_CLEAR;
|
||||
/* fill the fields */
|
||||
header.extension |= IPC_INITIAL_BLOCK(1);
|
||||
header.extension |= IPC_DATA_OFFSET_SZ(data_offset);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
|
||||
|
@ -108,12 +108,21 @@ int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc,
|
||||
int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc,
|
||||
struct skl_ipc_bind_unbind_msg *msg);
|
||||
|
||||
int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data);
|
||||
|
||||
int skl_ipc_unload_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data);
|
||||
|
||||
int skl_ipc_set_dx(struct sst_generic_ipc *ipc,
|
||||
u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx);
|
||||
|
||||
int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param);
|
||||
|
||||
int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param);
|
||||
|
||||
void skl_ipc_int_enable(struct sst_dsp *dsp);
|
||||
void skl_ipc_op_int_enable(struct sst_dsp *ctx);
|
||||
void skl_ipc_op_int_disable(struct sst_dsp *ctx);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
@ -37,6 +38,8 @@
|
||||
#define SKL_INSTANCE_ID 0
|
||||
#define SKL_BASE_FW_MODULE_ID 0
|
||||
|
||||
#define SKL_NUM_MODULES 1
|
||||
|
||||
static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
|
||||
{
|
||||
u32 cur_sts;
|
||||
@ -77,7 +80,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
|
||||
init_waitqueue_head(&skl->boot_wait);
|
||||
|
||||
if (ctx->fw == NULL) {
|
||||
ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
|
||||
ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
|
||||
skl_dsp_disable_core(ctx);
|
||||
@ -115,27 +118,28 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
|
||||
dev_err(ctx->dev,
|
||||
"Timeout waiting for ROM init done, reg:0x%x\n", reg);
|
||||
ret = -EIO;
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
}
|
||||
|
||||
ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
} else {
|
||||
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
|
||||
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
|
||||
if (ret == 0) {
|
||||
dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n");
|
||||
ret = -EIO;
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
}
|
||||
|
||||
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
|
||||
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
|
||||
}
|
||||
return 0;
|
||||
|
||||
transfer_firmware_failed:
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_load_base_firmware_failed:
|
||||
skl_dsp_disable_core(ctx);
|
||||
release_firmware(ctx->fw);
|
||||
@ -175,10 +179,15 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
|
||||
dx.core_mask = SKL_DSP_CORE0_MASK;
|
||||
dx.dx_mask = SKL_IPC_D3_MASK;
|
||||
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to set DSP to D3 state\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev,
|
||||
"D3 request to FW failed, continuing reset: %d", ret);
|
||||
|
||||
/* disable Interrupt */
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_cldma_int_disable(ctx);
|
||||
skl_ipc_op_int_disable(ctx);
|
||||
skl_ipc_int_disable(ctx);
|
||||
|
||||
ret = skl_dsp_disable_core(ctx);
|
||||
if (ret < 0) {
|
||||
@ -187,12 +196,6 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
|
||||
}
|
||||
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
|
||||
|
||||
/* disable Interrupt */
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_cldma_int_disable(ctx);
|
||||
skl_ipc_op_int_disable(ctx);
|
||||
skl_ipc_int_disable(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -201,11 +204,182 @@ static unsigned int skl_get_errorcode(struct sst_dsp *ctx)
|
||||
return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE);
|
||||
}
|
||||
|
||||
/*
|
||||
* since get/set_module are called from DAPM context,
|
||||
* we don't need lock for usage count
|
||||
*/
|
||||
static int skl_get_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return ++module->usage_cnt;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int skl_put_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return --module->usage_cnt;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx,
|
||||
char *mod_name, int mod_id)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
struct skl_module_table *skl_module;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
ret = request_firmware(&fw, mod_name, ctx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Request Module %s failed :%d\n",
|
||||
mod_name, ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL);
|
||||
if (skl_module == NULL) {
|
||||
release_firmware(fw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = sizeof(*skl_module->mod_info);
|
||||
skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
|
||||
if (skl_module->mod_info == NULL) {
|
||||
release_firmware(fw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skl_module->mod_info->mod_id = mod_id;
|
||||
skl_module->mod_info->fw = fw;
|
||||
list_add(&skl_module->list, &ctx->module_list);
|
||||
|
||||
return skl_module;
|
||||
}
|
||||
|
||||
/* get a module from it's unique ID */
|
||||
static struct skl_module_table *skl_module_get_from_id(
|
||||
struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
if (list_empty(&ctx->module_list)) {
|
||||
dev_err(ctx->dev, "Module list is empty\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return module;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int skl_transfer_module(struct sst_dsp *ctx,
|
||||
struct skl_load_module_info *module)
|
||||
{
|
||||
int ret;
|
||||
struct skl_sst *skl = ctx->thread_context;
|
||||
|
||||
ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data,
|
||||
module->fw->size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES,
|
||||
(void *)&module->mod_id);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "Failed to Load module: %d\n", ret);
|
||||
|
||||
ctx->cl_dev.ops.cl_stop_dma(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
|
||||
{
|
||||
struct skl_module_table *module_entry = NULL;
|
||||
int ret = 0;
|
||||
char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
|
||||
|
||||
snprintf(mod_name, sizeof(mod_name), "%s%s%s",
|
||||
"intel/dsp_fw_", guid, ".bin");
|
||||
|
||||
module_entry = skl_module_get_from_id(ctx, mod_id);
|
||||
if (module_entry == NULL) {
|
||||
module_entry = skl_fill_module_table(ctx, mod_name, mod_id);
|
||||
if (module_entry == NULL) {
|
||||
dev_err(ctx->dev, "Failed to Load module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!module_entry->usage_cnt) {
|
||||
ret = skl_transfer_module(ctx, module_entry->mod_info);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to Load module\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = skl_get_module(ctx, mod_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
int usage_cnt;
|
||||
struct skl_sst *skl = ctx->thread_context;
|
||||
int ret = 0;
|
||||
|
||||
usage_cnt = skl_put_module(ctx, mod_id);
|
||||
if (usage_cnt < 0) {
|
||||
dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
|
||||
return -EIO;
|
||||
}
|
||||
ret = skl_ipc_unload_modules(&skl->ipc,
|
||||
SKL_NUM_MODULES, &mod_id);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to UnLoad module\n");
|
||||
skl_get_module(ctx, mod_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void skl_clear_module_table(struct sst_dsp *ctx)
|
||||
{
|
||||
struct skl_module_table *module, *tmp;
|
||||
|
||||
if (list_empty(&ctx->module_list))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(module, tmp, &ctx->module_list, list) {
|
||||
list_del(&module->list);
|
||||
release_firmware(module->mod_info->fw);
|
||||
}
|
||||
}
|
||||
|
||||
static struct skl_dsp_fw_ops skl_fw_ops = {
|
||||
.set_state_D0 = skl_set_dsp_D0,
|
||||
.set_state_D3 = skl_set_dsp_D3,
|
||||
.load_fw = skl_load_base_firmware,
|
||||
.get_fw_errcode = skl_get_errorcode,
|
||||
.load_mod = skl_load_module,
|
||||
.unload_mod = skl_unload_module,
|
||||
};
|
||||
|
||||
static struct sst_ops skl_ops = {
|
||||
@ -223,7 +397,7 @@ static struct sst_dsp_device skl_dev = {
|
||||
};
|
||||
|
||||
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
|
||||
const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
|
||||
{
|
||||
struct skl_sst *skl;
|
||||
struct sst_dsp *sst;
|
||||
@ -244,11 +418,13 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
|
||||
sst = skl->dsp;
|
||||
|
||||
sst->fw_name = fw_name;
|
||||
sst->addr.lpe = mmio_base;
|
||||
sst->addr.shim = mmio_base;
|
||||
sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
|
||||
SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
|
||||
|
||||
INIT_LIST_HEAD(&sst->module_list);
|
||||
sst->dsp_ops = dsp_ops;
|
||||
sst->fw_ops = skl_fw_ops;
|
||||
|
||||
@ -259,23 +435,24 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
ret = sst->fw_ops.load_fw(sst);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Load base fw failed : %d", ret);
|
||||
return ret;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (dsp)
|
||||
*dsp = skl;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
skl_ipc_free(&skl->ipc);
|
||||
cleanup:
|
||||
skl_sst_dsp_cleanup(dev, skl);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
|
||||
|
||||
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
|
||||
{
|
||||
skl_clear_module_table(ctx->dsp);
|
||||
skl_ipc_free(&ctx->ipc);
|
||||
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
|
||||
ctx->dsp->ops->free(ctx->dsp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "skl-topology.h"
|
||||
#include "skl.h"
|
||||
#include "skl-tplg-interface.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
|
||||
#define SKL_CH_FIXUP_MASK (1 << 0)
|
||||
#define SKL_RATE_FIXUP_MASK (1 << 1)
|
||||
@ -129,17 +131,15 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
|
||||
{
|
||||
dev_dbg(ctx->dev, "Dumping config\n");
|
||||
dev_dbg(ctx->dev, "Input Format:\n");
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n",
|
||||
mcfg->in_fmt.valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "Output Format:\n");
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n",
|
||||
mcfg->out_fmt.valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
|
||||
}
|
||||
|
||||
static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
||||
@ -149,8 +149,24 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
||||
fmt->s_freq = params->s_freq;
|
||||
if (fixup & SKL_CH_FIXUP_MASK)
|
||||
fmt->channels = params->ch;
|
||||
if (fixup & SKL_FMT_FIXUP_MASK)
|
||||
fmt->valid_bit_depth = params->s_fmt;
|
||||
if (fixup & SKL_FMT_FIXUP_MASK) {
|
||||
fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
|
||||
|
||||
/*
|
||||
* 16 bit is 16 bit container whereas 24 bit is in 32 bit
|
||||
* container so update bit depth accordingly
|
||||
*/
|
||||
switch (fmt->valid_bit_depth) {
|
||||
case SKL_DEPTH_16BIT:
|
||||
fmt->bit_depth = fmt->valid_bit_depth;
|
||||
break;
|
||||
|
||||
default:
|
||||
fmt->bit_depth = SKL_DEPTH_32BIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -171,8 +187,9 @@ static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
|
||||
int in_fixup, out_fixup;
|
||||
struct skl_module_fmt *in_fmt, *out_fmt;
|
||||
|
||||
in_fmt = &m_cfg->in_fmt;
|
||||
out_fmt = &m_cfg->out_fmt;
|
||||
/* Fixups will be applied to pin 0 only */
|
||||
in_fmt = &m_cfg->in_fmt[0];
|
||||
out_fmt = &m_cfg->out_fmt[0];
|
||||
|
||||
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (is_fe) {
|
||||
@ -209,18 +226,25 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mcfg)
|
||||
{
|
||||
int multiplier = 1;
|
||||
struct skl_module_fmt *in_fmt, *out_fmt;
|
||||
|
||||
|
||||
/* Since fixups is applied to pin 0 only, ibs, obs needs
|
||||
* change for pin 0 only
|
||||
*/
|
||||
in_fmt = &mcfg->in_fmt[0];
|
||||
out_fmt = &mcfg->out_fmt[0];
|
||||
|
||||
if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
|
||||
multiplier = 5;
|
||||
|
||||
mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
|
||||
(mcfg->in_fmt.channels) *
|
||||
(mcfg->in_fmt.bit_depth >> 3) *
|
||||
mcfg->ibs = (in_fmt->s_freq / 1000) *
|
||||
(mcfg->in_fmt->channels) *
|
||||
(mcfg->in_fmt->bit_depth >> 3) *
|
||||
multiplier;
|
||||
|
||||
mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
|
||||
(mcfg->out_fmt.channels) *
|
||||
(mcfg->out_fmt.bit_depth >> 3) *
|
||||
mcfg->obs = (mcfg->out_fmt->s_freq / 1000) *
|
||||
(mcfg->out_fmt->channels) *
|
||||
(mcfg->out_fmt->bit_depth >> 3) *
|
||||
multiplier;
|
||||
}
|
||||
|
||||
@ -291,6 +315,83 @@ static int skl_tplg_alloc_pipe_widget(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* some modules can have multiple params set from user control and
|
||||
* need to be set after module is initialized. If set_param flag is
|
||||
* set module params will be done after module is initialised.
|
||||
*/
|
||||
static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
|
||||
struct skl_sst *ctx)
|
||||
{
|
||||
int i, ret;
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
const struct snd_kcontrol_new *k;
|
||||
struct soc_bytes_ext *sb;
|
||||
struct skl_algo_data *bc;
|
||||
struct skl_specific_cfg *sp_cfg;
|
||||
|
||||
if (mconfig->formats_config.caps_size > 0 &&
|
||||
mconfig->formats_config.set_params == SKL_PARAM_SET) {
|
||||
sp_cfg = &mconfig->formats_config;
|
||||
ret = skl_set_module_params(ctx, sp_cfg->caps,
|
||||
sp_cfg->caps_size,
|
||||
sp_cfg->param_id, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < w->num_kcontrols; i++) {
|
||||
k = &w->kcontrol_news[i];
|
||||
if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (void *) k->private_value;
|
||||
bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
|
||||
if (bc->set_params == SKL_PARAM_SET) {
|
||||
ret = skl_set_module_params(ctx,
|
||||
(u32 *)bc->params, bc->max,
|
||||
bc->param_id, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* some module param can set from user control and this is required as
|
||||
* when module is initailzed. if module param is required in init it is
|
||||
* identifed by set_param flag. if set_param flag is not set, then this
|
||||
* parameter needs to set as part of module init.
|
||||
*/
|
||||
static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
|
||||
{
|
||||
const struct snd_kcontrol_new *k;
|
||||
struct soc_bytes_ext *sb;
|
||||
struct skl_algo_data *bc;
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < w->num_kcontrols; i++) {
|
||||
k = &w->kcontrol_news[i];
|
||||
if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (struct soc_bytes_ext *)k->private_value;
|
||||
bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
|
||||
if (bc->set_params != SKL_PARAM_INIT)
|
||||
continue;
|
||||
|
||||
mconfig->formats_config.caps = (u32 *)&bc->params;
|
||||
mconfig->formats_config.caps_size = bc->max;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inside a pipe instance, we can have various modules. These modules need
|
||||
* to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
|
||||
@ -313,12 +414,25 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
||||
if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
|
||||
return -ENOMEM;
|
||||
|
||||
if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
|
||||
ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
|
||||
mconfig->id.module_id, mconfig->guid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* apply fix/conversion to module params based on
|
||||
* FE/BE params
|
||||
*/
|
||||
skl_tplg_update_module_params(w, ctx);
|
||||
ret = skl_init_module(ctx, mconfig, NULL);
|
||||
|
||||
skl_tplg_set_module_init_data(w);
|
||||
ret = skl_init_module(ctx, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_tplg_set_module_params(w, ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@ -326,6 +440,24 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
|
||||
struct skl_pipe *pipe)
|
||||
{
|
||||
struct skl_pipe_module *w_module = NULL;
|
||||
struct skl_module_cfg *mconfig = NULL;
|
||||
|
||||
list_for_each_entry(w_module, &pipe->w_list, node) {
|
||||
mconfig = w_module->w->priv;
|
||||
|
||||
if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod)
|
||||
return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
|
||||
mconfig->id.module_id);
|
||||
}
|
||||
|
||||
/* no modules to unload in this path, so return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
|
||||
* need create the pipeline. So we do following:
|
||||
@ -397,6 +529,58 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl,
|
||||
struct skl_module_cfg *src_mconfig)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *sink = NULL, *next_sink = NULL;
|
||||
struct skl_module_cfg *sink_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
int ret;
|
||||
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
|
||||
dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
|
||||
|
||||
next_sink = p->sink;
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->sink->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->sink)) {
|
||||
|
||||
sink = p->sink;
|
||||
sink_mconfig = sink->priv;
|
||||
|
||||
/* Bind source to sink, mixin is always source */
|
||||
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start sinks pipe first */
|
||||
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
|
||||
if (sink_mconfig->pipe->conn_type !=
|
||||
SKL_PIPE_CONN_TYPE_FE)
|
||||
ret = skl_run_pipe(ctx,
|
||||
sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sink)
|
||||
return skl_tplg_bind_sinks(next_sink, skl, src_mconfig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
|
||||
* we need to do following:
|
||||
@ -408,75 +592,62 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
* - Then run current pipe
|
||||
*/
|
||||
static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct skl_dapm_path_list *path_list;
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
struct skl_module_cfg *src_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
int ret = 0;
|
||||
|
||||
source = w;
|
||||
src_mconfig = source->priv;
|
||||
src_mconfig = w->priv;
|
||||
|
||||
/*
|
||||
* find which sink it is connected to, bind with the sink,
|
||||
* if sink is not started, start sink pipe first, then start
|
||||
* this pipe
|
||||
*/
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
|
||||
dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->sink->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->sink)) {
|
||||
|
||||
sink = p->sink;
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
|
||||
/* Bind source to sink, mixin is always source */
|
||||
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start sinks pipe first */
|
||||
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
path_list = kzalloc(
|
||||
sizeof(struct skl_dapm_path_list),
|
||||
GFP_KERNEL);
|
||||
if (path_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add connected path to one global list */
|
||||
path_list->dapm_path = p;
|
||||
list_add_tail(&path_list->node, &skl->dapm_path_list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start source pipe last after starting all sinks */
|
||||
ret = skl_run_pipe(ctx, src_mconfig->pipe);
|
||||
ret = skl_tplg_bind_sinks(w, skl, src_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start source pipe last after starting all sinks */
|
||||
if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
|
||||
return skl_run_pipe(ctx, src_mconfig->pipe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dapm_widget *skl_get_src_dsp_widget(
|
||||
struct snd_soc_dapm_widget *w, struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *src_w = NULL;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
src_w = p->source;
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
|
||||
dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that can
|
||||
* be any widgets type and we are only interested if they are
|
||||
* ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->source->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->source)) {
|
||||
return p->source;
|
||||
}
|
||||
}
|
||||
|
||||
if (src_w != NULL)
|
||||
return skl_get_src_dsp_widget(src_w, skl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* in the Post-PMU event of mixer we need to do following:
|
||||
* - Check if this pipe is running
|
||||
@ -490,7 +661,6 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
@ -504,32 +674,18 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
* one more sink before this sink got connected, Since source is
|
||||
* started, bind this sink to source and start this pipe.
|
||||
*/
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
|
||||
dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
|
||||
source = skl_get_src_dsp_widget(w, skl);
|
||||
if (source != NULL) {
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
src_pipe_started = 1;
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
* check pipe state, then no need to bind or start the
|
||||
* pipe
|
||||
*/
|
||||
if ((p->source->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->source)) {
|
||||
source = p->source;
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
src_pipe_started = 1;
|
||||
|
||||
/*
|
||||
* check pipe state, then no need to bind or start
|
||||
* the pipe
|
||||
*/
|
||||
if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
|
||||
src_pipe_started = 0;
|
||||
}
|
||||
if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
|
||||
src_pipe_started = 0;
|
||||
}
|
||||
|
||||
if (src_pipe_started) {
|
||||
@ -537,7 +693,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -552,54 +709,37 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
int ret = 0, path_found = 0;
|
||||
struct skl_dapm_path_list *path_list, *tmp_list;
|
||||
int ret = 0, i;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
sink = w;
|
||||
sink_mconfig = sink->priv;
|
||||
sink_mconfig = w->priv;
|
||||
|
||||
/* Stop the pipe */
|
||||
ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This list, dapm_path_list handling here does not need any locks
|
||||
* as we are under dapm lock while handling widget events.
|
||||
* List can be manipulated safely only under dapm widgets handler
|
||||
* routines
|
||||
*/
|
||||
list_for_each_entry_safe(path_list, tmp_list,
|
||||
&skl->dapm_path_list, node) {
|
||||
if (path_list->dapm_path->sink == sink) {
|
||||
dev_dbg(ctx->dev, "Path found = %s\n",
|
||||
path_list->dapm_path->name);
|
||||
source = path_list->dapm_path->source;
|
||||
src_mconfig = source->priv;
|
||||
path_found = 1;
|
||||
for (i = 0; i < sink_mconfig->max_in_queue; i++) {
|
||||
if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) {
|
||||
src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
|
||||
if (!src_mconfig)
|
||||
continue;
|
||||
/*
|
||||
* If path_found == 1, that means pmd for source
|
||||
* pipe has not occurred, source is connected to
|
||||
* some other sink. so its responsibility of sink
|
||||
* to unbind itself from source.
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
list_del(&path_list->node);
|
||||
kfree(path_list);
|
||||
break;
|
||||
ret = skl_unbind_modules(ctx,
|
||||
src_mconfig, sink_mconfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If path_found == 1, that means pmd for source pipe has
|
||||
* not occurred, source is connected to some other sink.
|
||||
* so its responsibility of sink to unbind itself from source.
|
||||
*/
|
||||
if (path_found) {
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -622,10 +762,12 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
int ret = 0;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, mconfig);
|
||||
skl_tplg_free_pipe_mem(skl, mconfig);
|
||||
|
||||
list_for_each_entry(w_module, &s_pipe->w_list, node) {
|
||||
dst_module = w_module->w->priv;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, dst_module);
|
||||
if (src_module == NULL) {
|
||||
src_module = dst_module;
|
||||
continue;
|
||||
@ -639,9 +781,8 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
}
|
||||
|
||||
ret = skl_delete_pipe(ctx, mconfig->pipe);
|
||||
skl_tplg_free_pipe_mem(skl, mconfig);
|
||||
|
||||
return ret;
|
||||
return skl_tplg_unload_pipe_modules(ctx, s_pipe);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -653,47 +794,34 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
int ret = 0, path_found = 0;
|
||||
struct skl_dapm_path_list *path_list, *tmp_path_list;
|
||||
int ret = 0, i;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
source = w;
|
||||
src_mconfig = source->priv;
|
||||
src_mconfig = w->priv;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, src_mconfig);
|
||||
/* Stop the pipe since this is a mixin module */
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
|
||||
if (path_list->dapm_path->source == source) {
|
||||
dev_dbg(ctx->dev, "Path found = %s\n",
|
||||
path_list->dapm_path->name);
|
||||
sink = path_list->dapm_path->sink;
|
||||
sink_mconfig = sink->priv;
|
||||
path_found = 1;
|
||||
|
||||
list_del(&path_list->node);
|
||||
kfree(path_list);
|
||||
break;
|
||||
for (i = 0; i < src_mconfig->max_out_queue; i++) {
|
||||
if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) {
|
||||
sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg;
|
||||
if (!sink_mconfig)
|
||||
continue;
|
||||
/*
|
||||
* This is a connecter and if path is found that means
|
||||
* unbind between source and sink has not happened yet
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = skl_unbind_modules(ctx, src_mconfig,
|
||||
sink_mconfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a connector and if path is found that means
|
||||
* unbind between source and sink has not happened yet
|
||||
*/
|
||||
if (path_found) {
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -774,6 +902,67 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
|
||||
unsigned int __user *data, unsigned int size)
|
||||
{
|
||||
struct soc_bytes_ext *sb =
|
||||
(struct soc_bytes_ext *)kcontrol->private_value;
|
||||
struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
struct skl *skl = get_skl_ctx(w->dapm->dev);
|
||||
|
||||
if (w->power)
|
||||
skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
|
||||
bc->max, bc->param_id, mconfig);
|
||||
|
||||
if (bc->params) {
|
||||
if (copy_to_user(data, &bc->param_id, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(data + 1, &size, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(data + 2, bc->params, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SKL_PARAM_VENDOR_ID 0xff
|
||||
|
||||
static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
|
||||
const unsigned int __user *data, unsigned int size)
|
||||
{
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
struct soc_bytes_ext *sb =
|
||||
(struct soc_bytes_ext *)kcontrol->private_value;
|
||||
struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private;
|
||||
struct skl *skl = get_skl_ctx(w->dapm->dev);
|
||||
|
||||
if (ac->params) {
|
||||
/*
|
||||
* if the param_is is of type Vendor, firmware expects actual
|
||||
* parameter id and size from the control.
|
||||
*/
|
||||
if (ac->param_id == SKL_PARAM_VENDOR_ID) {
|
||||
if (copy_from_user(ac->params, data, size))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_from_user(ac->params,
|
||||
data + 2 * sizeof(u32), size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (w->power)
|
||||
return skl_set_module_params(skl->skl_sst,
|
||||
(u32 *)ac->params, ac->max,
|
||||
ac->param_id, mconfig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The FE params are passed by hw_params of the DAI.
|
||||
* On hw_params, the params are stored in Gateway module of the FE and we
|
||||
@ -790,9 +979,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
|
||||
memcpy(pipe->p_params, params, sizeof(*params));
|
||||
|
||||
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
format = &mconfig->in_fmt;
|
||||
format = &mconfig->in_fmt[0];
|
||||
else
|
||||
format = &mconfig->out_fmt;
|
||||
format = &mconfig->out_fmt[0];
|
||||
|
||||
/* set the hw_params */
|
||||
format->s_freq = params->s_freq;
|
||||
@ -809,6 +998,7 @@ int skl_tplg_update_pipe_params(struct device *dev,
|
||||
break;
|
||||
|
||||
case SKL_DEPTH_24BIT:
|
||||
case SKL_DEPTH_32BIT:
|
||||
format->bit_depth = SKL_DEPTH_32BIT;
|
||||
break;
|
||||
|
||||
@ -846,7 +1036,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
|
||||
w = dai->playback_widget;
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (p->connect && p->sink->power &&
|
||||
is_skl_dsp_widget_type(p->sink))
|
||||
!is_skl_dsp_widget_type(p->sink))
|
||||
continue;
|
||||
|
||||
if (p->sink->priv) {
|
||||
@ -859,7 +1049,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
|
||||
w = dai->capture_widget;
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
if (p->connect && p->source->power &&
|
||||
is_skl_dsp_widget_type(p->source))
|
||||
!is_skl_dsp_widget_type(p->source))
|
||||
continue;
|
||||
|
||||
if (p->source->priv) {
|
||||
@ -920,6 +1110,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
|
||||
|
||||
memcpy(pipe->p_params, params, sizeof(*params));
|
||||
|
||||
if (link_type == NHLT_LINK_HDA)
|
||||
return 0;
|
||||
|
||||
/* update the blob based on virtual bus_id*/
|
||||
cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
|
||||
params->s_fmt, params->ch,
|
||||
@ -950,18 +1143,13 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
|
||||
if (p->connect && is_skl_dsp_widget_type(p->source) &&
|
||||
p->source->priv) {
|
||||
|
||||
if (!p->source->power) {
|
||||
ret = skl_tplg_be_fill_pipe_params(
|
||||
dai, p->source->priv,
|
||||
params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = skl_tplg_be_fill_pipe_params(dai,
|
||||
p->source->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = skl_tplg_be_set_src_pipe_params(
|
||||
dai, p->source, params);
|
||||
ret = skl_tplg_be_set_src_pipe_params(dai,
|
||||
p->source, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@ -980,15 +1168,10 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
|
||||
if (p->connect && is_skl_dsp_widget_type(p->sink) &&
|
||||
p->sink->priv) {
|
||||
|
||||
if (!p->sink->power) {
|
||||
ret = skl_tplg_be_fill_pipe_params(
|
||||
dai, p->sink->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = skl_tplg_be_fill_pipe_params(dai,
|
||||
p->sink->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = skl_tplg_be_set_sink_pipe_params(
|
||||
dai, p->sink, params);
|
||||
@ -1030,6 +1213,11 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
|
||||
{SKL_PGA_EVENT, skl_tplg_pga_event},
|
||||
};
|
||||
|
||||
static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
|
||||
{SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get,
|
||||
skl_tplg_tlv_control_set},
|
||||
};
|
||||
|
||||
/*
|
||||
* The topology binary passes the pin info for a module so initialize the pin
|
||||
* info passed into module instance
|
||||
@ -1045,6 +1233,7 @@ static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
|
||||
m_pin[i].id.instance_id = dfw_pin[i].instance_id;
|
||||
m_pin[i].in_use = false;
|
||||
m_pin[i].is_dynamic = is_dynamic;
|
||||
m_pin[i].pin_state = SKL_PIN_UNBIND;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1092,6 +1281,24 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
|
||||
return ppl->pipe;
|
||||
}
|
||||
|
||||
static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
|
||||
struct skl_dfw_module_fmt *src_fmt,
|
||||
int pins)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pins; i++) {
|
||||
dst_fmt[i].channels = src_fmt[i].channels;
|
||||
dst_fmt[i].s_freq = src_fmt[i].freq;
|
||||
dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
|
||||
dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth;
|
||||
dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg;
|
||||
dst_fmt[i].ch_map = src_fmt[i].ch_map;
|
||||
dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style;
|
||||
dst_fmt[i].sample_type = src_fmt[i].sample_type;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Topology core widget load callback
|
||||
*
|
||||
@ -1130,22 +1337,16 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
||||
mconfig->max_in_queue = dfw_config->max_in_queue;
|
||||
mconfig->max_out_queue = dfw_config->max_out_queue;
|
||||
mconfig->is_loadable = dfw_config->is_loadable;
|
||||
mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
|
||||
mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
|
||||
mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
|
||||
mconfig->in_fmt.valid_bit_depth =
|
||||
dfw_config->in_fmt.valid_bit_depth;
|
||||
mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
|
||||
mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
|
||||
mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
|
||||
mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
|
||||
mconfig->out_fmt.valid_bit_depth =
|
||||
dfw_config->out_fmt.valid_bit_depth;
|
||||
mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
|
||||
skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
|
||||
MODULE_MAX_IN_PINS);
|
||||
skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
|
||||
MODULE_MAX_OUT_PINS);
|
||||
|
||||
mconfig->params_fixup = dfw_config->params_fixup;
|
||||
mconfig->converter = dfw_config->converter;
|
||||
mconfig->m_type = dfw_config->module_type;
|
||||
mconfig->vbus_id = dfw_config->vbus_id;
|
||||
mconfig->mem_pages = dfw_config->mem_pages;
|
||||
|
||||
pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
|
||||
if (pipe)
|
||||
@ -1156,10 +1357,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
||||
mconfig->time_slot = dfw_config->time_slot;
|
||||
mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
|
||||
|
||||
mconfig->m_in_pin = devm_kzalloc(bus->dev,
|
||||
(mconfig->max_in_queue) *
|
||||
sizeof(*mconfig->m_in_pin),
|
||||
GFP_KERNEL);
|
||||
if (dfw_config->is_loadable)
|
||||
memcpy(mconfig->guid, dfw_config->uuid,
|
||||
ARRAY_SIZE(dfw_config->uuid));
|
||||
|
||||
mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
|
||||
sizeof(*mconfig->m_in_pin),
|
||||
GFP_KERNEL);
|
||||
if (!mconfig->m_in_pin)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1188,7 +1392,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
|
||||
dfw_config->caps.caps_size);
|
||||
dfw_config->caps.caps_size);
|
||||
mconfig->formats_config.param_id = dfw_config->caps.param_id;
|
||||
mconfig->formats_config.set_params = dfw_config->caps.set_params;
|
||||
|
||||
bind_event:
|
||||
if (tplg_w->event_type == 0) {
|
||||
@ -1209,8 +1415,70 @@ bind_event:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
|
||||
struct snd_soc_tplg_bytes_control *bc)
|
||||
{
|
||||
struct skl_algo_data *ac;
|
||||
struct skl_dfw_algo_data *dfw_ac =
|
||||
(struct skl_dfw_algo_data *)bc->priv.data;
|
||||
|
||||
ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL);
|
||||
if (!ac)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fill private data */
|
||||
ac->max = dfw_ac->max;
|
||||
ac->param_id = dfw_ac->param_id;
|
||||
ac->set_params = dfw_ac->set_params;
|
||||
|
||||
if (ac->max) {
|
||||
ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
|
||||
if (!ac->params)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dfw_ac->params)
|
||||
memcpy(ac->params, dfw_ac->params, ac->max);
|
||||
}
|
||||
|
||||
be->dobj.private = ac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
|
||||
struct snd_kcontrol_new *kctl,
|
||||
struct snd_soc_tplg_ctl_hdr *hdr)
|
||||
{
|
||||
struct soc_bytes_ext *sb;
|
||||
struct snd_soc_tplg_bytes_control *tplg_bc;
|
||||
struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
switch (hdr->ops.info) {
|
||||
case SND_SOC_TPLG_CTL_BYTES:
|
||||
tplg_bc = container_of(hdr,
|
||||
struct snd_soc_tplg_bytes_control, hdr);
|
||||
if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (struct soc_bytes_ext *)kctl->private_value;
|
||||
if (tplg_bc->priv.size)
|
||||
return skl_init_algo_data(
|
||||
bus->dev, sb, tplg_bc);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(bus->dev, "Control load not supported %d:%d:%d\n",
|
||||
hdr->ops.get, hdr->ops.put, hdr->ops.info);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_tplg_ops skl_tplg_ops = {
|
||||
.widget_load = skl_tplg_widget_load,
|
||||
.control_load = skl_tplg_control_load,
|
||||
.bytes_ext_ops = skl_tlv_ops,
|
||||
.bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
|
||||
};
|
||||
|
||||
/* This will be read from topology manifest, currently defined here */
|
||||
|
@ -36,6 +36,9 @@
|
||||
/* Maximum number of coefficients up down mixer module */
|
||||
#define UP_DOWN_MIXER_MAX_COEFF 6
|
||||
|
||||
#define MODULE_MAX_IN_PINS 8
|
||||
#define MODULE_MAX_OUT_PINS 8
|
||||
|
||||
enum skl_channel_index {
|
||||
SKL_CHANNEL_LEFT = 0,
|
||||
SKL_CHANNEL_RIGHT = 1,
|
||||
@ -55,12 +58,6 @@ enum skl_bitdepth {
|
||||
SKL_DEPTH_INVALID
|
||||
};
|
||||
|
||||
enum skl_interleaving {
|
||||
/* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */
|
||||
SKL_INTERLEAVING_PER_CHANNEL = 0,
|
||||
/* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */
|
||||
SKL_INTERLEAVING_PER_SAMPLE = 1,
|
||||
};
|
||||
|
||||
enum skl_s_freq {
|
||||
SKL_FS_8000 = 8000,
|
||||
@ -143,6 +140,16 @@ struct skl_up_down_mixer_cfg {
|
||||
s32 coeff[UP_DOWN_MIXER_MAX_COEFF];
|
||||
} __packed;
|
||||
|
||||
struct skl_algo_cfg {
|
||||
struct skl_base_cfg base_cfg;
|
||||
char params[0];
|
||||
} __packed;
|
||||
|
||||
struct skl_base_outfmt_cfg {
|
||||
struct skl_base_cfg base_cfg;
|
||||
struct skl_audio_data_format out_fmt;
|
||||
} __packed;
|
||||
|
||||
enum skl_dma_type {
|
||||
SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0,
|
||||
SKL_DMA_HDA_HOST_INPUT_CLASS = 1,
|
||||
@ -178,21 +185,34 @@ struct skl_module_fmt {
|
||||
u32 bit_depth;
|
||||
u32 valid_bit_depth;
|
||||
u32 ch_cfg;
|
||||
u32 interleaving_style;
|
||||
u32 sample_type;
|
||||
u32 ch_map;
|
||||
};
|
||||
|
||||
struct skl_module_cfg;
|
||||
|
||||
struct skl_module_inst_id {
|
||||
u32 module_id;
|
||||
u32 instance_id;
|
||||
};
|
||||
|
||||
enum skl_module_pin_state {
|
||||
SKL_PIN_UNBIND = 0,
|
||||
SKL_PIN_BIND_DONE = 1,
|
||||
};
|
||||
|
||||
struct skl_module_pin {
|
||||
struct skl_module_inst_id id;
|
||||
u8 pin_index;
|
||||
bool is_dynamic;
|
||||
bool in_use;
|
||||
enum skl_module_pin_state pin_state;
|
||||
struct skl_module_cfg *tgt_mcfg;
|
||||
};
|
||||
|
||||
struct skl_specific_cfg {
|
||||
u32 set_params;
|
||||
u32 param_id;
|
||||
u32 caps_size;
|
||||
u32 *caps;
|
||||
};
|
||||
@ -238,9 +258,13 @@ enum skl_module_state {
|
||||
};
|
||||
|
||||
struct skl_module_cfg {
|
||||
char guid[SKL_UUID_STR_SZ];
|
||||
struct skl_module_inst_id id;
|
||||
struct skl_module_fmt in_fmt;
|
||||
struct skl_module_fmt out_fmt;
|
||||
u8 domain;
|
||||
bool homogenous_inputs;
|
||||
bool homogenous_outputs;
|
||||
struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS];
|
||||
struct skl_module_fmt out_fmt[MODULE_MAX_OUT_PINS];
|
||||
u8 max_in_queue;
|
||||
u8 max_out_queue;
|
||||
u8 in_queue_mask;
|
||||
@ -258,6 +282,7 @@ struct skl_module_cfg {
|
||||
u32 params_fixup;
|
||||
u32 converter;
|
||||
u32 vbus_id;
|
||||
u32 mem_pages;
|
||||
struct skl_module_pin *m_in_pin;
|
||||
struct skl_module_pin *m_out_pin;
|
||||
enum skl_module_type m_type;
|
||||
@ -267,13 +292,15 @@ struct skl_module_cfg {
|
||||
struct skl_specific_cfg formats_config;
|
||||
};
|
||||
|
||||
struct skl_pipeline {
|
||||
struct skl_pipe *pipe;
|
||||
struct list_head node;
|
||||
struct skl_algo_data {
|
||||
u32 param_id;
|
||||
u32 set_params;
|
||||
u32 max;
|
||||
char *params;
|
||||
};
|
||||
|
||||
struct skl_dapm_path_list {
|
||||
struct snd_soc_dapm_path *dapm_path;
|
||||
struct skl_pipeline {
|
||||
struct skl_pipe *pipe;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
@ -305,8 +332,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
|
||||
|
||||
int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
|
||||
|
||||
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config,
|
||||
char *param);
|
||||
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
|
||||
|
||||
int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
||||
*src_module, struct skl_module_cfg *dst_module);
|
||||
@ -314,5 +340,10 @@ int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
||||
int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
||||
*src_module, struct skl_module_cfg *dst_module);
|
||||
|
||||
int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg);
|
||||
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg);
|
||||
|
||||
enum skl_bitdepth skl_get_bit_depth(int params);
|
||||
#endif
|
||||
|
@ -23,15 +23,13 @@
|
||||
* Default types range from 0~12. type can range from 0 to 0xff
|
||||
* SST types start at higher to avoid any overlapping in future
|
||||
*/
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103
|
||||
#define SKL_CONTROL_TYPE_BYTE_TLV 0x100
|
||||
|
||||
#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
|
||||
#define MAX_IN_QUEUE 8
|
||||
#define MAX_OUT_QUEUE 8
|
||||
|
||||
#define SKL_UUID_STR_SZ 40
|
||||
/* Event types goes here */
|
||||
/* Reserve event type 0 for no event handlers */
|
||||
enum skl_event_types {
|
||||
@ -72,6 +70,7 @@ enum skl_ch_cfg {
|
||||
SKL_CH_CFG_DUAL_MONO = 9,
|
||||
SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10,
|
||||
SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11,
|
||||
SKL_CH_CFG_4_CHANNEL = 12,
|
||||
SKL_CH_CFG_INVALID
|
||||
};
|
||||
|
||||
@ -79,7 +78,9 @@ enum skl_module_type {
|
||||
SKL_MODULE_TYPE_MIXER = 0,
|
||||
SKL_MODULE_TYPE_COPIER,
|
||||
SKL_MODULE_TYPE_UPDWMIX,
|
||||
SKL_MODULE_TYPE_SRCINT
|
||||
SKL_MODULE_TYPE_SRCINT,
|
||||
SKL_MODULE_TYPE_ALGO,
|
||||
SKL_MODULE_TYPE_BASE_OUTFMT
|
||||
};
|
||||
|
||||
enum skl_core_affinity {
|
||||
@ -110,6 +111,42 @@ enum skl_dev_type {
|
||||
SKL_DEVICE_NONE
|
||||
};
|
||||
|
||||
/**
|
||||
* enum skl_interleaving - interleaving style
|
||||
*
|
||||
* @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN]
|
||||
* @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN]
|
||||
*/
|
||||
enum skl_interleaving {
|
||||
SKL_INTERLEAVING_PER_CHANNEL = 0,
|
||||
SKL_INTERLEAVING_PER_SAMPLE = 1,
|
||||
};
|
||||
|
||||
enum skl_sample_type {
|
||||
SKL_SAMPLE_TYPE_INT_MSB = 0,
|
||||
SKL_SAMPLE_TYPE_INT_LSB = 1,
|
||||
SKL_SAMPLE_TYPE_INT_SIGNED = 2,
|
||||
SKL_SAMPLE_TYPE_INT_UNSIGNED = 3,
|
||||
SKL_SAMPLE_TYPE_FLOAT = 4
|
||||
};
|
||||
|
||||
enum module_pin_type {
|
||||
/* All pins of the module takes same PCM inputs or outputs
|
||||
* e.g. mixout
|
||||
*/
|
||||
SKL_PIN_TYPE_HOMOGENEOUS,
|
||||
/* All pins of the module takes different PCM inputs or outputs
|
||||
* e.g mux
|
||||
*/
|
||||
SKL_PIN_TYPE_HETEROGENEOUS,
|
||||
};
|
||||
|
||||
enum skl_module_param_type {
|
||||
SKL_PARAM_DEFAULT = 0,
|
||||
SKL_PARAM_INIT,
|
||||
SKL_PARAM_SET
|
||||
};
|
||||
|
||||
struct skl_dfw_module_pin {
|
||||
u16 module_id;
|
||||
u16 instance_id;
|
||||
@ -121,9 +158,15 @@ struct skl_dfw_module_fmt {
|
||||
u32 bit_depth;
|
||||
u32 valid_bit_depth;
|
||||
u32 ch_cfg;
|
||||
u32 interleaving_style;
|
||||
u32 sample_type;
|
||||
u32 ch_map;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_module_caps {
|
||||
u32 set_params:2;
|
||||
u32 rsvd:30;
|
||||
u32 param_id;
|
||||
u32 caps_size;
|
||||
u32 caps[HDA_SST_CFG_MAX];
|
||||
};
|
||||
@ -131,41 +174,57 @@ struct skl_dfw_module_caps {
|
||||
struct skl_dfw_pipe {
|
||||
u8 pipe_id;
|
||||
u8 pipe_priority;
|
||||
u16 conn_type;
|
||||
u32 memory_pages;
|
||||
u16 conn_type:4;
|
||||
u16 rsvd:4;
|
||||
u16 memory_pages:8;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_module {
|
||||
char uuid[SKL_UUID_STR_SZ];
|
||||
|
||||
u16 module_id;
|
||||
u16 instance_id;
|
||||
u32 max_mcps;
|
||||
u8 core_id;
|
||||
u8 max_in_queue;
|
||||
u8 max_out_queue;
|
||||
u8 is_loadable;
|
||||
u8 conn_type;
|
||||
u8 dev_type;
|
||||
u8 hw_conn_type;
|
||||
u8 time_slot;
|
||||
u32 mem_pages;
|
||||
u32 obs;
|
||||
u32 ibs;
|
||||
u32 params_fixup;
|
||||
u32 converter;
|
||||
u32 module_type;
|
||||
u32 vbus_id;
|
||||
u8 is_dynamic_in_pin;
|
||||
u8 is_dynamic_out_pin;
|
||||
|
||||
u32 max_in_queue:8;
|
||||
u32 max_out_queue:8;
|
||||
u32 time_slot:8;
|
||||
u32 core_id:4;
|
||||
u32 rsvd1:4;
|
||||
|
||||
u32 module_type:8;
|
||||
u32 conn_type:4;
|
||||
u32 dev_type:4;
|
||||
u32 hw_conn_type:4;
|
||||
u32 rsvd2:12;
|
||||
|
||||
u32 params_fixup:8;
|
||||
u32 converter:8;
|
||||
u32 input_pin_type:1;
|
||||
u32 output_pin_type:1;
|
||||
u32 is_dynamic_in_pin:1;
|
||||
u32 is_dynamic_out_pin:1;
|
||||
u32 is_loadable:1;
|
||||
u32 rsvd3:11;
|
||||
|
||||
struct skl_dfw_pipe pipe;
|
||||
struct skl_dfw_module_fmt in_fmt;
|
||||
struct skl_dfw_module_fmt out_fmt;
|
||||
struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE];
|
||||
struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE];
|
||||
struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
|
||||
struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
|
||||
struct skl_dfw_module_caps caps;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_algo_data {
|
||||
u32 set_params:2;
|
||||
u32 rsvd:30;
|
||||
u32 param_id;
|
||||
u32 max;
|
||||
char *params;
|
||||
char params[0];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "../common/sst-acpi.h"
|
||||
#include "skl.h"
|
||||
|
||||
/*
|
||||
@ -129,51 +130,13 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* power management
|
||||
*/
|
||||
static int skl_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
snd_hdac_bus_stop_chip(bus);
|
||||
snd_hdac_bus_enter_link_reset(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *hda = ebus_to_skl(ebus);
|
||||
|
||||
skl_init_pci(hda);
|
||||
|
||||
snd_hdac_bus_init_chip(bus, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int skl_runtime_suspend(struct device *dev)
|
||||
static int _skl_suspend(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
int ret;
|
||||
|
||||
dev_dbg(bus->dev, "in %s\n", __func__);
|
||||
|
||||
/* enable controller wake up event */
|
||||
snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
|
||||
|
||||
snd_hdac_ext_bus_link_power_down_all(ebus);
|
||||
|
||||
ret = skl_suspend_dsp(skl);
|
||||
@ -186,25 +149,84 @@ static int skl_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _skl_resume(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
skl_init_pci(skl);
|
||||
snd_hdac_bus_init_chip(bus, true);
|
||||
|
||||
return skl_resume_dsp(skl);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* power management
|
||||
*/
|
||||
static int skl_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
/*
|
||||
* Do not suspend if streams which are marked ignore suspend are
|
||||
* running, we need to save the state for these and continue
|
||||
*/
|
||||
if (skl->supend_active) {
|
||||
pci_save_state(pci);
|
||||
pci_disable_device(pci);
|
||||
return 0;
|
||||
} else {
|
||||
return _skl_suspend(ebus);
|
||||
}
|
||||
}
|
||||
|
||||
static int skl_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* resume only when we are not in suspend active, otherwise need to
|
||||
* restore the device
|
||||
*/
|
||||
if (skl->supend_active) {
|
||||
pci_restore_state(pci);
|
||||
ret = pci_enable_device(pci);
|
||||
} else {
|
||||
ret = _skl_resume(ebus);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int skl_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
dev_dbg(bus->dev, "in %s\n", __func__);
|
||||
|
||||
return _skl_suspend(ebus);
|
||||
}
|
||||
|
||||
static int skl_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
int status;
|
||||
|
||||
dev_dbg(bus->dev, "in %s\n", __func__);
|
||||
|
||||
/* Read STATESTS before controller reset */
|
||||
status = snd_hdac_chip_readw(bus, STATESTS);
|
||||
|
||||
skl_init_pci(skl);
|
||||
snd_hdac_bus_init_chip(bus, true);
|
||||
/* disable controller Wake Up event */
|
||||
snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
|
||||
|
||||
return skl_resume_dsp(skl);
|
||||
return _skl_resume(ebus);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
@ -241,6 +263,43 @@ static int skl_free(struct hdac_ext_bus *ebus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_machine_device_register(struct skl *skl, void *driver_data)
|
||||
{
|
||||
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
|
||||
struct platform_device *pdev;
|
||||
struct sst_acpi_mach *mach = driver_data;
|
||||
int ret;
|
||||
|
||||
mach = sst_acpi_find_machine(mach);
|
||||
if (mach == NULL) {
|
||||
dev_err(bus->dev, "No matching machine driver found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
skl->fw_name = mach->fw_filename;
|
||||
|
||||
pdev = platform_device_alloc(mach->drv_name, -1);
|
||||
if (pdev == NULL) {
|
||||
dev_err(bus->dev, "platform device alloc failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret) {
|
||||
dev_err(bus->dev, "failed to add machine device\n");
|
||||
platform_device_put(pdev);
|
||||
return -EIO;
|
||||
}
|
||||
skl->i2s_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void skl_machine_device_unregister(struct skl *skl)
|
||||
{
|
||||
if (skl->i2s_dev)
|
||||
platform_device_unregister(skl->i2s_dev);
|
||||
}
|
||||
|
||||
static int skl_dmic_device_register(struct skl *skl)
|
||||
{
|
||||
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
|
||||
@ -434,8 +493,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
|
||||
|
||||
/* codec detection */
|
||||
if (!bus->codec_mask) {
|
||||
dev_err(bus->dev, "no codecs found!\n");
|
||||
return -ENODEV;
|
||||
dev_info(bus->dev, "no hda codecs found!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -470,10 +528,15 @@ static int skl_probe(struct pci_dev *pci,
|
||||
|
||||
/* check if dsp is there */
|
||||
if (ebus->ppcap) {
|
||||
err = skl_machine_device_register(skl,
|
||||
(void *)pci_id->driver_data);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
err = skl_init_dsp(skl);
|
||||
if (err < 0) {
|
||||
dev_dbg(bus->dev, "error failed to register dsp\n");
|
||||
goto out_free;
|
||||
goto out_mach_free;
|
||||
}
|
||||
}
|
||||
if (ebus->mlcap)
|
||||
@ -508,6 +571,8 @@ out_dmic_free:
|
||||
skl_dmic_device_unregister(skl);
|
||||
out_dsp_free:
|
||||
skl_free_dsp(skl);
|
||||
out_mach_free:
|
||||
skl_machine_device_unregister(skl);
|
||||
out_free:
|
||||
skl->init_failed = 1;
|
||||
skl_free(ebus);
|
||||
@ -525,15 +590,26 @@ static void skl_remove(struct pci_dev *pci)
|
||||
pci_dev_put(pci);
|
||||
skl_platform_unregister(&pci->dev);
|
||||
skl_free_dsp(skl);
|
||||
skl_machine_device_unregister(skl);
|
||||
skl_dmic_device_unregister(skl);
|
||||
skl_free(ebus);
|
||||
dev_set_drvdata(&pci->dev, NULL);
|
||||
}
|
||||
|
||||
static struct sst_acpi_mach sst_skl_devdata[] = {
|
||||
{ "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
|
||||
{ "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
|
||||
NULL, NULL, NULL },
|
||||
{ "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
|
||||
NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
/* PCI IDs */
|
||||
static const struct pci_device_id skl_ids[] = {
|
||||
/* Sunrise Point-LP */
|
||||
{ PCI_DEVICE(0x8086, 0x9d70), 0},
|
||||
{ PCI_DEVICE(0x8086, 0x9d70),
|
||||
.driver_data = (unsigned long)&sst_skl_devdata},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, skl_ids);
|
||||
|
@ -61,13 +61,17 @@ struct skl {
|
||||
|
||||
unsigned int init_failed:1; /* delayed init failed */
|
||||
struct platform_device *dmic_dev;
|
||||
struct platform_device *i2s_dev;
|
||||
|
||||
void *nhlt; /* nhlt ptr */
|
||||
struct skl_sst *skl_sst; /* sst skl ctx */
|
||||
|
||||
struct skl_dsp_resource resource;
|
||||
struct list_head ppl_list;
|
||||
struct list_head dapm_path_list;
|
||||
|
||||
const char *fw_name;
|
||||
|
||||
int supend_active;
|
||||
};
|
||||
|
||||
#define skl_to_ebus(s) (&(s)->ebus)
|
||||
|
Loading…
Reference in New Issue
Block a user