audio: add usb karaoke driver [1/1]

PD#OTT-1624

Problem:
doesn't support Karaoke

Solution:
1) add usb karaoke driver
2) Default karaoke is disable. if enable it,
   add "CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA=y" in defconfig file.

Verify:
p212

Change-Id: I9f00873da930f9d5924fc752133ed51a4ae93636
Signed-off-by: fahui.feng <fahui.feng@amlogic.com>
Signed-off-by: Zhe Wang <Zhe.Wang@amlogic.com>
This commit is contained in:
fahui.feng
2019-04-26 10:14:59 +08:00
committed by Luke Go
parent 908e9b17f3
commit 487502b4bc
26 changed files with 2353 additions and 8 deletions

View File

@@ -13821,9 +13821,10 @@ F: drivers/amlogic/clk/clk_misc.c
F: drivers/amlogic/clk/clkc.h
F: drivers/amlogic/clk/gxl.c
F: drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c
F: drivers/amlogic/pinctrl/pinctrl_gxl.c
F: include/dt-bindings/clock/amlogic,gxl-clkc.h
F: include/linux/amlogic/media/sound/audin_regs.h
F: drivers/amlogic/pinctrl/*
F: drivers/amlogic/*
F: include/dt-bindings/clock/*
F: include/linux/amlogic/media/sound/*
F: sound/soc/Kconfig
F: sound/soc/Makefile
F: sound/soc/amlogic/auge/*

View File

@@ -944,6 +944,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -944,6 +944,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -1001,6 +1001,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -1001,6 +1001,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -940,6 +940,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -942,6 +942,15 @@
sound-dai = <&pcm_codec>;
};
};
amlkaraoke {
compatible = "amlogic, aml_karaoke";
dev_name = "aml_karaoke";
status = "okay";
interrupts = <0 48 1>;
interrupt-names = "aml_karaoke";
};
/* END OF AUDIO board specific */
rdma{
compatible = "amlogic, meson, rdma";

View File

@@ -84,6 +84,8 @@ source "drivers/amlogic/amaudio/Kconfig"
source "drivers/amlogic/amaudio2/Kconfig"
source "drivers/amlogic/amlkaraoke/Kconfig"
source "drivers/amlogic/audiodsp/Kconfig"
source "drivers/amlogic/audioinfo/Kconfig"

View File

@@ -90,6 +90,8 @@ obj-$(CONFIG_AMLOGIC_AMAUDIO2) += amaudio2/
obj-$(CONFIG_AMLOGIC_AUDIO_INFO) += audioinfo/
obj-$(CONFIG_AMLKARAOKE) += amlkaraoke/
obj-$(CONFIG_AMLOGIC_SUSPEND) += pm/
obj-$(CONFIG_AMLOGIC_LED) += led/

View File

@@ -0,0 +1,21 @@
# AML Karaoke control drivers
menuconfig AMLKARAOKE
bool "Amlogic karaoke Interface V1"
default y
help
Amlogic karaoke Interface V1, usb capture audio data, mix with i2s out directly.
---
---
if AMLKARAOKE
config AMLOGIC_SND_USB_CAPTURE_DATA
tristate "USB Capture Audio Data In, for aml karaoke"
depends on AMLOGIC_SND_SOC_MESON
help
capture usb audio data into a ring buffer. The ring buffer data would be mixed with i2s out data.
Say 'Y' for aml karaoker driver.
endif

View File

@@ -0,0 +1,17 @@
#
# Makefile for sound control interface
#
# Toplevel Module Dependency
#AML usb audio in to i2s out mixed
# AML USB capture
snd-usb-capture-objs := aml_usb_capture.o aml_audio_resampler.o aml_reverb.o
obj-$(CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA) += snd-usb-capture.o
aml-i2s-out-mix-objs := aml_i2s_out_mix.o
obj-$(CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA) += aml-i2s-out-mix.o
amlogic_karaoke-objs := aml_karaoke.o
obj-$(CONFIG_AMLKARAOKE) += amlogic_karaoke.o

View File

@@ -0,0 +1,116 @@
/*
* drivers/amlogic/amlkaraoke/aml_audio_resampler.c
*
* 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.
*
*/
#include <trace/events/printk.h>
#include "aml_audio_resampler.h"
#include <linux/types.h>
/*Clip from 16.16 fixed-point to 0.15 fixed-point*/
static inline short clip(int x)
{
if (x < -32768)
return -32768;
else if (x > 32767)
return 32767;
else
return x;
}
int resampler_init(struct resample_para *resample)
{
/* 64bit:long long */
static const long int k_phase_multiplier = 1L << 28;
resample->fraction_step = (unsigned int)
(resample->input_sr * k_phase_multiplier
/ resample->output_sr);
resample->sample_fraction = 0;
resample->lastsample_left = 0;
resample->lastsample_right = 0;
return 0;
}
int resample_process(
struct resample_para *resample,
unsigned int in_frame,
short *input, short *output) {
unsigned int input_index = 0;
unsigned int output_index = 0;
unsigned int fraction_step = resample->fraction_step;
static const unsigned int k_phase_mask = (1LU << 28) - 1;
unsigned int frac = resample->sample_fraction;
short lastsample_left = resample->lastsample_left;
short lastsample_right = resample->lastsample_right;
if (resample->channels == 2) {
while (input_index == 0) {
*output++ = clip((int)lastsample_left +
((((int)input[0] - (int)lastsample_left)
* ((int)frac >> 13)) >> 15));
*output++ = clip((int)lastsample_right +
((((int)input[1] - (int)lastsample_right)
* ((int)frac >> 13)) >> 15));
frac += fraction_step;
input_index += (frac >> 28);
frac = (frac & k_phase_mask);
output_index++;
}
while (input_index < in_frame) {
*output++ = clip((int)input[2 * input_index - 2]
+ ((((int)input[2 * input_index]
- (int)input[2 * input_index - 2])
* ((int)frac >> 13)) >> 15));
*output++ = clip((int)input[2 * input_index - 1]
+ ((((int)input[2 * input_index + 1]
- (int)input[2 * input_index - 1])
* ((int)frac >> 13)) >> 15));
frac += fraction_step;
input_index += (frac >> 28);
frac = (frac & k_phase_mask);
output_index++;
}
resample->lastsample_left = input[2 * in_frame - 2];
resample->lastsample_right = input[2 * in_frame - 1];
resample->sample_fraction = frac;
} else {
/*left channel as output*/
while (input_index == 0) {
*output++ = clip((int)lastsample_left +
((((int)input[0] - (int)lastsample_left)
* ((int)frac >> 13)) >> 15));
frac += fraction_step;
input_index += (frac >> 28);
frac = (frac & k_phase_mask);
output_index++;
}
while (input_index < in_frame) {
*output++ = clip((int)input[2 * input_index - 2]
+ ((((int)input[2 * input_index]
- (int)input[2 * input_index - 2])
* ((int)frac >> 13)) >> 15));
frac += fraction_step;
input_index += (frac >> 28);
frac = (frac & k_phase_mask);
output_index++;
}
resample->lastsample_left = input[2 * in_frame - 2];
resample->sample_fraction = frac;
}
return output_index;
}

View File

@@ -0,0 +1,37 @@
/*
* drivers/amlogic/amlkaraoke/aml_audio_resampler.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 __AUDIO_RESAMPLER_H__
#define __AUDIO_RESAMPLER_H__
struct resample_para {
unsigned int fraction_step;
unsigned int sample_fraction;
short lastsample_left;
short lastsample_right;
unsigned int input_sr;
unsigned int output_sr;
unsigned int channels;
};
int resampler_init(struct resample_para *resample);
int resample_process(
struct resample_para *resample,
unsigned int in_frame,
short *input, short *output);
#endif

View File

@@ -0,0 +1,677 @@
/*
* drivers/amlogic/amlkaraoke/aml_i2s_out_mix.c
*
* 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.
*
*/
/*
* A virtual path to get usb audio capture data to i2s mixed out.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/media/sound/aiu_regs.h>
#include <linux/amlogic/media/sound/usb_karaoke.h>
#include "aml_i2s_out_mix.h"
#define BASE_IRQ (32)
#define AM_IRQ(reg) ((reg) + BASE_IRQ)
#define INT_I2S_DDR AM_IRQ(48)
#define IRQ_OUT INT_I2S_DDR
#define I2S_INT_NUM (16) /* min 2, max 32 */
#define I2S_BLOCK_SIZE (64) /* block_size=32byte*channel_num, normal is 2*/
#define I2S_INT_BLOCK ((I2S_INT_NUM) * (I2S_BLOCK_SIZE))
#define MIN_LATENCY (64 * 32)
static int i2s_block_size;
static int speaker_channel_mask = 1;
struct i2s_info {
/* hw info */
unsigned int channels;
unsigned int format;
/* for mixer */
unsigned int int_num;
unsigned int i2s_block;
unsigned int int_block;
unsigned int latency;
};
/*Ultimate output from i2s */
struct i2s_output {
/* audio info */
i2s_audio_buffer i2s_buf;
/* work queue */
struct work_struct work;
/* is irq registered ? */
bool isInit;
/*i2s info for mixer*/
struct i2s_info i2sinfo;
};
typedef int (*mixer_f)(char *dst, char *src, unsigned int count,
unsigned int channels, unsigned int format);
static struct i2s_output s_i2s_output;
static inline s16 clip16(int x)
{
if (x < -32768)
return -32768;
else if (x > 32767)
return 32767;
return (s16)x;
}
static inline s32 clip32(long x)
{
if (x < -2147483647)
return -2147483647; //default:-2147483648
else if (x > 2147483647)
return 2147483647;
return (s32)x;
}
/* Average weight to mix */
static inline s16 aweight_mix16(s16 s1, s16 s2)
{
return clip16(((s32)s1 + (s32)s2) >> 1);
}
static inline s32 aweight_mix32(s32 s1, s32 s2)
{
return clip32(((int64_t)s1 + (int64_t)s2) >> 1);
}
/* Can expand to support more mixer method */
static inline s16 mix16(s16 s1, s16 s2)
{
return aweight_mix16(s1, s2);
}
static inline s32 mix32(s32 s1, s32 s2)
{
return aweight_mix32(s1, s2);
}
/* Get usb audio info. */
struct usb_audio_buffer *usb_get_audio_info(void)
{
return (struct usb_audio_buffer *)snd_usb_pcm_capture_buffer;
}
/* Get i2s audio info. */
i2s_audio_buffer *i2s_get_audio_info(void)
{
return (i2s_audio_buffer *)&s_i2s_output.i2s_buf;
}
/* Get i2s output. */
static struct i2s_output *i2s_get_output(void)
{
return (struct i2s_output *)&s_i2s_output;
}
/* i2s get memory size */
static unsigned int i2s_get_out_size(void)
{
return aml_read_cbus(AIU_MEM_I2S_END_PTR) -
aml_read_cbus(AIU_MEM_I2S_START_PTR) + i2s_block_size;
}
/* i2s get read pointer */
static unsigned int i2s_get_out_read_ptr(void)
{
return aml_read_cbus(AIU_MEM_I2S_RD_PTR) -
aml_read_cbus(AIU_MEM_I2S_START_PTR);
}
static int mixer_transfer(
char *dst, char *src,
unsigned int count,
unsigned int i2s_channels,
unsigned int i2s_format)
{
int i;
#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
int i2s_frame, i2s_frames_count;
/* bytes in one frame */
i2s_frame = i2s_channels * (i2s_format / 8);
/* frames in on block */
i2s_frames_count = count / i2s_frame;
if (i2s_channels == 2) {
if (i2s_format == 16) {
s16 *to = (s16 *)dst;
s16 *tfrom = (s16 *)src;
for (i = 0; i < i2s_frames_count * i2s_channels;
i++, to++)
*to = mix16(*to, *tfrom++);
} else if (i2s_format == 32 ||
i2s_format == 24) {
s32 *to = (s32 *)dst;
s16 *tfrom = (s16 *)src;
for (i = 0; i < i2s_frames_count * i2s_channels;
i++, to++)
*to = mix32(*to, (s32)(*tfrom++) << 16);
} else {
pr_err("Error: Unsupport format:%d\n", i2s_format);
}
} else if (i2s_channels == 8) {
if (i2s_format == 16) {
s16 *to = (s16 *)dst;
s16 *tfrom = (s16 *)src;
s16 *lf, *cf, *rf, *ls, *rs, *lef, *sbl, *sbr;
/* yet only cpu txhd support split 8ch, 16bit */
lf = to + 0;
cf = to + 1;
rf = to + 2;
ls = to + 3;
rs = to + 4;
lef = to + 5;
sbl = to + 6;
sbr = to + 7;
for (i = 0; i < i2s_frames_count; i++) {
if (speaker_channel_mask == 0) {
(*lf) = mix16(*lf, (*tfrom++));
(*cf) = mix16(*cf, (*tfrom++));
}
if (speaker_channel_mask == 1) {
(*rf) = mix16(*rf, (*tfrom++));
(*ls) = mix16(*ls, (*tfrom++));
}
if (speaker_channel_mask == 2) {
(*rs) = mix16(*rs, (*tfrom++));
(*lef) = mix16(*lef, (*tfrom++));
}
if (speaker_channel_mask == 3) {
(*sbl) = mix16(*sbl, (*tfrom++));
(*sbr) = mix16(*sbr, (*tfrom++));
}
lf += 8;
cf += 8;
rf += 8;
ls += 8;
rs += 8;
lef += 8;
sbl += 8;
sbr += 8;
}
} else if (i2s_format == 32 ||
i2s_format == 24) {
s32 *to = (s32 *)dst;
s16 *tfrom = (s16 *)src;
s32 *lf, *cf, *rf, *ls, *rs, *lef, *sbl, *sbr;
lf = to + 0;
cf = to + 1;
rf = to + 2;
ls = to + 3;
rs = to + 4;
lef = to + 5;
sbl = to + 6;
sbr = to + 7;
for (i = 0; i < i2s_frames_count; i++) {
if (speaker_channel_mask == 0) {
(*lf) = mix32(*lf, (*tfrom++) << 16);
(*cf) = mix32(*cf, (*tfrom++) << 16);
}
if (speaker_channel_mask == 1) {
(*rf) = mix32(*rf, (*tfrom++) << 16);
(*ls) = mix32(*ls, (*tfrom++) << 16);
}
if (speaker_channel_mask == 2) {
(*rs) = mix32(*rs, (*tfrom++) << 16);
(*lef) = mix32(*lef, (*tfrom++) << 16);
}
if (speaker_channel_mask == 3) {
(*sbl) = mix32(*sbl, (*tfrom++) << 16);
(*sbr) = mix32(*sbr, (*tfrom++) << 16);
}
lf += 8;
cf += 8;
rf += 8;
ls += 8;
rs += 8;
lef += 8;
sbl += 8;
sbr += 8;
}
} else {
pr_err("Error: Unsupport format:%d\n", i2s_format);
}
} else {
pr_err("Error: Unsupport channels:%d\n", i2s_channels);
}
#else
int j;
if (i2s_channels == 2) {
if (i2s_format == 16) {
s16 *to = (s16 *)(dst);
s16 *tfrom = (s16 *)(src);
s16 *lf, *rf;
lf = to;
rf = lf + 16;
for (i = 0; i < count; i += 64) {
for (j = 0; j < 16; j++) {
(*lf++) = mix16(*lf, *tfrom++);
(*rf++) = mix16(*rf, *tfrom++);
}
lf += 16;
rf += 16;
}
} else if (i2s_format == 32 ||
i2s_format == 24) {
s32 *to = (s32 *)dst;
s16 *tfrom = (s16 *)src;
s32 *lf, *rf;
s32 sample;
lf = to;
rf = to + 8;
for (i = 0; i < count; i += 64) {
for (j = 0; j < 8; j++) {
(*lf++) = mix32(
*lf, ((s32)(*tfrom++)) << 8);
(*rf++) = mix32(
*rf, ((s32)(*tfrom++)) << 8);
}
lf += 8;
rf += 8;
}
} else {
pr_err("Error: Unsupport format:%d\n", i2s_format);
}
} else if (i2s_channels == 8) {
if (i2s_format == 16) {
s16 *to = (s16 *)(dst);
s16 *tfrom = (s16 *)(src);
s16 *lf, *cf, *rf, *ls, *rs, *lef, *sbl, *sbr;
lf = to + 0 * 16;
cf = to + 1 * 16;
rf = to + 2 * 16;
ls = to + 3 * 16;
rs = to + 4 * 16;
lef = to + 5 * 16;
sbl = to + 6 * 16;
sbr = to + 7 * 16;
for (j = 0; j < count; j += 256) {
for (i = 0; i < 16; i++) {
if (speaker_channel_mask == 0) {
(*lf++) = mix16(
*lf, (*tfrom++));
(*cf++) = mix16(
*cf, (*tfrom++));
} else {
lf++;
cf++;
}
if (speaker_channel_mask == 1) {
(*rf++) = mix16(
*rf, (*tfrom++));
(*ls++) = mix16(
*ls, (*tfrom++));
} else {
rf++;
ls++;
}
if (speaker_channel_mask == 2) {
(*rs++) = mix16(
*rs, (*tfrom++));
(*lef++) = mix16(
*lef, (*tfrom++));
} else {
rs++;
lef++;
}
if (speaker_channel_mask == 3) {
(*sbl++) = mix16(
*sbl, (*tfrom++));
(*sbr++) = mix16(
*sbr, (*tfrom++));
} else {
sbl++;
sbr++;
}
}
lf += 7 * 16;
cf += 7 * 16;
rf += 7 * 16;
ls += 7 * 16;
rs += 7 * 16;
lef += 7 * 16;
sbl += 7 * 16;
sbr += 7 * 16;
}
} else if (i2s_format == 32 ||
i2s_format == 24) {
s32 *to = (s32 *)(dst);
s16 *tfrom = (s16 *)(src);
s32 *lf, *cf, *rf, *ls, *rs, *lef, *sbl, *sbr;
lf = to + 0 * 8;
cf = to + 1 * 8;
rf = to + 2 * 8;
ls = to + 3 * 8;
rs = to + 4 * 8;
lef = to + 5 * 8;
sbl = to + 6 * 8;
sbr = to + 7 * 8;
for (j = 0; j < count; j += 256) {
for (i = 0; i < 8; i++) {
if (speaker_channel_mask == 0) {
(*lf++) = mix32(
*lf,
((s32)(*tfrom++)) << 8);
(*cf++) = mix32(
*cf,
((s32)(*tfrom++)) << 8);
} else {
lf++;
cf++;
}
if (speaker_channel_mask == 1) {
(*rf++) = mix32(
*rf,
((s32)(*tfrom++)) << 8);
(*ls++) = mix32(
*ls,
((s32)(*tfrom++)) << 8);
} else {
rf++;
ls++;
}
if (speaker_channel_mask == 2) {
(*rs++) = mix32(
*rs,
((s32)(*tfrom++)) << 8);
(*lef++) = mix32(
*lef,
((s32)(*tfrom++)) << 8);
} else {
rs++;
lef++;
}
if (speaker_channel_mask == 3) {
(*sbl++) = mix32(
*sbl,
((s32)(*tfrom++)) << 8);
(*sbr++) = mix32(
*sbr,
((s32)(*tfrom++)) << 8);
} else {
sbl++;
sbr++;
}
}
lf += 7 * 8;
cf += 7 * 8;
rf += 7 * 8;
ls += 7 * 8;
rs += 7 * 8;
lef += 7 * 8;
sbl += 7 * 8;
sbr += 7 * 8;
}
} else {
pr_err("Error: Unsupport format:%d\n", i2s_format);
}
} else {
pr_err("Error: Unsupport channels:%d\n", i2s_channels);
}
#endif
return 0;
}
/*mix i2s memory audio data with usb audio record in,output stereo to i2s*/
static void i2s_out_mix(
i2s_audio_buffer *i2s_audio,
struct usb_audio_buffer *usb_audio,
struct i2s_info *p_i2sinfo,
mixer_f mixer)
{
i2s_audio_buffer *i2sbuf = i2s_audio;
struct usb_audio_buffer *usbbuf = usb_audio;
unsigned int i2s_out_ptr = i2s_get_out_read_ptr();
unsigned int alsa_delay = (aml_i2s_alsa_write_addr +
i2sbuf->size - i2s_out_ptr) % i2sbuf->size;
unsigned int i2s_mix_delay = (i2sbuf->wr +
i2sbuf->size - i2s_out_ptr) % i2sbuf->size;
unsigned long i2sirqflags, usbirqflags;
unsigned int mix_count = 0, mix_usb_count;
int avail = 0;
int i2s_frame, usb_frame, i2s_frames_count, usb_frames_count;
i2s_frame = p_i2sinfo->channels * (p_i2sinfo->format / 8);
usb_frame = 4; /* usb in: 2ch, 16bit */
spin_lock_irqsave(&i2sbuf->lock, i2sirqflags);
i2sbuf->rd = i2s_out_ptr;
/*(i2sbuf->size + i2sbuf->wr - i2s_out_ptr) % i2sbuf->size;*/
i2sbuf->level = i2s_mix_delay;
mix_count = p_i2sinfo->int_block;
i2s_frames_count = mix_count / i2s_frame;
/*update*/
usb_frames_count = i2s_frames_count;
mix_usb_count = usb_frames_count * usb_frame;
if (i2sbuf->level <= p_i2sinfo->int_block ||
(alsa_delay - i2s_mix_delay) < p_i2sinfo->int_block) {
i2sbuf->wr = (i2sbuf->rd + p_i2sinfo->latency) % i2sbuf->size;
i2sbuf->wr /= p_i2sinfo->int_block;
i2sbuf->wr *= p_i2sinfo->int_block;
i2sbuf->level = p_i2sinfo->latency;
goto EXIT;
}
if (i2sbuf->wr % p_i2sinfo->int_block) {
i2sbuf->wr /= p_i2sinfo->int_block;
i2sbuf->wr *= p_i2sinfo->int_block;
}
if (usbbuf->wr >= usbbuf->rd)
avail = usbbuf->wr - usbbuf->rd;
else
avail = usbbuf->wr + usbbuf->size - usbbuf->rd;
if (avail < mix_usb_count) {
/*
* pr_info("i2sOUT buffer underrun\n");
* goto EXIT;
*/
/*fill zero data*/
memset(usbbuf->addr + (usbbuf->rd + avail) % usbbuf->size,
0,
mix_usb_count - avail);
}
spin_lock_irqsave(&usbbuf->lock, usbirqflags);
mixer(i2sbuf->addr + i2sbuf->wr,
usbbuf->addr + usbbuf->rd,
mix_count,
p_i2sinfo->channels,
p_i2sinfo->format);
i2sbuf->wr = (i2sbuf->wr + mix_count) % i2sbuf->size;
i2sbuf->level = (i2sbuf->size + i2sbuf->wr - i2sbuf->rd) % i2sbuf->size;
usbbuf->rd = (usbbuf->rd + mix_usb_count) % usbbuf->size;
spin_unlock_irqrestore(&usbbuf->lock, usbirqflags);
EXIT:
spin_unlock_irqrestore(&i2sbuf->lock, i2sirqflags);
}
/* IRQ handler */
static irqreturn_t i2s_out_mix_callback(int irq, void *data)
{
struct i2s_output *p_i2s_out = (struct i2s_output *)data;
i2s_audio_buffer *i2s_buf = (i2s_audio_buffer *)&p_i2s_out->i2s_buf;
unsigned int i2s_size = i2s_get_out_size();
struct usb_audio_buffer *usbbuf =
(struct usb_audio_buffer *)usb_get_audio_info();
//struct i2s_info *p_i2sinfo = &p_i2s_out->i2sinfo;
/*check whether usb audio record start*/
if (!usbbuf || !usbbuf->addr || !usbbuf->running)
return IRQ_HANDLED;
/*update i2s buffer informaiton if needed.*/
if (i2s_size != i2s_buf->size) {
i2s_buf->size = i2s_size;
i2s_buf->addr = (unsigned char *)aml_i2s_playback_start_addr;
i2s_buf->paddr = aml_i2s_playback_phy_start_addr;
i2s_buf->rd = i2s_get_out_read_ptr();
}
schedule_work(&p_i2s_out->work);
//i2s_out_mix(i2s_buf, usbbuf, p_i2sinfo, mixer_transfer);
return IRQ_HANDLED;
}
/* Work Queue handler */
static void i2s_out_mix_work_handler(struct work_struct *data)
{
struct i2s_output *p_i2s_out = i2s_get_output();
i2s_audio_buffer *i2sbuf =
(i2s_audio_buffer *)i2s_get_audio_info();
struct usb_audio_buffer *usbbuf =
(struct usb_audio_buffer *)usb_get_audio_info();
struct i2s_info *p_i2sinfo = &p_i2s_out->i2sinfo;
if (!i2sbuf || !usbbuf || !p_i2sinfo->channels)
return;
/* mixer: usb info, 2 channels, 16 bits;
* i2s info, 2/8 channels, 16/32 bits
* case 1: 2 channel mixer with 2 channel i2s
* case 2: 2 channel mixer with 8 channel i2s,
* to special channels according to speaker_channel_mask
*/
i2s_out_mix(i2sbuf, usbbuf, p_i2sinfo, mixer_transfer);
}
int i2s_out_mix_init(void)
{
int ret = 0;
if (!builtin_mixer) {
pr_info("Not to mix usb in and i2s out\n");
return 0;
}
memset((void *)&s_i2s_output, 0, sizeof(struct i2s_output));
/* init i2s audio buffer */
spin_lock_init(&s_i2s_output.i2s_buf.lock);
s_i2s_output.i2s_buf.addr =
(unsigned char *)aml_i2s_playback_start_addr;
s_i2s_output.i2s_buf.paddr =
aml_i2s_playback_phy_start_addr;
s_i2s_output.i2s_buf.size =
i2s_get_out_size();
s_i2s_output.i2s_buf.rd =
i2s_get_out_read_ptr();
/*defalut for 2ch, 16bit,*/
i2s_block_size = I2S_BLOCK_SIZE;
s_i2s_output.i2sinfo.int_num = I2S_INT_NUM;
s_i2s_output.i2sinfo.i2s_block = i2s_block_size;
s_i2s_output.i2sinfo.int_block = I2S_INT_BLOCK;
s_i2s_output.i2sinfo.latency = MIN_LATENCY * 2;
s_i2s_output.i2sinfo.channels = aml_i2s_playback_channel;
s_i2s_output.i2sinfo.format = aml_i2s_playback_format;
if (s_i2s_output.i2sinfo.channels == 8) {
i2s_block_size *= 4;
s_i2s_output.i2sinfo.int_num *= 2;
s_i2s_output.i2sinfo.i2s_block = i2s_block_size;
s_i2s_output.i2sinfo.int_block = s_i2s_output.i2sinfo.int_num
* s_i2s_output.i2sinfo.i2s_block;
s_i2s_output.i2sinfo.latency *= 8;
}
pr_info("%s:%d, channels:%d, format:%d, i2s_block_size:%d\n",
__func__, __LINE__,
s_i2s_output.i2sinfo.channels,
s_i2s_output.i2sinfo.format,
i2s_block_size);
if (!s_i2s_output.isInit) {
s_i2s_output.isInit = true;
/*register irq*/
if (request_irq(
irq_karaoke, i2s_out_mix_callback,
IRQF_SHARED, "i2s_out_mix",
&s_i2s_output)) {
ret = -EINVAL;
}
pr_info("register irq\n");
}
/*irq block*/
#ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE
/* TODO: split mode aiu_mem_i2s_mask[15:0] must set 8'hffff_ffff. */
aml_cbus_update_bits(AIU_MEM_I2S_MASKS, 0xffff << 16, 4 << 16);
#else
aml_cbus_update_bits(
AIU_MEM_I2S_MASKS,
0xffff << 16,
s_i2s_output.i2sinfo.int_num << 16);
#endif
/*work queue*/
INIT_WORK(&s_i2s_output.work, i2s_out_mix_work_handler);
return ret;
}
EXPORT_SYMBOL(i2s_out_mix_init);
/* Deinit */
int i2s_out_mix_deinit(void)
{
if (!s_i2s_output.isInit)
return -1;
free_irq(IRQ_OUT, &s_i2s_output);
memset((void *)&s_i2s_output, 0, sizeof(struct i2s_output));
return 0;
}
EXPORT_SYMBOL(i2s_out_mix_deinit);

View File

@@ -0,0 +1,52 @@
/*
* drivers/amlogic/amlkaraoke/aml_i2s_out_mix.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 __AML_I2S_OUT_MIX_H
#define __AML_I2S_OUT_MIX_H
/*
* A virtual path to get usb audio capture data to i2s mixed out.
*
*/
extern unsigned long aml_i2s_playback_start_addr;
extern unsigned long aml_i2s_playback_phy_start_addr;
extern unsigned long aml_i2s_alsa_write_addr;
extern int builtin_mixer;
extern struct usb_audio_buffer *snd_usb_pcm_capture_buffer;
extern unsigned int aml_i2s_playback_channel;
extern unsigned int aml_i2s_playback_format;
extern int irq_karaoke;
/*Keep same struct with usb_capture.h */
typedef
struct usb_audio_buffer {
dma_addr_t paddr;
unsigned char *addr;
unsigned int size;
unsigned int wr;
unsigned int rd;
unsigned int level;
unsigned int channels;
unsigned int rate; /* rate in Hz */
unsigned int running;
spinlock_t lock; /* lock the ringbuffer */
} i2s_audio_buffer;
#endif

View File

@@ -0,0 +1,305 @@
/*
* drivers/amlogic/amlkaraoke/aml_karaoke.c
*
* 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.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/amlogic/major.h>
#include "aml_karaoke.h"
int builtin_mixer = 1;
EXPORT_SYMBOL(builtin_mixer);
static ssize_t show_builtin_mixer(struct class *class,
struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", builtin_mixer);
}
static ssize_t store_builtin_mixer(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
if (val < 0)
val = 0;
builtin_mixer = val;
pr_info("builtin_mixer set to %d\n", builtin_mixer);
return count;
}
int reverb_time;
EXPORT_SYMBOL(reverb_time);
static ssize_t show_reverb_time(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reverb_time);
}
static ssize_t store_reverb_time(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
if (val < 0)
val = 0;
if (val > 6)
val = 6;
reverb_time = val;
pr_info("reverb_time set to %d\n", reverb_time);
return count;
}
int usb_mic_digital_gain = 256;
EXPORT_SYMBOL(usb_mic_digital_gain);
static ssize_t show_usb_mic_digital_gain(struct class *class,
struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", usb_mic_digital_gain);
}
static ssize_t store_usb_mic_digital_gain(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
if (val < 0)
val = 0;
if (val > 256)
val = 256;
usb_mic_digital_gain = val;
pr_info("usb_mic_digital_gain set to %d\n", usb_mic_digital_gain);
return count;
}
int reverb_enable;
EXPORT_SYMBOL(reverb_enable);
static ssize_t show_reverb_enable(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reverb_enable);
}
static ssize_t store_reverb_enable(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
reverb_enable = val;
pr_info("reverb_enable set to %d\n", reverb_enable);
return count;
}
int reverb_highpass;
EXPORT_SYMBOL(reverb_highpass);
static ssize_t show_reverb_highpass(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reverb_highpass);
}
static ssize_t store_reverb_highpass(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
reverb_highpass = val;
pr_info("reverb_highpass set to %d\n", reverb_highpass);
return count;
}
/* reverb in gain [0%, 100%]*/
int reverb_in_gain = 100;
EXPORT_SYMBOL(reverb_in_gain);
static ssize_t show_reverb_in_gain(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reverb_in_gain);
}
static ssize_t store_reverb_in_gain(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
if (val < 0)
val = 0;
if (val > 100)
val = 100;
reverb_in_gain = val;
pr_info("reverb_in_gain set to %d\n", reverb_in_gain);
return count;
}
/* reverb out gain [0%, 100%]*/
int reverb_out_gain = 100;
EXPORT_SYMBOL(reverb_out_gain);
static ssize_t show_reverb_out_gain(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", reverb_out_gain);
}
static ssize_t store_reverb_out_gain(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (buf[0] && kstrtoint(buf, 10, &val))
return -EINVAL;
if (val < 0)
val = 0;
if (val > 100)
val = 100;
reverb_out_gain = val;
pr_info("reverb_out_gain set to %d\n", reverb_out_gain);
return count;
}
static struct class_attribute amlkaraoke_attrs[] = {
__ATTR(builtin_mixer, 0664,
show_builtin_mixer, store_builtin_mixer),
__ATTR(reverb_time, 0644,
show_reverb_time, store_reverb_time),
__ATTR(usb_mic_digital_gain, 0644,
show_usb_mic_digital_gain, store_usb_mic_digital_gain),
__ATTR(reverb_enable, 0644,
show_reverb_enable, store_reverb_enable),
__ATTR(reverb_highpass, 0644,
show_reverb_highpass, store_reverb_highpass),
__ATTR(reverb_in_gain, 0644,
show_reverb_in_gain, store_reverb_in_gain),
__ATTR(reverb_out_gain, 0644,
show_reverb_out_gain, store_reverb_out_gain),
__ATTR_NULL,
};
static struct class amlkaraoke_class = {
.name = AMAUDIO_CLASS_NAME,
.class_attrs = amlkaraoke_attrs,
};
int irq_karaoke;
static int amlkaraoke_probe(struct platform_device *pdev)
{
int int_karaoke = platform_get_irq_byname(pdev, "aml_karaoke");
pr_info("%s irq %d num", __func__, int_karaoke);
irq_karaoke = int_karaoke;
return 0;
}
static int amlkaraoke_init(void)
{
int ret = class_register(&amlkaraoke_class);
if (ret) {
pr_err("amlkaraoke class create fail.\n");
goto err;
}
pr_info("amlkaraoke init success!\n");
err:
return ret;
}
static int amlkaraoke_exit(void)
{
class_unregister(&amlkaraoke_class);
pr_info("amlkaraoke_exit!\n");
return 0;
}
static const struct of_device_id amlogic_match[] = {
{.compatible = "amlogic, aml_karaoke",},
{}
};
static struct platform_driver aml_karaoke_driver = {
.driver = {
.name = "aml_karaoke_driver",
.owner = THIS_MODULE,
.of_match_table = amlogic_match,
},
.probe = amlkaraoke_probe,
.remove = NULL,
};
static int __init aml_karaoke_modinit(void)
{
amlkaraoke_init();
return platform_driver_register(&aml_karaoke_driver);
}
static void __exit aml_karaoke_modexit(void)
{
amlkaraoke_exit();
platform_driver_unregister(&aml_karaoke_driver);
}
module_init(aml_karaoke_modinit);
module_exit(aml_karaoke_modexit);
MODULE_DESCRIPTION("AMLOGIC Karaoke Interface driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Amlogic Inc.");
MODULE_VERSION("1.0.0");

View File

@@ -0,0 +1,30 @@
/*
* drivers/amlogic/amlkaraoke/aml_karaoke.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 _AML_KARAOKE_H_
#define _AML_KARAOKE_H_
/*
* A virtual path to get usb audio capture data to i2s mixed out.
*/
#define AMLKARAOKE_DRIVER_NAME "amlkaraoke"
#define AMLKARAOKE_DRIVER_NAME "amlkaraoke"
#define AMLKARAOKE_DEVICE_NAME "amlkaraoke-dev"
#define AMAUDIO_CLASS_NAME "amlkaraoke"
#endif

View File

@@ -0,0 +1,235 @@
/*
* drivers/amlogic/amlkaraoke/aml_reverb.c
*
* 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.
*
*/
#include <linux/init.h>
#include <linux/slab.h>
#include "aml_reverb.h"
struct af_equalizer filer;
/* Compute a realistic decay */
static int decay_table[][8] = {
{0, 0, 0, 0, 0, 0, 0, 0}, /*decay 0ms*/
{2068, 0, 0, 0, 0, 0, 0, 0}, /*decay time 20ms*/
{13045, 130, 1, 0, 0, 0, 0, 0}, /*decay time 60ms*/
{20675, 2068, 207, 21, 2, 0, 0, 0}, /*decay time 120ms*/
{19284, 5193, 1119, 241, 52, 11, 2, 1}, /*decay time 180ms*/
{20615, 7751, 2331, 701, 211, 63, 19, 6}, /*decay time 230ms*/
{27255, 10851, 4320, 1720, 685, 273, 109, 43},
/*decay time 300ms, max value*/
};
static inline int clip16(int x)
{
if (x < -32768)
return -32768;
if (x > 32767)
return 32767;
return x;
}
int reverb_start(struct reverb_t *aml_reverb)
{
struct reverb_t *reverb = aml_reverb;
int i;
reverb->counter = 0;
reverb->in_gain = ((long long)(32767 * reverb_in_gain + 16384))
>> 7;
reverb->out_gain = ((long long)(32767 * reverb_out_gain + 16384))
>> 7;
reverb->time = 0;
reverb->numdelays = 8;
reverb->maxsamples = 0;
reverb->reverbbuf = NULL;
for (i = 0; i < MAXREVERBS; i++) {
reverb->delay[i] = 40 * i + 8;
reverb->decay[i] = decay_table[reverb->time][i];
}
for (i = 0; i < reverb->numdelays; i++) {
/* stereo channel */
reverb->samples[i] = reverb->delay[i] * MAXRATE * 2;
if (reverb->samples[i] > reverb->maxsamples)
reverb->maxsamples = reverb->samples[i];
}
i = sizeof(s16) * reverb->maxsamples;
reverb->reverbbuf = kzalloc(i, GFP_KERNEL);
if (!reverb->reverbbuf)
return -1;
for (i = 0; i < reverb->numdelays; i++)
reverb->in_gain = (reverb->in_gain *
(32768 - ((reverb->decay[i] * reverb->decay[i]
+ 16384) >> 15)) + 16384) >> 15;
pr_info("reverb: reverb->in_gain = %d, reverb->maxsamples = %d\n",
reverb->in_gain, reverb->maxsamples);
if (reverb_highpass)
eq_init(&filer);
return 0;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples processed.
*/
int reverb_process(struct reverb_t *aml_reverb,
s16 *ibuf, s16 *obuf, int isamp,
int channels, int channel)
{
struct reverb_t *reverb = aml_reverb;
int len, done, offset;
int i, j, k;
int d_in, d_out;
long long tmp;
/* check whether reverb is enabled */
if (!reverb_enable)
return -1;
if (reverb->time != reverb_time) {
for (i = 0; i < MAXREVERBS; i++)
reverb->decay[i] = decay_table[reverb_time][i];
memset(reverb->reverbbuf, 0, reverb->maxsamples);
pr_info("reverb: reset reverb parameters!\n");
}
i = reverb->counter;
len = isamp >> (channels >> 1);
offset = channels;
for (done = 0; done < len; done++) {
d_in = (int)*ibuf;
ibuf += offset;
tmp = 0;
if (reverb_highpass)
d_in = fourth_order_IIR(d_in, &filer, channel);
tmp = ((long long)d_in * reverb->in_gain)
>> (15 - DATA_FRACTION_BIT);
/* Mix decay of delay and input as output */
for (j = 0; j < reverb->numdelays; j++) {
k = (i + reverb->maxsamples - reverb->samples[j])
% reverb->maxsamples;
tmp += ((long long)reverb->reverbbuf[k]
* reverb->decay[j]) >> 15;
}
d_in = clip16((tmp + HALF_ERROR) >> DATA_FRACTION_BIT);
tmp = (tmp * reverb->out_gain) >> 15;
d_out = clip16((tmp + HALF_ERROR) >> DATA_FRACTION_BIT);
*obuf = (s16)d_out;
obuf += offset;
reverb->reverbbuf[i] = (s16)d_in;
i++;
i += (offset >> 1);
i %= reverb->maxsamples;
}
reverb->counter = i;
reverb->time = reverb_time;
return 0;
}
/*
* Clean up reverb effect.
*/
int reverb_stop(struct reverb_t *aml_reverb)
{
struct reverb_t *reverb = aml_reverb;
kfree(reverb->reverbbuf);
reverb->reverbbuf = NULL;
return 0;
}
static int eq_coefficients[2][SECTION][COEFF_COUNT] = {
{ /* B coefficients */
{16777216, -33554433, 16777216}, /*B1*/
{16777216, -32950274, 16179410}, /*B2*/
},
{ /* A coefficients*/
{16777216, -33554431, 16777216}, /*A1*/
{16777216, -33297782, 16526985}, /*A2*/
},
};
void eq_init(struct af_equalizer *eq)
{
int i, j;
for (i = 0; i < SECTION; i++) {
for (j = 0; j < COEFF_COUNT; j++) {
eq->b[i][j] = eq_coefficients[0][i][j];
eq->a[i][j] = eq_coefficients[1][i][j];
}
}
memset(eq->cx, 0, sizeof(int) * CF * SECTION * 2);
memset(eq->cy, 0, sizeof(int) * CF * SECTION * 2);
}
int fourth_order_IIR(int input, struct af_equalizer *eq_ptr, int channel)
{
int sample = input;
int y = 0, i = 0;
long long temp = 0;
int cx[2][2], cy[2][2];
cx[0][0] = eq_ptr->cx[channel][0][0];
cx[0][1] = eq_ptr->cx[channel][0][1];
cx[1][0] = eq_ptr->cx[channel][1][0];
cx[1][1] = eq_ptr->cx[channel][1][1];
cy[0][0] = eq_ptr->cy[channel][0][0];
cy[0][1] = eq_ptr->cy[channel][0][1];
cy[1][0] = eq_ptr->cy[channel][1][0];
cy[1][1] = eq_ptr->cy[channel][1][1];
sample <<= DATA_FRACTION_BIT;
for (i = 0; i < SECTION; i++) {
temp = (long long)sample * (eq_ptr->b[i][0]);
temp += (long long)cx[i][0] * (eq_ptr->b[i][1]);
temp += (long long)cx[i][1] * (eq_ptr->b[i][2]);
temp -= (long long)cy[i][0] * (eq_ptr->a[i][1]);
temp -= (long long)cy[i][1] * (eq_ptr->a[i][2]);
y = (int)(temp >> COEFF_FRACTION_BIT);
cx[i][1] = cx[i][0];
cx[i][0] = sample;
cy[i][1] = cy[i][0];
cy[i][0] = y;
sample = y;
}
eq_ptr->cx[channel][0][0] = cx[0][0];
eq_ptr->cx[channel][0][1] = cx[0][1];
eq_ptr->cx[channel][1][0] = cx[1][0];
eq_ptr->cx[channel][1][1] = cx[1][1];
eq_ptr->cy[channel][0][0] = cy[0][0];
eq_ptr->cy[channel][0][1] = cy[0][1];
eq_ptr->cy[channel][1][0] = cy[1][0];
eq_ptr->cy[channel][1][1] = cy[1][1];
return (y + HALF_ERROR) >> DATA_FRACTION_BIT;
}

View File

@@ -0,0 +1,84 @@
/*
* drivers/amlogic/amlkaraoke/aml_reverb.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 AML_REVERB_H
#define AML_REVERB_H
#define MAXRATE 48 /*sample num in ms */
#define MAXREVERBS 8
extern int reverb_time;
extern int reverb_enable;
extern int reverb_highpass;
extern int reverb_in_gain;
extern int reverb_out_gain;
struct reverb_t {
int counter;
int numdelays;
int in_gain;
int out_gain;
int time;
int delay[MAXREVERBS];
int decay[MAXREVERBS];
int samples[MAXREVERBS];
int maxsamples;
s16 *reverbbuf;
};
#define DATA_FRACTION_BIT 0
#define HALF_ERROR 0 /*((0x1) << (DATA_FRACTION_BIT - 1))*/
/* Count if coefficients */
#define COEFF_COUNT 3
/*Section of cascaded two order IIR filter*/
#define SECTION 2
/*Channel Count*/
#define CF 2
#define COEFF_FRACTION_BIT 24
struct af_equalizer {
/*B coefficient array*/
int b[SECTION][COEFF_COUNT];
/*A coefficient array*/
int a[SECTION][COEFF_COUNT];
/*Circular buffer for channel input data*/
int cx[CF][SECTION][2];
/*Circular buffer for channel output data*/
int cy[CF][SECTION][2];
};
struct audio_format {
short left;
short right;
};
int reverb_start(struct reverb_t *aml_reverb);
int reverb_process(struct reverb_t *aml_reverb,
s16 *ibuf, s16 *obuf, int isamp,
int channels, int channel);
int reverb_drain(struct reverb_t *aml_reverb, s16 *obuf, int osamp);
int reverb_stop(struct reverb_t *aml_reverb);
void eq_init(struct af_equalizer *eq);
int fourth_order_IIR(int input,
struct af_equalizer *eq_ptr,
int channel);
#endif

View File

@@ -0,0 +1,552 @@
/*
* drivers/amlogic/amlkaraoke/aml_usb_capture.c
*
* 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.
*
*/
/*
* A virtual path to get usb audio capture data.
*
*/
#include <linux/slab.h>
#include <linux/bug.h>
#include <sound/core.h>
#include <linux/amlogic/media/sound/usb_karaoke.h>
#include "aml_usb_capture.h"
#include "aml_reverb.h"
/*void *snd_usb_pcm_capture_buffer = NULL;*/
struct usb_audio_buffer *snd_usb_pcm_capture_buffer;
EXPORT_SYMBOL(snd_usb_pcm_capture_buffer);
/* delay time for no audio effect to avoid noise. */
int usb_ignore_effect_time;
struct usb_input *s_usb_capture;
struct reverb_t aml_reverb_l;
struct reverb_t aml_reverb_r;
/* i2s info */
static unsigned int aml_i2s_playback_channels;
static unsigned int aml_i2s_playback_sample;
void aml_i2s_set_ch_r_info(unsigned int channels, unsigned int samplerate)
{
aml_i2s_playback_sample = samplerate;
aml_i2s_playback_channels = channels;
pr_info("[%s]:[%d] samplerate:%d, channels:%d\n",
__func__, __LINE__, samplerate, channels);
/* limit resample usb in to 2 channels */
if (aml_i2s_playback_channels > 2)
aml_i2s_playback_channels = 2;
}
static inline short clip(int x)
{
if (x < -32768)
x = -32768;
else if (x > 32767)
x = 32767;
return x & 0xFFFF;
}
struct usb_input *usb_audio_get_capture(void)
{
return s_usb_capture;
}
static void usb_audio_data_tuning_mic_gain(unsigned char *audiobuf,
int frames)
{
s16 *tuningbuf = (s16 *)audiobuf;
int i;
WARN_ON(!audiobuf);
for (i = 0; i < frames; i++) {
*tuningbuf = ((*tuningbuf) * (usb_mic_digital_gain)) >> 8;
*tuningbuf = clip(*tuningbuf);
tuningbuf += 1;
}
}
static int usb_audio_mono2stereo(unsigned char *dst,
unsigned char *src, int frames)
{
int j;
s16 *transfer_src = (s16 *)src;
s16 *transfer_dst = (s16 *)dst;
for (j = 0; j < frames; j++) {
transfer_dst[2 * j] = transfer_src[j];
transfer_dst[2 * j + 1] = transfer_src[j];
}
return 0;
}
static int usb_resample_and_mono2stereo_malloc_buffer(
struct usb_input *usbinput,
unsigned int out_rate)
{
if (!usbinput->out_buffer &&
!usbinput->usb_buf &&
!usbinput->usb_buf->rate) {
pr_info("usb resample buffer alloc failed\n");
return -ENOMEM;
}
return 0;
}
static int usb_resample_and_mono2stereo_free_buffer(
struct usb_input *usbinput)
{
if (usbinput &&
(usbinput->out_buffer || usbinput->mono2stereo)) {
kfree(usbinput->out_buffer);
kfree(usbinput->mono2stereo);
}
return 0;
}
static int usb_audio_capture_malloc_buffer(
struct usb_audio_buffer *usb_audio, int size)
{
usb_audio->addr = (unsigned char *)
kzalloc(size, GFP_KERNEL);
if (!usb_audio->addr) {
pr_info("USB audio capture buffer alloc failed\n");
return -ENOMEM;
}
usb_audio->size = size;
return 0;
}
static int usb_audio_capture_free_buffer(
struct usb_audio_buffer *usb_audio)
{
if (usb_audio && usb_audio->addr) {
kfree(usb_audio->addr);
usb_audio->addr = NULL;
}
return 0;
}
static int usb_check_resample_and_mono2stereo_buffer(
struct usb_input *usbinput, unsigned int rate)
{
int ret = -1;
if (rate &&
((!usbinput->out_buffer) || (!usbinput->mono2stereo))) {
usb_resample_and_mono2stereo_malloc_buffer(
usbinput,
rate);
ret = 0;
}
return ret;
}
static int usb_check_and_do_mono2stereo(struct usb_input *usbinput,
unsigned int channels,
unsigned char *cp,
unsigned int frames)
{
int ret = -1;
struct usb_audio_buffer *usb_buf = usbinput->usb_buf;
if (!usb_buf) {
pr_info("check and do mono2stereo, invalid usb buffer\n");
return ret;
}
if (channels && channels != usb_buf->channels &&
usbinput->mono2stereo) {
usb_audio_mono2stereo(
usbinput->mono2stereo,
cp,
frames);
ret = 0;
}
return ret;
}
static void usb_check_resample_init(struct usb_input *usbinput,
unsigned int rate,
unsigned int channels)
{
struct usb_audio_buffer *usb_buf = usbinput->usb_buf;
if (!usb_buf) {
pr_info("check mono2stereo init, invalid usb buffer\n");
return;
}
if (!usbinput->resample_request &&
usb_buf->rate && rate && channels &&
rate != usb_buf->rate) {
usbinput->resample_request = true;
usbinput->resampler.input_sr = usb_buf->rate;
usbinput->resampler.output_sr = rate;
usbinput->resampler.channels = channels;
resampler_init(&usbinput->resampler);
pr_info("check mono2stereo init, resample from %d ch, %d to %d ch, %d",
usb_buf->channels,
usb_buf->rate,
channels,
rate);
}
}
static int usb_check_and_do_resample(struct usb_input *usbinput,
unsigned int channels,
unsigned char *cp,
unsigned int in_frames)
{
struct usb_audio_buffer *usb_buf = usbinput->usb_buf;
void *in_buffer;
unsigned int out_frames;
if (!usb_buf) {
pr_info("check and do resample, invalid usb buffer\n");
return -1;
}
if ((usb_buf->channels == 1) &&
(channels != usb_buf->channels) &&
usbinput->mono2stereo) {
in_buffer = usbinput->mono2stereo;
/* in_frames <<= 1; */
} else {
/* in_frames = bytes >> subs->
* usb_input->resampler.channels;
*/
in_buffer = cp;
}
out_frames = resample_process(
&usbinput->resampler,
in_frames,
(short *)(in_buffer),
(short *)usbinput->out_buffer);
return out_frames;
}
static void usb_check_and_do_reverb(struct reverb_t *reverb_L,
struct reverb_t *reverb_R,
unsigned int channels,
unsigned char *cp,
unsigned int frames)
{
if (channels == 1) {
reverb_process(reverb_L, (s16 *)cp, (s16 *)cp,
frames, channels, 0);
} else {
reverb_process(reverb_L, (s16 *)cp, (s16 *)cp,
frames, channels, 0);
reverb_process(reverb_R, (s16 *)cp + 1,
(s16 *)cp + 1, frames,
channels, 1);
}
}
static void usb_audio_copy_ringbuffer(struct usb_audio_buffer *usb_buf,
unsigned char *cp_src,
unsigned int cp_bytes)
{
uint avail;
unsigned long usbirqflags;
spin_lock_irqsave(&usb_buf->lock, usbirqflags);
if (usb_buf->wr >= usb_buf->rd)
avail = usb_buf->rd + usb_buf->size - usb_buf->wr;
else
avail = usb_buf->rd - usb_buf->wr;
if (avail >= cp_bytes) {
if (usb_buf->wr + cp_bytes > usb_buf->size) {
memcpy(usb_buf->addr + usb_buf->wr,
cp_src, usb_buf->size - usb_buf->wr);
memcpy(usb_buf->addr,
cp_src + usb_buf->size - usb_buf->wr,
cp_bytes + usb_buf->wr - usb_buf->size);
} else {
memcpy(usb_buf->addr + usb_buf->wr,
cp_src, cp_bytes);
}
usb_buf->wr = (usb_buf->wr + cp_bytes) %
usb_buf->size;
usb_buf->level = (usb_buf->size + usb_buf->wr
- usb_buf->rd) % usb_buf->size;
} else {
/*reset buffer ptr*/
usb_buf->wr = (usb_buf->rd + usb_buf->size / 2) % usb_buf->size;
}
spin_unlock_irqrestore(&usb_buf->lock, usbirqflags);
}
int usb_set_capture_status(bool isrunning)
{
struct usb_input *usbinput = usb_audio_get_capture();
if (!usbinput)
return -1;
if (!usbinput->usb_buf)
return -2;
usbinput->usb_buf->running = isrunning;
return 0;
}
EXPORT_SYMBOL(usb_set_capture_status);
int usb_audio_capture_init(void)
{
struct usb_audio_buffer *usb_buffer = NULL;
struct usb_input *s_usb_input;
int ret = 0;
unsigned int buffer_size = 0;
s_usb_input = kzalloc(sizeof(*s_usb_input), GFP_KERNEL);
if (!s_usb_input) {
ret = -ENOMEM;
goto err;
}
usb_buffer = kzalloc(sizeof(*usb_buffer), GFP_KERNEL);
if (!usb_buffer) {
ret = -ENOMEM;
goto err;
}
s_usb_input->usb_buf = usb_buffer;
snd_usb_pcm_capture_buffer = usb_buffer;
if (usb_audio_capture_malloc_buffer(
usb_buffer,
USB_AUDIO_CAPTURE_BUFFER_SIZE)) {
ret = -ENOMEM;
goto err;
}
buffer_size = (USB_AUDIO_CAPTURE_PACKAGE_SIZE * (48000
/ 8000) + 1) * 4;
if (!s_usb_input->out_buffer) {
s_usb_input->out_buffer = (unsigned char *)
kzalloc(buffer_size, GFP_KERNEL);
if (!s_usb_input->out_buffer) {
ret = -ENOMEM;
goto err;
}
}
s_usb_input->mono2stereo = (unsigned char *)
kzalloc(buffer_size * 2, GFP_KERNEL);
if (!s_usb_input->mono2stereo) {
ret = -ENOMEM;
goto err;
}
/*snd_usb_pcm_capture_buffer->addr = usb_buffer->addr;*/
spin_lock_init(&usb_buffer->lock);
s_usb_capture = s_usb_input;
reverb_start(&aml_reverb_l);
reverb_start(&aml_reverb_r);
/* ignore some usb data */
usb_ignore_effect_time = 3;
err:
if (!s_usb_input) {
if (!s_usb_input->out_buffer)
kfree(s_usb_input->out_buffer);
kfree(s_usb_input);
}
if (!usb_buffer) {
usb_audio_capture_free_buffer(usb_buffer);
kfree(usb_buffer);
}
return ret;
}
EXPORT_SYMBOL(usb_audio_capture_init);
int usb_audio_capture_deinit(void)
{
struct usb_input *usbinput = usb_audio_get_capture();
if (!usbinput)
return 0;
if (snd_usb_pcm_capture_buffer) {
usb_audio_capture_free_buffer(usbinput->usb_buf);
kfree(usbinput->usb_buf);
usbinput->usb_buf = NULL;
snd_usb_pcm_capture_buffer = NULL;
}
usb_resample_and_mono2stereo_free_buffer(usbinput);
kfree(usbinput);
reverb_stop(&aml_reverb_l);
reverb_stop(&aml_reverb_r);
return 0;
}
EXPORT_SYMBOL(usb_audio_capture_deinit);
int retire_capture_usb(struct snd_pcm_runtime *runtime,
unsigned char *cp, unsigned int bytes,
unsigned int oldptr, unsigned int stride)
{
struct usb_input *usbinput = usb_audio_get_capture();
struct usb_audio_buffer *usb_buf = NULL;
unsigned char *cp_src = NULL;
unsigned char *effect_cp = NULL;
unsigned int effect_bytes = 0;
unsigned int frame_count = 0;
unsigned int cp_bytes = 0;
unsigned int in_frames = 0, out_frames = 0;
int ret = 0;
if ((builtin_mixer && usb_ignore_effect_time) || (!builtin_mixer)) {
/* At the beginning when usb capture start,
* noise is captured in the first audio data,
* so deley 3ms for no audio effect.
*/
if (usb_ignore_effect_time)
usb_ignore_effect_time--;
/* copy a data chunk */
if (oldptr + bytes > runtime->buffer_size * stride) {
unsigned int bytes1 =
runtime->buffer_size * stride - oldptr;
memcpy(runtime->dma_area + oldptr, cp, bytes1);
memcpy(runtime->dma_area,
cp + bytes1, bytes - bytes1);
} else {
memcpy(runtime->dma_area + oldptr, cp, bytes);
}
goto exit;
}
effect_cp = cp;
effect_bytes = bytes;
frame_count = bytes_to_frames(runtime, effect_bytes);
/* check data is valid */
if (NULL == effect_cp || 0 == effect_bytes) {
/* pr_info("retire usb, invalid data,cp:%p, bytes:%d\n",
* cp, bytes);
*/
ret = -1;
goto exit;
}
/* Audio effect reverb */
if (reverb_enable) {
usb_check_and_do_reverb(
&aml_reverb_l, &aml_reverb_r,
runtime->channels,
effect_cp, frame_count);
}
/* Tuning usb mic gain */
usb_audio_data_tuning_mic_gain(effect_cp,
frame_count);
/* copy a data chunk */
if (oldptr + effect_bytes >
runtime->buffer_size * stride) {
unsigned int bytes1 =
runtime->buffer_size * stride - oldptr;
memcpy(runtime->dma_area + oldptr,
effect_cp, bytes1);
memcpy(runtime->dma_area,
effect_cp + bytes1, effect_bytes - bytes1);
} else {
memcpy(runtime->dma_area + oldptr,
effect_cp, effect_bytes);
}
/* Audio reverb effect is added to the source,
* countine to do resample/mono2stereo, then copy
* audio data to ring buffer.
*/
usb_buf = usbinput->usb_buf;
usb_buf->channels = runtime->channels;
usb_buf->rate = runtime->rate;
/*usb resample and mono2stereo buffer prepared*/
usb_check_resample_and_mono2stereo_buffer(
usbinput, aml_i2s_playback_sample);
/*mono to stereo, default that i2s channel is stereo.*/
in_frames = frame_count;
usb_check_and_do_mono2stereo(
usbinput, aml_i2s_playback_channels,
cp, in_frames);
/* Resample Init */
usb_check_resample_init(usbinput, aml_i2s_playback_sample,
aml_i2s_playback_channels);
if ((bytes) && (usbinput->resample_request) &&
(usbinput->out_buffer)) {
/* bytes to frame, bytes / (channel * bytes_per_frame),
* default:16bit
*/
out_frames = usb_check_and_do_resample(
usbinput,
aml_i2s_playback_channels,
cp, in_frames);
if (out_frames < 0) {
pr_info("do reample failed\n");
goto exit;
}
cp_src = usbinput->out_buffer;
cp_bytes = out_frames * 4;
} else {
/*snd_printdd(KERN_ERR "usb record not resample\n");*/
if (usb_buf->channels == 1 && usbinput->mono2stereo &&
aml_i2s_playback_channels &&
aml_i2s_playback_channels != usb_buf->channels) {
cp_src = usbinput->mono2stereo;
cp_bytes = in_frames * 4;
} else {
cp_src = cp;
cp_bytes = bytes;
}
}
/* copy audio audio to ring buffer.
* Default, ring buffer, stereo channel, 16bit
*/
usb_audio_copy_ringbuffer(usb_buf, cp_src, cp_bytes);
exit:
return ret;
}
EXPORT_SYMBOL(retire_capture_usb);

View File

@@ -0,0 +1,59 @@
/*
* drivers/amlogic/amlkaraoke/aml_usb_capture.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 __AML_USB_CAPTURE_H
#define __AML_USB_CAPTURE_H
/*
* A virtual path to get usb audio capture data.
*
*/
#include <sound/pcm.h>
#include "aml_audio_resampler.h"
/* Keep same with aml_i2s_out_mix.h */
struct usb_audio_buffer {
dma_addr_t paddr;
unsigned char *addr;
unsigned int size;
unsigned int wr;
unsigned int rd;
unsigned int level;
unsigned int channels; /* channels */
unsigned int rate; /* rate in Hz */
unsigned int running;
spinlock_t lock; /*lock the ringbuffer */
};
struct usb_input {
struct usb_audio_buffer *usb_buf;
/* Resample if needed */
bool resample_request;
struct resample_para resampler;
unsigned char *out_buffer;
unsigned char *mono2stereo;
};
#define USB_AUDIO_CAPTURE_BUFFER_SIZE (1024 * 4)
#define USB_AUDIO_CAPTURE_PACKAGE_SIZE (512)
extern int usb_mic_digital_gain;
extern int builtin_mixer;
extern int reverb_enable;
#endif

View File

@@ -0,0 +1,27 @@
/*
* include/linux/amlogic/media/sound/usb_karaoke.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 __USB_KARAOKE_H
#define __USB_KARAOKE_H
int i2s_out_mix_init(void);
int i2s_out_mix_deinit(void);
void aml_i2s_set_ch_r_info(
unsigned int channels,
unsigned int samplerate);
#endif

View File

@@ -48,6 +48,9 @@
#include <linux/amlogic/media/sound/aout_notify.h>
#include "spdif_dai.h"
#include "dmic.h"
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
#include <linux/amlogic/media/sound/usb_karaoke.h>
#endif
static int i2s_clk_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -279,12 +282,34 @@ static int aml_dai_i2s_prepare(struct snd_pcm_substream *substream,
/* use the hw same sync for i2s/958 */
pr_debug("i2s/958 same source\n");
}
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
aml_i2s_set_ch_r_info(runtime->channels, runtime->rate);
i2s_out_mix_init();
#endif
if (runtime->channels == 8) {
pr_debug("8ch PCM output->notify HDMI\n");
aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM,
substream);
dev_info(substream->pcm->card->dev, "8ch PCM output->notify HDMI\n");
aout_notifier_call_chain(
AOUT_EVENT_IEC_60958_PCM,
substream);
}
}
return 0;
}
static int aml_dai_i2s_hw_free(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
struct aml_runtime_data *prtd = substream->runtime->private_data;
struct audio_stream *s = &prtd->s;
if (s && s->device_type == AML_AUDIO_I2SOUT) {
i2s_out_mix_deinit();
aml_i2s_set_ch_r_info(0, 0);
}
#endif
return 0;
}
@@ -431,6 +456,7 @@ static struct snd_soc_dai_ops aml_dai_i2s_ops = {
.prepare = aml_dai_i2s_prepare,
.trigger = aml_dai_i2s_trigger,
.hw_params = aml_dai_i2s_hw_params,
.hw_free = aml_dai_i2s_hw_free,
.set_fmt = aml_dai_set_i2s_fmt,
.set_sysclk = aml_dai_set_i2s_sysclk,
};

View File

@@ -28,4 +28,5 @@ struct aml_i2s {
int clk_data_pos;
unsigned long mclk;
};
#endif

View File

@@ -1240,6 +1240,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = &as->substream[direction];
int ret = 0;
subs->interface = -1;
subs->altset_idx = 0;
@@ -1253,7 +1254,28 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
subs->dsd_dop.channel = 0;
subs->dsd_dop.marker = 1;
return setup_hw_info(runtime, subs);
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
if (direction == SNDRV_PCM_STREAM_CAPTURE) {
ret = usb_audio_capture_init();
if (ret)
goto err;
}
ret = setup_hw_info(runtime, subs);
return ret;
err:
snd_printdd(KERN_ERR "built-in mixer alloc usb buffer failed.\n");
if (direction == SNDRV_PCM_STREAM_CAPTURE)
usb_audio_capture_deinit();
ret = setup_hw_info(runtime, subs);
#else
ret = setup_hw_info(runtime, subs);
#endif
return ret;
}
static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
@@ -1273,6 +1295,11 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
subs->pcm_substream = NULL;
snd_usb_autosuspend(subs->stream->chip);
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
if (direction == SNDRV_PCM_STREAM_CAPTURE)
usb_audio_capture_deinit();
#endif
return 0;
}
@@ -1336,6 +1363,9 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
spin_unlock_irqrestore(&subs->lock, flags);
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
retire_capture_usb(runtime, cp, bytes, oldptr, stride);
#else
/* copy a data chunk */
if (oldptr + bytes > runtime->buffer_size * stride) {
unsigned int bytes1 =
@@ -1345,6 +1375,7 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
} else {
memcpy(runtime->dma_area + oldptr, cp, bytes);
}
#endif
}
if (period_elapsed)
@@ -1677,13 +1708,20 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
err = start_endpoints(subs);
if (err < 0)
return err;
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
err = usb_set_capture_status(true);
#endif
subs->data_endpoint->retire_data_urb = retire_capture_urb;
subs->running = 1;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
stop_endpoints(subs, false);
subs->running = 0;
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
usb_set_capture_status(false);
#endif
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
subs->data_endpoint->retire_data_urb = NULL;

View File

@@ -10,5 +10,14 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt);
#ifdef CONFIG_AMLOGIC_SND_USB_CAPTURE_DATA
int usb_set_capture_status(bool isrunning);
int usb_audio_capture_init(void);
int usb_audio_capture_deinit(void);
int retire_capture_usb(
struct snd_pcm_runtime *runtime,
unsigned char *cp, unsigned int bytes,
unsigned int oldptr, unsigned int stride);
#endif
#endif /* __USBAUDIO_PCM_H */