audio: auge: transfer audio data to wake word algorithm [1/3]

PD#TV-3389

Problem:
add vad wake engine in kernel

Solution:
transfer audio data to wake engine

Verify:
x301

Change-Id: I7f44d0141141775bb40f01dbc344a295a72c9d87
Signed-off-by: Xing Wang <xing.wang@amlogic.com>

Conflicts:
	MAINTAINERS
This commit is contained in:
Xing Wang
2019-01-25 22:55:49 +08:00
committed by Dongjin Kim
parent 35d1d8035a
commit 8aa35da1e3
4 changed files with 347 additions and 41 deletions

View File

@@ -14785,6 +14785,13 @@ F: arch/arm/boot/dts/amlogic/mesong12b.dtsi
F: arch/arm64/boot/dts/amlogic/g12b*_a.dts
F: arch/arm64/boot/dts/amlogic/mesong12b.dtsi
AMLOGIC MESONAXG SBR DTS
M: Bing Jiang <Bing.Jiang@amlogic.com>
F: arch/arm64/boot/dts/amlogic/axg_s400_v03sbr.dts
F: arch/arm/boot/dts/amlogic/axg_s400_v03sbr.dts
F: sound/soc/codecs/amlogic/tas5782m.c
F: sound/soc/codecs/amlogic/tas5782m.h
AMLOGIC SM1 DTS
M: Zhiqiang Liang <Zhiqiang.Liang@amlogic.com>
F: arch/arm64/boot/dts/amlogic/mesongsm1.dtsi
@@ -14806,6 +14813,7 @@ M: shaochan.liu <shaochan.liu@amlogic.com>
F: arch/arm64/boot/dts/amlogic/mesonsm1_skt-panel.dtsi
F: arch/arm/boot/dts/amlogic/mesonsm1_skt-panel.dtsi
AMLOGIC TL1 VAD
M: Wenbiao Zhang <wenbiao.zhang@amlogic.com>
F: include/linux/amlogic/vad_api.h

View File

@@ -1213,7 +1213,7 @@
* 0: in user space
* 1: in kernel space
*/
level = <0>;
level = <1>;
status = "disabled";
};

View File

@@ -0,0 +1,24 @@
/*
* include/linux/amlogic/vad_api.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __VAD_API_H__
#define __VAD_API_H__
extern int register_vad_callback(int (*callback)(char *, int, int, int, int));
extern void unregister_vad_callback(void);
#endif /* __VAD_API_H__ */

View File

@@ -32,10 +32,17 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/amlogic/pm.h>
#include <linux/pm_wakeirq.h>
#include <linux/pm_wakeup.h>
#include <linux/input.h>
#include <linux/amlogic/vad_api.h>
#include "vad_hw_coeff.c"
#include "vad_hw.h"
#include "vad.h"
@@ -44,6 +51,12 @@
#define DMA_BUFFER_BYTES_MAX (2 * 1024 * 1024)
#define VAD_READ_FRAME_COUNT (1024 / 2)
/*#define __VAD_DUMP_DATA__*/
#define VAD_DUMP_FILE_NAME "/mnt/vaddump.pcm"
enum vad_level {
LEVEL_USER,
LEVEL_KERNEL,
@@ -52,6 +65,7 @@ enum vad_level {
struct vad {
struct aml_audio_controller *actrl;
struct device *dev;
struct input_dev *input_device;
struct clk *gate;
struct clk *pll;
@@ -59,13 +73,13 @@ struct vad {
struct toddr *tddr;
struct tasklet_struct tasklet;
char *buf;
struct task_struct *thread;
struct snd_dma_buffer dma_buffer;
unsigned int start_last;
unsigned int end_last;
unsigned int addr;
int switch_buffer;
/* vad flag interrupt */
@@ -92,6 +106,15 @@ struct vad {
* 0: check hot word in user space
*/
enum vad_level level;
int (*callback)(char *buf, int frames, int rate,
int channels, int bitdepth);
#ifdef __VAD_DUMP_DATA__
struct file *fp;
mm_segment_t fs;
loff_t pos;
#endif
};
static struct vad *s_vad;
@@ -110,6 +133,34 @@ static struct vad *get_vad(void)
return p_vad;
}
static void vad_key_report(void)
{
struct vad *p_vad = get_vad();
if (!p_vad)
return;
pr_info("%s\n", __func__);
input_event(p_vad->input_device, EV_KEY, KEY_POWER, 1);
input_sync(p_vad->input_device);
input_event(p_vad->input_device, EV_KEY, KEY_POWER, 0);
input_sync(p_vad->input_device);
}
static void vad_input_device_init(struct vad *pvad)
{
pvad->input_device->name = "vad_keypad";
pvad->input_device->phys = "vad_keypad/input3";
pvad->input_device->dev.parent = pvad->dev;
pvad->input_device->id.bustype = BUS_ISA;
pvad->input_device->id.vendor = 0x0001;
pvad->input_device->id.product = 0x0001;
pvad->input_device->id.version = 0x0100;
pvad->input_device->evbit[0] = BIT_MASK(EV_KEY);
pvad->input_device->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
set_bit(KEY_POWER, pvad->input_device->keybit);
}
static bool vad_is_enable(void)
{
struct vad *p_vad = get_vad();
@@ -156,46 +207,200 @@ bool vad_pdm_is_running(void)
return false;
}
static void vad_notify_user_space(struct device *dev)
static void vad_notify_user_space(struct vad *p_vad)
{
pr_info("Notify to wake up user space\n");
pm_wakeup_event(dev, 2000);
pm_wakeup_event(p_vad->dev, 2000);
p_vad->addr = 0;
vad_key_report();
}
static int vad_engine_check(void)
/* For test */
static int vad_in_kernel_test;
static int vad_wakeup_count;
int register_vad_callback(int (*callback)(char *, int, int, int, int))
{
return 1;
struct vad *p_vad = get_vad();
if (!p_vad)
return -1;
p_vad->callback = callback;
return 0;
}
EXPORT_SYMBOL(register_vad_callback);
void unregister_vad_callback(void)
{
struct vad *p_vad = get_vad();
if (!p_vad)
return;
p_vad->callback = NULL;
}
EXPORT_SYMBOL(unregister_vad_callback);
/* transfer audio data to algorithm
* 0: no wake word found
* 1: contained wake word, should wake up system
*/
static int vad_transfer_data_to_algorithm(
struct vad *p_vad, char *buf, int frames,
int rate, int channels, int bitdepth)
{
int ret = 0;
/* TODO: for test */
if (vad_in_kernel_test) {
if (vad_wakeup_count < 50)
return 0;
vad_wakeup_count = 0;
return 1;
}
/* Do check by algorithm */
if (p_vad->callback != NULL)
ret = p_vad->callback(buf, frames, rate, channels, bitdepth);
return ret;
}
/* Check buffer in kernel for VAD */
static void vad_transfer_buffer_output(struct vad *p_vad)
static int vad_engine_check(struct vad *p_vad)
{
unsigned char *hwbuf;
int bytes, read_bytes;
int start, end, size, last_addr, curr_addr;
int chnum, bytes_per_sample;
int frame_count = VAD_READ_FRAME_COUNT;
for (;;) {
if (vad_engine_check()) {
vad_notify_user_space(p_vad->dev);
break;
if (!p_vad->tddr || !p_vad->tddr->actrl ||
!p_vad->tddr->in_use)
return 0;
hwbuf = p_vad->dma_buffer.area;
size = p_vad->dma_buffer.bytes - 8;
start = p_vad->dma_buffer.addr;
end = start + size;
last_addr = p_vad->addr;
curr_addr = aml_toddr_get_position(p_vad->tddr);
if (curr_addr < start || curr_addr > end ||
last_addr < start || last_addr > end) {
pr_info("%s line:%d, start:%x,end:%x, addr:%x, curr_addr=%x\n",
__func__, __LINE__,
start,
end,
p_vad->addr,
curr_addr);
p_vad->addr = curr_addr;
return 0;
}
bytes = (curr_addr - last_addr + size) % size;
chnum = p_vad->tddr->channels;
/* bytes for each sample */
bytes_per_sample = p_vad->tddr->bitdepth >> 3;
read_bytes = frame_count * chnum * bytes_per_sample;
if (bytes < read_bytes) {
pr_debug("%s line:%d, %d bytes, need more data\n",
__func__, __LINE__, bytes);
return 0;
}
if (!p_vad->buf) {
p_vad->buf = kzalloc(read_bytes, GFP_KERNEL);
if (!p_vad->buf) {
pr_err("%s failed to allocate buffer for vad\n",
__func__);
return -ENOMEM;
}
}
memset(p_vad->buf, 0x0, read_bytes);
pr_debug("start:%x,end:%x, curr_addr:%x, last_addr:%x, offset:%d, read_bytes:%d\n",
start,
end,
curr_addr,
last_addr,
bytes,
read_bytes);
if (last_addr + read_bytes <= end) {
memcpy(p_vad->buf,
hwbuf + last_addr - start,
read_bytes);
p_vad->addr = last_addr + read_bytes;
} else {
int tmp_bytes = end - last_addr;
memcpy(p_vad->buf,
hwbuf + last_addr - start,
tmp_bytes);
memcpy(p_vad->buf + tmp_bytes,
hwbuf,
read_bytes - tmp_bytes);
p_vad->addr = start + read_bytes - tmp_bytes;
}
#ifdef __VAD_DUMP_DATA__
vfs_write(p_vad->fp, p_vad->buf, read_bytes, &p_vad->pos);
#endif
return vad_transfer_data_to_algorithm(p_vad,
p_vad->buf, frame_count,
p_vad->tddr->rate,
p_vad->tddr->channels,
p_vad->tddr->bitdepth);
}
static void vad_tasklet(unsigned long data)
static int vad_freeze_thread(void *data)
{
struct vad *p_vad = (struct vad *)data;
struct vad *p_vad = data;
vad_transfer_buffer_output(p_vad);
if (!p_vad)
return 0;
current->flags |= PF_NOFREEZE;
for (;;) {
msleep_interruptible(10);
if (kthread_should_stop()) {
pr_info("%s line:%d\n", __func__, __LINE__);
break;
}
if (!p_vad || !p_vad->en || !p_vad->callback ||
!p_vad->tddr || !p_vad->switch_buffer) {
msleep_interruptible(10);
continue;
}
if (vad_engine_check(p_vad) > 0)
vad_notify_user_space(p_vad);
}
dev_info(p_vad->dev, "vad: freeze thread exit\n");
return 0;
}
static irqreturn_t vad_wakeup_isr(int irq, void *data)
{
struct vad *p_vad = (struct vad *)data;
pr_info("%s\n", __func__);
if (p_vad->level == LEVEL_KERNEL)
tasklet_schedule(&p_vad->tasklet);
if (vad_in_kernel_test) {
pr_info("%s vad_wakeup_count:%d\n", __func__, vad_wakeup_count);
if ((p_vad->level == LEVEL_KERNEL) && p_vad->switch_buffer)
vad_wakeup_count++;
}
return IRQ_HANDLED;
}
@@ -253,8 +458,33 @@ static int vad_init(struct vad *p_vad)
if (p_vad->level == LEVEL_KERNEL) {
flag = IRQF_SHARED | IRQF_NO_SUSPEND;
tasklet_init(&p_vad->tasklet, vad_tasklet,
(unsigned long)p_vad);
/* Start Micro/Audio Dock firmware loader thread */
if (!p_vad->thread) {
p_vad->thread =
kthread_create(vad_freeze_thread, p_vad,
"vad_freeze_thread");
if (IS_ERR(p_vad->thread)) {
int err = PTR_ERR(p_vad->thread);
p_vad->thread = NULL;
dev_info(p_vad->dev,
"vad freeze: Creating thread failed\n");
return err;
}
wake_up_process(p_vad->thread);
vad_wakeup_count = 0;
}
#ifdef __VAD_DUMP_DATA__
p_vad->fp = filp_open(VAD_DUMP_FILE_NAME, O_RDWR | O_CREAT, 0666);
if (IS_ERR(p_vad->fp)) {
pr_err("create file %s error/n", VAD_DUMP_FILE_NAME);
return -1;
}
p_vad->fs = get_fs();
p_vad->pos = 0;
set_fs(KERNEL_DS);
#endif
} else if (p_vad->level == LEVEL_USER)
flag = IRQF_SHARED;
@@ -285,8 +515,20 @@ static int vad_init(struct vad *p_vad)
static void vad_deinit(struct vad *p_vad)
{
if (p_vad->level == LEVEL_KERNEL)
tasklet_kill(&p_vad->tasklet);
if (p_vad->level == LEVEL_KERNEL) {
#ifdef __VAD_DUMP_DATA__
if (p_vad->fp) {
set_fs(p_vad->fs);
filp_close(p_vad->fp, NULL);
}
#endif
if (p_vad->thread) {
kthread_stop(p_vad->thread);
p_vad->thread = NULL;
}
kfree(p_vad->buf);
p_vad->buf = NULL;
}
/* free irq */
free_irq(p_vad->irq_wakeup, p_vad);
@@ -302,13 +544,16 @@ static void vad_deinit(struct vad *p_vad)
void vad_update_buffer(int isvad)
{
struct vad *p_vad = get_vad();
unsigned int start, end, addr;
unsigned int start, end;
unsigned int rd_th;
if (!p_vad || !p_vad->en || !p_vad->tddr)
if (!p_vad || !p_vad->en || !p_vad->tddr ||
!p_vad->tddr->in_use || !p_vad->tddr->actrl)
return;
addr = aml_toddr_get_position(p_vad->tddr);
if (p_vad->switch_buffer == isvad)
return;
p_vad->switch_buffer = isvad;
if (isvad) { /* switch to vad buffer */
struct toddr *tddr = p_vad->tddr;
@@ -319,34 +564,27 @@ void vad_update_buffer(int isvad)
rd_th = 0x100;
pr_debug("Switch to VAD buffer\n");
pr_debug("\t ASAL start:%d, end:%d, bytes:%d, current:%d\n",
pr_debug("\t ASAL start:%x, end:%x, bytes:%d\n",
tddr->start_addr, tddr->end_addr,
tddr->end_addr - tddr->start_addr, addr);
tddr->end_addr - tddr->start_addr);
start = p_vad->dma_buffer.addr;
end = start + p_vad->dma_buffer.bytes - 8;
pr_debug("\t VAD start:%d, end:%d, bytes:%d\n",
pr_debug("\t VAD start:%x, end:%x, bytes:%d\n",
start, end,
end - start);
} else {
pr_debug("Switch to ALSA buffer\n");
//addr = aml_toddr_get_addr(p_vad->tddr, VAD_WAKEUP_ADDR);
addr = aml_toddr_get_position(p_vad->tddr);
start = p_vad->start_last;
end = p_vad->end_last;
rd_th = 0x40;
vad_set_trunk_data_readable(true);
}
p_vad->addr = addr;
aml_toddr_set_buf(p_vad->tddr, start, end);
aml_toddr_force_finish(p_vad->tddr);
aml_toddr_update_fifos_rd_th(p_vad->tddr, rd_th);
p_vad->addr = 0;
}
int vad_transfer_chunk_data(unsigned long data, int frames)
@@ -483,6 +721,9 @@ static int vad_set_enable_enum(
return 0;
}
if (p_vad->en == ucontrol->value.integer.value[0])
return 0;
p_vad->en = ucontrol->value.integer.value[0];
if (p_vad->en) {
@@ -576,14 +817,31 @@ static int vad_set_switch_enum(
return 0;
}
p_vad->switch_buffer = ucontrol->value.integer.value[0];
if (p_vad->en)
vad_update_buffer(p_vad->switch_buffer);
vad_update_buffer(ucontrol->value.integer.value[0]);
return 0;
}
static int vad_test_get_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = vad_in_kernel_test;
return 0;
}
static int vad_test_set_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
vad_in_kernel_test = ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_kcontrol_new vad_controls[] = {
SOC_SINGLE_BOOL_EXT("VAD enable",
0,
@@ -600,6 +858,10 @@ static const struct snd_kcontrol_new vad_controls[] = {
vad_get_switch_enum,
vad_set_switch_enum),
SOC_SINGLE_BOOL_EXT("VAD Test",
0,
vad_test_get_enum,
vad_test_set_enum),
};
int card_add_vad_kcontrols(struct snd_soc_card *card)
@@ -722,6 +984,18 @@ static int vad_platform_probe(struct platform_device *pdev)
p_vad->src,
p_vad->level);
p_vad->input_device = input_allocate_device();
if (!p_vad->input_device) {
kfree(p_vad);
return -ENOMEM;
}
vad_input_device_init(p_vad);
if (input_register_device(p_vad->input_device)) {
input_free_device(p_vad->input_device);
kfree(p_vad);
return -EINVAL;
}
s_vad = p_vad;
device_init_wakeup(dev, 1);