mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
sound: add resample node for axg_s420_v03gva.dts [1/1]
PD#SWPL-365 Problem: System can't use resample function on GVA project. Solution: Add resample node in axg_s420_v03gva.dts Verify: Passed on s420 Change-Id: Ie48c579e9e9f6e1d61147f5410c95e5bca55b0a7 Signed-off-by: riqun.ou <riqun.ou@amlogic.com> audio: codecs: fix TL1 reference board volume too small [2/2] PD#SWPL-2652 Problem: X301/T309 AMP volume configured to small Solution: change the default external AMP codec mixer name to a match the ini parsing function in audio hal. TODO: need dynamically check the different codec mixer name in ini file. Verify: verify by x301 Change-Id: Iba835d37ea02bec3095556d54fbf327bd6390904 Signed-off-by: Jian Xu <jian.xu@amlogic.com> audio: codec: the DAC gain set of acodec not work [1/1] PD#SWPL-3134 Problem: the DAC gain set is not work Solution: update and aml_codec_tl1_acodec.c, change the method of getting codec pointer from kcontrol pointer. Verify: verify by T962x2_X301 board Change-Id: I02f02b73fab3b8391ef4866a71ffaf0d48bbbbbf Signed-off-by: Shuyu Li <shuyu.li@amlogic.com> audio: add tas5805 [1/1] PD#SWPL-3081 Problem: compatiable tas5805 module Solution: add tas5805 drivers Verify: T962X2 Change-Id: I3608e47fe768af0f924751a8bcc103389d0811de Signed-off-by: Yonghao Jiao <yonghao.jiao@amlogic.com> audio: auge: add HDMIRX SPDIF in support [1/2] PD#SWPL-2956 Problem: HDMI in DTS/Dolby input has noise when treated to LPCM Solution: By default,we are using PAO mode for HDMIRX,but we have not enabled the PaPb search for 61937 raw data input, after add that, we can detect the raw data, but it can not by clear when switch from NONE-LPCM to NONE-LPCM, need add IRQ function to clear that by sw.we enabled spdif in from HDMIRX, which is the same design as txl/txlx.we can get the Pc information now. also we add a new interface to set spdif in source when hdmirx input. Verify: x301 Change-Id: I3c4e8b387308ef862a069c29d15b8b5a9e865564 Signed-off-by: Jian Xu <jian.xu@amlogic.com> udio: auge: tl1 acodec mapping to data 1 [1/2] PD#SWPL-3277 Problem: tl1 internal codec volume can by adjusted Solution: configure the acodec data layout mapping to a fixed volume Verify: x301 Change-Id: I14d3762b39a0a5291722ef5489026f10fc960120 Signed-off-by: Jian Xu <jian.xu@amlogic.com> Conflicts: arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts audio: Fix TDM not wokring on HDMI I2S in [1/1] PD#SWPL-2887 Problem: When TDM is used for HDMI I2S in, the solt set is wrong. Solution: Fix the lane max value from 3 to 4 Verify: A113 Change-Id: I33f5ca21bf1e2407d83fa5d0f22e21f7b1f5e749 Signed-off-by: yujie.wu <yujie.wu@amlogic.com> audio: codec: fix to resume ad82584's volume after suspend [1/1] PD#SWPL-3456 Problem: 1) After suspend, volume is not resume 2) ARC connected, still sound out from spk Solution: 1) save channel volume when suspend, then resume the volume 2) add mute mixer control for ad82584 Verify: x301 Change-Id: Ic6e7502e3f9689cc2d1053295f26aaf4d5be2603 Signed-off-by: Xing Wang <xing.wang@amlogic.com> audio: auge: add vad driver [1/1] PD#SWPL-2404 Problem: VAD for wake up Solution: Add vad driver to fetch VAD buffer and ALSA buffer, in userspace, they will be combined for wakeup engine Verify: x301 Change-Id: I3b4de5fdfe173ce18e58a187a3adeda601e226b3 Signed-off-by: Xing Wang <xing.wang@amlogic.com> audio: fix pop snd after bootup [2/2] PD#SWPL-3178 Problem: output big pop noise after system boot up. Solution: Add zero and then unmute the stream. NOTICE: user should unmute the AD82584F after boot. Verify: local verified. Change-Id: I21555318f9347c5340a28f7e5cf0e3e2cde849a0 Signed-off-by: Shuai Li <shuai.li@amlogic.com> audio: fix no audio after resume of ad82584f [1/1] PD#SWPL-4090 Problem: No audio after suspend and resume. Suspend won't save the mute value, and after resume, it could not be restored. Solution: Add a mute val to save when suspend, and after resume, restore the mute value. Verify: Verified by QA. Change-Id: Ie24bb11f5c565048391846a66b5d12bab1d55666 Signed-off-by: Shuai Li <shuai.li@amlogic.com> audio: auge: fix sharebuffer channel map [1/1] PD#SWPL-2645 Problem: play ddp source, then play pcm source, no sound Solution: 1. fix same source control and channel map issue 2. i2s 8ch, spdif 2ch, channels are not mapped, make spdif 8 channel mask 3. when same source used, keep mpll use same mpll Verify: x301 Change-Id: I2fe4bbcbcbfff0a1c1a6cebf61d1da5aba9b7a9d Signed-off-by: Xing Wang <xing.wang@amlogic.com> Conflicts: arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts arch/arm64/boot/dts/amlogic/tl1_t962x2_x301.dts audio: add stream mute and continuous clk [1/1] PD#SWPL-2952 Problem: Pop noise could be heard when switching between sources. Solution: 1. Add stream digital mute functions. 2. continuous clock to eliminate the clk reset issue. Verify: Local verified. Change-Id: I372f4c03aaf875d75aa903c9c2dfda00619af000 Signed-off-by: Shuai Li <shuai.li@amlogic.com> dts: tl1: enable resample for tl1 [1/2] PD#SWPL-3365 Problem: not resample for audio in, so output in wrong sample rate Solution: 1. enable resample in dts 2. fix to check whether resample is needed Verify: x301 Change-Id: I99238cc21a00ab53df6a1f8ab1703bc9ab48cbaa Signed-off-by: Xing Wang <xing.wang@amlogic.com> Conflicts: arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts arch/arm64/boot/dts/amlogic/tl1_t962x2_x301.dts
This commit is contained in:
18
Documentation/devicetree/bindings/sound/tas5805.txt
Normal file
18
Documentation/devicetree/bindings/sound/tas5805.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Texas Instruments TAS5805 Audio amplifier
|
||||
|
||||
The TAS5805 serial control bus communicates through the I2C protocol only. The
|
||||
serial bus is also used for periodic codec fault checking/reporting during
|
||||
audio playback. For more product information please see the links below:
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "ti, tas5805"
|
||||
- reg : I2C slave address
|
||||
|
||||
Example:
|
||||
|
||||
tas5805: tas5805@7c {
|
||||
status = "okay";
|
||||
compatible = "ti,tas5805";
|
||||
reg = <0x2d>;
|
||||
};
|
||||
10
MAINTAINERS
10
MAINTAINERS
@@ -13982,6 +13982,14 @@ F: sound/soc/codecs/amlogic/Makefile
|
||||
F: sound/soc/codecs/amlogic/tas5707.c
|
||||
F: sound/soc/codecs/amlogic/tas5707.h
|
||||
|
||||
AMLOGIC Audio codec driver
|
||||
M: Yonghao Jiao <yonghao.jiao@amogic.com>
|
||||
F: arch/arm/configs/meson32_deconfig
|
||||
F: sound/soc/codecs/amlogic/Kconfig
|
||||
F: sound/soc/codecs/amlogic/Makefile
|
||||
F: sound/soc/codecs/amlogic/tas5805.c
|
||||
F: sound/soc/codecs/amlogic/tas5805.h
|
||||
|
||||
AMLOGIC AXG ADD DTS FOR A113D SOCKET AND DEVELOPMENT BOARDS
|
||||
M: Yun Cai <yun.cai@amlogic.com>
|
||||
F: arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts
|
||||
@@ -14672,10 +14680,12 @@ AMLOGIC TL1 SOUND CARD
|
||||
AMLOGIC TL1 AUDIO EXTERANL INPUT/OUTPUT DRIVERS
|
||||
AMLOGIC TL1 NEW EQDRC
|
||||
AMLOGIC TL1 MIXER CONTROLS
|
||||
AMLOGIC TL1 VAD DRIVER
|
||||
M: Xing Wang <xing.wang@amlogic.com
|
||||
F: arch/arm/boot/dts/amlogic/tl1_pxp.dts
|
||||
F: include/dt-bindings/clock/amlogic,tl1-audio-clk.h
|
||||
F: include/linux/amlogic/media/sound/misc.h
|
||||
F: include/linux/amlogic/major.h
|
||||
F: sound/soc/amlogic/auge/*
|
||||
|
||||
AMLOGIC LCD DRIVERS
|
||||
|
||||
@@ -312,7 +312,6 @@
|
||||
sound-dai = <&dummy_codec>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/* Audio Related end */
|
||||
@@ -1129,6 +1128,34 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
vad:vad {
|
||||
compatible = "amlogic, snd-vad";
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&clkaudio CLKID_AUDIO_GATE_TOVAD
|
||||
&clkc CLKID_FCLK_DIV5
|
||||
&clkaudio CLKID_AUDIO_VAD>;
|
||||
clock-names = "gate", "pll", "clk";
|
||||
|
||||
interrupts = <GIC_SPI 155 IRQ_TYPE_EDGE_RISING
|
||||
GIC_SPI 47 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "irq_wakeup", "irq_frame_sync";
|
||||
|
||||
/*
|
||||
* Data src sel:
|
||||
* 0: tdmin_a;
|
||||
* 1: tdmin_b;
|
||||
* 2: tdmin_c;
|
||||
* 3: spdifin;
|
||||
* 4: pdmin;
|
||||
* 5: loopback_b;
|
||||
* 6: tdmin_lb;
|
||||
* 7: loopback_a;
|
||||
*/
|
||||
src = <4>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
}; /* end of audiobus */
|
||||
|
||||
&pinctrl_periphs {
|
||||
|
||||
1776
arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts
Normal file
1776
arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -167,6 +167,7 @@
|
||||
reg = <0xff632000 0x1c>;
|
||||
tdmout_index = <0>;
|
||||
tdmin_index = <0>;
|
||||
dat1_ch_sel = <1>;
|
||||
};
|
||||
|
||||
auge_sound {
|
||||
@@ -178,7 +179,7 @@
|
||||
aml-audio-card,dai-link@0 {
|
||||
format = "i2s";
|
||||
mclk-fs = <256>;
|
||||
//continuous-clock;
|
||||
continuous-clock;
|
||||
//bitclock-inversion;
|
||||
//frame-inversion;
|
||||
/* master mode */
|
||||
@@ -937,7 +938,7 @@
|
||||
|
||||
clocks = <&clkaudio CLKID_AUDIO_MCLK_A
|
||||
&clkc CLKID_MPLL0
|
||||
&clkc CLKID_MPLL1>;
|
||||
&clkc CLKID_MPLL0>;
|
||||
clock-names = "mclk", "clk_srcpll", "samesource_sysclk";
|
||||
|
||||
pinctrl-names = "tdm_pins";
|
||||
@@ -1024,7 +1025,7 @@
|
||||
* 7: "Enable:192K",
|
||||
*/
|
||||
asrc_id = <0>;
|
||||
auto_asrc = <0>;
|
||||
auto_asrc = <3>;
|
||||
|
||||
status = "okay";
|
||||
};
|
||||
@@ -1107,8 +1108,8 @@
|
||||
|
||||
asrca: resample@0 {
|
||||
compatible = "amlogic, tl1-resample-a";
|
||||
clocks = <&clkc CLKID_MPLL3
|
||||
&clkaudio CLKID_AUDIO_MCLK_F
|
||||
clocks = <&clkc CLKID_MPLL0
|
||||
&clkaudio CLKID_AUDIO_MCLK_A
|
||||
&clkaudio CLKID_AUDIO_RESAMPLE_A>;
|
||||
clock-names = "resample_pll", "resample_src", "resample_clk";
|
||||
/*same with toddr_src
|
||||
@@ -1123,7 +1124,7 @@
|
||||
*/
|
||||
resample_module = <3>;
|
||||
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
asrcb: resample@1 {
|
||||
@@ -1149,6 +1150,41 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
vad:vad {
|
||||
compatible = "amlogic, snd-vad";
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&clkaudio CLKID_AUDIO_GATE_TOVAD
|
||||
&clkc CLKID_FCLK_DIV5
|
||||
&clkaudio CLKID_AUDIO_VAD>;
|
||||
clock-names = "gate", "pll", "clk";
|
||||
|
||||
interrupts = <GIC_SPI 155 IRQ_TYPE_EDGE_RISING
|
||||
GIC_SPI 47 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "irq_wakeup", "irq_frame_sync";
|
||||
|
||||
/*
|
||||
* Data src sel:
|
||||
* 0: tdmin_a;
|
||||
* 1: tdmin_b;
|
||||
* 2: tdmin_c;
|
||||
* 3: spdifin;
|
||||
* 4: pdmin;
|
||||
* 5: loopback_b;
|
||||
* 6: tdmin_lb;
|
||||
* 7: loopback_a;
|
||||
*/
|
||||
src = <4>;
|
||||
|
||||
/*
|
||||
* deal with hot word in user space or kernel space
|
||||
* 0: in user space
|
||||
* 1: in kernel space
|
||||
*/
|
||||
level = <0>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
}; /* end of audiobus */
|
||||
|
||||
&pinctrl_periphs {
|
||||
@@ -1229,6 +1265,14 @@
|
||||
pinctrl-0=<&i2c2_z_pins>;
|
||||
clock-frequency = <400000>;
|
||||
|
||||
tas5805: tas5805@36 {
|
||||
compatible = "ti,tas5805";
|
||||
#sound-dai-cells = <0>;
|
||||
codec_name = "tas5805";
|
||||
reg = <0x2d>;
|
||||
status = "disable";
|
||||
};
|
||||
|
||||
ad82584f: ad82584f@62 {
|
||||
compatible = "ESMT, ad82584f";
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
@@ -482,6 +482,7 @@ CONFIG_AMLOGIC_SND_CODEC_AMLT9015S=y
|
||||
CONFIG_AMLOGIC_SND_CODEC_TXLX_ACODEC=y
|
||||
CONFIG_AMLOGIC_SND_CODEC_TL1_ACODEC=y
|
||||
CONFIG_AMLOGIC_SND_SOC_TAS5707=y
|
||||
CONFIG_AMLOGIC_SND_SOC_TAS5805=y
|
||||
CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101=y
|
||||
CONFIG_AMLOGIC_SND_SOC_PCM186X=y
|
||||
CONFIG_AMLOGIC_SND_SOC_SSM3525=y
|
||||
|
||||
@@ -854,6 +854,7 @@
|
||||
unifykey-index-3 = <&keysn_3>;
|
||||
unifykey-index-4 = <&keysn_4>;
|
||||
unifykey-index-5 = <&keysn_5>;
|
||||
unifykey-index-6 = <&keysn_6>;
|
||||
|
||||
keysn_0: key_0{
|
||||
key-name = "usid";
|
||||
@@ -887,6 +888,11 @@
|
||||
key-device = "normal";
|
||||
key-permit = "read","write","del";
|
||||
};
|
||||
keysn_6:key_6{
|
||||
key-name = "gva_certs";
|
||||
key-device = "normal";
|
||||
key-permit = "read","write","del";
|
||||
};
|
||||
};//End unifykey
|
||||
audio_data: audio_data {
|
||||
compatible = "amlogic, audio_data";
|
||||
@@ -1111,7 +1117,7 @@
|
||||
#sound-dai-cells = <0>;
|
||||
clocks = <&clkaudio CLKID_AUDIO_PDM
|
||||
&clkc CLKID_FCLK_DIV3
|
||||
&clkc CLKID_MPLL3
|
||||
&clkc CLKID_MPLL2
|
||||
&clkaudio CLKID_AUDIO_PDMIN0
|
||||
&clkaudio CLKID_AUDIO_PDMIN1>;
|
||||
clock-names = "gate",
|
||||
|
||||
@@ -668,13 +668,14 @@
|
||||
compatible = "amlogic, unifykey";
|
||||
status = "ok";
|
||||
|
||||
unifykey-num = <6>;
|
||||
unifykey-num = <7>;
|
||||
unifykey-index-0 = <&keysn_0>;
|
||||
unifykey-index-1 = <&keysn_1>;
|
||||
unifykey-index-2 = <&keysn_2>;
|
||||
unifykey-index-3 = <&keysn_3>;
|
||||
unifykey-index-4 = <&keysn_4>;
|
||||
unifykey-index-5 = <&keysn_5>;
|
||||
unifykey-index-6 = <&keysn_6>;
|
||||
|
||||
keysn_0: key_0{
|
||||
key-name = "usid";
|
||||
@@ -708,6 +709,11 @@
|
||||
key-device = "normal";
|
||||
key-permit = "read","write","del";
|
||||
};
|
||||
keysn_6:key_6{
|
||||
key-name = "gva_certs";
|
||||
key-device = "normal";
|
||||
key-permit = "read","write","del";
|
||||
};
|
||||
};//End unifykey
|
||||
audio_data: audio_data {
|
||||
compatible = "amlogic, audio_data";
|
||||
@@ -934,7 +940,7 @@
|
||||
#sound-dai-cells = <0>;
|
||||
clocks = <&clkaudio CLKID_AUDIO_PDM
|
||||
&clkc CLKID_FCLK_DIV3
|
||||
&clkc CLKID_MPLL3
|
||||
&clkc CLKID_MPLL2
|
||||
&clkaudio CLKID_AUDIO_PDMIN0
|
||||
&clkaudio CLKID_AUDIO_PDMIN1>;
|
||||
clock-names = "gate",
|
||||
@@ -992,6 +998,26 @@
|
||||
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
audioresample: resample {
|
||||
compatible = "amlogic, axg-resample";
|
||||
clocks = <&clkc CLKID_MPLL3
|
||||
&clkaudio CLKID_AUDIO_MCLK_F
|
||||
&clkaudio CLKID_AUDIO_RESAMPLE_CTRL>;
|
||||
clock-names = "resample_pll", "resample_src", "resample_clk";
|
||||
/*same with toddr_src
|
||||
* TDMIN_A,
|
||||
* TDMIN_B,
|
||||
* TDMIN_C,
|
||||
* SPDIFIN,
|
||||
* PDMIN,
|
||||
* NONE,
|
||||
* TDMIN_LB,
|
||||
* LOOPBACK,
|
||||
*/
|
||||
resample_module = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
}; /* end of audiobus */
|
||||
|
||||
&pinctrl_periphs {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define AMAUDIO2_MAJOR (13+(AML_BASE))
|
||||
#define VFM_MAJOR (14+(AML_BASE))
|
||||
#define IONVIDEO_MAJOR (15+(AML_BASE))
|
||||
#define VAD_MAJOR (16+(AML_BASE))
|
||||
/*
|
||||
*#define UIO_MAJOR 4+(AML_BASE)
|
||||
*#define USB_DEV_EP_MAJOR 5+(AML_BASE)
|
||||
|
||||
@@ -29,4 +29,7 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \
|
||||
pwrdet_hw.o \
|
||||
sharebuffer.o \
|
||||
extn.o \
|
||||
frhdmirx_hw.o
|
||||
frhdmirx_hw.o \
|
||||
vad.o \
|
||||
vad_hw.o \
|
||||
vad_dev.o
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ddr_mngr.h"
|
||||
#include "resample.h"
|
||||
#include "effects_v2.h"
|
||||
#include "vad.h"
|
||||
|
||||
#include <linux/amlogic/iomap.h>
|
||||
#include <linux/amlogic/media/sound/auge_utils.h>
|
||||
@@ -951,6 +952,10 @@ int snd_card_add_kcontrols(struct snd_soc_card *card)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = card_add_vad_kcontrols(card);
|
||||
if (ret < 0)
|
||||
pr_warn_once("Failed to add VAD controls\n");
|
||||
|
||||
return snd_soc_add_card_controls(card,
|
||||
snd_auge_controls, ARRAY_SIZE(snd_auge_controls));
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "audio_ddr_mngr: " fmt
|
||||
|
||||
@@ -23,6 +24,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "regs.h"
|
||||
#include "ddr_mngr.h"
|
||||
#include "audio_utils.h"
|
||||
@@ -33,131 +37,11 @@
|
||||
#include "effects_hw_v2.h"
|
||||
#include "effects_v2.h"
|
||||
#include "pwrdet_hw.h"
|
||||
#include "vad.h"
|
||||
|
||||
#define DRV_NAME "audio-ddr-manager"
|
||||
|
||||
static DEFINE_MUTEX(ddr_mutex);
|
||||
#if 0
|
||||
struct ddr_desc {
|
||||
/* start address of DDR */
|
||||
unsigned int start;
|
||||
/* finish address of DDR */
|
||||
unsigned int finish;
|
||||
/* interrupt address or counts of DDR blocks */
|
||||
unsigned int intrpt;
|
||||
/* fifo total counts */
|
||||
unsigned int fifo_depth;
|
||||
/* fifo start threshold */
|
||||
unsigned int fifo_thr;
|
||||
enum ddr_types data_type;
|
||||
unsigned int edian;
|
||||
unsigned int pp_mode;
|
||||
//unsigned int reg_base;
|
||||
struct clk *ddr;
|
||||
struct clk *ddr_arb;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ddr_chipinfo {
|
||||
/* INT and Start address is same or separated */
|
||||
bool int_start_same_addr;
|
||||
/* force finished */
|
||||
bool force_finished;
|
||||
/* same source */
|
||||
bool same_src_fn;
|
||||
/* insert channel number */
|
||||
bool insert_chnum;
|
||||
|
||||
/* ddr bus in urgent */
|
||||
bool ugt;
|
||||
|
||||
/* source sel switch to ctrl1
|
||||
* for toddr, 0: source sel is controlled by ctrl0
|
||||
* 1: source sel is controlled by ctrl1
|
||||
* for frddr, 0: source sel is controlled by ctrl0
|
||||
* 1: source sel is controlled by ctrl2
|
||||
*/
|
||||
bool src_sel_ctrl;
|
||||
|
||||
/*
|
||||
* resample source sel switch
|
||||
* resample : from ctrl0 to ctrl3
|
||||
* toddr : from ctrl0 to ctrl1
|
||||
*/
|
||||
bool asrc_src_sel_ctrl;
|
||||
/* spdif in 32bit, only support left justified */
|
||||
bool asrc_only_left_j;
|
||||
|
||||
/* toddr number max
|
||||
* 0: default, 3 toddr, axg, g12a, g12b
|
||||
* 4: 4 toddr, tl1
|
||||
*/
|
||||
int fifo_num;
|
||||
};
|
||||
|
||||
struct toddr {
|
||||
//struct ddr_desc dscrpt;
|
||||
struct device *dev;
|
||||
unsigned int resample: 1;
|
||||
unsigned int ext_signed: 1;
|
||||
unsigned int msb_bit;
|
||||
unsigned int lsb_bit;
|
||||
unsigned int reg_base;
|
||||
unsigned int bitdepth;
|
||||
unsigned int channels;
|
||||
unsigned int rate;
|
||||
enum toddr_src src;
|
||||
unsigned int fifo_id;
|
||||
|
||||
unsigned int asrc_src_sel;
|
||||
|
||||
int is_lb; /* check whether for loopback */
|
||||
int irq;
|
||||
bool in_use: 1;
|
||||
struct aml_audio_controller *actrl;
|
||||
struct ddr_chipinfo *chipinfo;
|
||||
};
|
||||
|
||||
enum status {
|
||||
DISABLED,
|
||||
READY, /* controls has set enable, but ddr is not in running */
|
||||
RUNNING,
|
||||
};
|
||||
|
||||
struct toddr_attach {
|
||||
bool enable;
|
||||
int id;
|
||||
int status;
|
||||
/* which module should be attached,
|
||||
* check which toddr in use should be attached
|
||||
*/
|
||||
enum toddr_src attach_module;
|
||||
};
|
||||
|
||||
struct frddr_attach {
|
||||
bool enable;
|
||||
int status;
|
||||
/* which module for attach ,
|
||||
* check which frddr in use should be added
|
||||
*/
|
||||
enum frddr_dest attach_module;
|
||||
};
|
||||
|
||||
struct frddr {
|
||||
//struct ddr_desc dscrpt;
|
||||
struct device *dev;
|
||||
enum frddr_dest dest;
|
||||
struct aml_audio_controller *actrl;
|
||||
unsigned int reg_base;
|
||||
unsigned int fifo_id;
|
||||
|
||||
unsigned int msb;
|
||||
unsigned int type;
|
||||
|
||||
int irq;
|
||||
bool in_use;
|
||||
struct ddr_chipinfo *chipinfo;
|
||||
};
|
||||
|
||||
#define DDRMAX 4
|
||||
static struct frddr frddrs[DDRMAX];
|
||||
@@ -173,6 +57,11 @@ static struct toddr_attach attach_pwrdet;
|
||||
static void aml_check_pwrdet(bool enable);
|
||||
static bool aml_check_pwrdet_module(int src);
|
||||
|
||||
/* VAD */
|
||||
static struct toddr_attach attach_vad;
|
||||
static void aml_check_vad(struct toddr *to, bool enable);
|
||||
|
||||
|
||||
/* Audio EQ DRC */
|
||||
static struct frddr_attach attach_aed;
|
||||
static void aml_check_aed(bool enable, int dst);
|
||||
@@ -213,7 +102,7 @@ static struct toddr *register_toddr_l(struct device *dev,
|
||||
to->dev = dev;
|
||||
to->actrl = actrl;
|
||||
to->in_use = true;
|
||||
pr_info("toddrs[%d] registered by device %s\n", i, dev_name(dev));
|
||||
pr_debug("toddrs[%d] registered by device %s\n", i, dev_name(dev));
|
||||
return to;
|
||||
}
|
||||
|
||||
@@ -259,7 +148,7 @@ static int unregister_toddr_l(struct device *dev, void *data)
|
||||
to->dev = NULL;
|
||||
to->actrl = NULL;
|
||||
to->in_use = false;
|
||||
pr_info("toddrs[%d] released by device %s\n", i, dev_name(dev));
|
||||
pr_debug("toddrs[%d] released by device %s\n", i, dev_name(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -275,8 +164,6 @@ int fetch_toddr_index_by_src(int toddr_src)
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("invalid toddr src\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -291,8 +178,6 @@ struct toddr *fetch_toddr_by_src(int toddr_src)
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("invalid toddr src\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -332,6 +217,9 @@ int aml_toddr_set_buf(struct toddr *to, unsigned int start,
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
to->start_addr = start;
|
||||
to->end_addr = end;
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_START_ADDR, reg_base);
|
||||
aml_audiobus_write(actrl, reg, start);
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_FINISH_ADDR, reg_base);
|
||||
@@ -347,6 +235,41 @@ int aml_toddr_set_buf(struct toddr *to, unsigned int start,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aml_toddr_set_buf_startaddr(struct toddr *to, unsigned int start)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
to->start_addr = start;
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_START_ADDR, reg_base);
|
||||
aml_audiobus_write(actrl, reg, start);
|
||||
|
||||
/* int address */
|
||||
if (to->chipinfo
|
||||
&& (!to->chipinfo->int_start_same_addr)) {
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_INIT_ADDR, reg_base);
|
||||
aml_audiobus_write(actrl, reg, start);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aml_toddr_set_buf_endaddr(struct toddr *to, unsigned int end)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
to->end_addr = end;
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_FINISH_ADDR, reg_base);
|
||||
aml_audiobus_write(actrl, reg, end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aml_toddr_set_intrpt(struct toddr *to, unsigned int intrpt)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
@@ -371,6 +294,34 @@ unsigned int aml_toddr_get_position(struct toddr *to)
|
||||
return aml_audiobus_read(actrl, reg);
|
||||
}
|
||||
|
||||
unsigned int aml_toddr_get_addr(struct toddr *to, enum status_sel sel)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg_sel, reg, addr;
|
||||
|
||||
reg_sel = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
|
||||
aml_audiobus_update_bits(actrl, reg_sel,
|
||||
0xf << 8,
|
||||
sel << 8);
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_STATUS2, reg_base);
|
||||
addr = aml_audiobus_read(actrl, reg);
|
||||
|
||||
if (sel == VAD_WAKEUP_ADDR) {
|
||||
/* clear VAD addr/cnt */
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
|
||||
aml_audiobus_update_bits(actrl, reg, 0x1 << 1, 0x1 << 1);
|
||||
}
|
||||
|
||||
/* reset to default, current write addr */
|
||||
aml_audiobus_update_bits(actrl, reg_sel,
|
||||
0xf << 8,
|
||||
0x0 << 8);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void aml_toddr_enable(struct toddr *to, bool enable)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
@@ -383,9 +334,16 @@ void aml_toddr_enable(struct toddr *to, bool enable)
|
||||
/* check resample */
|
||||
aml_check_resample(to, enable);
|
||||
|
||||
/* check power detect */
|
||||
if (aml_check_pwrdet_module(to->src))
|
||||
aml_check_pwrdet(enable);
|
||||
if (to->chipinfo
|
||||
&& to->chipinfo->wakeup) {
|
||||
if (to->chipinfo->wakeup == 1) {
|
||||
/* check power detect */
|
||||
if (aml_check_pwrdet_module(to->src))
|
||||
aml_check_pwrdet(enable);
|
||||
} else if (to->chipinfo->wakeup == 2)
|
||||
/* check VAD */
|
||||
aml_check_vad(to, enable);
|
||||
}
|
||||
|
||||
if (!enable)
|
||||
aml_audiobus_write(actrl, reg, 0x0);
|
||||
@@ -444,6 +402,35 @@ void aml_toddr_set_fifos(struct toddr *to, unsigned int thresh)
|
||||
}
|
||||
}
|
||||
|
||||
void aml_toddr_update_fifos_rd_th(struct toddr *to, int th)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg, mask, val;
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
|
||||
if (to->chipinfo
|
||||
&& to->chipinfo->src_sel_ctrl) {
|
||||
mask = 0xfff << 12;
|
||||
val = (th - 1) << 12;
|
||||
} else {
|
||||
mask = 0xff << 16;
|
||||
val = (th - 1) << 16;
|
||||
}
|
||||
aml_audiobus_update_bits(actrl, reg, mask, val);
|
||||
}
|
||||
|
||||
void aml_toddr_force_finish(struct toddr *to)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
|
||||
aml_audiobus_update_bits(actrl, reg, 1 << 25, 1 << 25);
|
||||
aml_audiobus_update_bits(actrl, reg, 1 << 25, 0 << 25);
|
||||
}
|
||||
|
||||
void aml_toddr_set_format(struct toddr *to, struct toddr_fmt *fmt)
|
||||
{
|
||||
struct aml_audio_controller *actrl = to->actrl;
|
||||
@@ -499,8 +486,6 @@ void aml_toddr_set_resample(struct toddr *to, bool enable)
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
pr_info("toddr selects data to %s resample\n",
|
||||
enable ? "enable" : "disable");
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
|
||||
aml_audiobus_update_bits(actrl, reg, 1<<30, enable<<30);
|
||||
}
|
||||
@@ -511,9 +496,6 @@ void aml_toddr_set_resample_ab(struct toddr *to, int asrc_src_sel, bool enable)
|
||||
unsigned int reg_base = to->reg_base;
|
||||
unsigned int reg;
|
||||
|
||||
pr_info("toddr selects data to %s resample %c\n",
|
||||
enable ? "enable" : "disable",
|
||||
(asrc_src_sel == 0) ? 'a' : 'b');
|
||||
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
|
||||
if (asrc_src_sel == 0)
|
||||
aml_audiobus_update_bits(actrl, reg, 1 << 27, enable << 27);
|
||||
@@ -546,11 +528,12 @@ static void aml_resample_enable(
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("Resample %d in running, module:%d, toddr:%d, asrc_src_sel:%d\n",
|
||||
p_attach_resample->id,
|
||||
p_attach_resample->attach_module,
|
||||
pr_info("toddr %d selects data to %s resample_%c for module:%s\n",
|
||||
to->fifo_id,
|
||||
to->asrc_src_sel);
|
||||
enable ? "enable" : "disable",
|
||||
(p_attach_resample->id == 0) ? 'a' : 'b',
|
||||
toddr_src_get_str(p_attach_resample->attach_module)
|
||||
);
|
||||
|
||||
if (enable) {
|
||||
int bitwidth = to->bitdepth;
|
||||
@@ -708,17 +691,18 @@ void aml_pwrdet_enable(bool enable, int pwrdet_module)
|
||||
|
||||
if (!to) {
|
||||
attach_pwrdet.status = READY;
|
||||
pr_info("not in capture, power detect is ready\n");
|
||||
} else {
|
||||
attach_pwrdet.status = RUNNING;
|
||||
aml_set_pwrdet(to, enable);
|
||||
pr_info("Capture with power detect\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (attach_pwrdet.status == RUNNING) {
|
||||
struct toddr *to = fetch_toddr_by_src(pwrdet_module);
|
||||
|
||||
aml_set_pwrdet(to, enable);
|
||||
if (to)
|
||||
aml_set_pwrdet(to, enable);
|
||||
}
|
||||
attach_pwrdet.status = DISABLED;
|
||||
}
|
||||
@@ -751,6 +735,79 @@ static void aml_check_pwrdet(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static void aml_vad_enable(
|
||||
struct toddr_attach *p_attach_vad,
|
||||
bool enable)
|
||||
{
|
||||
struct toddr *to = fetch_toddr_by_src(p_attach_vad->attach_module);
|
||||
|
||||
if (!to)
|
||||
return;
|
||||
|
||||
vad_set_toddr_info(enable ? to : NULL);
|
||||
|
||||
/* vad enable or not */
|
||||
vad_enable(enable);
|
||||
}
|
||||
|
||||
void aml_set_vad(bool enable, int module)
|
||||
{
|
||||
struct toddr_attach *p_attach_vad = &attach_vad;
|
||||
bool update_running = false;
|
||||
|
||||
p_attach_vad->enable = enable;
|
||||
p_attach_vad->attach_module = module;
|
||||
|
||||
if (enable) {
|
||||
if ((p_attach_vad->status == DISABLED)
|
||||
|| (p_attach_vad->status == READY)) {
|
||||
struct toddr *to = fetch_toddr_by_src(
|
||||
p_attach_vad->attach_module);
|
||||
|
||||
if (!to) {
|
||||
p_attach_vad->status = READY;
|
||||
} else {
|
||||
p_attach_vad->status = RUNNING;
|
||||
update_running = true;
|
||||
pr_info("Capture with VAD\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (p_attach_vad->status == RUNNING)
|
||||
update_running = true;
|
||||
|
||||
p_attach_vad->status = DISABLED;
|
||||
}
|
||||
|
||||
if (update_running)
|
||||
aml_vad_enable(p_attach_vad, enable);
|
||||
}
|
||||
|
||||
/*
|
||||
* when try to enable vad, if toddr is not in used,
|
||||
* set vad status as ready
|
||||
*/
|
||||
static void aml_check_vad(struct toddr *to, bool enable)
|
||||
{
|
||||
struct toddr_attach *p_attach_vad = &attach_vad;
|
||||
bool is_vad = false;
|
||||
|
||||
if (p_attach_vad->enable
|
||||
&& (to->src == p_attach_vad->attach_module))
|
||||
is_vad = true;
|
||||
|
||||
/* vad in enable */
|
||||
if (is_vad) {
|
||||
if (enable)
|
||||
p_attach_vad->status = RUNNING;
|
||||
else
|
||||
p_attach_vad->status = DISABLED;
|
||||
|
||||
aml_vad_enable(p_attach_vad, enable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* from DDRS */
|
||||
static struct frddr *register_frddr_l(struct device *dev,
|
||||
struct aml_audio_controller *actrl,
|
||||
@@ -786,7 +843,7 @@ static struct frddr *register_frddr_l(struct device *dev,
|
||||
from->dev = dev;
|
||||
from->actrl = actrl;
|
||||
from->in_use = true;
|
||||
pr_info("frddrs[%d] registered by device %s\n", i, dev_name(dev));
|
||||
pr_debug("frddrs[%d] registered by device %s\n", i, dev_name(dev));
|
||||
return from;
|
||||
}
|
||||
|
||||
@@ -826,7 +883,7 @@ static int unregister_frddr_l(struct device *dev, void *data)
|
||||
from->dev = NULL;
|
||||
from->actrl = NULL;
|
||||
from->in_use = false;
|
||||
pr_info("frddrs[%d] released by device %s\n", i, dev_name(dev));
|
||||
pr_debug("frddrs[%d] released by device %s\n", i, dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -841,7 +898,6 @@ int fetch_frddr_index_by_src(int frddr_src)
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("invalid frdd_src\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -856,8 +912,6 @@ struct frddr *fetch_frddr_by_src(int frddr_src)
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("invalid frddr src\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -877,7 +931,8 @@ int aml_check_sharebuffer_valid(struct frddr *fr, int ss_sel)
|
||||
&& (frddrs[i].fifo_id != current_fifo_id)
|
||||
&& (frddrs[i].dest == ss_sel)) {
|
||||
|
||||
pr_info("ss_sel:%d used, invalid for share buffer\n",
|
||||
pr_info(" frddr:%d, ss_sel:%d used, invalid for share buffer\n",
|
||||
i,
|
||||
ss_sel);
|
||||
ret = 0;
|
||||
break;
|
||||
@@ -971,9 +1026,18 @@ void aml_frddr_enable(struct frddr *fr, bool enable)
|
||||
/* ensure disable before enable frddr */
|
||||
aml_audiobus_update_bits(actrl, reg, 1<<31, enable<<31);
|
||||
|
||||
if (!enable)
|
||||
if (!enable) {
|
||||
aml_audiobus_write(actrl, reg, 0x0);
|
||||
|
||||
/* clr src sel and its en */
|
||||
if (fr->chipinfo
|
||||
&& fr->chipinfo->src_sel_ctrl) {
|
||||
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2,
|
||||
reg_base);
|
||||
aml_audiobus_write(actrl, reg, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for Audio EQ/DRC */
|
||||
if (aml_check_aed_module(fr->dest))
|
||||
aml_check_aed(enable, fr->dest);
|
||||
@@ -1019,8 +1083,6 @@ void aml_frddr_select_dst_ss(struct frddr *fr,
|
||||
unsigned int reg_base = fr->reg_base;
|
||||
unsigned int reg, ss_valid;
|
||||
|
||||
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base);
|
||||
|
||||
ss_valid = aml_check_sharebuffer_valid(fr, dst);
|
||||
|
||||
/* same source en */
|
||||
@@ -1029,21 +1091,54 @@ void aml_frddr_select_dst_ss(struct frddr *fr,
|
||||
&& ss_valid) {
|
||||
int s_v = 0, s_m = 0;
|
||||
|
||||
switch (sel) {
|
||||
case 1:
|
||||
s_m = 0xf << 4;
|
||||
s_v = enable ? (dst << 4 | 1 << 7) : 0 << 4;
|
||||
break;
|
||||
case 2:
|
||||
s_m = 0xf << 8;
|
||||
s_v = enable ? (dst << 8 | 1 << 11) : 0 << 8;
|
||||
break;
|
||||
default:
|
||||
pr_warn_once("sel :%d is not supported for same source\n",
|
||||
sel);
|
||||
break;
|
||||
if (fr->chipinfo
|
||||
&& fr->chipinfo->src_sel_ctrl) {
|
||||
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2,
|
||||
reg_base);
|
||||
|
||||
switch (sel) {
|
||||
case 1:
|
||||
s_m = 0x17 << 8;
|
||||
s_v = enable ?
|
||||
(dst << 8 | 1 << 12) : 0 << 8;
|
||||
break;
|
||||
case 2:
|
||||
s_m = 0x17 << 16;
|
||||
s_v = enable ?
|
||||
(dst << 16 | 1 << 20) : 0 << 16;
|
||||
break;
|
||||
default:
|
||||
pr_warn_once("sel :%d is not supported for same source\n",
|
||||
sel);
|
||||
break;
|
||||
}
|
||||
s_m |= 0xff << 24;
|
||||
if (enable)
|
||||
s_v |= (fr->channels - 1) << 24;
|
||||
else
|
||||
s_v |= 0x0 << 24;
|
||||
} else {
|
||||
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0,
|
||||
reg_base);
|
||||
|
||||
switch (sel) {
|
||||
case 1:
|
||||
s_m = 0xf << 4;
|
||||
s_v = enable ?
|
||||
(dst << 4 | 1 << 7) : 0 << 4;
|
||||
break;
|
||||
case 2:
|
||||
s_m = 0xf << 8;
|
||||
s_v = enable ?
|
||||
(dst << 8 | 1 << 11) : 0 << 8;
|
||||
break;
|
||||
default:
|
||||
pr_warn_once("sel :%d is not supported for same source\n",
|
||||
sel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pr_info("%s sel:%d, dst_src:%d\n",
|
||||
pr_debug("%s sel:%d, dst_src:%d\n",
|
||||
__func__, sel, dst);
|
||||
aml_audiobus_update_bits(actrl, reg, s_m, s_v);
|
||||
}
|
||||
@@ -1073,8 +1168,11 @@ unsigned int aml_frddr_get_fifo_id(struct frddr *fr)
|
||||
}
|
||||
|
||||
void aml_frddr_set_format(struct frddr *fr,
|
||||
unsigned int msb, unsigned int frddr_type)
|
||||
unsigned int chnum,
|
||||
unsigned int msb,
|
||||
unsigned int frddr_type)
|
||||
{
|
||||
fr->channels = chnum;
|
||||
fr->msb = msb;
|
||||
fr->type = frddr_type;
|
||||
}
|
||||
@@ -1336,11 +1434,13 @@ int card_add_ddr_kcontrols(struct snd_soc_card *card)
|
||||
static struct ddr_chipinfo axg_ddr_chipinfo = {
|
||||
.int_start_same_addr = true,
|
||||
.asrc_only_left_j = true,
|
||||
.wakeup = 1,
|
||||
};
|
||||
|
||||
static struct ddr_chipinfo g12a_ddr_chipinfo = {
|
||||
.same_src_fn = true,
|
||||
.asrc_only_left_j = true,
|
||||
.wakeup = 1,
|
||||
};
|
||||
|
||||
static struct ddr_chipinfo tl1_ddr_chipinfo = {
|
||||
@@ -1349,6 +1449,7 @@ static struct ddr_chipinfo tl1_ddr_chipinfo = {
|
||||
.src_sel_ctrl = true,
|
||||
.asrc_src_sel_ctrl = true,
|
||||
.fifo_num = 4,
|
||||
.wakeup = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id aml_ddr_mngr_device_id[] = {
|
||||
@@ -1368,11 +1469,45 @@ static const struct of_device_id aml_ddr_mngr_device_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aml_ddr_mngr_device_id);
|
||||
|
||||
static bool pm_audio_in_suspend;
|
||||
|
||||
void pm_audio_set_suspend(bool is_suspend)
|
||||
{
|
||||
pm_audio_in_suspend = is_suspend;
|
||||
}
|
||||
|
||||
bool pm_audio_is_suspend(void)
|
||||
{
|
||||
return pm_audio_in_suspend;
|
||||
}
|
||||
|
||||
/* Detects a suspend and resume event */
|
||||
static int ddr_pm_event(struct notifier_block *notifier,
|
||||
unsigned long pm_event, void *unused)
|
||||
{
|
||||
pr_info("%s, pm_event:%lu\n", __func__, pm_event);
|
||||
|
||||
switch (pm_event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
pm_audio_set_suspend(true);
|
||||
break;
|
||||
case PM_POST_SUSPEND:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block ddr_pm_notifier_block = {
|
||||
.notifier_call = ddr_pm_event,
|
||||
};
|
||||
|
||||
static int aml_ddr_mngr_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ddr_chipinfo *p_ddr_chipinfo;
|
||||
int ddr_num = 3; /* early chipset support max 3 ddr num */
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
p_ddr_chipinfo = (struct ddr_chipinfo *)
|
||||
of_device_get_match_data(&pdev->dev);
|
||||
@@ -1436,6 +1571,12 @@ static int aml_ddr_mngr_platform_probe(struct platform_device *pdev)
|
||||
frddrs[DDR_D].fifo_id = DDR_D;
|
||||
}
|
||||
}
|
||||
|
||||
ret = register_pm_notifier(&ddr_pm_notifier_block);
|
||||
if (ret)
|
||||
pr_warn("[%s] failed to register PM notifier %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,16 @@ enum frddr_dest {
|
||||
SPDIFOUT_B,
|
||||
};
|
||||
|
||||
enum status_sel {
|
||||
CURRENT_DDR_ADDR,
|
||||
NEXT_FINISH_ADDR,
|
||||
COUNT_CURRENT_DDR_ACK,
|
||||
COUNT_NEXT_FINISH_DDR_ACK,
|
||||
VAD_WAKEUP_ADDR, /* from tl1, vad */
|
||||
VAD_FS_ADDR,
|
||||
VAD_FIFO_CNT,
|
||||
};
|
||||
|
||||
struct toddr_fmt {
|
||||
unsigned int type;
|
||||
unsigned int msb;
|
||||
@@ -83,6 +93,140 @@ struct toddr_fmt {
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
#if 0
|
||||
struct ddr_desc {
|
||||
/* start address of DDR */
|
||||
unsigned int start;
|
||||
/* finish address of DDR */
|
||||
unsigned int finish;
|
||||
/* interrupt address or counts of DDR blocks */
|
||||
unsigned int intrpt;
|
||||
/* fifo total counts */
|
||||
unsigned int fifo_depth;
|
||||
/* fifo start threshold */
|
||||
unsigned int fifo_thr;
|
||||
enum ddr_types data_type;
|
||||
unsigned int edian;
|
||||
unsigned int pp_mode;
|
||||
//unsigned int reg_base;
|
||||
struct clk *ddr;
|
||||
struct clk *ddr_arb;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ddr_chipinfo {
|
||||
/* INT and Start address is same or separated */
|
||||
bool int_start_same_addr;
|
||||
/* force finished */
|
||||
bool force_finished;
|
||||
/* same source */
|
||||
bool same_src_fn;
|
||||
/* insert channel number */
|
||||
bool insert_chnum;
|
||||
|
||||
/* ddr bus in urgent */
|
||||
bool ugt;
|
||||
|
||||
/* source sel switch to ctrl1
|
||||
* for toddr, 0: source sel is controlled by ctrl0
|
||||
* 1: source sel is controlled by ctrl1
|
||||
* for frddr, 0: source sel is controlled by ctrl0
|
||||
* 1: source sel is controlled by ctrl2
|
||||
*/
|
||||
bool src_sel_ctrl;
|
||||
|
||||
/*
|
||||
* resample source sel switch
|
||||
* resample : from ctrl0 to ctrl3
|
||||
* toddr : from ctrl0 to ctrl1
|
||||
*/
|
||||
bool asrc_src_sel_ctrl;
|
||||
/* spdif in 32bit, only support left justified */
|
||||
bool asrc_only_left_j;
|
||||
|
||||
/* toddr number max
|
||||
* 0: default, 3 toddr, axg, g12a, g12b
|
||||
* 4: 4 toddr, tl1
|
||||
*/
|
||||
int fifo_num;
|
||||
|
||||
/* power detect or VAD
|
||||
* 0: disabled
|
||||
* 1: power detect
|
||||
* 2: vad
|
||||
*/
|
||||
int wakeup;
|
||||
};
|
||||
|
||||
struct toddr {
|
||||
//struct ddr_desc dscrpt;
|
||||
struct device *dev;
|
||||
unsigned int resample: 1;
|
||||
unsigned int ext_signed: 1;
|
||||
unsigned int msb_bit;
|
||||
unsigned int lsb_bit;
|
||||
unsigned int reg_base;
|
||||
unsigned int bitdepth;
|
||||
unsigned int channels;
|
||||
unsigned int rate;
|
||||
|
||||
unsigned int start_addr;
|
||||
unsigned int end_addr;
|
||||
|
||||
enum toddr_src src;
|
||||
unsigned int fifo_id;
|
||||
|
||||
unsigned int asrc_src_sel;
|
||||
|
||||
int is_lb; /* check whether for loopback */
|
||||
int irq;
|
||||
bool in_use: 1;
|
||||
struct aml_audio_controller *actrl;
|
||||
struct ddr_chipinfo *chipinfo;
|
||||
};
|
||||
|
||||
enum status {
|
||||
DISABLED,
|
||||
READY, /* controls has set enable, but ddr is not in running */
|
||||
RUNNING,
|
||||
};
|
||||
|
||||
struct toddr_attach {
|
||||
bool enable;
|
||||
int id;
|
||||
int status;
|
||||
/* which module should be attached,
|
||||
* check which toddr in use should be attached
|
||||
*/
|
||||
enum toddr_src attach_module;
|
||||
};
|
||||
|
||||
struct frddr_attach {
|
||||
bool enable;
|
||||
int status;
|
||||
/* which module for attach ,
|
||||
* check which frddr in use should be added
|
||||
*/
|
||||
enum frddr_dest attach_module;
|
||||
};
|
||||
|
||||
struct frddr {
|
||||
//struct ddr_desc dscrpt;
|
||||
struct device *dev;
|
||||
enum frddr_dest dest;
|
||||
struct aml_audio_controller *actrl;
|
||||
unsigned int reg_base;
|
||||
unsigned int fifo_id;
|
||||
|
||||
unsigned int channels;
|
||||
unsigned int msb;
|
||||
unsigned int type;
|
||||
|
||||
int irq;
|
||||
bool in_use;
|
||||
struct ddr_chipinfo *chipinfo;
|
||||
};
|
||||
|
||||
/* to ddrs */
|
||||
int fetch_toddr_index_by_src(int toddr_src);
|
||||
struct toddr *fetch_toddr_by_src(int toddr_src);
|
||||
@@ -92,11 +236,17 @@ struct toddr *aml_audio_register_toddr(struct device *dev,
|
||||
int aml_audio_unregister_toddr(struct device *dev, void *data);
|
||||
int aml_toddr_set_buf(struct toddr *to, unsigned int start,
|
||||
unsigned int end);
|
||||
int aml_toddr_set_buf_startaddr(struct toddr *to, unsigned int start);
|
||||
int aml_toddr_set_buf_endaddr(struct toddr *to, unsigned int end);
|
||||
|
||||
int aml_toddr_set_intrpt(struct toddr *to, unsigned int intrpt);
|
||||
unsigned int aml_toddr_get_position(struct toddr *to);
|
||||
unsigned int aml_toddr_get_addr(struct toddr *to, enum status_sel sel);
|
||||
void aml_toddr_select_src(struct toddr *to, enum toddr_src);
|
||||
void aml_toddr_enable(struct toddr *to, bool enable);
|
||||
void aml_toddr_set_fifos(struct toddr *to, unsigned int thresh);
|
||||
void aml_toddr_update_fifos_rd_th(struct toddr *to, int th);
|
||||
void aml_toddr_force_finish(struct toddr *to);
|
||||
void aml_toddr_set_format(struct toddr *to, struct toddr_fmt *fmt);
|
||||
void aml_toddr_insert_chanum(struct toddr *to);
|
||||
unsigned int aml_toddr_read(struct toddr *to);
|
||||
@@ -106,6 +256,8 @@ void aml_toddr_write(struct toddr *to, unsigned int val);
|
||||
void aml_set_resample(int id, bool enable, int resample_module);
|
||||
/* power detect */
|
||||
void aml_pwrdet_enable(bool enable, int pwrdet_module);
|
||||
/* Voice Activity Detection */
|
||||
void aml_set_vad(bool enable, int module);
|
||||
|
||||
/* from ddrs */
|
||||
int fetch_frddr_index_by_src(int frddr_src);
|
||||
@@ -130,7 +282,9 @@ void aml_frddr_set_fifos(struct frddr *fr,
|
||||
unsigned int depth, unsigned int thresh);
|
||||
unsigned int aml_frddr_get_fifo_id(struct frddr *fr);
|
||||
void aml_frddr_set_format(struct frddr *fr,
|
||||
unsigned int msb, unsigned int frddr_type);
|
||||
unsigned int chnum,
|
||||
unsigned int msb,
|
||||
unsigned int frddr_type);
|
||||
/* audio eq drc */
|
||||
void aml_set_aed(bool enable, int aed_module);
|
||||
|
||||
@@ -144,5 +298,8 @@ const char *frddr_src_get_str(int idx);
|
||||
|
||||
int card_add_ddr_kcontrols(struct snd_soc_card *card);
|
||||
|
||||
void pm_audio_set_suspend(bool is_suspend);
|
||||
bool pm_audio_is_suspend(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -613,6 +613,72 @@ static int frhdmirx_set_mode(
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_HDMI
|
||||
/* spdif in audio format detect: LPCM or NONE-LPCM */
|
||||
struct sppdif_audio_info {
|
||||
unsigned char aud_type;
|
||||
/*IEC61937 package presamble Pc value*/
|
||||
short pc;
|
||||
char *aud_type_str;
|
||||
};
|
||||
|
||||
static const char *const spdif_audio_type_texts[] = {
|
||||
"LPCM",
|
||||
"AC3",
|
||||
"EAC3",
|
||||
"DTS",
|
||||
"DTS-HD",
|
||||
"TRUEHD",
|
||||
"PAUSE"
|
||||
};
|
||||
|
||||
static const struct sppdif_audio_info type_texts[] = {
|
||||
{0, 0, "LPCM"},
|
||||
{1, 0x1, "AC3"},
|
||||
{2, 0x15, "EAC3"},
|
||||
{3, 0xb, "DTS-I"},
|
||||
{3, 0x0c, "DTS-II"},
|
||||
{3, 0x0d, "DTS-III"},
|
||||
{3, 0x11, "DTS-IV"},
|
||||
{4, 0, "DTS-HD"},
|
||||
{5, 0x16, "TRUEHD"},
|
||||
{6, 0x103, "PAUSE"},
|
||||
{6, 0x003, "PAUSE"},
|
||||
{6, 0x100, "PAUSE"},
|
||||
};
|
||||
|
||||
static const struct soc_enum hdmirx_audio_type_enum =
|
||||
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(spdif_audio_type_texts),
|
||||
spdif_audio_type_texts);
|
||||
|
||||
static int hdmiin_check_audio_type(void)
|
||||
{
|
||||
int total_num = sizeof(type_texts)/sizeof(struct sppdif_audio_info);
|
||||
int pc = frhdmirx_get_chan_status_pc();
|
||||
int audio_type = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < total_num; i++) {
|
||||
if (pc == type_texts[i].pc) {
|
||||
audio_type = type_texts[i].aud_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s audio type:%d\n", __func__, audio_type);
|
||||
|
||||
return audio_type;
|
||||
}
|
||||
|
||||
static int hdmirx_audio_type_get_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
hdmiin_check_audio_type();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct snd_kcontrol_new extn_controls[] = {
|
||||
/* Out */
|
||||
@@ -664,6 +730,10 @@ static const struct snd_kcontrol_new extn_controls[] = {
|
||||
0,
|
||||
aml_get_atmos_audio_edid,
|
||||
aml_set_atmos_audio_edid),
|
||||
SOC_ENUM_EXT("HDMIIN Audio Type",
|
||||
hdmirx_audio_type_enum,
|
||||
hdmirx_audio_type_get_enum,
|
||||
NULL),
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -723,8 +793,8 @@ static int extn_platform_probe(struct platform_device *pdev)
|
||||
/* Default ARC SRC */
|
||||
p_extn->arc_src = 1;
|
||||
|
||||
/* Default: PAO mode */
|
||||
p_extn->hdmirx_mode = 1;
|
||||
/* Default: SPDIF in mode */
|
||||
p_extn->hdmirx_mode = 0;
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev,
|
||||
&extn_component,
|
||||
|
||||
@@ -84,7 +84,11 @@ void frhdmirx_ctrl(int channels, int src)
|
||||
/* PAO mode */
|
||||
if (src) {
|
||||
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL0,
|
||||
0x1 << 23 | 0x1 << 22 | 0x1 << 7);
|
||||
0x1 << 23 | /* slect pao mode */
|
||||
0x1 << 22 | /* capture input by fall edge*/
|
||||
0x1 << 7 | /* start sending ch num info out */
|
||||
0x1 << 8 | /* start detect PAPB */
|
||||
0x1 << 6 /* chan status sel: pao pc/pd value */);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +105,7 @@ void frhdmirx_ctrl(int channels, int src)
|
||||
0x1 << 30 | /* chnum_sel */
|
||||
lane_mask << 24 | /* chnum_sel */
|
||||
0x1 << 22 | /* clk_inv */
|
||||
0x0 << 11 /* req_sel, Sync 4 spdifin by which */
|
||||
0x0 << 11 /* req_sel, Sync 4 spdifin by which */
|
||||
);
|
||||
|
||||
/* nonpcm2pcm_th */
|
||||
@@ -110,3 +114,14 @@ void frhdmirx_ctrl(int channels, int src)
|
||||
/* enable irq bits */
|
||||
frhdmirx_enable_irq_bits(channels, src);
|
||||
}
|
||||
|
||||
unsigned int frhdmirx_get_chan_status_pc(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = audiobus_read(EE_AUDIO_FRHDMIRX_STAT1);
|
||||
return (val >> 16) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
extern void frhdmirx_enable(bool enable);
|
||||
extern void frhdmirx_src_select(int src);
|
||||
extern void frhdmirx_ctrl(int channels, int src);
|
||||
|
||||
extern unsigned int frhdmirx_get_chan_status_pc(void);
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@@ -192,7 +194,7 @@ void audio_locker_set(int enable)
|
||||
int audio_locker_get(void)
|
||||
{
|
||||
if (!s_locker) {
|
||||
pr_err("audio locker is not init\n");
|
||||
pr_debug("audio locker is not init\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "iomap.h"
|
||||
#include "regs.h"
|
||||
#include "ddr_mngr.h"
|
||||
#include "vad.h"
|
||||
|
||||
/*#define __PTM_PDM_CLK__*/
|
||||
|
||||
@@ -55,8 +56,8 @@ static struct snd_pcm_hardware aml_pdm_hardware = {
|
||||
.channels_min = PDM_CHANNELS_MIN,
|
||||
.channels_max = PDM_CHANNELS_MAX,
|
||||
|
||||
.buffer_bytes_max = 32 * 1024,
|
||||
.period_bytes_max = 16 * 1024,
|
||||
.buffer_bytes_max = 512 * 1024,
|
||||
.period_bytes_max = 256 * 1024,
|
||||
.period_bytes_min = 32,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
@@ -592,6 +593,10 @@ static int aml_pdm_dai_prepare(
|
||||
unsigned int bitwidth;
|
||||
unsigned int toddr_type, lsb;
|
||||
|
||||
if (vad_pdm_is_running()
|
||||
&& pm_audio_is_suspend())
|
||||
return 0;
|
||||
|
||||
/* set bclk */
|
||||
bitwidth = snd_pcm_format_width(runtime->format);
|
||||
lsb = 32 - bitwidth;
|
||||
@@ -690,12 +695,21 @@ static int aml_pdm_dai_trigger(
|
||||
{
|
||||
struct aml_pdm *p_pdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
pr_info("%s, cmd:%d\n", __func__, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
|
||||
if (vad_pdm_is_running()
|
||||
&& pm_audio_is_suspend()) {
|
||||
pm_audio_set_suspend(false);
|
||||
/* VAD switch to alsa buffer */
|
||||
vad_update_buffer(0);
|
||||
break;
|
||||
}
|
||||
|
||||
pdm_fifo_reset();
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
@@ -709,6 +723,13 @@ static int aml_pdm_dai_trigger(
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (vad_pdm_is_running()
|
||||
&& pm_audio_is_suspend()) {
|
||||
/* switch to VAD buffer */
|
||||
vad_update_buffer(1);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(substream->pcm->card->dev, "pdm capture stop\n");
|
||||
pdm_enable(0);
|
||||
aml_toddr_enable(p_pdm->tddr, 0);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@@ -82,7 +84,7 @@ static struct audioresample *get_audioresample(int id)
|
||||
p_resample = ((id == 0) ? s_resample_a : s_resample_b);
|
||||
|
||||
if (!p_resample) {
|
||||
pr_info("Not init audio resample\n");
|
||||
pr_debug("Not init audio resample\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -105,19 +107,6 @@ static int resample_clk_set(struct audioresample *p_resample)
|
||||
|
||||
/* enable clock */
|
||||
if (p_resample->enable) {
|
||||
ret = clk_prepare_enable(p_resample->clk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable resample_clk clock: %d\n",
|
||||
ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(p_resample->sclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable resample_src clock: %d\n",
|
||||
ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (p_resample->out_rate) {
|
||||
#ifdef __PTM_RESAMPLE_CLK__
|
||||
@@ -144,6 +133,20 @@ static int resample_clk_set(struct audioresample *p_resample)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(p_resample->sclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable resample_src clock: %d\n",
|
||||
ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(p_resample->clk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable resample_clk clock: %d\n",
|
||||
ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("%s, resample_pll:%lu, sclk:%lu, clk:%lu\n",
|
||||
__func__,
|
||||
clk_get_rate(p_resample->pll),
|
||||
@@ -226,7 +229,7 @@ static int resample_get_enum(
|
||||
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_resample) {
|
||||
pr_info("audio resample is not init\n");
|
||||
pr_debug("audio resample is not init\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -248,8 +251,9 @@ int resample_set(int id, int index)
|
||||
|
||||
p_resample->asrc_rate_idx = index;
|
||||
|
||||
pr_info("%s %s\n",
|
||||
pr_info("%s resample_%c %s\n",
|
||||
__func__,
|
||||
(id == 0) ? 'a' : 'b',
|
||||
auge_resample_texts[index]);
|
||||
|
||||
if (audio_resample_set(p_resample, (bool)index, resample_rate))
|
||||
@@ -274,7 +278,7 @@ static int resample_set_enum(
|
||||
int index = ucontrol->value.enumerated.item[0];
|
||||
|
||||
if (!p_resample) {
|
||||
pr_info("audio resample is not init\n");
|
||||
pr_debug("audio resample is not init\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -355,7 +359,7 @@ static int resample_module_get_enum(
|
||||
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_resample) {
|
||||
pr_info("audio resample is not init\n");
|
||||
pr_debug("audio resample is not init\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -371,7 +375,7 @@ static int resample_module_set_enum(
|
||||
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_resample) {
|
||||
pr_info("audio resample is not init\n");
|
||||
pr_debug("audio resample is not init\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -582,4 +586,3 @@ static struct platform_driver resample_platform_driver = {
|
||||
.probe = resample_platform_probe,
|
||||
};
|
||||
module_platform_driver(resample_platform_driver);
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ int resample_init(int id, int input_sr)
|
||||
else
|
||||
pr_err("unsupport input sample rate:%d\n", input_sr);
|
||||
|
||||
pr_info("resample id:%d, clk_rate = %u, input_sr = %d, Avg_cnt_init = %u\n",
|
||||
id,
|
||||
pr_info("resample id:%c, clk_rate = %u, input_sr = %d, Avg_cnt_init = %u\n",
|
||||
(id == 0) ? 'a' : 'b',
|
||||
clk_rate,
|
||||
input_sr,
|
||||
Avg_cnt_init);
|
||||
|
||||
@@ -34,7 +34,9 @@ static int sharebuffer_spdifout_prepare(struct snd_pcm_substream *substream,
|
||||
|
||||
spdifout_samesource_set(spdif_id,
|
||||
aml_frddr_get_fifo_id(fr),
|
||||
bit_depth, true);
|
||||
bit_depth,
|
||||
runtime->channels,
|
||||
true);
|
||||
|
||||
/* spdif to hdmitx */
|
||||
spdifout_to_hdmitx_ctrl(spdif_id);
|
||||
@@ -59,7 +61,9 @@ static int sharebuffer_spdifout_free(struct snd_pcm_substream *substream,
|
||||
if (spdif_id != 1)
|
||||
spdifout_samesource_set(spdif_id,
|
||||
aml_frddr_get_fifo_id(fr),
|
||||
bit_depth, false);
|
||||
bit_depth,
|
||||
runtime->channels,
|
||||
false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -90,7 +94,8 @@ int sharebuffer_prepare(struct snd_pcm_substream *substream,
|
||||
// TODO: same with tdm
|
||||
} else if (samesource_sel < 5) {
|
||||
/* same source with spdif a/b */
|
||||
sharebuffer_spdifout_prepare(substream, fr, samesource_sel - 3);
|
||||
sharebuffer_spdifout_prepare(substream,
|
||||
fr, samesource_sel - 3);
|
||||
}
|
||||
|
||||
/* frddr, share buffer, src_sel1 */
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -51,6 +51,8 @@
|
||||
/* for debug */
|
||||
/*#define __SPDIFIN_INSERT_CHNUM__*/
|
||||
|
||||
/*#define __SPDIFIN_AUDIO_TYPE_HW__*/
|
||||
|
||||
struct spdif_chipinfo {
|
||||
unsigned int id;
|
||||
|
||||
@@ -236,7 +238,7 @@ static int spdifin_check_audio_type(void)
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s audio type:%d\n", __func__, audio_type);
|
||||
/*pr_debug("%s audio type:%d\n", __func__, audio_type);*/
|
||||
|
||||
return audio_type;
|
||||
}
|
||||
@@ -253,6 +255,7 @@ static int spdifin_audio_type_get_enum(
|
||||
|
||||
/* For fake */
|
||||
static bool is_mute;
|
||||
static int spdifin_src;
|
||||
static int aml_audio_set_spdif_mute(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
@@ -268,6 +271,36 @@ static int aml_audio_get_spdif_mute(struct snd_kcontrol *kcontrol,
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const char *const spdifin_src_texts[] = {
|
||||
"spdifin pad", "spdifout", "N/A", "HDMIRX"
|
||||
};
|
||||
|
||||
const struct soc_enum spdifin_src_enum =
|
||||
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(spdifin_src_texts),
|
||||
spdifin_src_texts);
|
||||
|
||||
int spdifin_source_get_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.enumerated.item[0] = spdifin_src;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spdifin_source_set_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int src = ucontrol->value.enumerated.item[0];
|
||||
|
||||
if (src > 3) {
|
||||
pr_err("bad parameter for spdifin src set\n");
|
||||
return -1;
|
||||
}
|
||||
spdifin_set_src(src);
|
||||
spdifin_src = src;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_spdif_controls[] = {
|
||||
|
||||
@@ -288,6 +321,10 @@ static const struct snd_kcontrol_new snd_spdif_controls[] = {
|
||||
SOC_SINGLE_BOOL_EXT("Audio spdif mute",
|
||||
0, aml_audio_get_spdif_mute,
|
||||
aml_audio_set_spdif_mute),
|
||||
SOC_ENUM_EXT("Audio spdifin source",
|
||||
spdifin_src_enum,
|
||||
spdifin_source_get_enum,
|
||||
spdifin_source_set_enum),
|
||||
|
||||
};
|
||||
|
||||
@@ -525,11 +562,12 @@ static void spdifin_status_event(struct aml_spdif *p_spdif)
|
||||
pr_info("Event: EXTCON_SPDIFIN_SAMPLERATE, new sample rate:%s\n",
|
||||
spdifin_samplerate[mode + 1]);
|
||||
|
||||
#ifdef __SPDIFIN_AUDIO_TYPE_HW__
|
||||
/* resample enable, by hw */
|
||||
if (!spdifin_check_audiotype_by_sw(p_spdif))
|
||||
resample_set(p_spdif->asrc_id,
|
||||
p_spdif->auto_asrc);
|
||||
|
||||
#endif
|
||||
extcon_set_state(p_spdif->edev,
|
||||
EXTCON_SPDIFIN_SAMPLERATE, 1);
|
||||
}
|
||||
@@ -547,16 +585,18 @@ static void spdifin_status_event(struct aml_spdif *p_spdif)
|
||||
EXTCON_SPDIFIN_AUDIOTYPE, 1);
|
||||
|
||||
#ifdef __PTM_SPDIF_CLK__
|
||||
#ifdef __SPDIFIN_AUDIO_TYPE_HW__
|
||||
/* resample disable, by hw */
|
||||
if (!spdifin_check_audiotype_by_sw(p_spdif))
|
||||
resample_set(p_spdif->asrc_id, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (intrpt_status & 0x10)
|
||||
pr_info("Pd changed\n");
|
||||
} else {
|
||||
if (intrpt_status & 0x8)
|
||||
pr_info("CH status changed\n");
|
||||
pr_debug("CH status changed\n");
|
||||
|
||||
if (intrpt_status & 0x10) {
|
||||
int val = spdifin_get_ch_status0to31();
|
||||
@@ -579,9 +619,11 @@ static void spdifin_status_event(struct aml_spdif *p_spdif)
|
||||
extcon_set_state(p_spdif->edev,
|
||||
EXTCON_SPDIFIN_AUDIOTYPE, 0);
|
||||
|
||||
#ifdef __SPDIFIN_AUDIO_TYPE_HW__
|
||||
/* resample to 48k, by hw */
|
||||
if (!spdifin_check_audiotype_by_sw(p_spdif))
|
||||
resample_set(p_spdif->asrc_id, p_spdif->auto_asrc);
|
||||
#endif
|
||||
}
|
||||
if (intrpt_status & 0x40)
|
||||
pr_info("valid changed\n");
|
||||
@@ -818,7 +860,7 @@ static struct snd_pcm_ops aml_spdif_ops = {
|
||||
.mmap = aml_spdif_mmap,
|
||||
};
|
||||
|
||||
#define PREALLOC_BUFFER (32 * 1024)
|
||||
#define PREALLOC_BUFFER (128 * 1024)
|
||||
#define PREALLOC_BUFFER_MAX (256 * 1024)
|
||||
static int aml_spdif_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
@@ -827,7 +869,7 @@ static int aml_spdif_new(struct snd_soc_pcm_runtime *rtd)
|
||||
|
||||
p_spdif = (struct aml_spdif *)dev_get_drvdata(dev);
|
||||
|
||||
pr_info("%s spdif_%s, clk continuous:%d\n",
|
||||
pr_debug("%s spdif_%s, clk continuous:%d\n",
|
||||
__func__,
|
||||
(p_spdif->id == 0) ? "a":"b",
|
||||
p_spdif->clk_cont);
|
||||
@@ -934,9 +976,12 @@ static int aml_dai_spdif_startup(
|
||||
pr_err("Can't enable pcm clk_spdifin clock: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef __SPDIFIN_AUDIO_TYPE_HW__
|
||||
/* resample to 48k in default, by hw */
|
||||
if (!spdifin_check_audiotype_by_sw(p_spdif))
|
||||
resample_set(p_spdif->asrc_id, p_spdif->auto_asrc);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -963,10 +1008,11 @@ static void aml_dai_spdif_shutdown(
|
||||
clk_disable_unprepare(p_spdif->sysclk);
|
||||
clk_disable_unprepare(p_spdif->gate_spdifout);
|
||||
} else {
|
||||
#ifdef __SPDIFIN_AUDIO_TYPE_HW__
|
||||
/* resample disabled, by hw */
|
||||
if (!spdifin_check_audiotype_by_sw(p_spdif))
|
||||
resample_set(p_spdif->asrc_id, 0);
|
||||
|
||||
#endif
|
||||
clk_disable_unprepare(p_spdif->clk_spdifin);
|
||||
clk_disable_unprepare(p_spdif->fixed_clk);
|
||||
clk_disable_unprepare(p_spdif->gate_spdifin);
|
||||
@@ -1003,7 +1049,9 @@ static int aml_dai_spdif_prepare(
|
||||
}
|
||||
|
||||
fifo_id = aml_frddr_get_fifo_id(fr);
|
||||
aml_frddr_set_format(fr, bit_depth - 1,
|
||||
aml_frddr_set_format(fr,
|
||||
runtime->channels,
|
||||
bit_depth - 1,
|
||||
spdifout_get_frddr_type(bit_depth));
|
||||
aml_frddr_select_dst(fr, dst);
|
||||
aml_frddr_set_fifos(fr, 0x40, 0x20);
|
||||
@@ -1182,7 +1230,7 @@ static int aml_dai_spdif_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
static int aml_dai_set_spdif_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
{
|
||||
pr_info("%s , fmt %#x\n", __func__, fmt);
|
||||
pr_debug("%s , fmt %#x\n", __func__, fmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1196,10 +1244,10 @@ static void aml_set_spdifclk(struct aml_spdif *p_spdif)
|
||||
int ret;
|
||||
|
||||
if (spdif_is_4x_clk()) {
|
||||
pr_info("set 4x audio clk for 958\n");
|
||||
pr_debug("set 4x audio clk for 958\n");
|
||||
p_spdif->sysclk_freq *= 4;
|
||||
} else {
|
||||
pr_info("set normal 512 fs /4 fs\n");
|
||||
pr_debug("set normal 512 fs /4 fs\n");
|
||||
}
|
||||
mpll_freq = p_spdif->sysclk_freq * mul;
|
||||
|
||||
@@ -1221,10 +1269,10 @@ static void aml_set_spdifclk(struct aml_spdif *p_spdif)
|
||||
pr_err("Can't enable clk_spdifout clock: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
pr_info("\t set spdifout clk:%d, mpll:%d\n",
|
||||
pr_debug("\t set spdifout clk:%d, mpll:%d\n",
|
||||
p_spdif->sysclk_freq,
|
||||
mpll_freq);
|
||||
pr_info("\t get spdifout clk:%lu, mpll:%lu\n",
|
||||
pr_debug("\t get spdifout clk:%lu, mpll:%lu\n",
|
||||
clk_get_rate(p_spdif->clk_spdifout),
|
||||
clk_get_rate(p_spdif->sysclk));
|
||||
}
|
||||
@@ -1372,7 +1420,7 @@ static int aml_spdif_parse_of(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
p_spdif->auto_asrc = 0;
|
||||
|
||||
pr_info("SPDIF id %d asrc_id:%d auto_asrc:%d\n",
|
||||
pr_debug("SPDIF id %d asrc_id:%d auto_asrc:%d\n",
|
||||
p_spdif->id,
|
||||
p_spdif->asrc_id,
|
||||
p_spdif->auto_asrc);
|
||||
@@ -1497,7 +1545,7 @@ static int aml_spdif_platform_probe(struct platform_device *pdev)
|
||||
dev_warn_once(dev,
|
||||
"check whether to update spdif chipinfo\n");
|
||||
|
||||
pr_info("%s, spdif ID = %u\n", __func__, aml_spdif->id);
|
||||
pr_debug("%s, spdif ID = %u\n", __func__, aml_spdif->id);
|
||||
|
||||
/* get audio controller */
|
||||
node_prt = of_get_parent(node);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "iomap.h"
|
||||
@@ -64,7 +64,7 @@ void aml_spdifin_chnum_en(struct aml_audio_controller *actrl,
|
||||
reg = EE_AUDIO_SPDIFIN_CTRL0;
|
||||
aml_audiobus_update_bits(actrl, reg, 1 << 26, is_enable << 26);
|
||||
|
||||
pr_info("%s spdifin ctrl0:0x%x\n",
|
||||
pr_debug("%s spdifin ctrl0:0x%x\n",
|
||||
__func__,
|
||||
aml_audiobus_read(actrl, reg));
|
||||
}
|
||||
@@ -234,7 +234,7 @@ void aml_spdif_fifo_ctrl(
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("%s, bit depth:%d, frddr type:%d, toddr:type:%d\n",
|
||||
pr_debug("%s, bit depth:%d, frddr type:%d, toddr:type:%d\n",
|
||||
__func__,
|
||||
bitwidth,
|
||||
frddr_type,
|
||||
@@ -403,12 +403,16 @@ void spdifout_clk_ctrl(int spdif_id, bool is_enable)
|
||||
audiobus_write(reg, is_enable << 31 | 0x0 << 24 | 0x3 << 0);
|
||||
}
|
||||
|
||||
void spdifout_fifo_ctrl(int spdif_id, int fifo_id, int bitwidth)
|
||||
static void spdifout_fifo_ctrl(int spdif_id,
|
||||
int fifo_id, int bitwidth, int channels)
|
||||
{
|
||||
unsigned int frddr_type = spdifout_get_frddr_type(bitwidth);
|
||||
unsigned int offset, reg;
|
||||
unsigned int offset, reg, i, chmask = 0;
|
||||
|
||||
pr_info("spdif_%s fifo ctrl, frddr:%d type:%d, %d bits\n",
|
||||
for (i = 0; i < channels; i++)
|
||||
chmask |= (1 << i);
|
||||
|
||||
pr_debug("spdif_%s fifo ctrl, frddr:%d type:%d, %d bits\n",
|
||||
(spdif_id == 0) ? "a":"b",
|
||||
fifo_id,
|
||||
frddr_type,
|
||||
@@ -419,7 +423,7 @@ void spdifout_fifo_ctrl(int spdif_id, int fifo_id, int bitwidth)
|
||||
reg = EE_AUDIO_SPDIFOUT_CTRL0 + offset * spdif_id;
|
||||
audiobus_update_bits(reg,
|
||||
0x3<<21|0x1<<20|0x1<<19|0xff<<4,
|
||||
0x0<<21|0<<20|0<<19|0x3<<4);
|
||||
0x0<<21|0<<20|0<<19|chmask<<4);
|
||||
|
||||
offset = EE_AUDIO_SPDIFOUT_B_CTRL1 - EE_AUDIO_SPDIFOUT_CTRL1;
|
||||
reg = EE_AUDIO_SPDIFOUT_CTRL1 + offset * spdif_id;
|
||||
@@ -454,7 +458,7 @@ void spdifout_enable(int spdif_id, bool is_enable)
|
||||
{
|
||||
unsigned int offset, reg;
|
||||
|
||||
pr_info("spdif_%s is set to %s\n",
|
||||
pr_debug("spdif_%s is set to %s\n",
|
||||
(spdif_id == 0) ? "a":"b",
|
||||
is_enable ? "enable":"disable");
|
||||
|
||||
@@ -471,7 +475,7 @@ void spdifout_enable(int spdif_id, bool is_enable)
|
||||
}
|
||||
|
||||
void spdifout_samesource_set(int spdif_index, int fifo_id,
|
||||
int bitwidth, bool is_enable)
|
||||
int bitwidth, int channels, bool is_enable)
|
||||
{
|
||||
int spdif_id;
|
||||
|
||||
@@ -485,7 +489,7 @@ void spdifout_samesource_set(int spdif_index, int fifo_id,
|
||||
spdifout_clk_ctrl(spdif_id, /*is_enable*/true);
|
||||
|
||||
if (is_enable)
|
||||
spdifout_fifo_ctrl(spdif_id, fifo_id, bitwidth);
|
||||
spdifout_fifo_ctrl(spdif_id, fifo_id, bitwidth, channels);
|
||||
}
|
||||
|
||||
int spdifin_get_sample_rate(void)
|
||||
@@ -528,6 +532,11 @@ int spdifin_get_audio_type(void)
|
||||
return (val >> 16) & 0xff;
|
||||
}
|
||||
|
||||
void spdifin_set_src(int src)
|
||||
{
|
||||
audiobus_update_bits(EE_AUDIO_SPDIFIN_CTRL0, 0x3 << 4, src << 4);
|
||||
}
|
||||
|
||||
void spdif_set_channel_status_info(
|
||||
struct iec958_chsts *chsts, int spdif_id)
|
||||
{
|
||||
@@ -589,7 +598,7 @@ void spdif_set_channel_status_info(
|
||||
|
||||
void spdifout_play_with_zerodata(unsigned int spdif_id)
|
||||
{
|
||||
pr_info("%s, spdif id:%d enable:%d\n",
|
||||
pr_debug("%s, spdif id:%d enable:%d\n",
|
||||
__func__,
|
||||
spdif_id,
|
||||
spdifout_is_enable(spdif_id));
|
||||
@@ -626,7 +635,8 @@ void spdifout_play_with_zerodata(unsigned int spdif_id)
|
||||
#endif
|
||||
|
||||
/* spdif ctrl */
|
||||
spdifout_fifo_ctrl(spdif_id, frddr_index, bitwidth);
|
||||
spdifout_fifo_ctrl(spdif_id,
|
||||
frddr_index, bitwidth, runtime.channels);
|
||||
|
||||
/* channel status info */
|
||||
spdif_get_channel_status_info(&chsts, sample_rate);
|
||||
@@ -645,7 +655,7 @@ void spdifout_play_with_zerodata(unsigned int spdif_id)
|
||||
|
||||
void spdifout_play_with_zerodata_free(unsigned int spdif_id)
|
||||
{
|
||||
pr_info("%s, spdif id:%d\n",
|
||||
pr_debug("%s, spdif id:%d\n",
|
||||
__func__,
|
||||
spdif_id);
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ extern void aml_spdifout_get_aed_info(int spdifout_id,
|
||||
extern void spdifout_to_hdmitx_ctrl(int spdif_index);
|
||||
|
||||
extern void spdifout_samesource_set(int spdif_index, int fifo_id,
|
||||
int bitwidth, bool is_enable);
|
||||
int bitwidth, int channels, bool is_enable);
|
||||
extern void spdifout_enable(int spdif_id, bool is_enable);
|
||||
|
||||
extern int spdifin_get_sample_rate(void);
|
||||
@@ -88,4 +88,5 @@ extern void spdif_set_channel_status_info(
|
||||
|
||||
extern void spdifout_play_with_zerodata(unsigned int spdif_id);
|
||||
extern void spdifout_play_with_zerodata_free(unsigned int spdif_id);
|
||||
extern void spdifin_set_src(int src);
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -38,10 +38,11 @@
|
||||
|
||||
#include "ddr_mngr.h"
|
||||
#include "tdm_hw.h"
|
||||
#include "sharebuffer.h"
|
||||
#include "vad.h"
|
||||
|
||||
/*#define __PTM_TDM_CLK__*/
|
||||
|
||||
#include "sharebuffer.h"
|
||||
|
||||
#define DRV_NAME "aml_tdm"
|
||||
|
||||
@@ -55,22 +56,22 @@ static void dump_pcm_setting(struct pcm_setting *setting)
|
||||
if (setting == NULL)
|
||||
return;
|
||||
|
||||
pr_info("dump_pcm_setting(%p)\n", setting);
|
||||
pr_info("\tpcm_mode(%d)\n", setting->pcm_mode);
|
||||
pr_info("\tsysclk(%d)\n", setting->sysclk);
|
||||
pr_info("\tsysclk_bclk_ratio(%d)\n", setting->sysclk_bclk_ratio);
|
||||
pr_info("\tbclk(%d)\n", setting->bclk);
|
||||
pr_info("\tbclk_lrclk_ratio(%d)\n", setting->bclk_lrclk_ratio);
|
||||
pr_info("\tlrclk(%d)\n", setting->lrclk);
|
||||
pr_info("\ttx_mask(%#x)\n", setting->tx_mask);
|
||||
pr_info("\trx_mask(%#x)\n", setting->rx_mask);
|
||||
pr_info("\tslots(%d)\n", setting->slots);
|
||||
pr_info("\tslot_width(%d)\n", setting->slot_width);
|
||||
pr_info("\tlane_mask_in(%#x)\n", setting->lane_mask_in);
|
||||
pr_info("\tlane_mask_out(%#x)\n", setting->lane_mask_out);
|
||||
pr_info("\tlane_oe_mask_in(%#x)\n", setting->lane_oe_mask_in);
|
||||
pr_info("\tlane_oe_mask_out(%#x)\n", setting->lane_oe_mask_out);
|
||||
pr_info("\tlane_lb_mask_in(%#x)\n", setting->lane_lb_mask_in);
|
||||
pr_debug("dump_pcm_setting(%p)\n", setting);
|
||||
pr_debug("\tpcm_mode(%d)\n", setting->pcm_mode);
|
||||
pr_debug("\tsysclk(%d)\n", setting->sysclk);
|
||||
pr_debug("\tsysclk_bclk_ratio(%d)\n", setting->sysclk_bclk_ratio);
|
||||
pr_debug("\tbclk(%d)\n", setting->bclk);
|
||||
pr_debug("\tbclk_lrclk_ratio(%d)\n", setting->bclk_lrclk_ratio);
|
||||
pr_debug("\tlrclk(%d)\n", setting->lrclk);
|
||||
pr_debug("\ttx_mask(%#x)\n", setting->tx_mask);
|
||||
pr_debug("\trx_mask(%#x)\n", setting->rx_mask);
|
||||
pr_debug("\tslots(%d)\n", setting->slots);
|
||||
pr_debug("\tslot_width(%d)\n", setting->slot_width);
|
||||
pr_debug("\tlane_mask_in(%#x)\n", setting->lane_mask_in);
|
||||
pr_debug("\tlane_mask_out(%#x)\n", setting->lane_mask_out);
|
||||
pr_debug("\tlane_oe_mask_in(%#x)\n", setting->lane_oe_mask_in);
|
||||
pr_debug("\tlane_oe_mask_out(%#x)\n", setting->lane_oe_mask_out);
|
||||
pr_debug("\tlane_lb_mask_in(%#x)\n", setting->lane_lb_mask_in);
|
||||
}
|
||||
|
||||
struct tdm_chipinfo {
|
||||
@@ -115,6 +116,9 @@ struct aml_tdm {
|
||||
/* virtual link for i2s to hdmitx */
|
||||
int i2s2hdmitx;
|
||||
int acodec_adc;
|
||||
uint last_mpll_freq;
|
||||
uint last_mclk_freq;
|
||||
uint last_fmt;
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware aml_tdm_hardware = {
|
||||
@@ -367,33 +371,6 @@ struct snd_soc_platform_driver aml_tdm_platform = {
|
||||
.pcm_new = aml_tdm_new,
|
||||
};
|
||||
|
||||
static int aml_dai_tdm_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(p_tdm->mclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable mclk: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
err:
|
||||
pr_err("failed enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aml_dai_tdm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
/* disable clock and gate */
|
||||
clk_disable_unprepare(p_tdm->mclk);
|
||||
}
|
||||
|
||||
static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
@@ -403,7 +380,6 @@ static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream,
|
||||
|
||||
bit_depth = snd_pcm_format_width(runtime->format);
|
||||
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
struct frddr *fr = p_tdm->fddr;
|
||||
enum frddr_dest dst;
|
||||
@@ -448,7 +424,9 @@ static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream,
|
||||
p_tdm->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
aml_frddr_set_format(fr, bit_depth - 1,
|
||||
aml_frddr_set_format(fr,
|
||||
runtime->channels,
|
||||
bit_depth - 1,
|
||||
tdmout_get_frddr_type(bit_depth));
|
||||
aml_frddr_select_dst(fr, dst);
|
||||
aml_frddr_set_fifos(fr, 0x40, 0x20);
|
||||
@@ -459,6 +437,10 @@ static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream,
|
||||
unsigned int toddr_type;
|
||||
struct toddr_fmt fmt;
|
||||
|
||||
if (vad_tdm_is_running(p_tdm->id)
|
||||
&& pm_audio_is_suspend())
|
||||
return 0;
|
||||
|
||||
switch (bit_depth) {
|
||||
case 8:
|
||||
case 16:
|
||||
@@ -542,6 +524,16 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
&& vad_tdm_is_running(p_tdm->id)
|
||||
&& pm_audio_is_suspend()) {
|
||||
pm_audio_set_suspend(false);
|
||||
/* VAD switch to alsa buffer */
|
||||
vad_update_buffer(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset fifo here.
|
||||
* If not, xrun will cause channel mapping mismatch
|
||||
*/
|
||||
@@ -561,6 +553,14 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
&& vad_tdm_is_running(p_tdm->id)
|
||||
&& pm_audio_is_suspend()) {
|
||||
/* switch to VAD buffer */
|
||||
vad_update_buffer(1);
|
||||
break;
|
||||
}
|
||||
|
||||
aml_tdm_enable(p_tdm->actrl,
|
||||
substream->stream, p_tdm->id, false);
|
||||
|
||||
@@ -727,6 +727,11 @@ static int aml_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Must enabe channel number for VAD */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
&& (vad_tdm_is_running(p_tdm->id)))
|
||||
tdmin_set_chnum_en(p_tdm->actrl, p_tdm->id, true);
|
||||
|
||||
/* share buffer trigger */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
&& p_tdm->chipinfo
|
||||
@@ -744,6 +749,15 @@ static int aml_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
rate * ratio * mux);
|
||||
}
|
||||
|
||||
if (!p_tdm->contns_clk && !IS_ERR(p_tdm->mclk)) {
|
||||
pr_debug("%s(), enable mclk for %s", __func__, cpu_dai->name);
|
||||
ret = clk_prepare_enable(p_tdm->mclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -758,6 +772,11 @@ static int aml_dai_tdm_hw_free(struct snd_pcm_substream *substream,
|
||||
aml_tdm_set_channel_mask(p_tdm->actrl,
|
||||
substream->stream, p_tdm->id, i, 0);
|
||||
|
||||
/* Disable channel number for VAD */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
&& (vad_tdm_is_running(p_tdm->id)))
|
||||
tdmin_set_chnum_en(p_tdm->actrl, p_tdm->id, false);
|
||||
|
||||
/* share buffer free */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
&& p_tdm->chipinfo
|
||||
@@ -769,6 +788,12 @@ static int aml_dai_tdm_hw_free(struct snd_pcm_substream *substream,
|
||||
fr, p_tdm->samesource_sel);
|
||||
}
|
||||
|
||||
/* disable clock and gate */
|
||||
if (!p_tdm->contns_clk && !IS_ERR(p_tdm->mclk)) {
|
||||
pr_info("%s(), disable mclk for %s", __func__, cpu_dai->name);
|
||||
clk_disable_unprepare(p_tdm->mclk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -776,8 +801,14 @@ static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
pr_info("asoc aml_dai_set_tdm_fmt, %#x, %p, id(%d), clksel(%d)\n",
|
||||
pr_debug("asoc aml_dai_set_tdm_fmt, %#x, %p, id(%d), clksel(%d)\n",
|
||||
fmt, p_tdm, p_tdm->id, p_tdm->clk_sel);
|
||||
if (p_tdm->last_fmt == fmt) {
|
||||
pr_debug("%s(), fmt not change\n", __func__);
|
||||
goto capture;
|
||||
} else
|
||||
p_tdm->last_fmt = fmt;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
|
||||
case SND_SOC_DAIFMT_CONT:
|
||||
p_tdm->contns_clk = true;
|
||||
@@ -794,9 +825,9 @@ static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
|
||||
aml_tdm_set_format(p_tdm->actrl,
|
||||
&(p_tdm->setting), p_tdm->clk_sel, p_tdm->id, fmt,
|
||||
cpu_dai->capture_active,
|
||||
cpu_dai->playback_active);
|
||||
1, 1);
|
||||
|
||||
capture:
|
||||
/* update skew for ACODEC_ADC */
|
||||
if (cpu_dai->capture_active
|
||||
&& p_tdm->chipinfo
|
||||
@@ -832,6 +863,7 @@ static int aml_dai_set_tdm_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned int ratio = aml_mpll_mclk_ratio(freq);
|
||||
unsigned int mpll_freq = 0;
|
||||
|
||||
p_tdm->setting.sysclk = freq;
|
||||
|
||||
@@ -844,10 +876,22 @@ static int aml_dai_set_tdm_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
ratio = 20;
|
||||
#endif
|
||||
|
||||
clk_set_rate(p_tdm->clk, freq * ratio);
|
||||
clk_set_rate(p_tdm->mclk, freq);
|
||||
mpll_freq = freq * ratio;
|
||||
if (mpll_freq != p_tdm->last_mpll_freq) {
|
||||
clk_set_rate(p_tdm->clk, mpll_freq);
|
||||
p_tdm->last_mpll_freq = mpll_freq;
|
||||
} else {
|
||||
pr_debug("%s(), mpll no change, keep clk\n", __func__);
|
||||
}
|
||||
|
||||
pr_info("set mclk:%d, mpll:%d, get mclk:%lu, mpll:%lu\n",
|
||||
if (freq != p_tdm->last_mclk_freq) {
|
||||
clk_set_rate(p_tdm->mclk, freq);
|
||||
p_tdm->last_mclk_freq = freq;
|
||||
} else {
|
||||
pr_debug("%s(), mclk no change, keep clk\n", __func__);
|
||||
}
|
||||
|
||||
pr_debug("set mclk:%d, mpll:%d, get mclk:%lu, mpll:%lu\n",
|
||||
freq,
|
||||
freq * ratio,
|
||||
clk_get_rate(p_tdm->mclk),
|
||||
@@ -868,10 +912,10 @@ static int aml_dai_set_bclk_ratio(struct snd_soc_dai *cpu_dai,
|
||||
|
||||
if (p_tdm->setting.pcm_mode == SND_SOC_DAIFMT_I2S ||
|
||||
p_tdm->setting.pcm_mode == SND_SOC_DAIFMT_LEFT_J) {
|
||||
pr_info("aml_dai_set_bclk_ratio, select I2S mode\n");
|
||||
pr_debug("aml_dai_set_bclk_ratio, select I2S mode\n");
|
||||
lrclk_hi = bclk_ratio / 2;
|
||||
} else {
|
||||
pr_info("aml_dai_set_bclk_ratio, select TDM mode\n");
|
||||
pr_debug("aml_dai_set_bclk_ratio, select TDM mode\n");
|
||||
}
|
||||
aml_tdm_set_bclk_ratio(p_tdm->actrl,
|
||||
p_tdm->clk_sel, lrclk_hi, bclk_ratio);
|
||||
@@ -914,15 +958,15 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
|
||||
lanes_oe_in_cnt = pop_count(p_tdm->setting.lane_oe_mask_in);
|
||||
lanes_lb_cnt = pop_count(p_tdm->setting.lane_lb_mask_in);
|
||||
|
||||
pr_info("%s(), txmask(%#x), rxmask(%#x)\n",
|
||||
pr_debug("%s(), txmask(%#x), rxmask(%#x)\n",
|
||||
__func__, tx_mask, rx_mask);
|
||||
pr_info("\tlanes_out_cnt(%d), lanes_in_cnt(%d)\n",
|
||||
pr_debug("\tlanes_out_cnt(%d), lanes_in_cnt(%d)\n",
|
||||
lanes_out_cnt, lanes_in_cnt);
|
||||
pr_info("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n",
|
||||
pr_debug("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n",
|
||||
lanes_oe_out_cnt, lanes_oe_in_cnt);
|
||||
pr_info("\tlanes_lb_cnt(%d)\n",
|
||||
pr_debug("\tlanes_lb_cnt(%d)\n",
|
||||
lanes_lb_cnt);
|
||||
pr_info("\tslots(%d), slot_width(%d)\n",
|
||||
pr_debug("\tslots(%d), slot_width(%d)\n",
|
||||
slots, slot_width);
|
||||
p_tdm->setting.tx_mask = tx_mask;
|
||||
p_tdm->setting.rx_mask = rx_mask;
|
||||
@@ -971,7 +1015,7 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
|
||||
} else {
|
||||
if (lanes_lb_cnt)
|
||||
in_src = p_tdm->id + 3;
|
||||
if (lanes_in_cnt && lanes_in_cnt <= 3)
|
||||
if (lanes_in_cnt && lanes_in_cnt <= 4)
|
||||
in_src = p_tdm->id;
|
||||
if (in_src > 5) {
|
||||
pr_err("unknown src(%d) for tdmin\n", in_src);
|
||||
@@ -1020,9 +1064,22 @@ static int aml_dai_tdm_remove(struct snd_soc_dai *cpu_dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_dai_tdm_mute_stream(struct snd_soc_dai *cpu_dai,
|
||||
int mute, int stream)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
pr_debug("tdm playback mute: %d\n", mute);
|
||||
aml_tdm_mute_playback(p_tdm->actrl, p_tdm->id, mute);
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
pr_debug("tdm capture mute: %d\n", mute);
|
||||
aml_tdm_mute_capture(p_tdm->actrl, p_tdm->id, mute);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops aml_dai_tdm_ops = {
|
||||
.startup = aml_dai_tdm_startup,
|
||||
.shutdown = aml_dai_tdm_shutdown,
|
||||
.prepare = aml_dai_tdm_prepare,
|
||||
.trigger = aml_dai_tdm_trigger,
|
||||
.hw_params = aml_dai_tdm_hw_params,
|
||||
@@ -1032,6 +1089,7 @@ static struct snd_soc_dai_ops aml_dai_tdm_ops = {
|
||||
.set_bclk_ratio = aml_dai_set_bclk_ratio,
|
||||
.set_clkdiv = aml_dai_set_clkdiv,
|
||||
.set_tdm_slot = aml_dai_set_tdm_slot,
|
||||
.mute_stream = aml_dai_tdm_mute_stream,
|
||||
};
|
||||
|
||||
#define AML_DAI_TDM_RATES (SNDRV_PCM_RATE_8000_384000)
|
||||
|
||||
@@ -188,6 +188,72 @@ void aml_tdm_fifo_ctrl(
|
||||
|
||||
}
|
||||
|
||||
static void aml_clk_set_tdmout_by_id(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int sclk_sel,
|
||||
unsigned int lrclk_sel,
|
||||
bool sclk_ws_inv,
|
||||
bool is_master,
|
||||
bool binv)
|
||||
{
|
||||
unsigned int val_sclk_ws_inv = 0;
|
||||
unsigned int reg = EE_AUDIO_CLK_TDMOUT_A_CTRL + tdm_index;
|
||||
|
||||
/* This is just a copy from previous setting. WHY??? */
|
||||
val_sclk_ws_inv = sclk_ws_inv && is_master;
|
||||
if (val_sclk_ws_inv)
|
||||
aml_audiobus_update_bits(actrl, reg,
|
||||
0x3<<30|1<<28|0xf<<24|0xf<<20,
|
||||
0x3<<30|val_sclk_ws_inv<<28|
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
else
|
||||
aml_audiobus_update_bits(actrl, reg,
|
||||
0x3<<30|1<<29|0xf<<24|0xf<<20,
|
||||
0x3<<30|binv<<29|
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
}
|
||||
|
||||
static void aml_clk_set_tdmin_by_id(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int sclk_sel,
|
||||
unsigned int lrclk_sel)
|
||||
{
|
||||
unsigned int reg =
|
||||
EE_AUDIO_CLK_TDMIN_A_CTRL + tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg,
|
||||
0xff<<20,
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
}
|
||||
|
||||
static void aml_tdmout_invert_lrclk(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
bool finv)
|
||||
{
|
||||
unsigned int off_set =
|
||||
EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1;
|
||||
unsigned int reg_out =
|
||||
EE_AUDIO_TDMOUT_A_CTRL1 + off_set * tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out, 0x1<<28, finv<<28);
|
||||
}
|
||||
|
||||
static void aml_tdmout_bclk_skew(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int bclkout_skew)
|
||||
{
|
||||
unsigned int off_set =
|
||||
EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
|
||||
unsigned int reg_out =
|
||||
EE_AUDIO_TDMOUT_A_CTRL0 + off_set * tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out, 0x1f<<15, bclkout_skew<<15);
|
||||
}
|
||||
|
||||
void aml_tdm_set_format(
|
||||
struct aml_audio_controller *actrl,
|
||||
struct pcm_setting *p_config,
|
||||
@@ -223,24 +289,7 @@ void aml_tdm_set_format(
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: clk tree
|
||||
reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id;
|
||||
reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL + id;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out,
|
||||
0xff<<20,
|
||||
valb<<24|valf<<20);
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_in,
|
||||
0xff<<20,
|
||||
valb<<24|valf<<20);
|
||||
|
||||
if (p_config->sclk_ws_inv)
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out,
|
||||
1 << 28,
|
||||
0 << 28);
|
||||
aml_clk_set_tdmin_by_id(actrl, id, valb, valf);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
@@ -355,27 +404,11 @@ void aml_tdm_set_format(
|
||||
|
||||
/* TDM out */
|
||||
if (playback_active) {
|
||||
|
||||
reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id;
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x3<<30, 0x3<<30);
|
||||
|
||||
if (p_config->sclk_ws_inv && master_mode)
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1 << 28,
|
||||
0x1 << 28);
|
||||
else
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1<<29, binv<<29);
|
||||
|
||||
off_set = EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1;
|
||||
reg_out = EE_AUDIO_TDMOUT_A_CTRL1 + off_set * id;
|
||||
aml_audiobus_update_bits(actrl, reg_out, 0x1<<28, finv<<28);
|
||||
|
||||
off_set = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
|
||||
reg_out = EE_AUDIO_TDMOUT_A_CTRL0 + off_set * id;
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1f<<15, bclkout_skew<<15);
|
||||
aml_clk_set_tdmout_by_id(actrl,
|
||||
id, valb, valf,
|
||||
p_config->sclk_ws_inv, master_mode, binv);
|
||||
aml_tdmout_invert_lrclk(actrl, id, finv);
|
||||
aml_tdmout_bclk_skew(actrl, id, bclkout_skew);
|
||||
}
|
||||
|
||||
/* TDM in */
|
||||
@@ -492,6 +525,19 @@ void aml_update_tdmin_src(
|
||||
0xf << 20, in_src << 20);
|
||||
}
|
||||
|
||||
void tdmin_set_chnum_en(
|
||||
struct aml_audio_controller *actrl,
|
||||
int index, bool enable)
|
||||
{
|
||||
unsigned int reg, offset;
|
||||
|
||||
offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
|
||||
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
|
||||
|
||||
aml_audiobus_update_bits(actrl, reg,
|
||||
0x1 << 6, enable << 6);
|
||||
}
|
||||
|
||||
void aml_tdm_set_channel_mask(
|
||||
struct aml_audio_controller *actrl,
|
||||
int stream, int index, int lane, int mask)
|
||||
@@ -647,3 +693,44 @@ void i2s_to_hdmitx_ctrl(int tdm_index)
|
||||
| tdm_index << 4 /* Bclk_sel */
|
||||
);
|
||||
}
|
||||
|
||||
void aml_tdm_mute_playback(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute)
|
||||
{
|
||||
unsigned int offset, reg;
|
||||
unsigned int mute_mask = 0xffffffff;
|
||||
unsigned int mute_val = 0;
|
||||
int i = 0, lanes = 4;
|
||||
|
||||
if (mute)
|
||||
mute_val = 0xffffffff;
|
||||
|
||||
offset = EE_AUDIO_TDMOUT_B_MUTE0
|
||||
- EE_AUDIO_TDMOUT_A_MUTE0;
|
||||
reg = EE_AUDIO_TDMOUT_A_MUTE0 + offset * tdm_index;
|
||||
for (i = 0; i < lanes; i++)
|
||||
aml_audiobus_update_bits(actrl, reg + i, mute_mask, mute_val);
|
||||
}
|
||||
|
||||
void aml_tdm_mute_capture(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute)
|
||||
{
|
||||
unsigned int offset, reg;
|
||||
unsigned int mute_mask = 0xffffffff;
|
||||
unsigned int mute_val = 0;
|
||||
int i = 0, lanes = 4;
|
||||
|
||||
if (mute)
|
||||
mute_val = 0xffffffff;
|
||||
|
||||
offset = EE_AUDIO_TDMIN_B_MUTE0
|
||||
- EE_AUDIO_TDMIN_A_MUTE0;
|
||||
reg = EE_AUDIO_TDMIN_A_MUTE0 + offset * tdm_index;
|
||||
for (i = 0; i < lanes; i++)
|
||||
aml_audiobus_update_bits(actrl, reg + i, mute_mask, mute_val);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,10 @@ extern void aml_update_tdmin_src(
|
||||
struct aml_audio_controller *actrl,
|
||||
int index, int in_src);
|
||||
|
||||
extern void tdmin_set_chnum_en(
|
||||
struct aml_audio_controller *actrl,
|
||||
int index, bool enable);
|
||||
|
||||
extern void aml_tdm_set_channel_mask(
|
||||
struct aml_audio_controller *actrl,
|
||||
int stream, int index, int lanes, int mask);
|
||||
@@ -138,4 +142,12 @@ extern void aml_tdm_clk_pad_select(
|
||||
int tdm_index, int clk_sel);
|
||||
|
||||
extern void i2s_to_hdmitx_ctrl(int tdm_index);
|
||||
void aml_tdm_mute_playback(
|
||||
struct aml_audio_controller *actrl,
|
||||
int index,
|
||||
bool mute);
|
||||
void aml_tdm_mute_capture(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute);
|
||||
#endif
|
||||
|
||||
786
sound/soc/amlogic/auge/vad.c
Normal file
786
sound/soc/amlogic/auge/vad.c
Normal file
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad.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.
|
||||
*/
|
||||
#define DEBUG
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <linux/amlogic/pm.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
|
||||
#include "vad_hw_coeff.c"
|
||||
#include "vad_hw.h"
|
||||
#include "vad.h"
|
||||
|
||||
#define DRV_NAME "VAD"
|
||||
|
||||
#define DMA_BUFFER_BYTES_MAX (2 * 1024 * 1024)
|
||||
|
||||
enum vad_level {
|
||||
LEVEL_USER,
|
||||
LEVEL_KERNEL,
|
||||
};
|
||||
|
||||
struct vad {
|
||||
struct aml_audio_controller *actrl;
|
||||
struct device *dev;
|
||||
|
||||
struct clk *gate;
|
||||
struct clk *pll;
|
||||
struct clk *clk;
|
||||
|
||||
struct toddr *tddr;
|
||||
|
||||
struct tasklet_struct tasklet;
|
||||
|
||||
struct snd_dma_buffer dma_buffer;
|
||||
unsigned int start_last;
|
||||
unsigned int end_last;
|
||||
unsigned int addr;
|
||||
|
||||
int switch_buffer;
|
||||
|
||||
/* vad flag interrupt */
|
||||
int irq_wakeup;
|
||||
/* frame sync interrupt */
|
||||
int irq_fs;
|
||||
/* data source select
|
||||
* Data src sel:
|
||||
* 0: tdmin_a;
|
||||
* 1: tdmin_b;
|
||||
* 2: tdmin_c;
|
||||
* 3: spdifin;
|
||||
* 4: pdmin;
|
||||
* 5: loopback_b;
|
||||
* 6: tdmin_lb;
|
||||
* 7: loopback_a;
|
||||
*/
|
||||
int src;
|
||||
/* Enable */
|
||||
int en;
|
||||
|
||||
/* user space or kernel space to check hot word
|
||||
* 1: check hot word in kernel
|
||||
* 0: check hot word in user space
|
||||
*/
|
||||
enum vad_level level;
|
||||
};
|
||||
|
||||
static struct vad *s_vad;
|
||||
|
||||
static struct vad *get_vad(void)
|
||||
{
|
||||
struct vad *p_vad;
|
||||
|
||||
p_vad = s_vad;
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("Not init vad\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p_vad;
|
||||
}
|
||||
|
||||
static bool vad_is_enable(void)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
|
||||
if (!p_vad)
|
||||
return false;
|
||||
|
||||
return p_vad->en;
|
||||
}
|
||||
|
||||
static bool vad_src_check(enum vad_src src)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
|
||||
if (!p_vad)
|
||||
return false;
|
||||
|
||||
if (p_vad->src == src)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vad_tdm_is_running(int tdm_idx)
|
||||
{
|
||||
enum vad_src src;
|
||||
|
||||
if (tdm_idx > 2)
|
||||
return false;
|
||||
|
||||
src = (enum vad_src)tdm_idx;
|
||||
|
||||
if (vad_is_enable() && vad_src_check(src))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vad_pdm_is_running(void)
|
||||
{
|
||||
if (vad_is_enable() && vad_src_check(VAD_SRC_PDMIN))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void vad_notify_user_space(struct device *dev)
|
||||
{
|
||||
pr_info("Notify to wake up user space\n");
|
||||
|
||||
pm_wakeup_event(dev, 2000);
|
||||
}
|
||||
|
||||
static int vad_engine_check(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check buffer in kernel for VAD */
|
||||
static void vad_transfer_buffer_output(struct vad *p_vad)
|
||||
{
|
||||
|
||||
for (;;) {
|
||||
if (vad_engine_check()) {
|
||||
vad_notify_user_space(p_vad->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vad_tasklet(unsigned long data)
|
||||
{
|
||||
struct vad *p_vad = (struct vad *)data;
|
||||
|
||||
vad_transfer_buffer_output(p_vad);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t vad_fs_isr(int irq, void *data)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vad_set_clks(struct vad *p_vad, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
int ret = 0;
|
||||
|
||||
/* enable clock gate */
|
||||
ret = clk_prepare_enable(p_vad->gate);
|
||||
|
||||
/* enable clock */
|
||||
ret = clk_prepare_enable(p_vad->pll);
|
||||
if (ret) {
|
||||
pr_err("Can't enable vad pll: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_set_rate(p_vad->clk, 25000000);
|
||||
ret = clk_prepare_enable(p_vad->clk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable vad clk: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* disable clock and gate */
|
||||
clk_disable_unprepare(p_vad->clk);
|
||||
clk_disable_unprepare(p_vad->pll);
|
||||
clk_disable_unprepare(p_vad->gate);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_init(struct vad *p_vad)
|
||||
{
|
||||
int ret = 0, flag = 0;
|
||||
|
||||
/* malloc buffer */
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
|
||||
p_vad->dev,
|
||||
DMA_BUFFER_BYTES_MAX,
|
||||
&p_vad->dma_buffer);
|
||||
if (ret) {
|
||||
dev_err(p_vad->dev, "Cannot allocate buffer(s)\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register irq */
|
||||
if (p_vad->level == LEVEL_KERNEL) {
|
||||
flag = IRQF_SHARED | IRQF_NO_SUSPEND;
|
||||
|
||||
tasklet_init(&p_vad->tasklet, vad_tasklet,
|
||||
(unsigned long)p_vad);
|
||||
|
||||
} else if (p_vad->level == LEVEL_USER)
|
||||
flag = IRQF_SHARED;
|
||||
|
||||
ret = request_irq(p_vad->irq_wakeup,
|
||||
vad_wakeup_isr, flag, "vad_wakeup",
|
||||
p_vad);
|
||||
if (ret) {
|
||||
dev_err(p_vad->dev, "failed to claim irq_wakeup %u\n",
|
||||
p_vad->irq_wakeup);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = request_irq(p_vad->irq_fs,
|
||||
vad_fs_isr, 0, "vad_fs",
|
||||
p_vad);
|
||||
if (ret) {
|
||||
dev_err(p_vad->dev, "failed to claim irq_fs %u\n",
|
||||
p_vad->irq_fs);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* clock ready */
|
||||
vad_set_clks(p_vad, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vad_deinit(struct vad *p_vad)
|
||||
{
|
||||
if (p_vad->level == LEVEL_KERNEL)
|
||||
tasklet_kill(&p_vad->tasklet);
|
||||
|
||||
/* free irq */
|
||||
free_irq(p_vad->irq_wakeup, p_vad);
|
||||
free_irq(p_vad->irq_fs, p_vad);
|
||||
|
||||
/* free buffer */
|
||||
snd_dma_free_pages(&p_vad->dma_buffer);
|
||||
|
||||
/* clock disabled */
|
||||
vad_set_clks(p_vad, false);
|
||||
}
|
||||
|
||||
void vad_update_buffer(int isvad)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
unsigned int start, end, addr;
|
||||
unsigned int rd_th;
|
||||
|
||||
if (!p_vad || !p_vad->en || !p_vad->tddr)
|
||||
return;
|
||||
|
||||
addr = aml_toddr_get_position(p_vad->tddr);
|
||||
|
||||
if (isvad) { /* switch to vad buffer */
|
||||
struct toddr *tddr = p_vad->tddr;
|
||||
|
||||
p_vad->start_last = tddr->start_addr;
|
||||
p_vad->end_last = tddr->end_addr;
|
||||
|
||||
rd_th = 0x100;
|
||||
|
||||
pr_debug("Switch to VAD buffer\n");
|
||||
pr_debug("\t ASAL start:%d, end:%d, bytes:%d, current:%d\n",
|
||||
tddr->start_addr, tddr->end_addr,
|
||||
tddr->end_addr - tddr->start_addr, 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",
|
||||
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);
|
||||
}
|
||||
|
||||
int vad_transfer_chunk_data(unsigned long data, int frames)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
char __user *buf = (char __user *)data;
|
||||
unsigned char *hwbuf;
|
||||
int bytes;
|
||||
int start, end, addr, size;
|
||||
int chnum, bytes_per_sample;
|
||||
|
||||
if (!buf || !p_vad || !p_vad->en || !p_vad->tddr)
|
||||
return 0;
|
||||
|
||||
size = p_vad->dma_buffer.bytes;
|
||||
start = p_vad->dma_buffer.addr;
|
||||
end = start + size - 8;
|
||||
addr = p_vad->addr;
|
||||
hwbuf = p_vad->dma_buffer.area;
|
||||
|
||||
if (addr < start || addr > end)
|
||||
return 0;
|
||||
|
||||
chnum = p_vad->tddr->channels;
|
||||
/* bytes for each sample */
|
||||
bytes_per_sample = p_vad->tddr->bitdepth >> 3;
|
||||
|
||||
bytes = frames * chnum * bytes_per_sample < size ?
|
||||
frames * chnum * bytes_per_sample : size;
|
||||
|
||||
pr_debug("%s dma bytes:%d, wanted bytes:%d, actual bytes:%d\n",
|
||||
__func__,
|
||||
size,
|
||||
frames * chnum * bytes_per_sample,
|
||||
bytes);
|
||||
|
||||
pr_debug("%s dma bytes:%d, start:%d, end:%d, current:%d\n",
|
||||
__func__,
|
||||
size,
|
||||
start,
|
||||
end,
|
||||
addr);
|
||||
|
||||
if (addr - start >= bytes) {
|
||||
if (copy_to_user(buf,
|
||||
hwbuf + addr - bytes - start,
|
||||
bytes))
|
||||
return 0;
|
||||
} else {
|
||||
int tmp_bytes = bytes - (addr - start);
|
||||
int tmp_offset = (end - tmp_bytes) - start;
|
||||
|
||||
if (copy_to_user(buf,
|
||||
hwbuf + tmp_offset,
|
||||
tmp_bytes))
|
||||
return 0;
|
||||
|
||||
if (copy_to_user(buf + tmp_bytes,
|
||||
hwbuf,
|
||||
addr - start))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* After data copied, reset dma buffer */
|
||||
memset(hwbuf, 0x0, size);
|
||||
|
||||
return bytes / (chnum * bytes_per_sample);
|
||||
}
|
||||
|
||||
void vad_set_toddr_info(struct toddr *to)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
|
||||
if (!p_vad || !p_vad->en)
|
||||
return;
|
||||
|
||||
pr_debug("%s update vad toddr:%p\n", __func__, to);
|
||||
|
||||
p_vad->tddr = to;
|
||||
}
|
||||
|
||||
void vad_enable(bool enable)
|
||||
{
|
||||
struct vad *p_vad = get_vad();
|
||||
|
||||
if (!p_vad || !p_vad->en)
|
||||
return;
|
||||
|
||||
/* Force VAD enable to set parameters */
|
||||
if (enable) {
|
||||
int *p_de_coeff = vad_de_coeff;
|
||||
int len_de = ARRAY_SIZE(vad_de_coeff);
|
||||
int *p_win_coeff = vad_ram_coeff;
|
||||
int len_ram = ARRAY_SIZE(vad_ram_coeff);
|
||||
|
||||
vad_set_enable(true);
|
||||
vad_set_ram_coeff(len_ram, p_win_coeff);
|
||||
vad_set_de_params(len_de, p_de_coeff);
|
||||
vad_set_pwd();
|
||||
vad_set_cep();
|
||||
vad_set_src(p_vad->src);
|
||||
vad_set_in();
|
||||
|
||||
/* reset then enable VAD */
|
||||
vad_set_enable(false);
|
||||
}
|
||||
vad_set_enable(enable);
|
||||
}
|
||||
|
||||
static int vad_get_enable_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = p_vad->en;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_set_enable_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_vad->en = ucontrol->value.integer.value[0];
|
||||
|
||||
if (p_vad->en) {
|
||||
vad_init(p_vad);
|
||||
|
||||
aml_set_vad(p_vad->en, p_vad->src);
|
||||
} else {
|
||||
aml_set_vad(p_vad->en, p_vad->src);
|
||||
|
||||
vad_deinit(p_vad);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char *const vad_src_txt[] = {
|
||||
"TDMIN_A",
|
||||
"TDMIN_B",
|
||||
"TDMIN_C",
|
||||
"SPDIFIN",
|
||||
"PDMIN",
|
||||
"LOOPBACK_B",
|
||||
"TDMIN_LB",
|
||||
"LOOPBACK_A",
|
||||
};
|
||||
|
||||
const struct soc_enum vad_src_enum =
|
||||
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(vad_src_txt),
|
||||
vad_src_txt);
|
||||
|
||||
static int vad_get_src_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = p_vad->src;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_set_src_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_vad->src = ucontrol->value.integer.value[0];
|
||||
|
||||
if (p_vad->en)
|
||||
aml_set_vad(p_vad->en, p_vad->src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_get_switch_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = p_vad->switch_buffer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_set_switch_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct vad *p_vad = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!p_vad) {
|
||||
pr_debug("VAD is not inited\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_vad->switch_buffer = ucontrol->value.integer.value[0];
|
||||
|
||||
if (p_vad->en)
|
||||
vad_update_buffer(p_vad->switch_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new vad_controls[] = {
|
||||
SOC_SINGLE_BOOL_EXT("VAD enable",
|
||||
0,
|
||||
vad_get_enable_enum,
|
||||
vad_set_enable_enum),
|
||||
|
||||
SOC_ENUM_EXT("VAD Source sel",
|
||||
vad_src_enum,
|
||||
vad_get_src_enum,
|
||||
vad_set_src_enum),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("VAD Switch",
|
||||
0,
|
||||
vad_get_switch_enum,
|
||||
vad_set_switch_enum),
|
||||
|
||||
};
|
||||
|
||||
int card_add_vad_kcontrols(struct snd_soc_card *card)
|
||||
{
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
struct vad *p_vad = get_vad();
|
||||
|
||||
if (!p_vad)
|
||||
return -ENODEV;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(vad_controls); idx++) {
|
||||
err = snd_ctl_add(card->snd_card,
|
||||
snd_ctl_new1(&vad_controls[idx],
|
||||
p_vad));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id vad_device_id[] = {
|
||||
{
|
||||
.compatible = "amlogic, snd-vad",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, vad_device_id);
|
||||
|
||||
static int vad_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *node_prt = NULL;
|
||||
struct platform_device *pdev_parent;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct aml_audio_controller *actrl = NULL;
|
||||
struct vad *p_vad = NULL;
|
||||
int ret = 0;
|
||||
|
||||
p_vad = devm_kzalloc(&pdev->dev, sizeof(struct vad), GFP_KERNEL);
|
||||
if (!p_vad)
|
||||
return -ENOMEM;
|
||||
|
||||
p_vad->dev = dev;
|
||||
dev_set_drvdata(dev, p_vad);
|
||||
|
||||
/* get audio controller */
|
||||
node_prt = of_get_parent(node);
|
||||
if (node_prt == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
pdev_parent = of_find_device_by_node(node_prt);
|
||||
of_node_put(node_prt);
|
||||
actrl = (struct aml_audio_controller *)
|
||||
platform_get_drvdata(pdev_parent);
|
||||
p_vad->actrl = actrl;
|
||||
|
||||
/* clock */
|
||||
p_vad->gate = devm_clk_get(&pdev->dev, "gate");
|
||||
if (IS_ERR(p_vad->gate)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't get vad clock gate\n");
|
||||
return PTR_ERR(p_vad->gate);
|
||||
}
|
||||
p_vad->pll = devm_clk_get(&pdev->dev, "pll");
|
||||
if (IS_ERR(p_vad->pll)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't retrieve vad pll clock\n");
|
||||
return PTR_ERR(p_vad->pll);
|
||||
}
|
||||
p_vad->clk = devm_clk_get(&pdev->dev, "clk");
|
||||
if (IS_ERR(p_vad->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't retrieve vad clock\n");
|
||||
return PTR_ERR(p_vad->clk);
|
||||
}
|
||||
ret = clk_set_parent(p_vad->clk, p_vad->pll);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't set p_vad->clk parent clock\n");
|
||||
return PTR_ERR(p_vad->clk);
|
||||
}
|
||||
|
||||
/* irqs */
|
||||
p_vad->irq_wakeup = platform_get_irq_byname(pdev, "irq_wakeup");
|
||||
if (p_vad->irq_wakeup < 0) {
|
||||
dev_err(dev, "Failed to get irq_wakeup:%d\n",
|
||||
p_vad->irq_wakeup);
|
||||
return -ENXIO;
|
||||
}
|
||||
p_vad->irq_fs = platform_get_irq_byname(pdev, "irq_frame_sync");
|
||||
if (p_vad->irq_fs < 0) {
|
||||
dev_err(dev, "Failed to get irq_frame_sync:%d\n",
|
||||
p_vad->irq_fs);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* data source select */
|
||||
ret = of_property_read_u32(node, "src",
|
||||
&p_vad->src);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to get vad data src select:%d\n",
|
||||
p_vad->src);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* to deal with hot word in user space or kernel space */
|
||||
ret = of_property_read_u32(node, "level",
|
||||
&p_vad->level);
|
||||
if (ret < 0) {
|
||||
dev_info(dev,
|
||||
"Failed to get vad level, default in user space\n");
|
||||
p_vad->level = 0;
|
||||
}
|
||||
|
||||
pr_info("%s vad data source sel:%d, level:%d\n",
|
||||
__func__,
|
||||
p_vad->src,
|
||||
p_vad->level);
|
||||
|
||||
s_vad = p_vad;
|
||||
|
||||
device_init_wakeup(dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vad_platform_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vad *p_vad = dev_get_drvdata(dev);
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* whether in freeze */
|
||||
if (is_pm_freeze_mode()
|
||||
&& vad_is_enable()) {
|
||||
pr_info("%s, Entry in freeze\n", __func__);
|
||||
|
||||
if (p_vad->level == LEVEL_USER)
|
||||
dev_pm_set_wake_irq(dev, p_vad->irq_wakeup);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vad_platform_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vad *p_vad = dev_get_drvdata(dev);
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* whether in freeze mode */
|
||||
if (is_pm_freeze_mode()
|
||||
&& vad_is_enable()) {
|
||||
pr_info("%s, Exist from freeze\n", __func__);
|
||||
|
||||
if (p_vad->level == LEVEL_USER)
|
||||
dev_pm_clear_wake_irq(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver vad_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = vad_device_id,
|
||||
},
|
||||
.probe = vad_platform_probe,
|
||||
.suspend = vad_platform_suspend,
|
||||
.resume = vad_platform_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(vad_driver);
|
||||
|
||||
MODULE_AUTHOR("Amlogic, Inc.");
|
||||
MODULE_DESCRIPTION("Amlogic Voice Activity Detection ASoc driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("Platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, vad_device_id);
|
||||
47
sound/soc/amlogic/auge/vad.h
Normal file
47
sound/soc/amlogic/auge/vad.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad.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_H__
|
||||
#define __VAD_H__
|
||||
|
||||
#include "ddr_mngr.h"
|
||||
|
||||
enum vad_src {
|
||||
VAD_SRC_TDMIN_A,
|
||||
VAD_SRC_TDMIN_B,
|
||||
VAD_SRC_TDMIN_C,
|
||||
VAD_SRC_SPDIFIN,
|
||||
VAD_SRC_PDMIN,
|
||||
VAD_SRC_LOOPBACK_B,
|
||||
VAD_SRC_TDMIN_LB,
|
||||
VAD_SRC_LOOPBACK_A,
|
||||
};
|
||||
|
||||
extern void vad_update_buffer(int isvad);
|
||||
extern int vad_transfer_chunk_data(unsigned long data, int frames);
|
||||
|
||||
extern bool vad_tdm_is_running(int tdm_idx);
|
||||
extern bool vad_pdm_is_running(void);
|
||||
|
||||
extern void vad_enable(bool enable);
|
||||
extern void vad_set_toddr_info(struct toddr *to);
|
||||
|
||||
extern void vad_set_trunk_data_readable(bool en);
|
||||
|
||||
extern int card_add_vad_kcontrols(struct snd_soc_card *card);
|
||||
|
||||
#endif
|
||||
175
sound/soc/amlogic/auge/vad_dev.c
Normal file
175
sound/soc/amlogic/auge/vad_dev.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad_dev.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.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <sound/asound.h>
|
||||
|
||||
#include <linux/amlogic/major.h>
|
||||
|
||||
#include "vad.h"
|
||||
|
||||
#define DRV_NAME "vad"
|
||||
|
||||
#define IOCTL_READI_SUSPENDED_FRAMES _IOR('Z', 0x0, struct snd_xferi)
|
||||
|
||||
static bool readable;
|
||||
|
||||
bool vad_is_trunk_data_readable(void)
|
||||
{
|
||||
return readable;
|
||||
}
|
||||
|
||||
void vad_set_trunk_data_readable(bool en)
|
||||
{
|
||||
readable = en;
|
||||
}
|
||||
|
||||
static ssize_t readable_show(struct class *cla, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", readable);
|
||||
}
|
||||
|
||||
static struct class_attribute vad_attrs[] = {
|
||||
__ATTR_RO(readable),
|
||||
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static struct class vad_class = {
|
||||
.name = DRV_NAME,
|
||||
.class_attrs = vad_attrs,
|
||||
};
|
||||
|
||||
static int vad_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vad_unlocked_ioctl(
|
||||
struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
if (cmd == IOCTL_READI_SUSPENDED_FRAMES) {
|
||||
struct snd_xferi xferi;
|
||||
struct snd_xferi __user *_xferi =
|
||||
(struct snd_xferi __user *)arg;
|
||||
int result;
|
||||
|
||||
if (!vad_is_trunk_data_readable())
|
||||
return 0;
|
||||
|
||||
if (put_user(0, &_xferi->result))
|
||||
return 0;
|
||||
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
|
||||
return 0;
|
||||
|
||||
result = vad_transfer_chunk_data(
|
||||
(unsigned long)xferi.buf,
|
||||
xferi.frames);
|
||||
|
||||
__put_user(result, &_xferi->result);
|
||||
|
||||
pr_debug("VAD resume trunk data, frames:%lu, result:%d\n",
|
||||
xferi.frames,
|
||||
result);
|
||||
|
||||
/* if audio data is read, waiting for next time */
|
||||
vad_set_trunk_data_readable(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long vad_ioctl_compat(
|
||||
struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
#else
|
||||
#define vad_ioctl_compat NULL
|
||||
#endif
|
||||
|
||||
static const struct file_operations vad_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vad_open,
|
||||
.unlocked_ioctl = vad_unlocked_ioctl,
|
||||
.compat_ioctl = vad_ioctl_compat,
|
||||
};
|
||||
|
||||
static int __init vad_init(void)
|
||||
{
|
||||
struct device *vad_dev;
|
||||
struct class *p_vad_class;
|
||||
int ret = 0;
|
||||
|
||||
ret = register_chrdev(VAD_MAJOR, DRV_NAME, &vad_fops);
|
||||
if (ret) {
|
||||
pr_err("Can't register char devie for " DRV_NAME "\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
ret = class_register(&vad_class);
|
||||
if (ret < 0) {
|
||||
pr_err("Create vad class failed\n");
|
||||
ret = -EEXIST;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
p_vad_class = &vad_class;
|
||||
vad_dev = device_create(p_vad_class,
|
||||
NULL, MKDEV(VAD_MAJOR, 0),
|
||||
NULL, DRV_NAME);
|
||||
if (vad_dev == NULL) {
|
||||
ret = -EEXIST;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
pr_info("Register %s", DRV_NAME);
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
class_destroy(p_vad_class);
|
||||
err1:
|
||||
unregister_chrdev(VAD_MAJOR, DRV_NAME);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vad_exit(void)
|
||||
{
|
||||
unregister_chrdev(VAD_MAJOR, DRV_NAME);
|
||||
}
|
||||
|
||||
module_init(vad_init);
|
||||
module_exit(vad_exit);
|
||||
109
sound/soc/amlogic/auge/vad_hw.c
Normal file
109
sound/soc/amlogic/auge/vad_hw.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad_hw.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 "vad_hw.h"
|
||||
|
||||
|
||||
void vad_set_ram_coeff(int len, int *params)
|
||||
{
|
||||
int i, ctrl_v;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ctrl_v = 0x1 << 31 | (i << 0);
|
||||
vad_write(VAD_LUT_WR, params[i]);
|
||||
vad_write(VAD_LUT_CTRL, ctrl_v);
|
||||
}
|
||||
}
|
||||
|
||||
/* parameters for downsample and emphasis filter */
|
||||
void vad_set_de_params(int len, int *params)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
vad_write(VAD_FIR_CTRL + i, params[i]);
|
||||
}
|
||||
|
||||
/* Power detection */
|
||||
void vad_set_pwd(void)
|
||||
{
|
||||
/* frame for 32 ms */
|
||||
vad_write(VAD_FRAME_CTRL0,
|
||||
0x2 << 30 |
|
||||
0x1 << 24 |
|
||||
0x1 << 16);
|
||||
|
||||
vad_write(VAD_FRAME_CTRL1, 0x00000d65);
|
||||
vad_write(VAD_FRAME_CTRL2, 0xd00103ff);
|
||||
}
|
||||
|
||||
void vad_set_cep(void)
|
||||
{
|
||||
vad_write(VAD_CEP_CTRL0, 0x11050000);
|
||||
vad_write(VAD_CEP_CTRL1, 0x0000001b);
|
||||
vad_write(VAD_CEP_CTRL2, 0xc001fd);
|
||||
vad_write(VAD_CEP_CTRL3, 0x137f0000);
|
||||
vad_write(VAD_CEP_CTRL4, 0x186d0000);
|
||||
vad_write(VAD_CEP_CTRL5, 0xfd00f61);
|
||||
vad_write(VAD_DEC_CTRL, 0x10030001);
|
||||
}
|
||||
|
||||
void vad_set_src(int src)
|
||||
{
|
||||
audiobus_update_bits(EE_AUDIO_TOVAD_CTRL0,
|
||||
0x7 << 12,
|
||||
src << 12);
|
||||
}
|
||||
|
||||
void vad_set_in(void)
|
||||
{
|
||||
/* two channel enable */
|
||||
vad_write(VAD_IN_SEL0, 0x00000001);
|
||||
vad_write(VAD_IN_SEL1, 0x00000002);
|
||||
|
||||
vad_write(VAD_TO_DDR, 0xa0000719);
|
||||
}
|
||||
|
||||
void vad_set_enable(bool enable)
|
||||
{
|
||||
audiobus_update_bits(EE_AUDIO_TOVAD_CTRL0,
|
||||
0x1 << 31 | 0x1 << 30,
|
||||
enable << 31 | 0x1 << 30);
|
||||
|
||||
if (enable) {
|
||||
vad_write(VAD_TOP_CTRL0, 0x7ff);
|
||||
vad_write(VAD_TOP_CTRL0, 0x0);
|
||||
|
||||
vad_write(VAD_TOP_CTRL1, 0xff);
|
||||
vad_write(VAD_TOP_CTRL1, 0x0);
|
||||
|
||||
vad_update_bits(VAD_TOP_CTRL0,
|
||||
0xfff << 20,
|
||||
1 << 31 | /* vad_en */
|
||||
1 << 30 | /* dec_fir_en */
|
||||
1 << 29 | /* pre_emp_en */
|
||||
1 << 28 | /* pre_ram_en */
|
||||
1 << 27 | /* frame_his_en */
|
||||
1 << 23 | /* ceps_ceps_en */
|
||||
1 << 22 | /* ceps_spec_en */
|
||||
0 << 20 /* two_channel_en */
|
||||
);
|
||||
} else {
|
||||
vad_write(VAD_TOP_CTRL0, 0x0);
|
||||
|
||||
vad_write(VAD_TOP_CTRL1, 0x0);
|
||||
}
|
||||
}
|
||||
38
sound/soc/amlogic/auge/vad_hw.h
Normal file
38
sound/soc/amlogic/auge/vad_hw.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad_hw.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_HW_H__
|
||||
#define __VAD_HW_H__
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
#include "iomap.h"
|
||||
|
||||
extern void vad_set_ram_coeff(int len, int *params);
|
||||
|
||||
|
||||
extern void vad_set_de_params(int len, int *params);
|
||||
|
||||
extern void vad_set_pwd(void);
|
||||
|
||||
extern void vad_set_cep(void);
|
||||
|
||||
extern void vad_set_src(int src);
|
||||
|
||||
extern void vad_set_in(void);
|
||||
|
||||
extern void vad_set_enable(bool enable);
|
||||
#endif
|
||||
167
sound/soc/amlogic/auge/vad_hw_coeff.c
Normal file
167
sound/soc/amlogic/auge/vad_hw_coeff.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* sound/soc/amlogic/auge/vad_hw_coeff.c
|
||||
*
|
||||
* Copyright (C) 2018 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* parameters for downsample and emphasis filter */
|
||||
static int vad_de_coeff[] = {
|
||||
0x31007f05,
|
||||
0x000003e6,
|
||||
0x0000070c,
|
||||
0x0716071a,
|
||||
0x071207fa,
|
||||
0x07d407b1,
|
||||
0x07a907d0,
|
||||
0x0728064c,
|
||||
0x06750671,
|
||||
0x075906b1,
|
||||
0x059404b4,
|
||||
0x04be0785,
|
||||
0x044c0361,
|
||||
0x024d0263,
|
||||
0x026a0000,
|
||||
};
|
||||
|
||||
static int vad_ram_coeff[] = {
|
||||
0x00003A3D,
|
||||
0x00003A42,
|
||||
0x00003A50,
|
||||
0x00003A67,
|
||||
0x00003A87,
|
||||
0x00003AB0,
|
||||
0x00003AE2,
|
||||
0x00003B1D,
|
||||
0x00003B61,
|
||||
0x00003BAF,
|
||||
0x00003C05,
|
||||
0x00003C64,
|
||||
0x00003CCC,
|
||||
0x00003D3C,
|
||||
0x00003DB5,
|
||||
0x00003E37,
|
||||
0x00003EC2,
|
||||
0x00003F55,
|
||||
0x00003FF0,
|
||||
0x0000284A,
|
||||
0x0000289F,
|
||||
0x000028F9,
|
||||
0x00002957,
|
||||
0x000029B9,
|
||||
0x00002A1F,
|
||||
0x00002A88,
|
||||
0x00002AF5,
|
||||
0x00002B66,
|
||||
0x00002BDA,
|
||||
0x00002C52,
|
||||
0x00002CCE,
|
||||
0x00002D4D,
|
||||
0x00002DCF,
|
||||
0x00002E54,
|
||||
0x00002EDC,
|
||||
0x00002F68,
|
||||
0x00002FF6,
|
||||
0x00001844,
|
||||
0x0000188E,
|
||||
0x000018D9,
|
||||
0x00001926,
|
||||
0x00001974,
|
||||
0x000019C3,
|
||||
0x00001A14,
|
||||
0x00001A65,
|
||||
0x00001AB8,
|
||||
0x00001B0C,
|
||||
0x00001B60,
|
||||
0x00001BB6,
|
||||
0x00001C0C,
|
||||
0x00001C63,
|
||||
0x00001CBB,
|
||||
0x00001D14,
|
||||
0x00001D6D,
|
||||
0x00001DC7,
|
||||
0x00001E22,
|
||||
0x00001E7C,
|
||||
0x00001ED8,
|
||||
0x00001F34,
|
||||
0x00001F90,
|
||||
0x00001FEC,
|
||||
0x00000824,
|
||||
0x00000853,
|
||||
0x00000881,
|
||||
0x000008AF,
|
||||
0x000008DE,
|
||||
0x0000090C,
|
||||
0x0000093B,
|
||||
0x00000969,
|
||||
0x00000997,
|
||||
0x000009C5,
|
||||
0x000009F3,
|
||||
0x00000A20,
|
||||
0x00000A4E,
|
||||
0x00000A7B,
|
||||
0x00000AA7,
|
||||
0x00000AD4,
|
||||
0x00000B00,
|
||||
0x00000B2C,
|
||||
0x00000B57,
|
||||
0x00000B82,
|
||||
0x00000BAD,
|
||||
0x00000BD7,
|
||||
0x00000C00,
|
||||
0x00000C29,
|
||||
0x00000C52,
|
||||
0x00000C7A,
|
||||
0x00000CA1,
|
||||
0x00000CC8,
|
||||
0x00000CEE,
|
||||
0x00000D13,
|
||||
0x00000D38,
|
||||
0x00000D5C,
|
||||
0x00000D7F,
|
||||
0x00000DA2,
|
||||
0x00000DC3,
|
||||
0x00000DE4,
|
||||
0x00000E05,
|
||||
0x00000E24,
|
||||
0x00000E42,
|
||||
0x00000E60,
|
||||
0x00000E7C,
|
||||
0x00000E98,
|
||||
0x00000EB3,
|
||||
0x00000ECD,
|
||||
0x00000EE6,
|
||||
0x00000EFE,
|
||||
0x00000F15,
|
||||
0x00000F2B,
|
||||
0x00000F40,
|
||||
0x00000F54,
|
||||
0x00000F66,
|
||||
0x00000F78,
|
||||
0x00000F89,
|
||||
0x00000F99,
|
||||
0x00000FA7,
|
||||
0x00000FB5,
|
||||
0x00000FC1,
|
||||
0x00000FCD,
|
||||
0x00000FD7,
|
||||
0x00000FE0,
|
||||
0x00000FE8,
|
||||
0x00000FEF,
|
||||
0x00000FF4,
|
||||
0x00000FF9,
|
||||
0x00000FFC,
|
||||
0x00000FFF,
|
||||
0x00000FFF
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "spdif_info: " fmt
|
||||
|
||||
@@ -111,7 +112,7 @@ void spdif_get_channel_status_info(
|
||||
chsts->chstat1_r = 0xe00;
|
||||
}
|
||||
}
|
||||
pr_info("rate: %d, channel status ch0_l:0x%x, ch0_r:0x%x, ch1_l:0x%x, ch1_r:0x%x\n",
|
||||
pr_debug("rate: %d, channel status ch0_l:0x%x, ch0_r:0x%x, ch1_l:0x%x, ch1_r:0x%x\n",
|
||||
rate,
|
||||
chsts->chstat0_l,
|
||||
chsts->chstat0_r,
|
||||
|
||||
@@ -136,6 +136,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_TAS571X if I2C
|
||||
select SND_SOC_TAS5720 if I2C
|
||||
select SND_SOC_TFA9879 if I2C
|
||||
select SND_SOC_TAS5805 if I2C
|
||||
select SND_SOC_TLV320AIC23_I2C if I2C
|
||||
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
@@ -812,6 +813,15 @@ config SND_SOC_TAS5720
|
||||
Enable support for Texas Instruments TAS5720L/M high-efficiency mono
|
||||
Class-D audio power amplifiers.
|
||||
|
||||
config SND_SOC_TAS5805
|
||||
tristate "Texas Instruments TAS5805 amplifiers"
|
||||
depends on I2C
|
||||
help
|
||||
Enable support for Texas Instruments TAS5805
|
||||
Class-D audio power amplifiers.
|
||||
control by I2C
|
||||
Select this if your TAS5805 is connected to I2C bus.
|
||||
|
||||
config SND_SOC_TFA9879
|
||||
tristate "NXP Semiconductors TFA9879 amplifier"
|
||||
depends on I2C
|
||||
|
||||
@@ -68,6 +68,17 @@ config AMLOGIC_SND_CODEC_PMU3
|
||||
AML PMU3 codec,
|
||||
this codec is internal
|
||||
|
||||
config AMLOGIC_SND_SOC_TAS5805
|
||||
bool "Texas Instruments TAS5805 amplifier"
|
||||
depends on AMLOGIC_SND_SOC_CODECS
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Enable support for Texas Instruments TAS5805 CODEC.
|
||||
Select this if your TAS5805 is connected via an I2C bus.
|
||||
Enable support for Texas Instruments TAS5805 CODEC.
|
||||
Select this if your TAS5805 is connected via an I2C bus.
|
||||
|
||||
config AMLOGIC_SND_CODEC_TXLX_ACODEC
|
||||
bool "Amlogic Audio txlx acodec"
|
||||
depends on AMLOGIC_SND_SOC_CODECS
|
||||
|
||||
@@ -18,6 +18,7 @@ snd-soc-pcm186x-objs := pcm186x.o pcm186x-i2c.o pcm186x-spi.o
|
||||
snd-soc-ssm3515-objs := ssm3515.o
|
||||
snd-soc-ssm3525-objs := ssm3525.o
|
||||
snd-soc-ad82584f-objs := ad82584f.o
|
||||
snd-soc-tas5805-objs := tas5805.o
|
||||
|
||||
# Amlogic
|
||||
obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC) += snd-soc-dummy_codec.o
|
||||
@@ -39,3 +40,4 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_TAS575X) += tas575x.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_ES7243) += es7243.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_AD82584F) += ad82584f.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_CS42528) += cs42528.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5805) += snd-soc-tas5805.o
|
||||
|
||||
@@ -36,12 +36,15 @@ static const DECLARE_TLV_DB_SCALE(mvol_tlv, -10300, 50, 1);
|
||||
static const DECLARE_TLV_DB_SCALE(chvol_tlv, -10300, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new ad82584f_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Master Volume", MVOL, 0,
|
||||
SOC_SINGLE_TLV("AMP Master Volume", MVOL, 0,
|
||||
0xff, 1, mvol_tlv),
|
||||
SOC_SINGLE_TLV("Ch1 Volume", C1VOL, 0,
|
||||
SOC_SINGLE_TLV("AMP Ch1 Volume", C1VOL, 0,
|
||||
0xff, 1, chvol_tlv),
|
||||
SOC_SINGLE_TLV("Ch2 Volume", C2VOL, 0,
|
||||
SOC_SINGLE_TLV("AMP Ch2 Volume", C2VOL, 0,
|
||||
0xff, 1, chvol_tlv),
|
||||
|
||||
SOC_SINGLE("AMP Ch1 Switch", MUTE, 5, 1, 1),
|
||||
SOC_SINGLE("AMP Ch2 Switch", MUTE, 4, 1, 1),
|
||||
};
|
||||
|
||||
static int ad82584f_reg_init(struct snd_soc_codec *codec);
|
||||
@@ -52,7 +55,7 @@ static const
|
||||
struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = {
|
||||
{0x00, 0x00},//##State_Control_1
|
||||
{0x01, 0x04},//##State_Control_2
|
||||
{0x02, 0x00},//##State_Control_3
|
||||
{0x02, 0x30},//##State_Control_3
|
||||
{0x03, 0x4e},//##Master_volume_control
|
||||
{0x04, 0x00},//##Channel_1_volume_control
|
||||
{0x05, 0x00},//##Channel_2_volume_control
|
||||
@@ -190,7 +193,7 @@ struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = {
|
||||
static const int m_reg_tab[AD82584F_REGISTER_COUNT][2] = {
|
||||
{0x00, 0x00},//##State_Control_1
|
||||
{0x01, 0x04},//##State_Control_2
|
||||
{0x02, 0x00},//##State_Control_3
|
||||
{0x02, 0x30},//##State_Control_3
|
||||
{0x03, 0x4e},//##Master_volume_control
|
||||
{0x04, 0x00},//##Channel_1_volume_control
|
||||
{0x05, 0x00},//##Channel_2_volume_control
|
||||
@@ -591,6 +594,12 @@ struct ad82584f_priv {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct ad82584f_platform_data *pdata;
|
||||
|
||||
unsigned char Ch1_vol;
|
||||
unsigned char Ch2_vol;
|
||||
unsigned char master_vol;
|
||||
unsigned char mute_val;
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
struct early_suspend early_suspend;
|
||||
#endif
|
||||
@@ -809,7 +818,7 @@ static int ad82584f_init(struct snd_soc_codec *codec)
|
||||
ad82584f_set_eq_drc(codec);
|
||||
|
||||
/*unmute,default power-on is mute.*/
|
||||
snd_soc_write(codec, 0x02, 0x00);
|
||||
/*snd_soc_write(codec, 0x02, 0x00);*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -844,8 +853,16 @@ static int ad82584f_remove(struct snd_soc_codec *codec)
|
||||
#ifdef CONFIG_PM
|
||||
static int ad82584f_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct ad82584f_priv *ad82584f = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
dev_info(codec->dev, "ad82584f_suspend!\n");
|
||||
|
||||
/* save volume */
|
||||
ad82584f->Ch1_vol = snd_soc_read(codec, C1VOL);
|
||||
ad82584f->Ch2_vol = snd_soc_read(codec, C2VOL);
|
||||
ad82584f->master_vol = snd_soc_read(codec, MVOL);
|
||||
ad82584f->mute_val = snd_soc_read(codec, MUTE);
|
||||
|
||||
ad82584f_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
@@ -853,9 +870,17 @@ static int ad82584f_suspend(struct snd_soc_codec *codec)
|
||||
|
||||
static int ad82584f_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct ad82584f_priv *ad82584f = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
dev_info(codec->dev, "ad82584f_resume!\n");
|
||||
|
||||
ad82584f_init(codec);
|
||||
|
||||
snd_soc_write(codec, C1VOL, ad82584f->Ch1_vol);
|
||||
snd_soc_write(codec, C2VOL, ad82584f->Ch2_vol);
|
||||
snd_soc_write(codec, MVOL, ad82584f->master_vol);
|
||||
snd_soc_write(codec, MUTE, ad82584f->mute_val);
|
||||
|
||||
ad82584f_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _AD82584F_H
|
||||
#define _AD82584F_H
|
||||
|
||||
#define MUTE 0x02
|
||||
#define MVOL 0x03
|
||||
#define C1VOL 0x04
|
||||
#define C2VOL 0x05
|
||||
|
||||
@@ -129,7 +129,8 @@ static int aml_DAC_Gain_set_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
|
||||
u32 reg_addr = ACODEC_1;
|
||||
u32 val = snd_soc_read(codec, reg_addr);
|
||||
|
||||
@@ -177,7 +178,8 @@ static int aml_DAC2_Gain_set_enum(
|
||||
struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
|
||||
u32 reg_addr = ACODEC_7;
|
||||
u32 val = snd_soc_read(codec, reg_addr);
|
||||
|
||||
@@ -784,6 +786,12 @@ static int aml_tl1_acodec_probe(struct platform_device *pdev)
|
||||
pr_info("aml_tl1_acodec tdmout_index=%d\n",
|
||||
aml_acodec->tdmout_index);
|
||||
|
||||
of_property_read_u32(
|
||||
pdev->dev.of_node,
|
||||
"dat1_ch_sel",
|
||||
&aml_acodec->dat1_ch_sel);
|
||||
pr_info("aml_tl1_acodec dat1_ch_sel=%d\n",
|
||||
aml_acodec->dat1_ch_sel);
|
||||
of_property_read_u32(
|
||||
pdev->dev.of_node,
|
||||
"tdmin_index",
|
||||
|
||||
@@ -185,6 +185,7 @@ static const struct snd_kcontrol_new tas5707_snd_controls[] = {
|
||||
0xff, 1, chvol_tlv),
|
||||
SOC_SINGLE("Ch1 Switch", DDX_SOFT_MUTE, 0, 1, 1),
|
||||
SOC_SINGLE("Ch2 Switch", DDX_SOFT_MUTE, 1, 1, 1),
|
||||
SOC_SINGLE("Shutdown Switch", DDX_SYS_CTL_2, 6, 1, 1),
|
||||
SOC_SINGLE_RANGE("Fine Master Volume", DDX_CHANNEL3_VOL, 0,
|
||||
0x80, 0x83, 0),
|
||||
SOC_SINGLE_BOOL_EXT("Set EQ Enable", 0,
|
||||
|
||||
547
sound/soc/codecs/amlogic/tas5805.c
Normal file
547
sound/soc/codecs/amlogic/tas5805.c
Normal file
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Driver for the TAS5805M Audio Amplifier
|
||||
*
|
||||
* Author: Andy Liu <andy-liu@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "tas5805.h"
|
||||
|
||||
#define TAS5805M_DRV_NAME "tas5805m"
|
||||
|
||||
#define TAS5805M_RATES (SNDRV_PCM_RATE_8000 | \
|
||||
SNDRV_PCM_RATE_11025 | \
|
||||
SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_22050 | \
|
||||
SNDRV_PCM_RATE_32000 | \
|
||||
SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000)
|
||||
#define TAS5805M_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define TAS5805M_REG_00 (0x00)
|
||||
#define TAS5805M_REG_03 (0x03)
|
||||
#define TAS5805M_REG_24 (0x24)
|
||||
#define TAS5805M_REG_25 (0x25)
|
||||
#define TAS5805M_REG_26 (0x26)
|
||||
#define TAS5805M_REG_27 (0x27)
|
||||
#define TAS5805M_REG_28 (0x28)
|
||||
#define TAS5805M_REG_29 (0x29)
|
||||
#define TAS5805M_REG_2A (0x2a)
|
||||
#define TAS5805M_REG_2B (0x2b)
|
||||
#define TAS5805M_REG_35 (0x35)
|
||||
#define TAS5805M_REG_7F (0x7f)
|
||||
|
||||
#define TAS5805M_PAGE_00 (0x00)
|
||||
#define TAS5805M_PAGE_2A (0x2a)
|
||||
|
||||
#define TAS5805M_BOOK_00 (0x00)
|
||||
#define TAS5805M_BOOK_8C (0x8c)
|
||||
|
||||
#define TAS5805M_VOLUME_MAX (158)
|
||||
#define TAS5805M_VOLUME_MIN (0)
|
||||
|
||||
const uint32_t tas5805m_volume[] = {
|
||||
0x0000001B, //0, -110dB
|
||||
0x0000001E, //1, -109dB
|
||||
0x00000021, //2, -108dB
|
||||
0x00000025, //3, -107dB
|
||||
0x0000002A, //4, -106dB
|
||||
0x0000002F, //5, -105dB
|
||||
0x00000035, //6, -104dB
|
||||
0x0000003B, //7, -103dB
|
||||
0x00000043, //8, -102dB
|
||||
0x0000004B, //9, -101dB
|
||||
0x00000054, //10, -100dB
|
||||
0x0000005E, //11, -99dB
|
||||
0x0000006A, //12, -98dB
|
||||
0x00000076, //13, -97dB
|
||||
0x00000085, //14, -96dB
|
||||
0x00000095, //15, -95dB
|
||||
0x000000A7, //16, -94dB
|
||||
0x000000BC, //17, -93dB
|
||||
0x000000D3, //18, -92dB
|
||||
0x000000EC, //19, -91dB
|
||||
0x00000109, //20, -90dB
|
||||
0x0000012A, //21, -89dB
|
||||
0x0000014E, //22, -88dB
|
||||
0x00000177, //23, -87dB
|
||||
0x000001A4, //24, -86dB
|
||||
0x000001D8, //25, -85dB
|
||||
0x00000211, //26, -84dB
|
||||
0x00000252, //27, -83dB
|
||||
0x0000029A, //28, -82dB
|
||||
0x000002EC, //29, -81dB
|
||||
0x00000347, //30, -80dB
|
||||
0x000003AD, //31, -79dB
|
||||
0x00000420, //32, -78dB
|
||||
0x000004A1, //33, -77dB
|
||||
0x00000532, //34, -76dB
|
||||
0x000005D4, //35, -75dB
|
||||
0x0000068A, //36, -74dB
|
||||
0x00000756, //37, -73dB
|
||||
0x0000083B, //38, -72dB
|
||||
0x0000093C, //39, -71dB
|
||||
0x00000A5D, //40, -70dB
|
||||
0x00000BA0, //41, -69dB
|
||||
0x00000D0C, //42, -68dB
|
||||
0x00000EA3, //43, -67dB
|
||||
0x0000106C, //44, -66dB
|
||||
0x0000126D, //45, -65dB
|
||||
0x000014AD, //46, -64dB
|
||||
0x00001733, //47, -63dB
|
||||
0x00001A07, //48, -62dB
|
||||
0x00001D34, //49, -61dB
|
||||
0x000020C5, //50, -60dB
|
||||
0x000024C4, //51, -59dB
|
||||
0x00002941, //52, -58dB
|
||||
0x00002E49, //53, -57dB
|
||||
0x000033EF, //54, -56dB
|
||||
0x00003A45, //55, -55dB
|
||||
0x00004161, //56, -54dB
|
||||
0x0000495C, //57, -53dB
|
||||
0x0000524F, //58, -52dB
|
||||
0x00005C5A, //59, -51dB
|
||||
0x0000679F, //60, -50dB
|
||||
0x00007444, //61, -49dB
|
||||
0x00008274, //62, -48dB
|
||||
0x0000925F, //63, -47dB
|
||||
0x0000A43B, //64, -46dB
|
||||
0x0000B845, //65, -45dB
|
||||
0x0000CEC1, //66, -44dB
|
||||
0x0000E7FB, //67, -43dB
|
||||
0x00010449, //68, -42dB
|
||||
0x0001240C, //69, -41dB
|
||||
0x000147AE, //70, -40dB
|
||||
0x00016FAA, //71, -39dB
|
||||
0x00019C86, //72, -38dB
|
||||
0x0001CEDC, //73, -37dB
|
||||
0x00020756, //74, -36dB
|
||||
0x000246B5, //75, -35dB
|
||||
0x00028DCF, //76, -34dB
|
||||
0x0002DD96, //77, -33dB
|
||||
0x00033718, //78, -32dB
|
||||
0x00039B87, //79, -31dB
|
||||
0x00040C37, //80, -30dB
|
||||
0x00048AA7, //81, -29dB
|
||||
0x00051884, //82, -28dB
|
||||
0x0005B7B1, //83, -27dB
|
||||
0x00066A4A, //84, -26dB
|
||||
0x000732AE, //85, -25dB
|
||||
0x00081385, //86, -24dB
|
||||
0x00090FCC, //87, -23dB
|
||||
0x000A2ADB, //88, -22dB
|
||||
0x000B6873, //89, -21dB
|
||||
0x000CCCCD, //90, -20dB
|
||||
0x000E5CA1, //91, -19dB
|
||||
0x00101D3F, //92, -18dB
|
||||
0x0012149A, //93, -17dB
|
||||
0x00144961, //94, -16dB
|
||||
0x0016C311, //95, -15dB
|
||||
0x00198A13, //96, -14dB
|
||||
0x001CA7D7, //97, -13dB
|
||||
0x002026F3, //98, -12dB
|
||||
0x00241347, //99, -11dB
|
||||
0x00287A27, //100, -10dB
|
||||
0x002D6A86, //101, -9dB
|
||||
0x0032F52D, //102, -8dB
|
||||
0x00392CEE, //103, -7dB
|
||||
0x004026E7, //104, -6dB
|
||||
0x0047FACD, //105, -5dB
|
||||
0x0050C336, //106, -4dB
|
||||
0x005A9DF8, //107, -3dB
|
||||
0x0065AC8C, //108, -2dB
|
||||
0x00721483, //109, -1dB
|
||||
0x00800000, //110, 0dB
|
||||
0x008F9E4D, //111, 1dB
|
||||
0x00A12478, //112, 2dB
|
||||
0x00B4CE08, //113, 3dB
|
||||
0x00CADDC8, //114, 4dB
|
||||
0x00E39EA9, //115, 5dB
|
||||
0x00FF64C1, //116, 6dB
|
||||
0x011E8E6A, //117, 7dB
|
||||
0x0141857F, //118, 8dB
|
||||
0x0168C0C6, //119, 9dB
|
||||
0x0194C584, //120, 10dB
|
||||
0x01C62940, //121, 11dB
|
||||
0x01FD93C2, //122, 12dB
|
||||
0x023BC148, //123, 13dB
|
||||
0x02818508, //124, 14dB
|
||||
0x02CFCC01, //125, 15dB
|
||||
0x0327A01A, //126, 16dB
|
||||
0x038A2BAD, //127, 17dB
|
||||
0x03F8BD7A, //128, 18dB
|
||||
0x0474CD1B, //129, 19dB
|
||||
0x05000000, //130, 20dB
|
||||
0x059C2F02, //131, 21dB
|
||||
0x064B6CAE, //132, 22dB
|
||||
0x07100C4D, //133, 23dB
|
||||
0x07ECA9CD, //134, 24dB
|
||||
0x08E43299, //135, 25dB
|
||||
0x09F9EF8E, //136, 26dB
|
||||
0x0B319025, //137, 27dB
|
||||
0x0C8F36F2, //138, 28dB
|
||||
0x0E1787B8, //139, 29dB
|
||||
0x0FCFB725, //140, 30dB
|
||||
0x11BD9C84, //141, 31dB
|
||||
0x13E7C594, //142, 32dB
|
||||
0x16558CCB, //143, 33dB
|
||||
0x190F3254, //144, 34dB
|
||||
0x1C1DF80E, //145, 35dB
|
||||
0x1F8C4107, //146, 36dB
|
||||
0x2365B4BF, //147, 37dB
|
||||
0x27B766C2, //148, 38dB
|
||||
0x2C900313, //149, 39dB
|
||||
0x32000000, //150, 40dB
|
||||
0x3819D612, //151, 41dB
|
||||
0x3EF23ECA, //152, 42dB
|
||||
0x46A07B07, //153, 43dB
|
||||
0x4F3EA203, //154, 44dB
|
||||
0x58E9F9F9, //155, 45dB
|
||||
0x63C35B8E, //156, 46dB
|
||||
0x6FEFA16D, //157, 47dB
|
||||
0x7D982575, //158, 48dB
|
||||
};
|
||||
|
||||
struct tas5805m_priv {
|
||||
struct regmap *regmap;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
int vol;
|
||||
int mute;
|
||||
};
|
||||
|
||||
const struct regmap_config tas5805m_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int tas5805m_vol_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->access =
|
||||
(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
|
||||
uinfo->count = 1;
|
||||
|
||||
uinfo->value.integer.min = TAS5805M_VOLUME_MIN;
|
||||
uinfo->value.integer.max = TAS5805M_VOLUME_MAX;
|
||||
uinfo->value.integer.step = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_mute_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->access =
|
||||
(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
|
||||
uinfo->count = 1;
|
||||
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
uinfo->value.integer.step = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_vol_locked_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
mutex_lock(&tas5805m->lock);
|
||||
ucontrol->value.integer.value[0] = tas5805m->vol;
|
||||
mutex_unlock(&tas5805m->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int get_volume_index(int vol)
|
||||
{
|
||||
int index;
|
||||
|
||||
index = vol;
|
||||
|
||||
if (index < TAS5805M_VOLUME_MIN)
|
||||
index = TAS5805M_VOLUME_MIN;
|
||||
|
||||
if (index > TAS5805M_VOLUME_MAX)
|
||||
index = TAS5805M_VOLUME_MAX;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static void tas5805m_set_volume(struct snd_soc_codec *codec, int vol)
|
||||
{
|
||||
unsigned int index;
|
||||
uint32_t volume_hex;
|
||||
uint8_t byte4;
|
||||
uint8_t byte3;
|
||||
uint8_t byte2;
|
||||
uint8_t byte1;
|
||||
|
||||
index = get_volume_index(vol);
|
||||
volume_hex = tas5805m_volume[index];
|
||||
|
||||
byte4 = ((volume_hex >> 24) & 0xFF);
|
||||
byte3 = ((volume_hex >> 16) & 0xFF);
|
||||
byte2 = ((volume_hex >> 8) & 0xFF);
|
||||
byte1 = ((volume_hex >> 0) & 0xFF);
|
||||
|
||||
//w 58 00 00
|
||||
snd_soc_write(codec, TAS5805M_REG_00, TAS5805M_PAGE_00);
|
||||
//w 58 7f 8c
|
||||
snd_soc_write(codec, TAS5805M_REG_7F, TAS5805M_BOOK_8C);
|
||||
//w 58 00 2a
|
||||
snd_soc_write(codec, TAS5805M_REG_00, TAS5805M_PAGE_2A);
|
||||
//w 58 24 xx xx xx xx
|
||||
snd_soc_write(codec, TAS5805M_REG_24, byte4);
|
||||
snd_soc_write(codec, TAS5805M_REG_25, byte3);
|
||||
snd_soc_write(codec, TAS5805M_REG_26, byte2);
|
||||
snd_soc_write(codec, TAS5805M_REG_27, byte1);
|
||||
//w 58 28 xx xx xx xx
|
||||
snd_soc_write(codec, TAS5805M_REG_28, byte4);
|
||||
snd_soc_write(codec, TAS5805M_REG_29, byte3);
|
||||
snd_soc_write(codec, TAS5805M_REG_2A, byte2);
|
||||
snd_soc_write(codec, TAS5805M_REG_2B, byte1);
|
||||
}
|
||||
|
||||
static int tas5805m_vol_locked_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
mutex_lock(&tas5805m->lock);
|
||||
|
||||
tas5805m->vol = ucontrol->value.integer.value[0];
|
||||
tas5805m_set_volume(codec, tas5805m->vol);
|
||||
|
||||
mutex_unlock(&tas5805m->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_mute(struct snd_soc_codec *codec, int mute)
|
||||
{
|
||||
u8 reg03_value = 0;
|
||||
u8 reg35_value = 0;
|
||||
// struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
if (mute) {
|
||||
//mute both left & right channels
|
||||
reg03_value = 0x0b;
|
||||
reg35_value = 0x00;
|
||||
} else {
|
||||
//unmute
|
||||
reg03_value = 0x03;
|
||||
reg35_value = 0x11;
|
||||
}
|
||||
|
||||
snd_soc_write(codec, TAS5805M_REG_00, TAS5805M_PAGE_00);
|
||||
snd_soc_write(codec, TAS5805M_REG_7F, TAS5805M_BOOK_00);
|
||||
snd_soc_write(codec, TAS5805M_REG_00, TAS5805M_PAGE_00);
|
||||
snd_soc_write(codec, TAS5805M_REG_03, reg03_value);
|
||||
snd_soc_write(codec, TAS5805M_REG_35, reg35_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_mute_locked_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
mutex_lock(&tas5805m->lock);
|
||||
|
||||
tas5805m->mute = ucontrol->value.integer.value[0];
|
||||
tas5805m_mute(codec, tas5805m->mute);
|
||||
|
||||
mutex_unlock(&tas5805m->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_mute_locked_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
mutex_lock(&tas5805m->lock);
|
||||
ucontrol->value.integer.value[0] = tas5805m->mute;
|
||||
mutex_unlock(&tas5805m->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new tas5805m_vol_control[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Volume",
|
||||
.info = tas5805m_vol_info,
|
||||
.get = tas5805m_vol_locked_get,
|
||||
.put = tas5805m_vol_locked_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Maser Volume Mute",
|
||||
.info = tas5805m_mute_info,
|
||||
.get = tas5805m_mute_locked_get,
|
||||
.put = tas5805m_mute_locked_put,
|
||||
}
|
||||
};
|
||||
|
||||
static int tas5805m_snd_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_add_codec_controls(codec, tas5805m_vol_control, 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_tas5805m = {
|
||||
.probe = tas5805m_snd_probe,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops tas5805m_dai_ops = {
|
||||
//.digital_mute = tas5805m_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver tas5805m_dai = {
|
||||
.name = "tas5805m-amplifier",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = TAS5805M_RATES,
|
||||
.formats = TAS5805M_FORMATS,
|
||||
},
|
||||
.ops = &tas5805m_dai_ops,
|
||||
};
|
||||
|
||||
static int tas5805m_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct tas5805m_priv *tas5805m;
|
||||
int ret;
|
||||
|
||||
tas5805m = devm_kzalloc(dev, sizeof(struct tas5805m_priv), GFP_KERNEL);
|
||||
if (!tas5805m)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, tas5805m);
|
||||
tas5805m->regmap = regmap;
|
||||
tas5805m->vol = 100; //100, -10dB
|
||||
|
||||
mutex_init(&tas5805m->lock);
|
||||
|
||||
ret =
|
||||
regmap_register_patch(regmap, tas5805m_init_sequence,
|
||||
ARRAY_SIZE(tas5805m_init_sequence));
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to initialize TAS5805M: %d\n", ret);
|
||||
goto err;
|
||||
|
||||
}
|
||||
|
||||
ret =
|
||||
snd_soc_register_codec(dev, &soc_codec_tas5805m, &tas5805m_dai, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to register CODEC: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int tas5805m_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct regmap_config config = tas5805m_regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return tas5805m_probe(&i2c->dev, regmap);
|
||||
}
|
||||
|
||||
static int tas5805m_remove(struct device *dev)
|
||||
{
|
||||
snd_soc_unregister_codec(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5805m_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
tas5805m_remove(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tas5805m_i2c_id[] = {
|
||||
{"tas5805",},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id tas5805m_of_match[] = {
|
||||
{.compatible = "ti,tas5805",},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tas5805m_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver tas5805m_i2c_driver = {
|
||||
.probe = tas5805m_i2c_probe,
|
||||
.remove = tas5805m_i2c_remove,
|
||||
.id_table = tas5805m_i2c_id,
|
||||
.driver = {
|
||||
.name = TAS5805M_DRV_NAME,
|
||||
.of_match_table = tas5805m_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(tas5805m_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Andy Liu <andy-liu@ti.com>");
|
||||
MODULE_DESCRIPTION("TAS5805M Audio Amplifier Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1880
sound/soc/codecs/amlogic/tas5805.h
Normal file
1880
sound/soc/codecs/amlogic/tas5805.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user