Skip to content

Commit 644c4ba

Browse files
WalkerChenLesmil
authored andcommitted
ASoC: starfive: Add StarFive JH7100 audio drivers
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: Geert Uytterhoeven <geert@linux-m68k.org> [geertu: convert not to use asoc_xxx()] Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
1 parent e446c88 commit 644c4ba

15 files changed

Lines changed: 4184 additions & 0 deletions

File tree

sound/soc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
5757
obj-$(CONFIG_SND_SOC) += qcom/
5858
obj-$(CONFIG_SND_SOC) += rockchip/
5959
obj-$(CONFIG_SND_SOC) += samsung/
60+
obj-$(CONFIG_SND_SOC) += starfive/
6061
obj-$(CONFIG_SND_SOC) += sh/
6162
obj-$(CONFIG_SND_SOC) += sof/
6263
obj-$(CONFIG_SND_SOC) += spear/

sound/soc/starfive/Kconfig

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,59 @@ config SND_SOC_JH7110_TDM
2222
select SND_SOC_GENERIC_DMAENGINE_PCM
2323
help
2424
Say Y or M if you want to add support for StarFive TDM driver.
25+
26+
config SND_STARFIVE_SPDIF
27+
tristate "starfive spdif"
28+
depends on SOC_STARFIVE || COMPILE_TEST
29+
select SND_SOC_GENERIC_DMAENGINE_PCM
30+
select REGMAP_MMIO
31+
help
32+
Say Y or M if you want to add support for codecs attached to the
33+
I2S interface on VIC vic_starlight board. You will also need to select
34+
the drivers for the rest of VIC audio subsystem.
35+
36+
config SND_STARFIVE_SPDIF_PCM
37+
bool "PCM PIO extension for spdif driver"
38+
depends on SND_STARFIVE_SPDIF
39+
help
40+
Say Y or N if you want to add a custom ALSA extension that registers
41+
a PCM and uses PIO to transfer data.
42+
43+
config SND_STARFIVE_PWMDAC
44+
tristate "starfive pwmdac Device Driver"
45+
depends on SOC_STARFIVE || COMPILE_TEST
46+
select SND_SOC_GENERIC_DMAENGINE_PCM
47+
help
48+
Say Y or M if you want to add support for sf pwmdac driver.
49+
50+
config SND_STARFIVE_PWMDAC_PCM
51+
bool "PCM PIO extension for pwmdac driver"
52+
depends on SND_STARFIVE_PWMDAC
53+
help
54+
Say Y or N if you want to add a custom ALSA extension that registers
55+
a PCM and uses PIO to transfer data.
56+
57+
config SND_STARFIVE_PDM
58+
tristate "starfive pdm Device Driver"
59+
depends on SOC_STARFIVE || COMPILE_TEST
60+
select REGMAP_MMIO
61+
help
62+
Say Y or M if you want to add support for sf pdm driver.
63+
64+
config SND_STARFIVE_I2SVAD
65+
tristate "starfive I2SVAD Device Driver"
66+
depends on SOC_STARFIVE || COMPILE_TEST
67+
select SND_SOC_GENERIC_DMAENGINE_PCM
68+
help
69+
Say Y or M if you want to add support for I2SVAD driver for
70+
starfive I2SVAD device.
71+
72+
config SND_STARFIVE_I2SVAD_PCM
73+
bool "PCM PIO extension for I2SVAD driver"
74+
depends on SND_STARFIVE_I2SVAD
75+
help
76+
Say Y or N if you want to add a custom ALSA extension that registers
77+
a PCM and uses PIO to transfer data.
78+
79+
This functionality is specially suited for I2SVAD devices that don't have
80+
DMA support.

sound/soc/starfive/Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
# StarFive Platform Support
22
obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
33
obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
4+
5+
snd-soc-starfive-spdif-y := spdif.o
6+
snd-soc-starfive-spdif-$(CONFIG_SND_STARFIVE_SPDIF_PCM) += spdif-pcm.o
7+
8+
obj-$(CONFIG_SND_STARFIVE_SPDIF) += snd-soc-starfive-spdif.o
9+
10+
snd-soc-starfive-pwmdac-y := pwmdac.o
11+
snd-soc-starfive-pwmdac-$(CONFIG_SND_STARFIVE_PWMDAC_PCM) += pwmdac-pcm.o
12+
snd-soc-starfive-pwmdac-transmitter-y := pwmdac-transmitter.o
13+
14+
obj-$(CONFIG_SND_STARFIVE_PWMDAC) += snd-soc-starfive-pwmdac.o
15+
obj-$(CONFIG_SND_STARFIVE_PWMDAC) += snd-soc-starfive-pwmdac-transmitter.o
16+
17+
snd-soc-starfive-pdm-y := pdm.o
18+
19+
obj-$(CONFIG_SND_STARFIVE_PDM) += snd-soc-starfive-pdm.o
20+
21+
snd-soc-starfive-i2svad-y := i2svad.o
22+
snd-soc-starfive-i2svad-$(CONFIG_SND_STARFIVE_I2SVAD_PCM) += i2svad-pcm.o
23+
24+
obj-$(CONFIG_SND_STARFIVE_I2SVAD) += snd-soc-starfive-i2svad.o

sound/soc/starfive/i2svad-pcm.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2021 StarFive Technology Co., Ltd.
4+
*/
5+
#include <linux/io.h>
6+
#include <linux/rcupdate.h>
7+
#include <sound/pcm.h>
8+
#include <sound/pcm_params.h>
9+
10+
#include "i2svad.h"
11+
12+
#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN)
13+
#define PERIOD_BYTES_MIN 4096
14+
#define PERIODS_MIN 2
15+
16+
#define i2svad_pcm_tx_fn(sample_bits) \
17+
static unsigned int i2svad_pcm_tx_##sample_bits(struct i2svad_dev *dev, \
18+
struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
19+
bool *period_elapsed) \
20+
{ \
21+
const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
22+
unsigned int period_pos = tx_ptr % runtime->period_size; \
23+
int i; \
24+
\
25+
for (i = 0; i < dev->fifo_th; i++) { \
26+
iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
27+
iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
28+
period_pos++; \
29+
if (++tx_ptr >= runtime->buffer_size) \
30+
tx_ptr = 0; \
31+
} \
32+
*period_elapsed = period_pos >= runtime->period_size; \
33+
return tx_ptr; \
34+
}
35+
36+
#define i2svad_pcm_rx_fn(sample_bits) \
37+
static unsigned int i2svad_pcm_rx_##sample_bits(struct i2svad_dev *dev, \
38+
struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
39+
bool *period_elapsed) \
40+
{ \
41+
u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
42+
unsigned int period_pos = rx_ptr % runtime->period_size; \
43+
int i; \
44+
\
45+
for (i = 0; i < dev->fifo_th; i++) { \
46+
p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
47+
p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
48+
period_pos++; \
49+
if (++rx_ptr >= runtime->buffer_size) \
50+
rx_ptr = 0; \
51+
} \
52+
*period_elapsed = period_pos >= runtime->period_size; \
53+
return rx_ptr; \
54+
}
55+
56+
i2svad_pcm_tx_fn(16);
57+
i2svad_pcm_rx_fn(16);
58+
59+
#undef i2svad_pcm_tx_fn
60+
#undef i2svad_pcm_rx_fn
61+
62+
static const struct snd_pcm_hardware i2svad_pcm_hardware = {
63+
.info = SNDRV_PCM_INFO_INTERLEAVED |
64+
SNDRV_PCM_INFO_MMAP |
65+
SNDRV_PCM_INFO_MMAP_VALID |
66+
SNDRV_PCM_INFO_BLOCK_TRANSFER,
67+
.rates = SNDRV_PCM_RATE_32000 |
68+
SNDRV_PCM_RATE_44100 |
69+
SNDRV_PCM_RATE_48000,
70+
.rate_min = 32000,
71+
.rate_max = 48000,
72+
.formats = SNDRV_PCM_FMTBIT_S16_LE,
73+
.channels_min = 2,
74+
.channels_max = 2,
75+
.buffer_bytes_max = BUFFER_BYTES_MAX,
76+
.period_bytes_min = PERIOD_BYTES_MIN,
77+
.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
78+
.periods_min = PERIODS_MIN,
79+
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
80+
.fifo_size = 16,
81+
};
82+
83+
static void i2svad_pcm_transfer(struct i2svad_dev *dev, bool push)
84+
{
85+
struct snd_pcm_substream *substream;
86+
bool active, period_elapsed;
87+
88+
rcu_read_lock();
89+
if (push)
90+
substream = rcu_dereference(dev->tx_substream);
91+
else
92+
substream = rcu_dereference(dev->rx_substream);
93+
active = substream && snd_pcm_running(substream);
94+
if (active) {
95+
unsigned int ptr;
96+
unsigned int new_ptr;
97+
98+
if (push) {
99+
ptr = READ_ONCE(dev->tx_ptr);
100+
new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
101+
&period_elapsed);
102+
cmpxchg(&dev->tx_ptr, ptr, new_ptr);
103+
} else {
104+
ptr = READ_ONCE(dev->rx_ptr);
105+
new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
106+
&period_elapsed);
107+
cmpxchg(&dev->rx_ptr, ptr, new_ptr);
108+
}
109+
110+
if (period_elapsed)
111+
snd_pcm_period_elapsed(substream);
112+
}
113+
rcu_read_unlock();
114+
}
115+
116+
void i2svad_pcm_push_tx(struct i2svad_dev *dev)
117+
{
118+
i2svad_pcm_transfer(dev, true);
119+
}
120+
121+
void i2svad_pcm_pop_rx(struct i2svad_dev *dev)
122+
{
123+
i2svad_pcm_transfer(dev, false);
124+
}
125+
126+
static int i2svad_pcm_open(struct snd_soc_component *component,
127+
struct snd_pcm_substream *substream)
128+
{
129+
struct snd_pcm_runtime *runtime = substream->runtime;
130+
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
131+
struct i2svad_dev *dev = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
132+
133+
snd_soc_set_runtime_hwparams(substream, &i2svad_pcm_hardware);
134+
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
135+
runtime->private_data = dev;
136+
137+
return 0;
138+
}
139+
140+
static int i2svad_pcm_close(struct snd_soc_component *component,
141+
struct snd_pcm_substream *substream)
142+
{
143+
synchronize_rcu();
144+
return 0;
145+
}
146+
147+
static int i2svad_pcm_hw_params(struct snd_soc_component *component,
148+
struct snd_pcm_substream *substream,
149+
struct snd_pcm_hw_params *hw_params)
150+
{
151+
struct snd_pcm_runtime *runtime = substream->runtime;
152+
struct i2svad_dev *dev = runtime->private_data;
153+
154+
switch (params_channels(hw_params)) {
155+
case 2:
156+
break;
157+
default:
158+
dev_err(dev->dev, "invalid channels number\n");
159+
return -EINVAL;
160+
}
161+
162+
switch (params_format(hw_params)) {
163+
case SNDRV_PCM_FORMAT_S16_LE:
164+
dev->tx_fn = i2svad_pcm_tx_16;
165+
dev->rx_fn = i2svad_pcm_rx_16;
166+
break;
167+
default:
168+
dev_err(dev->dev, "invalid format\n");
169+
return -EINVAL;
170+
}
171+
172+
return 0;
173+
}
174+
175+
static int i2svad_pcm_trigger(struct snd_soc_component *component,
176+
struct snd_pcm_substream *substream, int cmd)
177+
{
178+
struct snd_pcm_runtime *runtime = substream->runtime;
179+
struct i2svad_dev *dev = runtime->private_data;
180+
int ret = 0;
181+
182+
switch (cmd) {
183+
case SNDRV_PCM_TRIGGER_START:
184+
case SNDRV_PCM_TRIGGER_RESUME:
185+
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
186+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
187+
WRITE_ONCE(dev->tx_ptr, 0);
188+
rcu_assign_pointer(dev->tx_substream, substream);
189+
} else {
190+
WRITE_ONCE(dev->rx_ptr, 0);
191+
rcu_assign_pointer(dev->rx_substream, substream);
192+
}
193+
break;
194+
case SNDRV_PCM_TRIGGER_STOP:
195+
case SNDRV_PCM_TRIGGER_SUSPEND:
196+
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
197+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
198+
rcu_assign_pointer(dev->tx_substream, NULL);
199+
else
200+
rcu_assign_pointer(dev->rx_substream, NULL);
201+
break;
202+
default:
203+
ret = -EINVAL;
204+
break;
205+
}
206+
207+
return ret;
208+
}
209+
210+
static snd_pcm_uframes_t i2svad_pcm_pointer(struct snd_soc_component *component,
211+
struct snd_pcm_substream *substream)
212+
{
213+
struct snd_pcm_runtime *runtime = substream->runtime;
214+
struct i2svad_dev *dev = runtime->private_data;
215+
snd_pcm_uframes_t pos;
216+
217+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
218+
pos = READ_ONCE(dev->tx_ptr);
219+
else
220+
pos = READ_ONCE(dev->rx_ptr);
221+
222+
return pos < runtime->buffer_size ? pos : 0;
223+
}
224+
225+
static int i2svad_pcm_new(struct snd_soc_component *component,
226+
struct snd_soc_pcm_runtime *rtd)
227+
{
228+
size_t size = i2svad_pcm_hardware.buffer_bytes_max;
229+
230+
snd_pcm_set_managed_buffer_all(rtd->pcm,
231+
SNDRV_DMA_TYPE_CONTINUOUS,
232+
NULL, size, size);
233+
return 0;
234+
}
235+
236+
static const struct snd_soc_component_driver i2svad_pcm_component = {
237+
.open = i2svad_pcm_open,
238+
.close = i2svad_pcm_close,
239+
.hw_params = i2svad_pcm_hw_params,
240+
.trigger = i2svad_pcm_trigger,
241+
.pointer = i2svad_pcm_pointer,
242+
.pcm_construct = i2svad_pcm_new,
243+
};
244+
245+
int i2svad_pcm_register(struct platform_device *pdev)
246+
{
247+
return devm_snd_soc_register_component(&pdev->dev, &i2svad_pcm_component,
248+
NULL, 0);
249+
}

0 commit comments

Comments
 (0)