Skip to content

Commit

Permalink
ASoC: starfive: Add StarFive JH7100 audio drivers
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Yan <michael.yan@starfivetech.com>
Signed-off-by: Jenny Zhang <jenny.zhang@starfivetech.com>
Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
  • Loading branch information
WalkerChenL authored and esmil committed Mar 22, 2022
1 parent 6ff883c commit 7c3b27a
Show file tree
Hide file tree
Showing 16 changed files with 4,268 additions and 0 deletions.
1 change: 1 addition & 0 deletions sound/soc/Kconfig
Expand Up @@ -83,6 +83,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sof/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sprd/Kconfig"
source "sound/soc/starfive/Kconfig"
source "sound/soc/sti/Kconfig"
source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions sound/soc/Makefile
Expand Up @@ -49,6 +49,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += starfive/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sof/
obj-$(CONFIG_SND_SOC) += spear/
Expand Down
59 changes: 59 additions & 0 deletions sound/soc/starfive/Kconfig
@@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 StarFive Technology Co., Ltd.

config SND_STARFIVE_SPDIF
tristate "starfive spdif"
depends on SOC_STARFIVE || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y or M if you want to add support for codecs attached to the
I2S interface on VIC vic_starlight board. You will also need to select
the drivers for the rest of VIC audio subsystem.

config SND_STARFIVE_SPDIF_PCM
bool "PCM PIO extension for spdif driver"
depends on SND_STARFIVE_SPDIF
help
Say Y or N if you want to add a custom ALSA extension that registers
a PCM and uses PIO to transfer data.

config SND_STARFIVE_PWMDAC
tristate "starfive pwmdac Device Driver"
depends on SOC_STARFIVE || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for sf pwmdac driver.

config SND_STARFIVE_PWMDAC_PCM
bool "PCM PIO extension for pwmdac driver"
depends on SND_STARFIVE_PWMDAC
help
Say Y or N if you want to add a custom ALSA extension that registers
a PCM and uses PIO to transfer data.

config SND_STARFIVE_PDM
tristate "starfive pdm Device Driver"
depends on SOC_STARFIVE || COMPILE_TEST
select REGMAP_MMIO
help
Say Y or M if you want to add support for sf pdm driver.

config SND_STARFIVE_I2SVAD
tristate "starfive I2SVAD Device Driver"
depends on SOC_STARFIVE || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2SVAD driver for
starfive I2SVAD device.

config SND_STARFIVE_I2SVAD_PCM
bool "PCM PIO extension for I2SVAD driver"
depends on SND_STARFIVE_I2SVAD
help
Say Y or N if you want to add a custom ALSA extension that registers
a PCM and uses PIO to transfer data.

This functionality is specially suited for I2SVAD devices that don't have
DMA support.

24 changes: 24 additions & 0 deletions sound/soc/starfive/Makefile
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2021 StarFive Technology Co., Ltd.
#
snd-soc-starfive-spdif-y := spdif.o
snd-soc-starfive-spdif-$(CONFIG_SND_STARFIVE_SPDIF_PCM) += spdif-pcm.o

obj-$(CONFIG_SND_STARFIVE_SPDIF) += snd-soc-starfive-spdif.o

snd-soc-starfive-pwmdac-y := pwmdac.o
snd-soc-starfive-pwmdac-$(CONFIG_SND_STARFIVE_PWMDAC_PCM) += pwmdac-pcm.o
snd-soc-starfive-pwmdac-transmitter-y := pwmdac-transmitter.o

obj-$(CONFIG_SND_STARFIVE_PWMDAC) += snd-soc-starfive-pwmdac.o
obj-$(CONFIG_SND_STARFIVE_PWMDAC) += snd-soc-starfive-pwmdac-transmitter.o

snd-soc-starfive-pdm-y := pdm.o

obj-$(CONFIG_SND_STARFIVE_PDM) += snd-soc-starfive-pdm.o

snd-soc-starfive-i2svad-y := i2svad.o
snd-soc-starfive-i2svad-$(CONFIG_SND_STARFIVE_I2SVAD_PCM) += i2svad-pcm.o

obj-$(CONFIG_SND_STARFIVE_I2SVAD) += snd-soc-starfive-i2svad.o
249 changes: 249 additions & 0 deletions sound/soc/starfive/i2svad-pcm.c
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 StarFive Technology Co., Ltd.
*/
#include <linux/io.h>
#include <linux/rcupdate.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>

#include "i2svad.h"

#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN)
#define PERIOD_BYTES_MIN 4096
#define PERIODS_MIN 2

#define i2svad_pcm_tx_fn(sample_bits) \
static unsigned int i2svad_pcm_tx_##sample_bits(struct i2svad_dev *dev, \
struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
bool *period_elapsed) \
{ \
const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
unsigned int period_pos = tx_ptr % runtime->period_size; \
int i; \
\
for (i = 0; i < dev->fifo_th; i++) { \
iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
period_pos++; \
if (++tx_ptr >= runtime->buffer_size) \
tx_ptr = 0; \
} \
*period_elapsed = period_pos >= runtime->period_size; \
return tx_ptr; \
}

#define i2svad_pcm_rx_fn(sample_bits) \
static unsigned int i2svad_pcm_rx_##sample_bits(struct i2svad_dev *dev, \
struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
bool *period_elapsed) \
{ \
u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
unsigned int period_pos = rx_ptr % runtime->period_size; \
int i; \
\
for (i = 0; i < dev->fifo_th; i++) { \
p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
period_pos++; \
if (++rx_ptr >= runtime->buffer_size) \
rx_ptr = 0; \
} \
*period_elapsed = period_pos >= runtime->period_size; \
return rx_ptr; \
}

i2svad_pcm_tx_fn(16);
i2svad_pcm_rx_fn(16);

#undef i2svad_pcm_tx_fn
#undef i2svad_pcm_rx_fn

static const struct snd_pcm_hardware i2svad_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.rate_min = 32000,
.rate_max = 48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
.periods_min = PERIODS_MIN,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
.fifo_size = 16,
};

static void i2svad_pcm_transfer(struct i2svad_dev *dev, bool push)
{
struct snd_pcm_substream *substream;
bool active, period_elapsed;

rcu_read_lock();
if (push)
substream = rcu_dereference(dev->tx_substream);
else
substream = rcu_dereference(dev->rx_substream);
active = substream && snd_pcm_running(substream);
if (active) {
unsigned int ptr;
unsigned int new_ptr;

if (push) {
ptr = READ_ONCE(dev->tx_ptr);
new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
&period_elapsed);
cmpxchg(&dev->tx_ptr, ptr, new_ptr);
} else {
ptr = READ_ONCE(dev->rx_ptr);
new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
&period_elapsed);
cmpxchg(&dev->rx_ptr, ptr, new_ptr);
}

if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
rcu_read_unlock();
}

void i2svad_pcm_push_tx(struct i2svad_dev *dev)
{
i2svad_pcm_transfer(dev, true);
}

void i2svad_pcm_pop_rx(struct i2svad_dev *dev)
{
i2svad_pcm_transfer(dev, false);
}

static int i2svad_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct i2svad_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));

snd_soc_set_runtime_hwparams(substream, &i2svad_pcm_hardware);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
runtime->private_data = dev;

return 0;
}

static int i2svad_pcm_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
synchronize_rcu();
return 0;
}

static int i2svad_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct i2svad_dev *dev = runtime->private_data;

switch (params_channels(hw_params)) {
case 2:
break;
default:
dev_err(dev->dev, "invalid channels number\n");
return -EINVAL;
}

switch (params_format(hw_params)) {
case SNDRV_PCM_FORMAT_S16_LE:
dev->tx_fn = i2svad_pcm_tx_16;
dev->rx_fn = i2svad_pcm_rx_16;
break;
default:
dev_err(dev->dev, "invalid format\n");
return -EINVAL;
}

return 0;
}

static int i2svad_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct i2svad_dev *dev = runtime->private_data;
int ret = 0;

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
WRITE_ONCE(dev->tx_ptr, 0);
rcu_assign_pointer(dev->tx_substream, substream);
} else {
WRITE_ONCE(dev->rx_ptr, 0);
rcu_assign_pointer(dev->rx_substream, substream);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
rcu_assign_pointer(dev->tx_substream, NULL);
else
rcu_assign_pointer(dev->rx_substream, NULL);
break;
default:
ret = -EINVAL;
break;
}

return ret;
}

static snd_pcm_uframes_t i2svad_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct i2svad_dev *dev = runtime->private_data;
snd_pcm_uframes_t pos;

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
pos = READ_ONCE(dev->tx_ptr);
else
pos = READ_ONCE(dev->rx_ptr);

return pos < runtime->buffer_size ? pos : 0;
}

static int i2svad_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
size_t size = i2svad_pcm_hardware.buffer_bytes_max;

snd_pcm_set_managed_buffer_all(rtd->pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
NULL, size, size);
return 0;
}

static const struct snd_soc_component_driver i2svad_pcm_component = {
.open = i2svad_pcm_open,
.close = i2svad_pcm_close,
.hw_params = i2svad_pcm_hw_params,
.trigger = i2svad_pcm_trigger,
.pointer = i2svad_pcm_pointer,
.pcm_construct = i2svad_pcm_new,
};

int i2svad_pcm_register(struct platform_device *pdev)
{
return devm_snd_soc_register_component(&pdev->dev, &i2svad_pcm_component,
NULL, 0);
}

0 comments on commit 7c3b27a

Please sign in to comment.