mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-30 13:34:44 +08:00
Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl', 'asoc/topic/fsl-esai', 'asoc/topic/fsl-sai', 'asoc/topic/fsl-spdif' and 'asoc/topic/fsl-ssi' into asoc-next
This commit is contained in:
commit
3a6a489fd8
@ -7,10 +7,11 @@ codec/DSP interfaces.
|
||||
|
||||
|
||||
Required properties:
|
||||
- compatible: Compatible list, contains "fsl,vf610-sai".
|
||||
- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
|
||||
- reg: Offset and length of the register set for the device.
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
- clock-names : Must include the "sai" entry.
|
||||
- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
|
||||
"mclk3" for bit clock and frame clock providing.
|
||||
- dmas : Generic dma devicetree binding as described in
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
- dma-names : Two dmas have to be defined, "tx" and "rx".
|
||||
@ -30,8 +31,10 @@ sai2: sai@40031000 {
|
||||
reg = <0x40031000 0x1000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sai2_1>;
|
||||
clocks = <&clks VF610_CLK_SAI2>;
|
||||
clock-names = "sai";
|
||||
clocks = <&clks VF610_CLK_PLATFORM_BUS>,
|
||||
<&clks VF610_CLK_SAI2>,
|
||||
<&clks 0>, <&clks 0>;
|
||||
clock-names = "bus", "mclk1", "mclk2", "mclk3";
|
||||
dma-names = "tx", "rx";
|
||||
dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
|
||||
<&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
|
||||
|
@ -1,30 +1,77 @@
|
||||
menu "SoC Audio for Freescale CPUs"
|
||||
|
||||
comment "Common SoC Audio options for Freescale CPUs:"
|
||||
|
||||
config SND_SOC_FSL_SAI
|
||||
tristate
|
||||
tristate "Synchronous Audio Interface (SAI) module support"
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y if you want to add Synchronous Audio Interface (SAI)
|
||||
support for the Freescale CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_SSI
|
||||
tristate
|
||||
tristate "Synchronous Serial Interface module support"
|
||||
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
|
||||
select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
|
||||
help
|
||||
Say Y if you want to add Synchronous Serial Interface (SSI)
|
||||
support for the Freescale CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_SPDIF
|
||||
tristate
|
||||
tristate "Sony/Philips Digital Interface module support"
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
|
||||
select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
|
||||
help
|
||||
Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
|
||||
support for the Freescale CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_ESAI
|
||||
tristate
|
||||
tristate "Enhanced Serial Audio Interface (ESAI) module support"
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
Say Y if you want to add Enhanced Synchronous Audio Interface
|
||||
(ESAI) support for the Freescale CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_UTILS
|
||||
tristate
|
||||
|
||||
menuconfig SND_POWERPC_SOC
|
||||
config SND_SOC_IMX_PCM_DMA
|
||||
tristate
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
||||
config SND_SOC_IMX_AUDMUX
|
||||
tristate "Digital Audio Mux module support"
|
||||
help
|
||||
Say Y if you want to add Digital Audio Mux (AUDMUX) support
|
||||
for the ARM i.MX CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_POWERPC_SOC
|
||||
tristate "SoC Audio for Freescale PowerPC CPUs"
|
||||
depends on FSL_SOC || PPC_MPC52xx
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the PowerPC CPUs.
|
||||
|
||||
config SND_IMX_SOC
|
||||
tristate "SoC Audio for Freescale i.MX CPUs"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the i.MX CPUs.
|
||||
|
||||
if SND_POWERPC_SOC
|
||||
|
||||
config SND_MPC52xx_DMA
|
||||
@ -33,6 +80,8 @@ config SND_MPC52xx_DMA
|
||||
config SND_SOC_POWERPC_DMA
|
||||
tristate
|
||||
|
||||
comment "SoC Audio support for Freescale PPC boards:"
|
||||
|
||||
config SND_SOC_MPC8610_HPCD
|
||||
tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
|
||||
# I2C is necessary for the CS4270 driver
|
||||
@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA
|
||||
|
||||
endif # SND_POWERPC_SOC
|
||||
|
||||
menuconfig SND_IMX_SOC
|
||||
tristate "SoC Audio for Freescale i.MX CPUs"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the i.MX CPUs.
|
||||
|
||||
if SND_IMX_SOC
|
||||
|
||||
config SND_SOC_IMX_SSI
|
||||
@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ
|
||||
tristate
|
||||
select FIQ
|
||||
|
||||
config SND_SOC_IMX_PCM_DMA
|
||||
tristate
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
||||
config SND_SOC_IMX_AUDMUX
|
||||
tristate
|
||||
comment "SoC Audio support for Freescale i.MX boards:"
|
||||
|
||||
config SND_MXC_SOC_WM1133_EV1
|
||||
tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
|
||||
@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
|
||||
endif # SND_IMX_SOC
|
||||
|
||||
endmenu
|
||||
|
@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-sai-objs := fsl_sai.o
|
||||
snd-soc-fsl-ssi-objs := fsl_ssi.o
|
||||
snd-soc-fsl-ssi-y := fsl_ssi.o
|
||||
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
|
||||
snd-soc-fsl-spdif-objs := fsl_spdif.o
|
||||
snd-soc-fsl-esai-objs := fsl_esai.o
|
||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||
|
@ -39,6 +39,8 @@
|
||||
* @fifo_depth: depth of tx/rx FIFO
|
||||
* @slot_width: width of each DAI slot
|
||||
* @hck_rate: clock rate of desired HCKx clock
|
||||
* @sck_rate: clock rate of desired SCKx clock
|
||||
* @hck_dir: the direction of HCKx pads
|
||||
* @sck_div: if using PSR/PM dividers for SCKx clock
|
||||
* @slave_mode: if fully using DAI slave mode
|
||||
* @synchronous: if using tx/rx synchronous mode
|
||||
@ -55,6 +57,8 @@ struct fsl_esai {
|
||||
u32 fifo_depth;
|
||||
u32 slot_width;
|
||||
u32 hck_rate[2];
|
||||
u32 sck_rate[2];
|
||||
bool hck_dir[2];
|
||||
bool sck_div[2];
|
||||
bool slave_mode;
|
||||
bool synchronous;
|
||||
@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
struct clk *clksrc = esai_priv->extalclk;
|
||||
bool tx = clk_id <= ESAI_HCKT_EXTAL;
|
||||
bool in = dir == SND_SOC_CLOCK_IN;
|
||||
u32 ret, ratio, ecr = 0;
|
||||
u32 ratio, ecr = 0;
|
||||
unsigned long clk_rate;
|
||||
int ret;
|
||||
|
||||
/* Bypass divider settings if the requirement doesn't change */
|
||||
if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
|
||||
return 0;
|
||||
|
||||
/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
|
||||
esai_priv->sck_div[tx] = true;
|
||||
@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
esai_priv->sck_div[tx] = false;
|
||||
|
||||
out:
|
||||
esai_priv->hck_dir[tx] = dir;
|
||||
esai_priv->hck_rate[tx] = freq;
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
|
||||
@ -294,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
u32 hck_rate = esai_priv->hck_rate[tx];
|
||||
u32 sub, ratio = hck_rate / freq;
|
||||
int ret;
|
||||
|
||||
/* Don't apply for fully slave mode*/
|
||||
if (esai_priv->slave_mode)
|
||||
/* Don't apply for fully slave mode or unchanged bclk */
|
||||
if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
|
||||
return 0;
|
||||
|
||||
if (ratio * freq > hck_rate)
|
||||
@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return fsl_esai_divisor_cal(dai, tx, ratio, true,
|
||||
ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
|
||||
esai_priv->sck_div[tx] ? 0 : ratio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Save current bclk rate */
|
||||
esai_priv->sck_rate[tx] = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
|
||||
@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
static int fsl_esai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Some platforms might use the same bit to gate all three or two of
|
||||
@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 width = snd_pcm_format_width(params_format(params));
|
||||
u32 channels = params_channels(params);
|
||||
u32 bclk, mask, val, ret;
|
||||
u32 bclk, mask, val;
|
||||
int ret;
|
||||
|
||||
bclk = params_rate(params) * esai_priv->slot_width * 2;
|
||||
|
||||
@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id fsl_esai_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx35-esai", },
|
||||
{ .compatible = "fsl,vf610-esai", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
#include "imx-pcm.h"
|
||||
|
||||
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
|
||||
FSL_SAI_CSR_FEIE)
|
||||
@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
|
||||
{
|
||||
struct fsl_sai *sai = (struct fsl_sai *)devid;
|
||||
struct device *dev = &sai->pdev->dev;
|
||||
u32 xcsr, mask;
|
||||
u32 flags, xcsr, mask;
|
||||
bool irq_none = true;
|
||||
|
||||
/* Only handle those what we enabled */
|
||||
/*
|
||||
* Both IRQ status bits and IRQ mask bits are in the xCSR but
|
||||
* different shifts. And we here create a mask only for those
|
||||
* IRQs that we activated.
|
||||
*/
|
||||
mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
|
||||
|
||||
/* Tx IRQ */
|
||||
regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
|
||||
xcsr &= mask;
|
||||
flags = xcsr & mask;
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_WSF)
|
||||
if (flags)
|
||||
irq_none = false;
|
||||
else
|
||||
goto irq_rx;
|
||||
|
||||
if (flags & FSL_SAI_CSR_WSF)
|
||||
dev_dbg(dev, "isr: Start of Tx word detected\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_SEF)
|
||||
if (flags & FSL_SAI_CSR_SEF)
|
||||
dev_warn(dev, "isr: Tx Frame sync error detected\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FEF) {
|
||||
if (flags & FSL_SAI_CSR_FEF) {
|
||||
dev_warn(dev, "isr: Transmit underrun detected\n");
|
||||
/* FIFO reset for safety */
|
||||
xcsr |= FSL_SAI_CSR_FR;
|
||||
}
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FWF)
|
||||
if (flags & FSL_SAI_CSR_FWF)
|
||||
dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FRF)
|
||||
if (flags & FSL_SAI_CSR_FRF)
|
||||
dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
|
||||
FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
|
||||
flags &= FSL_SAI_CSR_xF_W_MASK;
|
||||
xcsr &= ~FSL_SAI_CSR_xF_MASK;
|
||||
|
||||
if (flags)
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
|
||||
|
||||
irq_rx:
|
||||
/* Rx IRQ */
|
||||
regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
|
||||
xcsr &= mask;
|
||||
flags = xcsr & mask;
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_WSF)
|
||||
if (flags)
|
||||
irq_none = false;
|
||||
else
|
||||
goto out;
|
||||
|
||||
if (flags & FSL_SAI_CSR_WSF)
|
||||
dev_dbg(dev, "isr: Start of Rx word detected\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_SEF)
|
||||
if (flags & FSL_SAI_CSR_SEF)
|
||||
dev_warn(dev, "isr: Rx Frame sync error detected\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FEF) {
|
||||
if (flags & FSL_SAI_CSR_FEF) {
|
||||
dev_warn(dev, "isr: Receive overflow detected\n");
|
||||
/* FIFO reset for safety */
|
||||
xcsr |= FSL_SAI_CSR_FR;
|
||||
}
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FWF)
|
||||
if (flags & FSL_SAI_CSR_FWF)
|
||||
dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
|
||||
|
||||
if (xcsr & FSL_SAI_CSR_FRF)
|
||||
if (flags & FSL_SAI_CSR_FRF)
|
||||
dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
|
||||
FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
|
||||
flags &= FSL_SAI_CSR_xF_W_MASK;
|
||||
xcsr &= ~FSL_SAI_CSR_xF_MASK;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
if (flags)
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
|
||||
|
||||
out:
|
||||
if (irq_none)
|
||||
return IRQ_NONE;
|
||||
else
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int fsl_dir)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 val_cr2, reg_cr2;
|
||||
|
||||
if (fsl_dir == FSL_FMT_TRANSMITTER)
|
||||
reg_cr2 = FSL_SAI_TCR2;
|
||||
else
|
||||
reg_cr2 = FSL_SAI_RCR2;
|
||||
|
||||
regmap_read(sai->regmap, reg_cr2, &val_cr2);
|
||||
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
|
||||
u32 val_cr2 = 0;
|
||||
|
||||
switch (clk_id) {
|
||||
case FSL_SAI_CLK_BUS:
|
||||
@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(sai->regmap, reg_cr2, val_cr2);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
|
||||
FSL_SAI_CR2_MSEL_MASK, val_cr2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt, int fsl_dir)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
|
||||
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
|
||||
u32 val_cr2 = 0, val_cr4 = 0;
|
||||
|
||||
if (fsl_dir == FSL_FMT_TRANSMITTER) {
|
||||
reg_cr2 = FSL_SAI_TCR2;
|
||||
reg_cr4 = FSL_SAI_TCR4;
|
||||
} else {
|
||||
reg_cr2 = FSL_SAI_RCR2;
|
||||
reg_cr4 = FSL_SAI_RCR4;
|
||||
}
|
||||
|
||||
regmap_read(sai->regmap, reg_cr2, &val_cr2);
|
||||
regmap_read(sai->regmap, reg_cr4, &val_cr4);
|
||||
|
||||
if (sai->big_endian_data)
|
||||
val_cr4 &= ~FSL_SAI_CR4_MF;
|
||||
else
|
||||
if (!sai->big_endian_data)
|
||||
val_cr4 |= FSL_SAI_CR4_MF;
|
||||
|
||||
/* DAI mode */
|
||||
@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
* frame sync asserts with the first bit of the frame.
|
||||
*/
|
||||
val_cr2 |= FSL_SAI_CR2_BCP;
|
||||
val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
/*
|
||||
@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
* data word.
|
||||
*/
|
||||
val_cr2 |= FSL_SAI_CR2_BCP;
|
||||
val_cr4 &= ~FSL_SAI_CR4_FSP;
|
||||
val_cr4 |= FSL_SAI_CR4_FSE;
|
||||
sai->is_dsp_mode = true;
|
||||
break;
|
||||
@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
* frame sync asserts with the first bit of the frame.
|
||||
*/
|
||||
val_cr2 |= FSL_SAI_CR2_BCP;
|
||||
val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
|
||||
sai->is_dsp_mode = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
|
||||
val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
|
||||
val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
|
||||
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(sai->regmap, reg_cr2, val_cr2);
|
||||
regmap_write(sai->regmap, reg_cr4, val_cr4);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
|
||||
FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
|
||||
FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
|
||||
FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned int channels = params_channels(params);
|
||||
u32 word_width = snd_pcm_format_width(params_format(params));
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
reg_cr4 = FSL_SAI_TCR4;
|
||||
reg_cr5 = FSL_SAI_TCR5;
|
||||
reg_mr = FSL_SAI_TMR;
|
||||
} else {
|
||||
reg_cr4 = FSL_SAI_RCR4;
|
||||
reg_cr5 = FSL_SAI_RCR5;
|
||||
reg_mr = FSL_SAI_RMR;
|
||||
}
|
||||
|
||||
regmap_read(sai->regmap, reg_cr4, &val_cr4);
|
||||
regmap_read(sai->regmap, reg_cr4, &val_cr5);
|
||||
|
||||
val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
|
||||
val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
|
||||
|
||||
val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
|
||||
val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
|
||||
val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
|
||||
u32 val_cr4 = 0, val_cr5 = 0;
|
||||
|
||||
if (!sai->is_dsp_mode)
|
||||
val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
|
||||
@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
|
||||
val_cr5 |= FSL_SAI_CR5_WNW(word_width);
|
||||
val_cr5 |= FSL_SAI_CR5_W0W(word_width);
|
||||
|
||||
val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
|
||||
if (sai->big_endian_data)
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(0);
|
||||
else
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
|
||||
|
||||
val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
|
||||
val_mr = ~0UL - ((1 << channels) - 1);
|
||||
|
||||
regmap_write(sai->regmap, reg_cr4, val_cr4);
|
||||
regmap_write(sai->regmap, reg_cr5, val_cr5);
|
||||
regmap_write(sai->regmap, reg_mr, val_mr);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
|
||||
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
|
||||
val_cr4);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
|
||||
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
|
||||
FSL_SAI_CR5_FBT_MASK, val_cr5);
|
||||
regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 tcsr, rcsr;
|
||||
|
||||
/*
|
||||
@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
|
||||
regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
tcsr |= FSL_SAI_CSR_FRDE;
|
||||
rcsr &= ~FSL_SAI_CSR_FRDE;
|
||||
} else {
|
||||
rcsr |= FSL_SAI_CSR_FRDE;
|
||||
tcsr &= ~FSL_SAI_CSR_FRDE;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is recommended that the transmitter is the last enabled
|
||||
* and the first disabled.
|
||||
@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
tcsr |= FSL_SAI_CSR_TERE;
|
||||
rcsr |= FSL_SAI_CSR_TERE;
|
||||
if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
|
||||
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
|
||||
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
|
||||
}
|
||||
|
||||
regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
|
||||
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
|
||||
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
|
||||
tcsr &= ~FSL_SAI_CSR_TERE;
|
||||
rcsr &= ~FSL_SAI_CSR_TERE;
|
||||
}
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
|
||||
FSL_SAI_CSR_FRDE, 0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
|
||||
FSL_SAI_CSR_xIE_MASK, 0);
|
||||
|
||||
regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
|
||||
regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
|
||||
/* Check if the opposite FRDE is also disabled */
|
||||
if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
|
||||
FSL_SAI_CSR_TERE, 0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
|
||||
FSL_SAI_CSR_TERE, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
struct device *dev = &sai->pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = FSL_SAI_TCR3;
|
||||
else
|
||||
reg = FSL_SAI_RCR3;
|
||||
ret = clk_prepare_enable(sai->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable bus clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
|
||||
FSL_SAI_CR3_TRCE);
|
||||
|
||||
return 0;
|
||||
@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = FSL_SAI_TCR3;
|
||||
else
|
||||
reg = FSL_SAI_RCR3;
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
|
||||
|
||||
regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
|
||||
~FSL_SAI_CR3_TRCE);
|
||||
clk_disable_unprepare(sai->bus_clk);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
|
||||
@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
|
||||
FSL_SAI_MAXBURST_TX * 2);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
|
||||
@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
struct fsl_sai *sai;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int irq, ret;
|
||||
char tmp[8];
|
||||
int irq, ret, i;
|
||||
|
||||
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
|
||||
if (!sai)
|
||||
@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
|
||||
sai->pdev = pdev;
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
|
||||
sai->sai_on_imx = true;
|
||||
|
||||
sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
|
||||
if (sai->big_endian_regs)
|
||||
fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
|
||||
@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(base);
|
||||
|
||||
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"sai", base, &fsl_sai_regmap_config);
|
||||
"bus", base, &fsl_sai_regmap_config);
|
||||
|
||||
/* Compatible with old DTB cases */
|
||||
if (IS_ERR(sai->regmap))
|
||||
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"sai", base, &fsl_sai_regmap_config);
|
||||
if (IS_ERR(sai->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap init failed\n");
|
||||
return PTR_ERR(sai->regmap);
|
||||
}
|
||||
|
||||
/* No error out for old DTB cases but only mark the clock NULL */
|
||||
sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||
if (IS_ERR(sai->bus_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
|
||||
PTR_ERR(sai->bus_clk));
|
||||
sai->bus_clk = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
|
||||
sprintf(tmp, "mclk%d", i + 1);
|
||||
sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
|
||||
if (IS_ERR(sai->mclk_clk[i])) {
|
||||
dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
|
||||
i + 1, PTR_ERR(sai->mclk_clk[i]));
|
||||
sai->mclk_clk[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
|
||||
@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
||||
if (sai->sai_on_imx)
|
||||
return imx_pcm_dma_init(pdev);
|
||||
else
|
||||
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_sai_ids[] = {
|
||||
{ .compatible = "fsl,vf610-sai", },
|
||||
{ .compatible = "fsl,imx6sx-sai", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -35,6 +35,16 @@
|
||||
#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */
|
||||
|
||||
#define FSL_SAI_xCSR(tx) (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR)
|
||||
#define FSL_SAI_xCR1(tx) (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1)
|
||||
#define FSL_SAI_xCR2(tx) (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2)
|
||||
#define FSL_SAI_xCR3(tx) (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3)
|
||||
#define FSL_SAI_xCR4(tx) (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4)
|
||||
#define FSL_SAI_xCR5(tx) (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5)
|
||||
#define FSL_SAI_xDR(tx) (tx ? FSL_SAI_TDR : FSL_SAI_RDR)
|
||||
#define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
|
||||
#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
|
||||
|
||||
/* SAI Transmit/Recieve Control Register */
|
||||
#define FSL_SAI_CSR_TERE BIT(31)
|
||||
#define FSL_SAI_CSR_FR BIT(25)
|
||||
@ -48,6 +58,7 @@
|
||||
#define FSL_SAI_CSR_FWF BIT(17)
|
||||
#define FSL_SAI_CSR_FRF BIT(16)
|
||||
#define FSL_SAI_CSR_xIE_SHIFT 8
|
||||
#define FSL_SAI_CSR_xIE_MASK (0x1f << FSL_SAI_CSR_xIE_SHIFT)
|
||||
#define FSL_SAI_CSR_WSIE BIT(12)
|
||||
#define FSL_SAI_CSR_SEIE BIT(11)
|
||||
#define FSL_SAI_CSR_FEIE BIT(10)
|
||||
@ -108,6 +119,8 @@
|
||||
#define FSL_SAI_CLK_MAST2 2
|
||||
#define FSL_SAI_CLK_MAST3 3
|
||||
|
||||
#define FSL_SAI_MCLK_MAX 3
|
||||
|
||||
/* SAI data transfer numbers per DMA request */
|
||||
#define FSL_SAI_MAXBURST_TX 6
|
||||
#define FSL_SAI_MAXBURST_RX 6
|
||||
@ -115,10 +128,13 @@
|
||||
struct fsl_sai {
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
struct clk *bus_clk;
|
||||
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
|
||||
|
||||
bool big_endian_regs;
|
||||
bool big_endian_data;
|
||||
bool is_dsp_mode;
|
||||
bool sai_on_imx;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
|
@ -13,18 +13,18 @@
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-private.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "fsl_spdif.h"
|
||||
#include "imx-pcm.h"
|
||||
@ -69,17 +69,42 @@ struct spdif_mixer_control {
|
||||
u32 ready_buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_spdif_priv: Freescale SPDIF private data
|
||||
*
|
||||
* @fsl_spdif_control: SPDIF control data
|
||||
* @cpu_dai_drv: cpu dai driver
|
||||
* @pdev: platform device pointer
|
||||
* @regmap: regmap handler
|
||||
* @dpll_locked: dpll lock flag
|
||||
* @txrate: the best rates for playback
|
||||
* @txclk_df: STC_TXCLK_DF dividers value for playback
|
||||
* @sysclk_df: STC_SYSCLK_DF dividers value for playback
|
||||
* @txclk_src: STC_TXCLK_SRC values for playback
|
||||
* @rxclk_src: SRPC_CLKSRC_SEL values for capture
|
||||
* @txclk: tx clock sources for playback
|
||||
* @rxclk: rx clock sources for capture
|
||||
* @coreclk: core clock for register access via DMA
|
||||
* @sysclk: system clock for rx clock rate measurement
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
* @name: driver name
|
||||
*/
|
||||
struct fsl_spdif_priv {
|
||||
struct spdif_mixer_control fsl_spdif_control;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
bool dpll_locked;
|
||||
u8 txclk_div[SPDIF_TXRATE_MAX];
|
||||
u16 txrate[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 sysclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_src[SPDIF_TXRATE_MAX];
|
||||
u8 rxclk_src;
|
||||
struct clk *txclk[SPDIF_TXRATE_MAX];
|
||||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
|
||||
@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
unsigned long csfs = 0;
|
||||
u32 stc, mask, rate;
|
||||
u8 clk, div;
|
||||
u8 clk, txclk_df, sysclk_df;
|
||||
int ret;
|
||||
|
||||
switch (sample_rate) {
|
||||
@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div = spdif_priv->txclk_div[rate];
|
||||
if (div == 0) {
|
||||
dev_err(&pdev->dev, "the divisor can't be zero\n");
|
||||
txclk_df = spdif_priv->txclk_df[rate];
|
||||
if (txclk_df == 0) {
|
||||
dev_err(&pdev->dev, "the txclk_df can't be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sysclk_df = spdif_priv->sysclk_df[rate];
|
||||
|
||||
/* Don't mess up the clocks from other modules */
|
||||
if (clk != STC_TXCLK_SPDIF_ROOT)
|
||||
goto clk_set_bypass;
|
||||
|
||||
/*
|
||||
* The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
|
||||
* will divide by (div). So request 64 * fs * (div+1) which will
|
||||
* get rounded.
|
||||
* The S/PDIF block needs a clock of 64 * fs * txclk_df.
|
||||
* So request 64 * fs * (txclk_df + 1) to get rounded.
|
||||
*/
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to set tx clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_set_bypass:
|
||||
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
|
||||
(64 * sample_rate * div));
|
||||
(64 * sample_rate * txclk_df * sysclk_df));
|
||||
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
|
||||
clk_get_rate(spdif_priv->txclk[rate]));
|
||||
|
||||
@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
||||
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
|
||||
|
||||
/* select clock source and divisor */
|
||||
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
|
||||
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
|
||||
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
|
||||
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
|
||||
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
|
||||
|
||||
dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
|
||||
regmap_update_bits(regmap, REG_SPDIF_STC,
|
||||
STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
|
||||
|
||||
dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
|
||||
spdif_priv->txrate[rate], sample_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
|
||||
/* Reset module and interrupts only for first initialization */
|
||||
if (!cpu_dai->active) {
|
||||
ret = clk_prepare_enable(spdif_priv->coreclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable core clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Disable all the interrupts */
|
||||
@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
||||
@ -484,6 +530,7 @@ 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);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
|
||||
clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
|
||||
if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
|
||||
/* Get bus clock from system */
|
||||
busclk_freq = clk_get_rate(spdif_priv->rxclk);
|
||||
busclk_freq = clk_get_rate(spdif_priv->sysclk);
|
||||
}
|
||||
|
||||
/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
|
||||
@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index)
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
const u32 rate[] = { 32000, 44100, 48000 };
|
||||
bool is_sysclk = clk == spdif_priv->sysclk;
|
||||
u64 rate_ideal, rate_actual, sub;
|
||||
u32 div, arate;
|
||||
u32 sysclk_dfmin, sysclk_dfmax;
|
||||
u32 txclk_df, sysclk_df, arate;
|
||||
|
||||
for (div = 1; div <= 128; div++) {
|
||||
rate_ideal = rate[index] * (div + 1) * 64;
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
arate = rate_actual / 64;
|
||||
arate /= div;
|
||||
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
|
||||
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
|
||||
rate_ideal = rate[index] * (txclk_df + 1) * 64;
|
||||
if (round)
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
else
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
break;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub < savesub) {
|
||||
arate = rate_actual / 64;
|
||||
arate /= txclk_df * sysclk_df;
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
goto out;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
}
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub < savesub) {
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_div[index] = div;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
|
||||
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
|
||||
spdif_priv->txclk_src[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_div[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (spdif_priv->txclk[index] == spdif_priv->sysclk)
|
||||
dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->sysclk_df[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
|
||||
rate[index], spdif_priv->txrate[index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get system clock for rx clock rate calculation */
|
||||
spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
|
||||
if (IS_ERR(spdif_priv->sysclk)) {
|
||||
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->sysclk);
|
||||
}
|
||||
|
||||
/* Get core clock for data register access via DMA */
|
||||
spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(spdif_priv->coreclk)) {
|
||||
dev_err(&pdev->dev, "no core clock in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->coreclk);
|
||||
}
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id fsl_spdif_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx35-spdif", },
|
||||
{ .compatible = "fsl,vf610-spdif", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
|
||||
|
@ -143,20 +143,22 @@ enum spdif_gainsel {
|
||||
#define INT_RXFIFO_FUL (1 << 0)
|
||||
|
||||
/* SPDIF Clock register */
|
||||
#define STC_SYSCLK_DIV_OFFSET 11
|
||||
#define STC_SYSCLK_DIV_MASK (0x1ff << STC_SYSCLK_DIV_OFFSET)
|
||||
#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
|
||||
#define STC_SYSCLK_DF_OFFSET 11
|
||||
#define STC_SYSCLK_DF_MASK (0x1ff << STC_SYSCLK_DF_OFFSET)
|
||||
#define STC_SYSCLK_DF(x) ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK)
|
||||
#define STC_TXCLK_SRC_OFFSET 8
|
||||
#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET)
|
||||
#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
|
||||
#define STC_TXCLK_ALL_EN_OFFSET 7
|
||||
#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
|
||||
#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
|
||||
#define STC_TXCLK_DIV_OFFSET 0
|
||||
#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET)
|
||||
#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
|
||||
#define STC_TXCLK_DF_OFFSET 0
|
||||
#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET)
|
||||
#define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
|
||||
#define STC_TXCLK_SRC_MAX 8
|
||||
|
||||
#define STC_TXCLK_SPDIF_ROOT 1
|
||||
|
||||
/* SPDIF tx rate */
|
||||
enum spdif_txrate {
|
||||
SPDIF_TXRATE_32000 = 0,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,7 @@ struct ccsr_ssi {
|
||||
__be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
|
||||
};
|
||||
|
||||
#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000
|
||||
#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800
|
||||
#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400
|
||||
#define CCSR_SSI_SCR_TCH_EN 0x00000100
|
||||
@ -206,5 +207,64 @@ struct ccsr_ssi {
|
||||
#define CCSR_SSI_SACNT_FV 0x00000002
|
||||
#define CCSR_SSI_SACNT_AC97EN 0x00000001
|
||||
|
||||
#endif
|
||||
|
||||
struct device;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
|
||||
struct fsl_ssi_dbg {
|
||||
struct dentry *dbg_dir;
|
||||
struct dentry *dbg_stats;
|
||||
|
||||
struct {
|
||||
unsigned int rfrc;
|
||||
unsigned int tfrc;
|
||||
unsigned int cmdau;
|
||||
unsigned int cmddu;
|
||||
unsigned int rxt;
|
||||
unsigned int rdr1;
|
||||
unsigned int rdr0;
|
||||
unsigned int tde1;
|
||||
unsigned int tde0;
|
||||
unsigned int roe1;
|
||||
unsigned int roe0;
|
||||
unsigned int tue1;
|
||||
unsigned int tue0;
|
||||
unsigned int tfs;
|
||||
unsigned int rfs;
|
||||
unsigned int tls;
|
||||
unsigned int rls;
|
||||
unsigned int rff1;
|
||||
unsigned int rff0;
|
||||
unsigned int tfe1;
|
||||
unsigned int tfe0;
|
||||
} stats;
|
||||
};
|
||||
|
||||
void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr);
|
||||
|
||||
int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
|
||||
|
||||
void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg);
|
||||
|
||||
#else
|
||||
|
||||
struct fsl_ssi_dbg {
|
||||
};
|
||||
|
||||
static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
|
||||
struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
|
||||
{
|
||||
}
|
||||
#endif /* ! IS_ENABLED(CONFIG_DEBUG_FS) */
|
||||
|
||||
#endif
|
||||
|
163
sound/soc/fsl/fsl_ssi_dbg.c
Normal file
163
sound/soc/fsl/fsl_ssi_dbg.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
|
||||
*
|
||||
* Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
|
||||
*
|
||||
* Splitted from fsl_ssi.c
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "fsl_ssi.h"
|
||||
|
||||
void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
|
||||
{
|
||||
if (sisr & CCSR_SSI_SISR_RFRC)
|
||||
dbg->stats.rfrc++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TFRC)
|
||||
dbg->stats.tfrc++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_CMDAU)
|
||||
dbg->stats.cmdau++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_CMDDU)
|
||||
dbg->stats.cmddu++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RXT)
|
||||
dbg->stats.rxt++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RDR1)
|
||||
dbg->stats.rdr1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RDR0)
|
||||
dbg->stats.rdr0++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TDE1)
|
||||
dbg->stats.tde1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TDE0)
|
||||
dbg->stats.tde0++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_ROE1)
|
||||
dbg->stats.roe1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_ROE0)
|
||||
dbg->stats.roe0++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TUE1)
|
||||
dbg->stats.tue1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TUE0)
|
||||
dbg->stats.tue0++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TFS)
|
||||
dbg->stats.tfs++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RFS)
|
||||
dbg->stats.rfs++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TLS)
|
||||
dbg->stats.tls++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RLS)
|
||||
dbg->stats.rls++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RFF1)
|
||||
dbg->stats.rff1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_RFF0)
|
||||
dbg->stats.rff0++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TFE1)
|
||||
dbg->stats.tfe1++;
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TFE0)
|
||||
dbg->stats.tfe0++;
|
||||
}
|
||||
|
||||
/* Show the statistics of a flag only if its interrupt is enabled. The
|
||||
* compiler will optimze this code to a no-op if the interrupt is not
|
||||
* enabled.
|
||||
*/
|
||||
#define SIER_SHOW(flag, name) \
|
||||
do { \
|
||||
if (CCSR_SSI_SIER_##flag) \
|
||||
seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* fsl_sysfs_ssi_show: display SSI statistics
|
||||
*
|
||||
* Display the statistics for the current SSI device. To avoid confusion,
|
||||
* we only show those counts that are enabled.
|
||||
*/
|
||||
static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct fsl_ssi_dbg *ssi_dbg = s->private;
|
||||
|
||||
SIER_SHOW(RFRC_EN, rfrc);
|
||||
SIER_SHOW(TFRC_EN, tfrc);
|
||||
SIER_SHOW(CMDAU_EN, cmdau);
|
||||
SIER_SHOW(CMDDU_EN, cmddu);
|
||||
SIER_SHOW(RXT_EN, rxt);
|
||||
SIER_SHOW(RDR1_EN, rdr1);
|
||||
SIER_SHOW(RDR0_EN, rdr0);
|
||||
SIER_SHOW(TDE1_EN, tde1);
|
||||
SIER_SHOW(TDE0_EN, tde0);
|
||||
SIER_SHOW(ROE1_EN, roe1);
|
||||
SIER_SHOW(ROE0_EN, roe0);
|
||||
SIER_SHOW(TUE1_EN, tue1);
|
||||
SIER_SHOW(TUE0_EN, tue0);
|
||||
SIER_SHOW(TFS_EN, tfs);
|
||||
SIER_SHOW(RFS_EN, rfs);
|
||||
SIER_SHOW(TLS_EN, tls);
|
||||
SIER_SHOW(RLS_EN, rls);
|
||||
SIER_SHOW(RFF1_EN, rff1);
|
||||
SIER_SHOW(RFF0_EN, rff0);
|
||||
SIER_SHOW(TFE1_EN, tfe1);
|
||||
SIER_SHOW(TFE0_EN, tfe0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, fsl_ssi_stats_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fsl_ssi_stats_ops = {
|
||||
.open = fsl_ssi_stats_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
|
||||
{
|
||||
ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
|
||||
if (!ssi_dbg->dbg_dir)
|
||||
return -ENOMEM;
|
||||
|
||||
ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
|
||||
ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops);
|
||||
if (!ssi_dbg->dbg_stats) {
|
||||
debugfs_remove(ssi_dbg->dbg_dir);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
|
||||
{
|
||||
debugfs_remove(ssi_dbg->dbg_stats);
|
||||
debugfs_remove(ssi_dbg->dbg_dir);
|
||||
}
|
@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 65535, /* Limited by SDMA engine */
|
||||
|
@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev,
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform);
|
||||
|
||||
static void devm_card_release(struct device *dev, void *res)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user