forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
4.8 KiB
185 lines
4.8 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// |
|
// Copyright (c) 2020 BayLibre, SAS. |
|
// Author: Jerome Brunet <[email protected]> |
|
|
|
#include <linux/clk.h> |
|
#include <sound/pcm_params.h> |
|
#include <sound/soc.h> |
|
#include <sound/soc-dai.h> |
|
|
|
#include "aiu.h" |
|
#include "aiu-fifo.h" |
|
|
|
#define AIU_IEC958_DCU_FF_CTRL_EN BIT(0) |
|
#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1) |
|
#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2) |
|
#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2) |
|
#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3) |
|
#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4) |
|
#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5) |
|
#define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6) |
|
#define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3) |
|
#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6) |
|
#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) |
|
#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) |
|
#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) |
|
|
|
#define AIU_FIFO_SPDIF_BLOCK 8 |
|
|
|
static struct snd_pcm_hardware fifo_spdif_pcm = { |
|
.info = (SNDRV_PCM_INFO_INTERLEAVED | |
|
SNDRV_PCM_INFO_MMAP | |
|
SNDRV_PCM_INFO_MMAP_VALID | |
|
SNDRV_PCM_INFO_PAUSE), |
|
.formats = AIU_FORMATS, |
|
.rate_min = 5512, |
|
.rate_max = 192000, |
|
.channels_min = 2, |
|
.channels_max = 2, |
|
.period_bytes_min = AIU_FIFO_SPDIF_BLOCK, |
|
.period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX, |
|
.periods_min = 2, |
|
.periods_max = UINT_MAX, |
|
|
|
/* No real justification for this */ |
|
.buffer_bytes_max = 1 * 1024 * 1024, |
|
}; |
|
|
|
static void fifo_spdif_dcu_enable(struct snd_soc_component *component, |
|
bool enable) |
|
{ |
|
snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, |
|
AIU_IEC958_DCU_FF_CTRL_EN, |
|
enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0); |
|
} |
|
|
|
static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, |
|
struct snd_soc_dai *dai) |
|
{ |
|
struct snd_soc_component *component = dai->component; |
|
int ret; |
|
|
|
ret = aiu_fifo_trigger(substream, cmd, dai); |
|
if (ret) |
|
return ret; |
|
|
|
switch (cmd) { |
|
case SNDRV_PCM_TRIGGER_START: |
|
case SNDRV_PCM_TRIGGER_RESUME: |
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
|
fifo_spdif_dcu_enable(component, true); |
|
break; |
|
case SNDRV_PCM_TRIGGER_SUSPEND: |
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
|
case SNDRV_PCM_TRIGGER_STOP: |
|
fifo_spdif_dcu_enable(component, false); |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int fifo_spdif_prepare(struct snd_pcm_substream *substream, |
|
struct snd_soc_dai *dai) |
|
{ |
|
struct snd_soc_component *component = dai->component; |
|
int ret; |
|
|
|
ret = aiu_fifo_prepare(substream, dai); |
|
if (ret) |
|
return ret; |
|
|
|
snd_soc_component_update_bits(component, |
|
AIU_MEM_IEC958_BUF_CNTL, |
|
AIU_MEM_IEC958_BUF_CNTL_INIT, |
|
AIU_MEM_IEC958_BUF_CNTL_INIT); |
|
snd_soc_component_update_bits(component, |
|
AIU_MEM_IEC958_BUF_CNTL, |
|
AIU_MEM_IEC958_BUF_CNTL_INIT, 0); |
|
|
|
return 0; |
|
} |
|
|
|
static int fifo_spdif_hw_params(struct snd_pcm_substream *substream, |
|
struct snd_pcm_hw_params *params, |
|
struct snd_soc_dai *dai) |
|
{ |
|
struct snd_soc_component *component = dai->component; |
|
unsigned int val; |
|
int ret; |
|
|
|
ret = aiu_fifo_hw_params(substream, params, dai); |
|
if (ret) |
|
return ret; |
|
|
|
val = AIU_MEM_IEC958_CONTROL_RD_DDR | |
|
AIU_MEM_IEC958_CONTROL_MODE_LINEAR; |
|
|
|
switch (params_physical_width(params)) { |
|
case 16: |
|
val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT; |
|
break; |
|
case 32: |
|
break; |
|
default: |
|
dev_err(dai->dev, "Unsupported physical width %u\n", |
|
params_physical_width(params)); |
|
return -EINVAL; |
|
} |
|
|
|
snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL, |
|
AIU_MEM_IEC958_CONTROL_ENDIAN | |
|
AIU_MEM_IEC958_CONTROL_RD_DDR | |
|
AIU_MEM_IEC958_CONTROL_MODE_LINEAR | |
|
AIU_MEM_IEC958_CONTROL_MODE_16BIT, |
|
val); |
|
|
|
/* Number bytes read by the FIFO between each IRQ */ |
|
snd_soc_component_write(component, AIU_IEC958_BPF, |
|
params_period_bytes(params)); |
|
|
|
/* |
|
* AUTO_DISABLE and SYNC_HEAD are enabled by default but |
|
* this should be disabled in PCM (uncompressed) mode |
|
*/ |
|
snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, |
|
AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE | |
|
AIU_IEC958_DCU_FF_CTRL_IRQ_MODE | |
|
AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN, |
|
AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ); |
|
|
|
return 0; |
|
} |
|
|
|
const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { |
|
.trigger = fifo_spdif_trigger, |
|
.prepare = fifo_spdif_prepare, |
|
.hw_params = fifo_spdif_hw_params, |
|
.startup = aiu_fifo_startup, |
|
.shutdown = aiu_fifo_shutdown, |
|
}; |
|
|
|
int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) |
|
{ |
|
struct snd_soc_component *component = dai->component; |
|
struct aiu *aiu = snd_soc_component_get_drvdata(component); |
|
struct aiu_fifo *fifo; |
|
int ret; |
|
|
|
ret = aiu_fifo_dai_probe(dai); |
|
if (ret) |
|
return ret; |
|
|
|
fifo = dai->playback_dma_data; |
|
|
|
fifo->pcm = &fifo_spdif_pcm; |
|
fifo->mem_offset = AIU_MEM_IEC958_START; |
|
fifo->fifo_block = 1; |
|
fifo->pclk = aiu->spdif.clks[PCLK].clk; |
|
fifo->irq = aiu->spdif.irq; |
|
|
|
return 0; |
|
}
|
|
|