Merge series "Enable DMA mode on Intel Keem Bay platform" from Michael Sit Wei Hong <michael.wei.hong.sit@intel.com>:
v2: Update patch to align with latest kernel release. v1: Initial patch version, to enable DMA mode on Intel Keembay platform. Michael Sit Wei Hong (2): dt-bindings: sound: intel, keembay-i2s: Add info for device to use DMA ASoC: Intel: KMB: Enable DMA transfer mode .../bindings/sound/intel,keembay-i2s.yaml | 14 ++ sound/soc/intel/Kconfig | 2 + sound/soc/intel/keembay/kmb_platform.c | 157 ++++++++++++++++-- sound/soc/intel/keembay/kmb_platform.h | 9 + 4 files changed, 167 insertions(+), 15 deletions(-) -- 2.17.1
This commit is contained in:
commit
f3ddced14b
@ -45,6 +45,18 @@ properties:
|
||||
- const: osc
|
||||
- const: apb_clk
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: DMA controller phandle and DMA channel
|
||||
for TX and RX
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- description: "tx" for the transmit channel
|
||||
"rx" for the receive channel
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#sound-dai-cells"
|
||||
@ -70,4 +82,6 @@ examples:
|
||||
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "osc", "apb_clk";
|
||||
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>;
|
||||
dmas = <&axi_dma0 29 &axi_dma0 33>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
@ -203,6 +203,8 @@ config SND_SOC_INTEL_KEEMBAY
|
||||
tristate "Keembay Platforms"
|
||||
depends on ARCH_KEEMBAY || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select SND_DMAENGINE_PCM
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
If you have a Intel Keembay platform then enable this option
|
||||
by saying Y or m.
|
||||
|
@ -6,10 +6,12 @@
|
||||
//
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
@ -343,6 +345,53 @@ static const struct snd_soc_component_driver kmb_component = {
|
||||
.pointer = kmb_pcm_pointer,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver kmb_component_dma = {
|
||||
.name = "kmb",
|
||||
};
|
||||
|
||||
static int kmb_probe(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
if (kmb_i2s->use_pio)
|
||||
return 0;
|
||||
|
||||
snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
|
||||
&kmb_i2s->capture_dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
|
||||
{
|
||||
u32 dma_reg;
|
||||
|
||||
dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
|
||||
/* Enable DMA handshake for stream */
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_reg |= I2S_DMAEN_TXBLOCK;
|
||||
else
|
||||
dma_reg |= I2S_DMAEN_RXBLOCK;
|
||||
|
||||
writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
|
||||
}
|
||||
|
||||
static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
|
||||
{
|
||||
u32 dma_reg;
|
||||
|
||||
dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
|
||||
/* Disable DMA handshake for stream */
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dma_reg &= ~I2S_DMAEN_TXBLOCK;
|
||||
writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
|
||||
} else {
|
||||
dma_reg &= ~I2S_DMAEN_RXBLOCK;
|
||||
writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
|
||||
}
|
||||
writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
|
||||
}
|
||||
|
||||
static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
@ -356,7 +405,11 @@ static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
|
||||
else
|
||||
writel(1, kmb_i2s->i2s_base + IRER);
|
||||
|
||||
kmb_i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true);
|
||||
if (kmb_i2s->use_pio)
|
||||
kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
|
||||
config->chan_nr, true);
|
||||
else
|
||||
kmb_i2s_enable_dma(kmb_i2s, substream->stream);
|
||||
|
||||
if (kmb_i2s->clock_provider)
|
||||
writel(1, kmb_i2s->i2s_base + CER);
|
||||
@ -434,6 +487,7 @@ static int kmb_dai_trigger(struct snd_pcm_substream *substream,
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
kmb_i2s->active--;
|
||||
if (kmb_i2s->use_pio)
|
||||
kmb_i2s_stop(kmb_i2s, substream);
|
||||
break;
|
||||
default:
|
||||
@ -485,16 +539,22 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
config->data_width = 16;
|
||||
kmb_i2s->ccr = 0x00;
|
||||
kmb_i2s->xfer_resolution = 0x02;
|
||||
kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
config->data_width = 32;
|
||||
kmb_i2s->ccr = 0x14;
|
||||
kmb_i2s->xfer_resolution = 0x05;
|
||||
kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
config->data_width = 32;
|
||||
kmb_i2s->ccr = 0x10;
|
||||
kmb_i2s->xfer_resolution = 0x05;
|
||||
kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
break;
|
||||
default:
|
||||
dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
|
||||
@ -572,9 +632,56 @@ static int kmb_dai_prepare(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kmb_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
|
||||
if (kmb_i2s->use_pio)
|
||||
return 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = &kmb_i2s->play_dma_data;
|
||||
else
|
||||
dma_data = &kmb_i2s->capture_dma_data;
|
||||
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
|
||||
if (kmb_i2s->use_pio)
|
||||
kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
writel(0, kmb_i2s->i2s_base + ITER);
|
||||
else
|
||||
writel(0, kmb_i2s->i2s_base + IRER);
|
||||
|
||||
if (kmb_i2s->use_pio)
|
||||
kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
|
||||
else
|
||||
kmb_i2s_disable_dma(kmb_i2s, substream->stream);
|
||||
|
||||
if (!kmb_i2s->active) {
|
||||
writel(0, kmb_i2s->i2s_base + CER);
|
||||
writel(0, kmb_i2s->i2s_base + IER);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops kmb_dai_ops = {
|
||||
.startup = kmb_dai_startup,
|
||||
.trigger = kmb_dai_trigger,
|
||||
.hw_params = kmb_dai_hw_params,
|
||||
.hw_free = kmb_dai_hw_free,
|
||||
.prepare = kmb_dai_prepare,
|
||||
.set_fmt = kmb_set_dai_fmt,
|
||||
};
|
||||
@ -607,6 +714,7 @@ static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
|
||||
SNDRV_PCM_FMTBIT_S16_LE),
|
||||
},
|
||||
.ops = &kmb_dai_ops,
|
||||
.probe = kmb_probe,
|
||||
},
|
||||
};
|
||||
|
||||
@ -626,6 +734,7 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
|
||||
SNDRV_PCM_FMTBIT_S16_LE),
|
||||
},
|
||||
.ops = &kmb_dai_ops,
|
||||
.probe = kmb_probe,
|
||||
},
|
||||
};
|
||||
|
||||
@ -637,10 +746,12 @@ static const struct of_device_id kmb_plat_of_match[] = {
|
||||
|
||||
static int kmb_plat_dai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_dai_driver *kmb_i2s_dai;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct kmb_i2s_info *kmb_i2s;
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
u32 comp1_reg;
|
||||
|
||||
@ -682,7 +793,7 @@ static int kmb_plat_dai_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(kmb_i2s->clk_i2s);
|
||||
}
|
||||
|
||||
kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(kmb_i2s->i2s_base))
|
||||
return PTR_ERR(kmb_i2s->i2s_base);
|
||||
|
||||
@ -692,6 +803,13 @@ static int kmb_plat_dai_probe(struct platform_device *pdev)
|
||||
|
||||
kmb_i2s->dev = &pdev->dev;
|
||||
|
||||
comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
|
||||
|
||||
kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
|
||||
|
||||
kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas"));
|
||||
|
||||
if (kmb_i2s->use_pio) {
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
|
||||
@ -701,13 +819,22 @@ static int kmb_plat_dai_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
|
||||
|
||||
kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &kmb_component,
|
||||
kmb_i2s_dai, 1);
|
||||
} else {
|
||||
kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
|
||||
kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
|
||||
ret = snd_dmaengine_pcm_register(&pdev->dev,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register dmaengine: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
|
||||
kmb_i2s_dai, 1);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "not able to register dai\n");
|
||||
return ret;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/types.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
/* Register values with reference to KMB databook v1.1 */
|
||||
/* common register for all channel */
|
||||
@ -103,7 +104,12 @@
|
||||
#define DW_I2S_PROVIDER BIT(3)
|
||||
|
||||
#define I2S_RXDMA 0x01C0
|
||||
#define I2S_RRXDMA 0x01C4
|
||||
#define I2S_TXDMA 0x01C8
|
||||
#define I2S_RTXDMA 0x01CC
|
||||
#define I2S_DMACR 0x0200
|
||||
#define I2S_DMAEN_RXBLOCK (1 << 16)
|
||||
#define I2S_DMAEN_TXBLOCK (1 << 17)
|
||||
|
||||
/*
|
||||
* struct i2s_clk_config_data - represent i2s clk configuration data
|
||||
@ -131,6 +137,9 @@ struct kmb_i2s_info {
|
||||
u32 xfer_resolution;
|
||||
u32 fifo_th;
|
||||
bool clock_provider;
|
||||
/* data related to DMA transfers b/w i2s and DMAC */
|
||||
struct snd_dmaengine_dai_dma_data play_dma_data;
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
|
||||
struct i2s_clk_config_data config;
|
||||
int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
|
||||
|
Loading…
Reference in New Issue
Block a user