@@ -39,13 +39,12 @@ static struct spi_device *rt5514_spi;
struct rt5514_dsp {
struct device *dev;
+ struct work_struct start_work;
struct delayed_work copy_work;
struct mutex dma_lock;
struct snd_pcm_substream *substream;
unsigned int buf_base, buf_limit, buf_rp;
- size_t buf_size;
- size_t dma_offset;
- size_t dsp_offset;
+ size_t buf_size, get_size, dma_offset;
};
static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
@@ -80,6 +79,8 @@ static void rt5514_spi_copy_work(struct work_struct *work)
container_of(work, struct rt5514_dsp, copy_work.work);
struct snd_pcm_runtime *runtime;
size_t period_bytes, truncated_bytes = 0;
+ unsigned int cur_wp, remain_data;
+ u8 buf[8];
mutex_lock(&rt5514_dsp->dma_lock);
if (!rt5514_dsp->substream) {
@@ -90,8 +91,22 @@ static void rt5514_spi_copy_work(struct work_struct *work)
runtime = rt5514_dsp->substream->runtime;
period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
- if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
- period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+ if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) {
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ cur_wp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ remain_data = (cur_wp >= rt5514_dsp->buf_rp) ?
+ (cur_wp - rt5514_dsp->buf_rp) :
+ ((rt5514_dsp->buf_limit - rt5514_dsp->buf_rp) +
+ (cur_wp - rt5514_dsp->buf_base));
+
+ if (remain_data < period_bytes) {
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ goto done;
+ }
+ }
if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
rt5514_spi_burst_read(rt5514_dsp->buf_rp,
@@ -112,24 +127,57 @@ static void rt5514_spi_copy_work(struct work_struct *work)
runtime->dma_area + rt5514_dsp->dma_offset +
truncated_bytes, period_bytes - truncated_bytes);
- rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
- period_bytes - truncated_bytes;
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base + period_bytes -
+ truncated_bytes;
}
+ rt5514_dsp->get_size += period_bytes;
rt5514_dsp->dma_offset += period_bytes;
if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
rt5514_dsp->dma_offset = 0;
- rt5514_dsp->dsp_offset += period_bytes;
-
snd_pcm_period_elapsed(rt5514_dsp->substream);
- if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
- schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+
done:
mutex_unlock(&rt5514_dsp->dma_lock);
}
+static void rt5514_spi_start_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, start_work);
+ u8 buf[8];
+
+ rt5514_dsp->get_size = 0;
+ rt5514_dsp->dma_offset = 0;
+
+ /**
+ * The address area x1800XXXX is the register address, and it cannot
+ * support spi burst read perfectly. So we use the spi burst read
+ * individually to make sure the data correctly.
+ */
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base;
+
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+}
+
/* PCM for streaming audio from the DSP buffer */
static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
{
@@ -170,59 +218,6 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
-static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(rtd->platform);
- u8 buf[8];
-
- rt5514_dsp->dma_offset = 0;
- rt5514_dsp->dsp_offset = 0;
-
- /**
- * The address area x1800XXXX is the register address, and it cannot
- * support spi burst read perfectly. So we use the spi burst read
- * individually to make sure the data correctly.
- */
- rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
- sizeof(buf));
- rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
- buf[3] << 24;
-
- rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
- sizeof(buf));
- rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
- buf[3] << 24;
-
- rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf,
- sizeof(buf));
- rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
- buf[3] << 24;
-
- rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
- sizeof(buf));
- rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
- buf[3] << 24;
-
- return 0;
-}
-
-static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct rt5514_dsp *rt5514_dsp =
- snd_soc_platform_get_drvdata(rtd->platform);
-
- if (cmd == SNDRV_PCM_TRIGGER_START) {
- if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
- rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
- schedule_delayed_work(&rt5514_dsp->copy_work, 0);
- }
-
- return 0;
-}
-
static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
struct snd_pcm_substream *substream)
{
@@ -238,25 +233,45 @@ static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
.open = rt5514_spi_pcm_open,
.hw_params = rt5514_spi_hw_params,
.hw_free = rt5514_spi_hw_free,
- .trigger = rt5514_spi_trigger,
- .prepare = rt5514_spi_prepare,
.pointer = rt5514_spi_pcm_pointer,
.mmap = snd_pcm_lib_mmap_vmalloc,
.page = snd_pcm_lib_get_vmalloc_page,
};
+static irqreturn_t rt5514_spi_irq(int irq, void *data)
+{
+ struct rt5514_dsp *rt5514_dsp = data;
+
+ schedule_work(&rt5514_dsp->start_work);
+
+ return IRQ_HANDLED;
+}
+
static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
{
struct rt5514_dsp *rt5514_dsp;
+ int ret;
rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
GFP_KERNEL);
rt5514_dsp->dev = &rt5514_spi->dev;
mutex_init(&rt5514_dsp->dma_lock);
+ INIT_WORK(&rt5514_dsp->start_work, rt5514_spi_start_work);
INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
snd_soc_platform_set_drvdata(platform, rt5514_dsp);
+ if (rt5514_spi->irq) {
+ ret = devm_request_threaded_irq(&rt5514_spi->dev,
+ rt5514_spi->irq, NULL, rt5514_spi_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi",
+ rt5514_dsp);
+ if (ret)
+ dev_err(&rt5514_spi->dev,
+ "%s Failed to reguest IRQ: %d\n", __func__,
+ ret);
+ }
+
return 0;
}
@@ -17,10 +17,9 @@
*/
#define RT5514_SPI_BUF_LEN 240
-#define RT5514_BUFFER_VOICE_BASE 0x18001034
-#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
-#define RT5514_BUFFER_VOICE_RP 0x1800103c
-#define RT5514_BUFFER_VOICE_SIZE 0x18001040
+#define RT5514_BUFFER_VOICE_BASE 0x18000200
+#define RT5514_BUFFER_VOICE_LIMIT 0x18000204
+#define RT5514_BUFFER_VOICE_WP 0x1800020c
/* SPI Command */
enum {
The patch uses the IRQ to copy the PCM data to userspace continuously after the hotwording triggered from DSP. Signed-off-by: Oder Chiou <oder_chiou@realtek.com> --- sound/soc/codecs/rt5514-spi.c | 147 +++++++++++++++++++++++------------------- sound/soc/codecs/rt5514-spi.h | 7 +- 2 files changed, 84 insertions(+), 70 deletions(-)