mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
audio: add audio dsp
PD#138714: add audio dsp driver Change-Id: I289771eed7b5ec4ce6c8f657f32593461cc61770 Signed-off-by: Xing Wang <xing.wang@amlogic.com>
This commit is contained in:
@@ -13712,6 +13712,7 @@ F: sound/soc/amlogic/*
|
||||
F: sound/soc/codecs/Kconfig
|
||||
F: sound/soc/codecs/Makefile
|
||||
F: sound/soc/codecs/amlogic/*
|
||||
F: drivers/amlogic/audiodsp/*
|
||||
|
||||
AMLOGIC PPMGR DRIVER
|
||||
M: Guosong Zhou <guosong.zhou@amlogic.com>
|
||||
|
||||
@@ -500,6 +500,162 @@
|
||||
"clk_ge2d_gate";
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* AUDIO MESON DEVICES */
|
||||
i2s_dai: I2S {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-i2s-dai";
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL2>,
|
||||
<&clkc CLKID_AMCLK_COMP>,
|
||||
<&clkc CLKID_AIU_GLUE>,
|
||||
<&clkc CLKID_IEC958>,
|
||||
<&clkc CLKID_I2S_OUT>,
|
||||
<&clkc CLKID_AMCLK>,
|
||||
<&clkc CLKID_AIFIFO2>,
|
||||
<&clkc CLKID_MIXER>,
|
||||
<&clkc CLKID_MIXER_IFACE>,
|
||||
<&clkc CLKID_ADC>,
|
||||
<&clkc CLKID_AIU_TOP>,
|
||||
<&clkc CLKID_AOCLK_GATE>,
|
||||
<&clkc CLKID_I2S_SPDIF>;
|
||||
clock-names =
|
||||
"mpll2",
|
||||
"mclk",
|
||||
"top_glue",
|
||||
"aud_buf",
|
||||
"i2s_out",
|
||||
"amclk_measure",
|
||||
"aififo2",
|
||||
"aud_mixer",
|
||||
"mixer_reg",
|
||||
"adc",
|
||||
"top_level",
|
||||
"aoclk",
|
||||
"aud_in";
|
||||
/*DMIC;*/ /* I2s Mic or Dmic, default for I2S mic */
|
||||
};
|
||||
dmic:snd_dmic {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "aml, aml_snd_dmic";
|
||||
reg = <0x0 0xd0042000 0x0 0x2000>;
|
||||
status = "okay";
|
||||
resets = <
|
||||
&clkc CLKID_PDM_GATE
|
||||
>;
|
||||
reset-names = "pdm";
|
||||
pinctrl-names = "aml_dmic_pins";
|
||||
pinctrl-0 = <&aml_dmic_pins>;
|
||||
clocks = <&clkc CLKID_PDM_COMP>,
|
||||
<&clkc CLKID_AMCLK_COMP>;
|
||||
clock-names = "pdm", "mclk";
|
||||
};
|
||||
spdif_dai: SPDIF {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-spdif-dai";
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL1>,
|
||||
<&clkc CLKID_I958_COMP>,
|
||||
<&clkc CLKID_AMCLK_COMP>,
|
||||
<&clkc CLKID_I958_COMP_SPDIF>,
|
||||
<&clkc CLKID_CLK81>,
|
||||
<&clkc CLKID_IEC958>,
|
||||
<&clkc CLKID_IEC958_GATE>;
|
||||
clock-names =
|
||||
"mpll1",
|
||||
"i958",
|
||||
"mclk",
|
||||
"spdif",
|
||||
"clk_81",
|
||||
"iec958",
|
||||
"iec958_amclk";
|
||||
};
|
||||
pcm_dai: PCM {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-pcm-dai";
|
||||
pinctrl-names = "aml_audio_btpcm";
|
||||
pinctrl-0 = <&audio_pcm_pins>;
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL0>,
|
||||
<&clkc CLKID_PCM_MCLK_COMP>,
|
||||
<&clkc CLKID_PCM_SCLK_GATE>;
|
||||
clock-names =
|
||||
"mpll0",
|
||||
"pcm_mclk",
|
||||
"pcm_sclk";
|
||||
pcm_mode = <1>; /* 0=slave mode, 1=master mode */
|
||||
};
|
||||
i2s_plat: i2s_platform {
|
||||
compatible = "amlogic, aml-i2s";
|
||||
interrupts = <0 29 1>;
|
||||
};
|
||||
pcm_plat: pcm_platform {
|
||||
compatible = "amlogic, aml-pcm";
|
||||
};
|
||||
spdif_codec: spdif_codec{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-spdif-codec";
|
||||
pinctrl-names = "aml_audio_spdif";
|
||||
pinctrl-0 = <&audio_spdif_pins>;
|
||||
};
|
||||
pcm_codec: pcm_codec{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, pcm2BT-codec";
|
||||
};
|
||||
/* endof AUDIO MESON DEVICES */
|
||||
|
||||
/* AUDIO board specific */
|
||||
dummy_codec:dummy{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml_dummy_codec";
|
||||
status = "disable";
|
||||
};
|
||||
amlogic_codec:t9015{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml_codec_T9015";
|
||||
reg = <0x0 0xc8832000 0x0 0x14>;
|
||||
status = "okay";
|
||||
};
|
||||
aml_sound_meson {
|
||||
compatible = "aml, meson-snd-card";
|
||||
status = "okay";
|
||||
aml-sound-card,format = "i2s";
|
||||
aml_sound_card,name = "AML-MESONAUDIO";
|
||||
aml,audio-routing =
|
||||
"Ext Spk","LOUTL",
|
||||
"Ext Spk","LOUTR";
|
||||
|
||||
mute_gpio-gpios = <&gpio GPIOH_5 0>;
|
||||
mute_inv;
|
||||
hp_disable;
|
||||
hp_paraments = <800 300 0 5 1>;
|
||||
pinctrl-names = "audio_i2s_pins";
|
||||
pinctrl-0 = <&audio_i2s_pins>;
|
||||
cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
|
||||
codec_list = <&codec0 &codec1 &codec2>;
|
||||
plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
|
||||
cpudai0: cpudai0 {
|
||||
sound-dai = <&i2s_dai>;
|
||||
};
|
||||
cpudai1: cpudai1 {
|
||||
sound-dai = <&spdif_dai>;
|
||||
};
|
||||
cpudai2: cpudai2 {
|
||||
sound-dai = <&pcm_dai>;
|
||||
};
|
||||
codec0: codec0 {
|
||||
sound-dai = <&amlogic_codec>;
|
||||
};
|
||||
codec1: codec1 {
|
||||
sound-dai = <&spdif_codec>;
|
||||
};
|
||||
codec2: codec2 {
|
||||
sound-dai = <&pcm_codec>;
|
||||
};
|
||||
};
|
||||
/* END OF AUDIO board specific */
|
||||
|
||||
ppmgr {
|
||||
compatible = "amlogic, ppmgr";
|
||||
memory-region = <&ppmgr_reserved>;
|
||||
|
||||
@@ -561,6 +561,162 @@
|
||||
"clk_ge2d_gate";
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* AUDIO MESON DEVICES */
|
||||
i2s_dai: I2S {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-i2s-dai";
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL2>,
|
||||
<&clkc CLKID_AMCLK_COMP>,
|
||||
<&clkc CLKID_AIU_GLUE>,
|
||||
<&clkc CLKID_IEC958>,
|
||||
<&clkc CLKID_I2S_OUT>,
|
||||
<&clkc CLKID_AMCLK>,
|
||||
<&clkc CLKID_AIFIFO2>,
|
||||
<&clkc CLKID_MIXER>,
|
||||
<&clkc CLKID_MIXER_IFACE>,
|
||||
<&clkc CLKID_ADC>,
|
||||
<&clkc CLKID_AIU_TOP>,
|
||||
<&clkc CLKID_AOCLK_GATE>,
|
||||
<&clkc CLKID_I2S_SPDIF>;
|
||||
clock-names =
|
||||
"mpll2",
|
||||
"mclk",
|
||||
"top_glue",
|
||||
"aud_buf",
|
||||
"i2s_out",
|
||||
"amclk_measure",
|
||||
"aififo2",
|
||||
"aud_mixer",
|
||||
"mixer_reg",
|
||||
"adc",
|
||||
"top_level",
|
||||
"aoclk",
|
||||
"aud_in";
|
||||
/*DMIC;*/ /* I2s Mic or Dmic, default for I2S mic */
|
||||
};
|
||||
dmic:snd_dmic {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "aml, aml_snd_dmic";
|
||||
reg = <0x0 0xd0042000 0x0 0x2000>;
|
||||
status = "okay";
|
||||
resets = <
|
||||
&clkc CLKID_PDM_GATE
|
||||
>;
|
||||
reset-names = "pdm";
|
||||
pinctrl-names = "aml_dmic_pins";
|
||||
pinctrl-0 = <&aml_dmic_pins>;
|
||||
clocks = <&clkc CLKID_PDM_COMP>,
|
||||
<&clkc CLKID_AMCLK_COMP>;
|
||||
clock-names = "pdm", "mclk";
|
||||
};
|
||||
spdif_dai: SPDIF {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-spdif-dai";
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL1>,
|
||||
<&clkc CLKID_I958_COMP>,
|
||||
<&clkc CLKID_AMCLK_COMP>,
|
||||
<&clkc CLKID_I958_COMP_SPDIF>,
|
||||
<&clkc CLKID_CLK81>,
|
||||
<&clkc CLKID_IEC958>,
|
||||
<&clkc CLKID_IEC958_GATE>;
|
||||
clock-names =
|
||||
"mpll1",
|
||||
"i958",
|
||||
"mclk",
|
||||
"spdif",
|
||||
"clk_81",
|
||||
"iec958",
|
||||
"iec958_amclk";
|
||||
};
|
||||
pcm_dai: PCM {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-pcm-dai";
|
||||
pinctrl-names = "aml_audio_btpcm";
|
||||
pinctrl-0 = <&audio_pcm_pins>;
|
||||
clocks =
|
||||
<&clkc CLKID_MPLL0>,
|
||||
<&clkc CLKID_PCM_MCLK_COMP>,
|
||||
<&clkc CLKID_PCM_SCLK_GATE>;
|
||||
clock-names =
|
||||
"mpll0",
|
||||
"pcm_mclk",
|
||||
"pcm_sclk";
|
||||
pcm_mode = <1>; /* 0=slave mode, 1=master mode */
|
||||
};
|
||||
i2s_plat: i2s_platform {
|
||||
compatible = "amlogic, aml-i2s";
|
||||
interrupts = <0 29 1>;
|
||||
};
|
||||
pcm_plat: pcm_platform {
|
||||
compatible = "amlogic, aml-pcm";
|
||||
};
|
||||
spdif_codec: spdif_codec{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml-spdif-codec";
|
||||
pinctrl-names = "aml_audio_spdif";
|
||||
pinctrl-0 = <&audio_spdif_pins>;
|
||||
};
|
||||
pcm_codec: pcm_codec{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, pcm2BT-codec";
|
||||
};
|
||||
/* endof AUDIO MESON DEVICES */
|
||||
|
||||
/* AUDIO board specific */
|
||||
dummy_codec:dummy{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml_dummy_codec";
|
||||
status = "disable";
|
||||
};
|
||||
amlogic_codec:t9015{
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "amlogic, aml_codec_T9015";
|
||||
reg = <0x0 0xc8832000 0x0 0x14>;
|
||||
status = "okay";
|
||||
};
|
||||
aml_sound_meson {
|
||||
compatible = "aml, meson-snd-card";
|
||||
status = "okay";
|
||||
aml-sound-card,format = "i2s";
|
||||
aml_sound_card,name = "AML-MESONAUDIO";
|
||||
aml,audio-routing =
|
||||
"Ext Spk","LOUTL",
|
||||
"Ext Spk","LOUTR";
|
||||
|
||||
mute_gpio-gpios = <&gpio GPIOH_5 0>;
|
||||
mute_inv;
|
||||
hp_disable;
|
||||
hp_paraments = <800 300 0 5 1>;
|
||||
pinctrl-names = "audio_i2s_pins";
|
||||
pinctrl-0 = <&audio_i2s_pins>;
|
||||
cpu_list = <&cpudai0 &cpudai1 &cpudai2>;
|
||||
codec_list = <&codec0 &codec1 &codec2>;
|
||||
plat_list = <&i2s_plat &i2s_plat &pcm_plat>;
|
||||
cpudai0: cpudai0 {
|
||||
sound-dai = <&i2s_dai>;
|
||||
};
|
||||
cpudai1: cpudai1 {
|
||||
sound-dai = <&spdif_dai>;
|
||||
};
|
||||
cpudai2: cpudai2 {
|
||||
sound-dai = <&pcm_dai>;
|
||||
};
|
||||
codec0: codec0 {
|
||||
sound-dai = <&amlogic_codec>;
|
||||
};
|
||||
codec1: codec1 {
|
||||
sound-dai = <&spdif_codec>;
|
||||
};
|
||||
codec2: codec2 {
|
||||
sound-dai = <&pcm_codec>;
|
||||
};
|
||||
};
|
||||
/* END OF AUDIO board specific */
|
||||
|
||||
ppmgr {
|
||||
compatible = "amlogic, ppmgr";
|
||||
memory-region = <&ppmgr_reserved>;
|
||||
|
||||
@@ -232,6 +232,7 @@ CONFIG_AMLOGIC_TEMP_SENSOR=y
|
||||
CONFIG_AMLOGIC_CPUCORE_THERMAL=y
|
||||
CONFIG_AMLOGIC_GPU_THERMAL=y
|
||||
CONFIG_AMLOGIC_GPUCORE_THERMAL=y
|
||||
CONFIG_AMLOGIC_AUDIO_DSP=y
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
|
||||
@@ -67,5 +67,6 @@ source "drivers/amlogic/key_manage/Kconfig"
|
||||
|
||||
source "drivers/amlogic/thermal/Kconfig"
|
||||
|
||||
source "drivers/amlogic/audiodsp/Kconfig"
|
||||
endmenu
|
||||
endif
|
||||
|
||||
@@ -63,3 +63,4 @@ obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_AUDIO_DSP) += audiodsp/
|
||||
|
||||
11
drivers/amlogic/audiodsp/Kconfig
Normal file
11
drivers/amlogic/audiodsp/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# audio dsp configuration
|
||||
#
|
||||
menu "AMLOGIC Audio DSP process"
|
||||
|
||||
config AMLOGIC_AUDIO_DSP
|
||||
tristate "audio dsp control support"
|
||||
default n
|
||||
help
|
||||
support the amlogic audio dsp;
|
||||
|
||||
endmenu
|
||||
20
drivers/amlogic/audiodsp/Makefile
Normal file
20
drivers/amlogic/audiodsp/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
# Makefile for osd and tv
|
||||
|
||||
audiodsp-objs = audiodsp_module.o
|
||||
# dsp_control.o \
|
||||
# dsp_microcode.o \
|
||||
# dsp_mailbox.o \
|
||||
# dsp_monitor.o \
|
||||
# dsp_codec.o
|
||||
|
||||
#audiodsp-objs += pcmenc_module.o pcmenc_stream.o
|
||||
#audiodsp-objs += spdif_module.o
|
||||
|
||||
ifneq ($(KBUILD_SRC),)
|
||||
TOP_KBUILD_SRC := $(KBUILD_SRC)/
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_AUDIO_DSP) +=audiodsp.o
|
||||
|
||||
#EXTRA_CFLAGS = -DENABLE_WAIT_FORMAT
|
||||
86
drivers/amlogic/audiodsp/audiodsp_control.h
Normal file
86
drivers/amlogic/audiodsp/audiodsp_control.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/audiodsp_control.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 ENABLE_WAIT_FORMAT
|
||||
#define ENABLE_WAIT_FORMAT
|
||||
#endif
|
||||
|
||||
#ifndef AUDIODSP_CONTROL_H
|
||||
#define AUDIODSP_CONTROL_H
|
||||
|
||||
struct audiodsp_cmd {
|
||||
int cmd;
|
||||
int fmt;
|
||||
int data_len;
|
||||
char *data;
|
||||
};
|
||||
|
||||
#define AUDIODSP_SYNC_AUDIO_PAUSE _IO('a', 0x01)
|
||||
#define AUDIODSP_SYNC_AUDIO_RESUME _IO('a', 0x02)
|
||||
|
||||
#define AUDIODSP_SET_FMT _IOW('a', 1, long)
|
||||
#define AUDIODSP_START _IOW('a', 2, long)
|
||||
#define AUDIODSP_STOP _IOW('a', 3, long)
|
||||
#define AUDIODSP_DECODE_START _IOW('a', 4, long)
|
||||
#define AUDIODSP_DECODE_STOP _IOW('a', 5, long)
|
||||
#define AUDIODSP_REGISTER_FIRMWARE _IOW('a', 6, long)
|
||||
#define AUDIODSP_UNREGISTER_ALLFIRMWARE _IOW('a', 7, long)
|
||||
#define AUDIODSP_SYNC_AUDIO_START _IOW('a', 8, unsigned long)
|
||||
#define AUDIODSP_SYNC_AUDIO_TSTAMP_DISCONTINUITY \
|
||||
_IOW('a', 9, unsigned long)
|
||||
#define AUDIODSP_SYNC_SET_APTS _IOW('a', 10, unsigned long)
|
||||
|
||||
#ifdef ENABLE_WAIT_FORMAT
|
||||
#define AUDIODSP_WAIT_FORMAT _IOW('a', 11, long)
|
||||
#endif
|
||||
#define AUDIODSP_DROP_PCMDATA _IOW('a', 12, unsigned long)
|
||||
#define AUDIODSP_SKIP_BYTES _IOW('a', 13, unsigned long)
|
||||
|
||||
#define AUDIODSP_GET_CHANNELS_NUM _IOR('r', 1, long)
|
||||
#define AUDIODSP_GET_SAMPLERATE _IOR('r', 2, long)
|
||||
#define AUDIODSP_GET_BITS_PER_SAMPLE _IOR('r', 3, long)
|
||||
#define AUDIODSP_GET_PTS _IOR('r', 4, long)
|
||||
#define AUDIODSP_GET_DECODED_NB_FRAMES _IOR('r', 5, long)
|
||||
#define AUDIODSP_GET_FIRST_PTS_FLAG _IOR('r', 6, long)
|
||||
#define AUDIODSP_SYNC_GET_APTS _IOR('r', 7, unsigned long)
|
||||
#define AUDIODSP_SYNC_GET_PCRSCR _IOR('r', 8, unsigned long)
|
||||
#define AUDIODSP_AUTOMUTE_ON _IOW('r', 9, unsigned long)
|
||||
#define AUDIODSP_AUTOMUTE_OFF _IOW('r', 10, unsigned long)
|
||||
#define AUDIODSP_LOOKUP_APTS _IOR('r', 11, unsigned long)
|
||||
#define AUDIODSP_GET_PCM_LEVEL _IOR('r', 12, unsigned long)
|
||||
#define AUDIODSP_SET_PCM_BUF_SIZE _IOW('r', 13, long)
|
||||
|
||||
#define MCODEC_FMT_MPEG123 (1<<0)
|
||||
#define MCODEC_FMT_AAC (1<<1)
|
||||
#define MCODEC_FMT_AC3 (1<<2)
|
||||
#define MCODEC_FMT_DTS (1<<3)
|
||||
#define MCODEC_FMT_FLAC (1<<4)
|
||||
#define MCODEC_FMT_COOK (1<<5)
|
||||
#define MCODEC_FMT_AMR (1<<6)
|
||||
#define MCODEC_FMT_RAAC (1<<7)
|
||||
#define MCODEC_FMT_ADPCM (1<<8)
|
||||
#define MCODEC_FMT_WMA (1<<9)
|
||||
#define MCODEC_FMT_PCM (1<<10)
|
||||
#define MCODEC_FMT_WMAPRO (1<<11)
|
||||
#define MCODEC_FMT_ALAC (1<<12)
|
||||
#define MCODEC_FMT_AAC_LATM (1<<14)
|
||||
#define MCODEC_FMT_APE (1<<15)
|
||||
#define MCODEC_FMT_EAC3 (1<<16)
|
||||
#define MCODEC_FMT_NULL (1<<17)
|
||||
#define AUDIOINFO_FROM_AUDIODSP(format) ((format == MCODEC_FMT_AAC) || \
|
||||
(format == MCODEC_FMT_AAC_LATM))
|
||||
#endif
|
||||
1333
drivers/amlogic/audiodsp/audiodsp_module.c
Normal file
1333
drivers/amlogic/audiodsp/audiodsp_module.c
Normal file
File diff suppressed because it is too large
Load Diff
102
drivers/amlogic/audiodsp/audiodsp_module.h
Normal file
102
drivers/amlogic/audiodsp/audiodsp_module.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/audiodsp_module.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DSP_MODULES_H
|
||||
#define AUDIO_DSP_MODULES_H
|
||||
#include <linux/device.h>
|
||||
#include <linux/timer.h>
|
||||
#if 0 /* tmp_mask_for_kernel_4_4 */
|
||||
#include <linux/wakelock.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* #include <asm/dsp/audiodsp_control.h>
|
||||
* #include <asm/dsp/dsp_register.h>
|
||||
*/
|
||||
#include "audiodsp_control.h"
|
||||
#include <linux/amlogic/media/sound/dsp_register.h>
|
||||
|
||||
#include "codec_message.h"
|
||||
#include <linux/dma-mapping.h>
|
||||
struct audiodsp_priv {
|
||||
struct class *class;
|
||||
struct device *dev;
|
||||
struct device *micro_dev;
|
||||
struct timer_list dsp_mointer;
|
||||
struct list_head mcode_list;
|
||||
int mcode_id;
|
||||
spinlock_t mcode_lock;
|
||||
int code_mem_size;
|
||||
struct mail_msg *mailbox_reg;
|
||||
struct mail_msg *mailbox_reg2;
|
||||
struct dsp_working_info *dsp_work_details;
|
||||
unsigned long dsp_code_start;
|
||||
unsigned long dsp_code_size;
|
||||
void *dsp_stack_start;
|
||||
unsigned long dsp_stack_size;
|
||||
void *dsp_gstack_start;
|
||||
unsigned long dsp_gstack_size;
|
||||
unsigned long dsp_heap_start;
|
||||
unsigned long dsp_heap_size;
|
||||
struct mutex dsp_mutex;
|
||||
char dsp_codename[32];
|
||||
unsigned long dsp_start_time; /* system jiffies */
|
||||
unsigned long dsp_end_time; /* system jiffies */
|
||||
int stream_fmt;
|
||||
int last_stream_fmt;
|
||||
struct frame_fmt frame_format;
|
||||
struct frame_info cur_frame_info;
|
||||
unsigned int last_valid_pts;
|
||||
int out_len_after_last_valid_pts;
|
||||
int decode_error_count;
|
||||
int decode_fatal_err;
|
||||
int dsp_is_started;
|
||||
void *stream_buffer_mem;
|
||||
int stream_buffer_mem_size;
|
||||
int decoded_nb_frames;
|
||||
int first_lookup_over;
|
||||
int format_wait_count;
|
||||
unsigned long stream_buffer_start;
|
||||
unsigned long stream_buffer_end;
|
||||
unsigned long stream_buffer_size;
|
||||
struct mutex stream_buffer_mutex;
|
||||
struct completion decode_completion;
|
||||
void __iomem *p;
|
||||
|
||||
/* for power management */
|
||||
#if 0 /* tmp_mask_for_kernel_4_4 */
|
||||
struct wake_lock wakelock;
|
||||
#endif
|
||||
unsigned int dsp_abnormal_count;
|
||||
unsigned int last_ablevel;
|
||||
unsigned int last_pcmlevel;
|
||||
};
|
||||
|
||||
struct audiodsp_priv *audiodsp_privdata(void);
|
||||
|
||||
#define DSP_PRNT(fmt, args...) pr_info("[dsp]" fmt, ##args)
|
||||
extern void tsync_pcr_recover(void);
|
||||
extern void tsync_pcr_recover(void);
|
||||
extern unsigned int IEC958_mode_raw;
|
||||
extern unsigned int IEC958_mode_codec;
|
||||
/*extern int decopt; */
|
||||
extern struct audio_info *get_audio_info(void);
|
||||
extern void aml_alsa_hw_reprepare(void);
|
||||
extern void dsp_get_debug_interface(int flag);
|
||||
/* extern void audiodsp_moniter(unsigned long); */
|
||||
|
||||
#endif /*
*/
|
||||
93
drivers/amlogic/audiodsp/codec_message.h
Normal file
93
drivers/amlogic/audiodsp/codec_message.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/codec_message.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 _CODEC_MESSAGE_HEADERS
|
||||
#define _CODEC_MESSAGE_HEADERS
|
||||
|
||||
#define SUB_FMT_VALID (1<<1)
|
||||
#define CHANNEL_VALID (1<<2)
|
||||
#define SAMPLE_RATE_VALID (1<<3)
|
||||
#define DATA_WIDTH_VALID (1<<4)
|
||||
struct digit_raw_output_info {
|
||||
int framelength;
|
||||
unsigned char *framebuf;
|
||||
int frame_size;
|
||||
int frame_samples;
|
||||
unsigned char *rawptr;
|
||||
|
||||
/* for AC3 */
|
||||
int sampleratecode;
|
||||
int bsmod;
|
||||
int bpf;
|
||||
int brst;
|
||||
int length;
|
||||
int padsize;
|
||||
int mode;
|
||||
unsigned int syncword1;
|
||||
unsigned int syncword2;
|
||||
unsigned int syncword3;
|
||||
unsigned int syncword1_mask;
|
||||
unsigned int syncword2_mask;
|
||||
unsigned int syncword3_mask;
|
||||
unsigned int chstat0_l;
|
||||
unsigned int chstat0_r;
|
||||
unsigned int chstat1_l;
|
||||
unsigned int chstat1_r;
|
||||
unsigned int can_bypass;
|
||||
};
|
||||
|
||||
struct frame_fmt {
|
||||
int valid;
|
||||
int sub_fmt;
|
||||
int channel_num;
|
||||
int sample_rate;
|
||||
int data_width;
|
||||
int buffered_len; /*dsp codec,buffered origan data len */
|
||||
int format;
|
||||
unsigned int total_byte_parsed;
|
||||
union {
|
||||
unsigned int total_sample_decoded;
|
||||
void *pcm_encoded_info; /* used for encoded pcm info */
|
||||
} data;
|
||||
unsigned int bps;
|
||||
void *private_data;
|
||||
struct digit_raw_output_info *digit_raw_output_info;
|
||||
};
|
||||
|
||||
struct frame_info {
|
||||
int len;
|
||||
unsigned long offset; /*steam start to here */
|
||||
unsigned long buffered_len; /*data buffer in dsp,pcm datalen */
|
||||
int reversed[1]; /*for cache aligned 32 bytes */
|
||||
};
|
||||
|
||||
struct dsp_working_info {
|
||||
int status;
|
||||
int sp;
|
||||
int pc;
|
||||
int ilink1;
|
||||
int ilink2;
|
||||
int blink;
|
||||
int jiffies;
|
||||
int out_wp;
|
||||
int out_rp;
|
||||
int buffered_len; /* pcm buffered at the dsp side */
|
||||
int es_offset; /* stream read offset since start decoder */
|
||||
int reserved[5];
|
||||
};
|
||||
|
||||
#endif /*
*/
|
||||
206
drivers/amlogic/audiodsp/dsp_codec.c
Normal file
206
drivers/amlogic/audiodsp/dsp_codec.c
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_codec.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 pr_fmt(fmt) "audio_dsp: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cache.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <linux/amlogic/amports/ptsserv.h>
|
||||
#include <linux/amlogic/amports/timestamp.h>
|
||||
/* #include <asm/dsp/dsp_register.h> */
|
||||
|
||||
#include "dsp_microcode.h"
|
||||
#include "audiodsp_module.h"
|
||||
#include "dsp_control.h"
|
||||
|
||||
#include "dsp_mailbox.h"
|
||||
|
||||
#include "dsp_codec.h"
|
||||
|
||||
int dsp_codec_start(struct audiodsp_priv *priv)
|
||||
{
|
||||
return dsp_mailbox_send(priv, 1, M2B_IRQ2_DECODE_START, 0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
int dsp_codec_stop(struct audiodsp_priv *priv)
|
||||
{
|
||||
|
||||
return dsp_mailbox_send(priv, 1, M2B_IRQ3_DECODE_STOP, 0, 0, 0);
|
||||
}
|
||||
|
||||
int dsp_codec_get_bufer_data_len(struct audiodsp_priv *priv)
|
||||
{
|
||||
#define REVERSD_BYTES 32
|
||||
#define CACHE_ALIGNED(x) (x&(~0x1f))
|
||||
unsigned long rp, wp, len, flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
rp = dsp_codec_get_rd_addr(priv);
|
||||
wp = dsp_codec_get_wd_addr(priv);
|
||||
if (rp > wp)
|
||||
len = priv->stream_buffer_size - (rp - wp);
|
||||
else
|
||||
len = (wp - rp);
|
||||
|
||||
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
|
||||
len = CACHE_ALIGNED(len);
|
||||
local_irq_restore(flags);
|
||||
return len;
|
||||
}
|
||||
|
||||
int dsp_codec_get_bufer_data_len1(struct audiodsp_priv *priv,
|
||||
unsigned long wd_ptr)
|
||||
{
|
||||
#define REVERSD_BYTES 32
|
||||
#define CACHE_ALIGNED(x) (x&(~0x1f))
|
||||
unsigned long rp, wp, len, flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
rp = dsp_codec_get_rd_addr(priv);
|
||||
wp = ARC_2_ARM_ADDR_SWAP(wd_ptr);
|
||||
if (rp > wp)
|
||||
len = priv->stream_buffer_size - (rp - wp);
|
||||
else
|
||||
len = (wp - rp);
|
||||
|
||||
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
|
||||
len = CACHE_ALIGNED(len);
|
||||
local_irq_restore(flags);
|
||||
return len;
|
||||
}
|
||||
|
||||
unsigned long dsp_codec_inc_rd_addr(struct audiodsp_priv *priv, int size)
|
||||
{
|
||||
unsigned long rd, flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
rd = dsp_codec_get_rd_addr(priv);
|
||||
rd = rd + size;
|
||||
if (rd >= priv->stream_buffer_end)
|
||||
rd = rd - priv->stream_buffer_size;
|
||||
|
||||
DSP_WD(DSP_DECODE_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP((void *)rd));
|
||||
local_irq_restore(flags);
|
||||
return rd;
|
||||
}
|
||||
|
||||
u32 dsp_codec_get_current_pts(struct audiodsp_priv *priv)
|
||||
{
|
||||
#ifdef CONFIG_AM_PTSSERVER
|
||||
u32 pts;
|
||||
u32 delay_pts;
|
||||
int len;
|
||||
u64 frame_nums;
|
||||
int res;
|
||||
u32 offset, buffered_len, wp;
|
||||
|
||||
mutex_lock(&priv->stream_buffer_mutex);
|
||||
|
||||
if (priv->frame_format.channel_num == 0
|
||||
|| priv->frame_format.sample_rate == 0
|
||||
|| priv->frame_format.data_width == 0) {
|
||||
pr_info("unvalid audio format!\n");
|
||||
mutex_unlock(&priv->stream_buffer_mutex);
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
if (priv->stream_fmt == MCODEC_FMT_COOK) {
|
||||
pts = priv->cur_frame_info.offset;
|
||||
mutex_unlock(&priv->stream_buffer_mutex);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
||||
buffered_len = DSP_RD(DSP_BUFFERED_LEN);
|
||||
wp = DSP_RD(DSP_DECODE_OUT_WD_PTR);
|
||||
offset = DSP_RD(DSP_AFIFO_RD_OFFSET1);
|
||||
/* before audio start, the pts always be at the first index */
|
||||
if (!timestamp_apts_started())
|
||||
offset = 0;
|
||||
|
||||
if (priv->stream_fmt == MCODEC_FMT_COOK
|
||||
|| priv->stream_fmt == MCODEC_FMT_RAAC) {
|
||||
pts = DSP_RD(DSP_AFIFO_RD_OFFSET1);
|
||||
res = 0;
|
||||
} else {
|
||||
res =
|
||||
pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts,
|
||||
300);
|
||||
|
||||
if (!priv->first_lookup_over) {
|
||||
priv->first_lookup_over = 1;
|
||||
if (first_lookup_pts_failed(PTS_TYPE_AUDIO)) {
|
||||
|
||||
priv->out_len_after_last_valid_pts = 0;
|
||||
priv->last_valid_pts = pts;
|
||||
|
||||
mutex_unlock
|
||||
(&priv->stream_buffer_mutex);
|
||||
return pts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
/* printk("check out pts == %x\n", pts); */
|
||||
priv->out_len_after_last_valid_pts = 0;
|
||||
len =
|
||||
buffered_len + dsp_codec_get_bufer_data_len1(priv,
|
||||
wp);
|
||||
frame_nums =
|
||||
(len * 8 /
|
||||
(priv->frame_format.data_width *
|
||||
priv->frame_format.channel_num));
|
||||
delay_pts =
|
||||
div64_u64(frame_nums * 90 * 20,
|
||||
priv->frame_format.sample_rate / 50);
|
||||
/* printk("cal delay pts == %x\n", delay_pts); */
|
||||
if (pts > delay_pts)
|
||||
pts -= delay_pts;
|
||||
else
|
||||
pts = 0;
|
||||
|
||||
priv->last_valid_pts = pts;
|
||||
}
|
||||
|
||||
else if (priv->last_valid_pts >= 0) {
|
||||
pts = priv->last_valid_pts;
|
||||
len = priv->out_len_after_last_valid_pts;
|
||||
frame_nums =
|
||||
(len * 8 /
|
||||
(priv->frame_format.data_width *
|
||||
priv->frame_format.channel_num));
|
||||
pts += div64_u64(frame_nums * 90 * 20,
|
||||
priv->frame_format.sample_rate / 50);
|
||||
}
|
||||
|
||||
else {
|
||||
pr_info("checkout audio pts failed!\n");
|
||||
pts = -1;
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->stream_buffer_mutex);
|
||||
}
|
||||
return pts;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
39
drivers/amlogic/audiodsp/dsp_codec.h
Normal file
39
drivers/amlogic/audiodsp/dsp_codec.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_codec.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 DSP_CODEC_CTL_H
|
||||
#define DSP_CODEC_CTL_H
|
||||
|
||||
#include "dsp_control.h"
|
||||
#include "codec_message.h"
|
||||
int dsp_codec_start(struct audiodsp_priv *priv);
|
||||
int dsp_codec_stop(struct audiodsp_priv *priv);
|
||||
int dsp_codec_get_bufer_data_len(struct audiodsp_priv *priv);
|
||||
static inline unsigned long dsp_codec_get_rd_addr(struct audiodsp_priv *priv)
|
||||
{
|
||||
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_OUT_RD_ADDR));
|
||||
};
|
||||
|
||||
static inline unsigned long dsp_codec_get_wd_addr(struct audiodsp_priv *priv)
|
||||
{
|
||||
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_OUT_WD_ADDR));
|
||||
};
|
||||
|
||||
unsigned long dsp_codec_inc_rd_addr(struct audiodsp_priv *priv, int size);
|
||||
u32 dsp_codec_get_current_pts(struct audiodsp_priv *priv);
|
||||
|
||||
#endif /*
*/
|
||||
459
drivers/amlogic/audiodsp/dsp_control.c
Normal file
459
drivers/amlogic/audiodsp/dsp_control.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_control.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 pr_fmt(fmt) "audio_dsp: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "dsp_microcode.h"
|
||||
#include "audiodsp_module.h"
|
||||
#include "dsp_control.h"
|
||||
#include "dsp_codec.h"
|
||||
|
||||
/* #include <asm/dsp/dsp_register.h> */
|
||||
#include <linux/amlogic/amports/dsp_register.h>
|
||||
#include <linux/amlogic/iomap.h>
|
||||
#include <linux/amlogic/sound/aiu_regs.h>
|
||||
|
||||
#include "dsp_mailbox.h"
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define MIN_CACHE_ALIGN(x) (((x-4)&(~0x1f)))
|
||||
#define MAX_CACHE_ALIGN(x) ((x+0x1f)&(~0x1f))
|
||||
#define MAX_STREAM_BUF_MEM_SIZE (32*1024)
|
||||
|
||||
int decopt = 0x0000fffb;
|
||||
int subid;
|
||||
|
||||
#define RESET_AUD_ARC (1<<13)
|
||||
static void enable_dsp(int flag)
|
||||
{
|
||||
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
|
||||
/* power down the media cpu */
|
||||
if (!flag)
|
||||
WRITE_CBUS_REG_BITS(HHI_MEM_PD_REG0, 3, 0, 2);
|
||||
|
||||
/* reset */
|
||||
SET_MPEG_REG_MASK(RESET1_REGISTER, RESET_AUD_ARC);
|
||||
if (flag) {
|
||||
/* power on media cpu */
|
||||
WRITE_CBUS_REG_BITS(HHI_MEM_PD_REG0, 0, 0, 2);
|
||||
|
||||
/* enable */
|
||||
SET_MPEG_REG_MASK(MEDIA_CPU_CTL, 3);
|
||||
}
|
||||
#else /*
*/
|
||||
if (!flag)
|
||||
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 0);
|
||||
|
||||
aml_cbus_update_bits(RESET2_REGISTER, RESET_AUD_ARC, 0);
|
||||
if (flag) {
|
||||
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 1);
|
||||
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 0);
|
||||
}
|
||||
#endif /*
*/
|
||||
}
|
||||
|
||||
void halt_dsp(struct audiodsp_priv *priv)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (DSP_RD(DSP_STATUS) == DSP_STATUS_RUNNING) {
|
||||
|
||||
#ifndef AUDIODSP_RESET
|
||||
int i;
|
||||
|
||||
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_SLEEP, 0, 0, 0);
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (DSP_RD(DSP_STATUS) == DSP_STATUS_SLEEP)
|
||||
break;
|
||||
udelay(1000); /*waiting arc2 sleep */
|
||||
}
|
||||
if (i == 100)
|
||||
DSP_PRNT
|
||||
("dsp isn't sleeping when call dsp_stop\n");
|
||||
|
||||
#else /*
*/
|
||||
for (i = 0; i < 10; i++) {
|
||||
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_HALT, 0, 0, 0);
|
||||
udelay(1000); /*waiting arc2 self-halt */
|
||||
if (DSP_RD(DSP_STATUS) == DSP_STATUS_HALT)
|
||||
break;
|
||||
}
|
||||
if (i == 10)
|
||||
DSP_PRNT("warning,dsp self-halt time out\n");
|
||||
|
||||
#endif /*
*/
|
||||
}
|
||||
#ifdef AUDIODSP_RESET
|
||||
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING) {
|
||||
DSP_WD(DSP_STATUS, DSP_STATUS_HALT);
|
||||
return;
|
||||
}
|
||||
#endif /*
*/
|
||||
if (!priv->dsp_is_started) {
|
||||
enable_dsp(0); /*hardware halt the cpu */
|
||||
DSP_WD(DSP_STATUS, DSP_STATUS_HALT);
|
||||
/*mask the stream format is not valid */
|
||||
priv->last_stream_fmt = -1;
|
||||
} else
|
||||
DSP_WD(DSP_STATUS, DSP_STATUS_SLEEP);
|
||||
}
|
||||
|
||||
void reset_dsp(struct audiodsp_priv *priv)
|
||||
{
|
||||
halt_dsp(priv);
|
||||
|
||||
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
|
||||
CLEAR_MPEG_REG_MASK(MEDIA_CPU_CTL, (0xfff << 20));
|
||||
SET_MPEG_REG_MASK(MEDIA_CPU_CTL,
|
||||
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 20);
|
||||
SET_MPEG_REG_MASK(MEDIA_CPU_CTL, 1 << 16);
|
||||
|
||||
#else /*
*/
|
||||
aml_cbus_update_bits(MEDIA_CPU_CTL, (0xfff << 4), 0);
|
||||
aml_cbus_update_bits(MEDIA_CPU_CTL,
|
||||
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 4,
|
||||
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 4);
|
||||
|
||||
#endif /*
*/
|
||||
/* decode option */
|
||||
if (audioin_mode & 2)
|
||||
decopt &= ~(1 << 6);
|
||||
|
||||
if (IEC958_mode_codec) {
|
||||
if (IEC958_mode_codec == 4) /* dd+ */
|
||||
DSP_WD(DSP_DECODE_OPTION, decopt | (3 << 30));
|
||||
else
|
||||
DSP_WD(DSP_DECODE_OPTION, decopt | (1 << 31));
|
||||
|
||||
} else
|
||||
DSP_WD(DSP_DECODE_OPTION, decopt & (~(1 << 31)));
|
||||
|
||||
DSP_WD(DSP_CHIP_SUBID, subid);
|
||||
|
||||
pr_info("reset dsp : dec opt=%lx, subid=%lx\n",
|
||||
DSP_RD(DSP_DECODE_OPTION), DSP_RD(DSP_CHIP_SUBID));
|
||||
if (!priv->dsp_is_started) {
|
||||
DSP_PRNT("dsp reset now\n");
|
||||
enable_dsp(1);
|
||||
} else {
|
||||
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_WAKEUP, 0, 0, 0);
|
||||
DSP_WD(DSP_STATUS, DSP_STATUS_WAKEUP);
|
||||
udelay(1000); /*waiting arc625 run again */
|
||||
}
|
||||
}
|
||||
|
||||
static inline int dsp_set_stack(struct audiodsp_priv *priv)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
if (priv->dsp_stack_start == NULL)
|
||||
priv->dsp_stack_start =
|
||||
kzalloc(priv->dsp_stack_size, GFP_KERNEL);
|
||||
if (priv->dsp_stack_start == 0) {
|
||||
DSP_PRNT("kmalloc error,no memory for audio dsp stack\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf_map =
|
||||
dma_map_single(NULL, priv->dsp_stack_start,
|
||||
priv->dsp_stack_size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, priv->dsp_stack_size, DMA_FROM_DEVICE);
|
||||
DSP_WD(DSP_STACK_START,
|
||||
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start)));
|
||||
DSP_WD(DSP_STACK_END,
|
||||
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start) +
|
||||
priv->dsp_stack_size));
|
||||
DSP_PRNT("DSP statck start =%#lx,size=%#lx\n",
|
||||
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start),
|
||||
priv->dsp_stack_size);
|
||||
if (priv->dsp_gstack_start == 0)
|
||||
priv->dsp_gstack_start =
|
||||
kzalloc(priv->dsp_gstack_size, GFP_KERNEL);
|
||||
if (priv->dsp_gstack_start == 0) {
|
||||
DSP_PRNT("kmalloc error,no memory for audio dsp gp stack\n");
|
||||
kfree((void *)priv->dsp_stack_start);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf_map =
|
||||
dma_map_single(NULL, priv->dsp_gstack_start,
|
||||
priv->dsp_gstack_size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, priv->dsp_gstack_size,
|
||||
DMA_FROM_DEVICE);
|
||||
DSP_WD(DSP_GP_STACK_START,
|
||||
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start)));
|
||||
DSP_WD(DSP_GP_STACK_END,
|
||||
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start) +
|
||||
priv->dsp_gstack_size));
|
||||
DSP_PRNT("DSP gp statck start =%#lx,size=%#lx\n",
|
||||
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start),
|
||||
priv->dsp_gstack_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dsp_set_heap(struct audiodsp_priv *priv)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
if (priv->dsp_heap_size == 0)
|
||||
return 0;
|
||||
if (priv->dsp_heap_start == 0)
|
||||
priv->dsp_heap_start =
|
||||
(unsigned long)kmalloc(priv->dsp_heap_size, GFP_KERNEL);
|
||||
if (priv->dsp_heap_start == 0) {
|
||||
DSP_PRNT
|
||||
("kmalloc error,no memory for audio dsp dsp_set_heap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void *)priv->dsp_heap_start, 0, priv->dsp_heap_size);
|
||||
buf_map =
|
||||
dma_map_single(NULL, (void *)priv->dsp_heap_start,
|
||||
priv->dsp_heap_size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, priv->dsp_heap_size, DMA_FROM_DEVICE);
|
||||
DSP_WD(DSP_MEM_START,
|
||||
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start)));
|
||||
DSP_WD(DSP_MEM_END,
|
||||
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start) +
|
||||
priv->dsp_heap_size));
|
||||
DSP_PRNT("DSP heap start =%#lx,size=%#lx\n",
|
||||
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start),
|
||||
priv->dsp_heap_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dsp_set_stream_buffer(struct audiodsp_priv *priv)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
if (priv->stream_buffer_mem_size == 0) {
|
||||
DSP_WD(DSP_DECODE_OUT_START_ADDR, 0);
|
||||
DSP_WD(DSP_DECODE_OUT_END_ADDR, 0);
|
||||
DSP_WD(DSP_DECODE_OUT_RD_ADDR, 0);
|
||||
DSP_WD(DSP_DECODE_OUT_WD_ADDR, 0);
|
||||
return 0;
|
||||
}
|
||||
if (priv->stream_buffer_mem == NULL)
|
||||
priv->stream_buffer_mem =
|
||||
kmalloc(MAX_STREAM_BUF_MEM_SIZE, GFP_KERNEL);
|
||||
if (priv->stream_buffer_mem == NULL) {
|
||||
DSP_PRNT
|
||||
("kmalloc error,no memory for audio dsp stream buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void *)priv->stream_buffer_mem, 0,
|
||||
priv->stream_buffer_mem_size);
|
||||
buf_map =
|
||||
dma_map_single(NULL, (void *)priv->stream_buffer_mem,
|
||||
priv->stream_buffer_mem_size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, priv->stream_buffer_mem_size,
|
||||
DMA_FROM_DEVICE);
|
||||
priv->stream_buffer_start =
|
||||
MAX_CACHE_ALIGN((unsigned long)priv->stream_buffer_mem);
|
||||
priv->stream_buffer_end =
|
||||
MIN_CACHE_ALIGN((unsigned long)priv->stream_buffer_mem +
|
||||
priv->stream_buffer_mem_size);
|
||||
priv->stream_buffer_size =
|
||||
priv->stream_buffer_end - priv->stream_buffer_start;
|
||||
if (priv->stream_buffer_size < 0) {
|
||||
DSP_PRNT
|
||||
("Stream buffer set error,must more larger\n");
|
||||
DSP_PRNT("mensize=%d,buffer size=%ld\n",
|
||||
priv->stream_buffer_mem_size, priv->stream_buffer_size);
|
||||
|
||||
kfree(priv->stream_buffer_mem);
|
||||
priv->stream_buffer_mem = NULL;
|
||||
return -2;
|
||||
}
|
||||
DSP_WD(DSP_DECODE_OUT_START_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
|
||||
DSP_WD(DSP_DECODE_OUT_END_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_end));
|
||||
DSP_WD(DSP_DECODE_OUT_RD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
|
||||
DSP_WD(DSP_DECODE_OUT_WD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
|
||||
DSP_PRNT("DSP stream buffer to [%#lx-%#lx]\n",
|
||||
(ulong) ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start),
|
||||
(ulong) ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_end));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dsp_start(struct audiodsp_priv *priv, struct audiodsp_microcode *mcode)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
|
||||
mutex_lock(&priv->dsp_mutex);
|
||||
halt_dsp(priv);
|
||||
|
||||
/* remove the trick, bug fixed on dsp side */
|
||||
if (priv->stream_fmt != priv->last_stream_fmt) {
|
||||
if (audiodsp_microcode_load(audiodsp_privdata(), mcode) != 0) {
|
||||
pr_info("load microcode error\n");
|
||||
res = -1;
|
||||
goto exit;
|
||||
}
|
||||
priv->last_stream_fmt = priv->stream_fmt;
|
||||
}
|
||||
res = dsp_set_stack(priv);
|
||||
if (res)
|
||||
goto exit;
|
||||
|
||||
res = dsp_set_heap(priv);
|
||||
if (res)
|
||||
goto exit;
|
||||
|
||||
res = dsp_set_stream_buffer(priv);
|
||||
if (res)
|
||||
goto exit;
|
||||
|
||||
if (!priv->dsp_is_started)
|
||||
reset_dsp(priv);
|
||||
else {
|
||||
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_WAKEUP, 0, 0, 0);
|
||||
udelay(1000); /*waiting arc625 run again */
|
||||
}
|
||||
priv->dsp_start_time = jiffies;
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (DSP_RD(DSP_STATUS) == DSP_STATUS_RUNNING)
|
||||
break;
|
||||
udelay(1000);
|
||||
}
|
||||
if (i >= 1000) {
|
||||
DSP_PRNT("dsp not running\n");
|
||||
res = -1;
|
||||
} else {
|
||||
DSP_PRNT("dsp status=%lx\n", DSP_RD(DSP_STATUS));
|
||||
priv->dsp_is_started = 1;
|
||||
res = 0;
|
||||
}
|
||||
exit:
|
||||
mutex_unlock(&priv->dsp_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
int dsp_stop(struct audiodsp_priv *priv)
|
||||
{
|
||||
mutex_lock(&priv->dsp_mutex);
|
||||
|
||||
#ifdef AUDIODSP_RESET
|
||||
priv->dsp_is_started = 0;
|
||||
|
||||
#endif /*
*/
|
||||
halt_dsp(priv);
|
||||
priv->dsp_end_time = jiffies;
|
||||
|
||||
#if 0
|
||||
if (priv->dsp_stack_start != 0)
|
||||
kfree((void *)priv->dsp_stack_start);
|
||||
priv->dsp_stack_start = 0;
|
||||
if (priv->dsp_gstack_start != 0)
|
||||
kfree((void *)priv->dsp_gstack_start);
|
||||
priv->dsp_gstack_start = 0;
|
||||
if (priv->dsp_heap_start != 0)
|
||||
kfree((void *)priv->dsp_heap_start);
|
||||
priv->dsp_heap_start = 0;
|
||||
if (priv->stream_buffer_mem != NULL) {
|
||||
kfree(priv->stream_buffer_mem);
|
||||
priv->stream_buffer_mem = NULL;
|
||||
}
|
||||
#endif /*
*/
|
||||
mutex_unlock(&priv->dsp_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dsp_check_status(struct audiodsp_priv *priv)
|
||||
{
|
||||
|
||||
/* unsigned int dsp_halt_score = 0; */
|
||||
unsigned int ablevel = 0;
|
||||
int pcmlevel = 0;
|
||||
|
||||
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING)
|
||||
return 1;
|
||||
ablevel = aml_read_cbus(AIU_MEM_AIFIFO_LEVEL);
|
||||
pcmlevel = dsp_codec_get_bufer_data_len(priv);
|
||||
if ((ablevel == priv->last_ablevel && ablevel > 50 * 1024) &&
|
||||
(pcmlevel == priv->last_pcmlevel && pcmlevel < 512)) {
|
||||
priv->last_ablevel = ablevel;
|
||||
priv->last_pcmlevel = pcmlevel;
|
||||
pr_info("dsp not working ............\n");
|
||||
return 0;
|
||||
}
|
||||
priv->last_ablevel = ablevel;
|
||||
priv->last_pcmlevel = pcmlevel;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* bit31 - digital raw output
|
||||
* bit30 - IEC61937 pass over HDMI
|
||||
* bit 6 - audio in mode.
|
||||
00: spdif in mode
|
||||
01: i2s in mode
|
||||
* bit 5 - DTS passthrough working mode
|
||||
00: AIU 958 hw search raw mode
|
||||
01: PCM_RAW mode,the same as AC3/AC3+
|
||||
* bit 3:4 - used for the communication of dsp and player
|
||||
tansfer decoding information:
|
||||
* 00: used for libplayer_end to tell dsp_end that
|
||||
the file end has been notreached;
|
||||
* 01: used for libplayer_end to tell dsp_end that
|
||||
the file end has been reached;
|
||||
* 10: used for dsp_end to tell libplayer_end that
|
||||
* all the data in the dsp_end_buf
|
||||
has been decoded completely;
|
||||
* 11: reserved;
|
||||
* bit 2 - ARC DSP print flag
|
||||
* bit 1 - dts decoder policy select: 0:mute 1:noise
|
||||
* bit 0 - dd/dd+ decoder policy select 0:mute 1:noise
|
||||
*/
|
||||
static int __init decode_option_setup(char *s)
|
||||
{
|
||||
unsigned long value = 0xffffffffUL;
|
||||
|
||||
if (kstrtoul(s, 16, &value)) {
|
||||
decopt = 0x0000fffb;
|
||||
return -1;
|
||||
}
|
||||
decopt = (int)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("decopt=", decode_option_setup);
|
||||
static int __init decode_subid_setup(char *s)
|
||||
{
|
||||
unsigned long value = (unsigned long)(0);
|
||||
|
||||
if (kstrtoul(s, 16, &value)) {
|
||||
subid = (unsigned long)(0);
|
||||
return -1;
|
||||
}
|
||||
subid = (int)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("meson_id=", decode_subid_setup);
|
||||
38
drivers/amlogic/audiodsp/dsp_control.h
Normal file
38
drivers/amlogic/audiodsp/dsp_control.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_control.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 DSP_CONTROL_HEADER
|
||||
#define DSP_CONTROL_HEADER
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include "audiodsp_module.h"
|
||||
#include "dsp_microcode.h"
|
||||
/*#include <asm/system.h>*/
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
void halt_dsp(struct audiodsp_priv *priv);
|
||||
void reset_dsp(struct audiodsp_priv *priv);
|
||||
int dsp_start(struct audiodsp_priv *priv, struct audiodsp_microcode *mcode);
|
||||
int dsp_stop(struct audiodsp_priv *priv);
|
||||
int dsp_check_status(struct audiodsp_priv *priv);
|
||||
|
||||
extern unsigned int IEC958_mode_raw;
|
||||
extern unsigned int IEC958_mode_codec;
|
||||
extern unsigned int audioin_mode;
|
||||
|
||||
#define AUDIODSP_RESET
|
||||
#endif /*
*/
|
||||
408
drivers/amlogic/audiodsp/dsp_mailbox.c
Normal file
408
drivers/amlogic/audiodsp/dsp_mailbox.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_mailbox.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 pr_fmt(fmt) "audio_dsp: " fmt
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <linux/amlogic/amports/tsync.h>
|
||||
#include <linux/amlogic/amports/timestamp.h>
|
||||
#include "dsp_mailbox.h"
|
||||
#include "dsp_codec.h"
|
||||
|
||||
#define BASE_IRQ (32)
|
||||
|
||||
#define AM_IRQ(reg) (reg + BASE_IRQ)
|
||||
#define INT_ASSIST_MBOX1 AM_IRQ(146)
|
||||
|
||||
static struct audiodsp_work_t {
|
||||
char *buf;
|
||||
struct work_struct audiodsp_workqueue;
|
||||
} audiodsp_work;
|
||||
|
||||
int dsp_mailbox_send(struct audiodsp_priv *priv, int overwrite, int num,
|
||||
int cmd, const char *data, int len)
|
||||
{
|
||||
unsigned long flags;
|
||||
int res = -1;
|
||||
struct mail_msg *m;
|
||||
dma_addr_t buf_map;
|
||||
|
||||
m = &priv->mailbox_reg2[num];
|
||||
|
||||
local_irq_save(flags);
|
||||
if (overwrite || m->status == 0) {
|
||||
m->cmd = cmd;
|
||||
m->data = (char *)ARM_2_ARC_ADDR_SWAP((unsigned int)data);
|
||||
m->len = len;
|
||||
m->status = 1;
|
||||
after_change_mailbox(m);
|
||||
if (data != NULL && len > 0) {
|
||||
buf_map =
|
||||
dma_map_single(NULL, (void *)data, len,
|
||||
DMA_TO_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, len, DMA_TO_DEVICE);
|
||||
}
|
||||
MAIBOX2_IRQ_ENABLE(num);
|
||||
DSP_TRIGGER_IRQ(num);
|
||||
res = 0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return res;
|
||||
}
|
||||
|
||||
int get_mailbox_data(struct audiodsp_priv *priv, int num, struct mail_msg *msg)
|
||||
{
|
||||
unsigned long flags;
|
||||
dma_addr_t buf_map;
|
||||
struct mail_msg *m;
|
||||
|
||||
if (num > 31 || num < 0)
|
||||
return -1;
|
||||
local_irq_save(flags);
|
||||
m = &priv->mailbox_reg[num];
|
||||
pre_read_mailbox(m);
|
||||
/* dsp_addr_map =
|
||||
* dma_map_single(priv->dev,(void*)m,
|
||||
* sizeof(*m),DMA_FROM_DEVICE);
|
||||
* dma_unmap_single(priv->dev,dsp_addr_map,
|
||||
* sizeof(*m),DMA_FROM_DEVICE);
|
||||
*/
|
||||
msg->cmd = m->cmd;
|
||||
msg->data = m->data;
|
||||
msg->data = (char *)((unsigned int)msg->data + AUDIO_DSP_START_ADDR);
|
||||
msg->status = m->status;
|
||||
msg->len = m->len;
|
||||
if (msg->len && msg->data != NULL) {
|
||||
buf_map =
|
||||
dma_map_single(priv->dev, (void *)msg->data, msg->len,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_unmap_single(priv->dev, buf_map, msg->len, DMA_FROM_DEVICE);
|
||||
}
|
||||
m->status = 0;
|
||||
after_change_mailbox(m);
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t audiodsp_mailbox_irq(int irq, void *data)
|
||||
{
|
||||
struct audiodsp_priv *priv = (struct audiodsp_priv *)data;
|
||||
unsigned long status;
|
||||
struct mail_msg msg;
|
||||
int i = 0;
|
||||
unsigned long fiq_mask;
|
||||
|
||||
status = READ_VREG(MB1_REG);
|
||||
fiq_mask = READ_VREG(MB1_SEL);
|
||||
status = status & fiq_mask;
|
||||
|
||||
if (status & (1 << M1B_IRQ0_PRINT)) {
|
||||
get_mailbox_data(priv, M1B_IRQ0_PRINT, &msg);
|
||||
SYS_CLEAR_IRQ(M1B_IRQ0_PRINT);
|
||||
/* inv_dcache_range((unsigned long )msg.data,
|
||||
* (unsigned long)msg.data+msg.len);
|
||||
*/
|
||||
|
||||
DSP_PRNT("%s", msg.data);
|
||||
/* audiodsp_work.buf = msg.data; */
|
||||
/* schedule_work(&audiodsp_work.audiodsp_workqueue); */
|
||||
}
|
||||
if (status & (1 << M1B_IRQ1_BUF_OVERFLOW)) {
|
||||
SYS_CLEAR_IRQ(M1B_IRQ1_BUF_OVERFLOW);
|
||||
DSP_PRNT("DSP BUF over flow\n");
|
||||
}
|
||||
if (status & (1 << M1B_IRQ2_BUF_UNDERFLOW)) {
|
||||
SYS_CLEAR_IRQ(M1B_IRQ2_BUF_UNDERFLOW);
|
||||
DSP_PRNT("DSP BUF over flow\n");
|
||||
}
|
||||
if (status & (1 << M1B_IRQ3_DECODE_ERROR)) {
|
||||
SYS_CLEAR_IRQ(M1B_IRQ3_DECODE_ERROR);
|
||||
priv->decode_error_count++;
|
||||
}
|
||||
if (status & (1 << M1B_IRQ4_DECODE_FINISH_FRAME)) {
|
||||
struct frame_info *info;
|
||||
|
||||
SYS_CLEAR_IRQ(M1B_IRQ4_DECODE_FINISH_FRAME);
|
||||
get_mailbox_data(priv, M1B_IRQ4_DECODE_FINISH_FRAME, &msg);
|
||||
info = (struct frame_info *)msg.data;
|
||||
if (info != NULL) {
|
||||
priv->cur_frame_info.offset = info->offset;
|
||||
priv->cur_frame_info.buffered_len = info->buffered_len;
|
||||
}
|
||||
priv->decoded_nb_frames++;
|
||||
complete(&priv->decode_completion);
|
||||
}
|
||||
if (status & (1 << M1B_IRQ5_STREAM_FMT_CHANGED)) {
|
||||
struct frame_fmt *fmt;
|
||||
|
||||
SYS_CLEAR_IRQ(M1B_IRQ5_STREAM_FMT_CHANGED);
|
||||
get_mailbox_data(priv, M1B_IRQ5_STREAM_FMT_CHANGED, &msg);
|
||||
fmt = (void *)msg.data;
|
||||
/* DSP_PRNT("frame format changed"); */
|
||||
if (fmt == NULL || (sizeof(struct frame_fmt) < msg.len)) {
|
||||
DSP_PRNT("frame format message error\n");
|
||||
} else {
|
||||
DSP_PRNT("frame format changed,fmt->valid 0x%x\n",
|
||||
fmt->valid);
|
||||
if (fmt->valid & SUB_FMT_VALID) {
|
||||
priv->frame_format.sub_fmt = fmt->sub_fmt;
|
||||
priv->frame_format.valid |= SUB_FMT_VALID;
|
||||
}
|
||||
if (fmt->valid & CHANNEL_VALID) {
|
||||
priv->frame_format.channel_num =
|
||||
((fmt->channel_num >
|
||||
2) ? 2 : (fmt->channel_num));
|
||||
priv->frame_format.valid |= CHANNEL_VALID;
|
||||
}
|
||||
if (fmt->valid & SAMPLE_RATE_VALID) {
|
||||
priv->frame_format.sample_rate =
|
||||
fmt->sample_rate;
|
||||
priv->frame_format.valid |= SAMPLE_RATE_VALID;
|
||||
}
|
||||
if (fmt->valid & DATA_WIDTH_VALID) {
|
||||
priv->frame_format.data_width = fmt->data_width;
|
||||
priv->frame_format.valid |= DATA_WIDTH_VALID;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* if(fmt->data.pcm_encoded_info){
|
||||
* set_pcminfo_data(fmt->data.pcm_encoded_info);
|
||||
* }
|
||||
*/
|
||||
DSP_PRNT("audio info from dsp:sample_rate=%d channel_num=%d\n",
|
||||
priv->frame_format.sample_rate,
|
||||
priv->frame_format.channel_num);
|
||||
}
|
||||
if (status & (1 << M1B_IRQ8_IEC958_INFO)) {
|
||||
struct digit_raw_output_info *info;
|
||||
|
||||
SYS_CLEAR_IRQ(M1B_IRQ8_IEC958_INFO);
|
||||
get_mailbox_data(priv, M1B_IRQ8_IEC958_INFO, &msg);
|
||||
info = (void *)msg.data;
|
||||
#if 1
|
||||
IEC958_bpf = info->bpf;
|
||||
IEC958_brst = info->brst;
|
||||
IEC958_length = info->length;
|
||||
IEC958_padsize = info->padsize;
|
||||
IEC958_mode = info->mode;
|
||||
IEC958_syncword1 = info->syncword1;
|
||||
IEC958_syncword2 = info->syncword2;
|
||||
IEC958_syncword3 = info->syncword3;
|
||||
IEC958_syncword1_mask = info->syncword1_mask;
|
||||
IEC958_syncword2_mask = info->syncword2_mask;
|
||||
IEC958_syncword3_mask = info->syncword3_mask;
|
||||
IEC958_chstat0_l = info->chstat0_l;
|
||||
IEC958_chstat0_r = info->chstat0_r;
|
||||
IEC958_chstat1_l = info->chstat1_l;
|
||||
IEC958_chstat1_r = info->chstat1_r;
|
||||
#endif
|
||||
/*
|
||||
* IEC958_mode_codec = info->can_bypass;
|
||||
*/
|
||||
|
||||
DSP_PRNT("MAILBOX: got IEC958 info\n");
|
||||
/* schedule_work(&audiodsp_work.audiodsp_workqueue); */
|
||||
}
|
||||
|
||||
if (status & (1 << M1B_IRQ5_STREAM_RD_WD_TEST)) {
|
||||
DSP_WD((0x84100000 - 4096 + 20 * 20), 0);
|
||||
SYS_CLEAR_IRQ(M1B_IRQ5_STREAM_RD_WD_TEST);
|
||||
get_mailbox_data(priv, M1B_IRQ5_STREAM_RD_WD_TEST, &msg);
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
if ((DSP_RD((0x84100000 - 512 * 1024 + i * 20))) !=
|
||||
(0xff00 | i)) {
|
||||
DSP_PRNT
|
||||
("a9 read dsp err, now %#lx, should be %#x\n",
|
||||
(DSP_RD((0x84100000 - 512 * 1024 + i * 20))),
|
||||
12 - i);
|
||||
}
|
||||
/* DSP_PRNT("A9 audio dsp reg%d value 0x%x\n",
|
||||
* i,DSP_RD((0x84100000-4096+i*20)));
|
||||
*/
|
||||
}
|
||||
for (i = 0; i < 12; i++) {
|
||||
DSP_WD((0x84100000 - 512 * 1024 + i * 20),
|
||||
(i % 2) ? i : (0xf0 | i));
|
||||
|
||||
}
|
||||
DSP_WD((0x84100000 - 512 * 1024 + 20 * 20), DSP_STATUS_HALT);
|
||||
/* DSP_PRNT("A9 mail box handle finished\n");
|
||||
* dsp_mailbox_send(priv, 1,
|
||||
* M1B_IRQ5_STREAM_RD_WD_TEST, 0, NULL,0);
|
||||
*/
|
||||
}
|
||||
|
||||
if (status & (1 << M1B_IRQ7_DECODE_FATAL_ERR)) {
|
||||
int err_code;
|
||||
|
||||
SYS_CLEAR_IRQ(M1B_IRQ7_DECODE_FATAL_ERR);
|
||||
get_mailbox_data(priv, M1B_IRQ7_DECODE_FATAL_ERR, &msg);
|
||||
|
||||
err_code = msg.cmd;
|
||||
priv->decode_fatal_err = err_code;
|
||||
|
||||
if (err_code & 0x01) {
|
||||
timestamp_pcrscr_set(timestamp_vpts_get());
|
||||
timestamp_pcrscr_enable(1);
|
||||
} else if (err_code & 0x02) {
|
||||
pr_info("Set decode_fatal_err flag, Reset audiodsp!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audiodsp_mailbox_work_queue(struct work_struct *work)
|
||||
{
|
||||
struct audiodsp_work_t *pwork =
|
||||
container_of(work, struct audiodsp_work_t, audiodsp_workqueue);
|
||||
char *message;
|
||||
|
||||
if (pwork) {
|
||||
message = pwork->buf;
|
||||
if (message)
|
||||
pr_info("%s", message);
|
||||
else
|
||||
pr_info("the message from mailbox is NULL\n");
|
||||
} else {
|
||||
pr_info("the work queue for audiodsp mailbox is empty\n");
|
||||
}
|
||||
}
|
||||
|
||||
int audiodsp_init_mailbox(struct audiodsp_priv *priv)
|
||||
{
|
||||
int err = 0;
|
||||
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
|
||||
err = request_irq(INT_ASSIST_MBOX1, audiodsp_mailbox_irq,
|
||||
IRQF_SHARED, "audiodsp_mailbox", (void *)priv);
|
||||
#else
|
||||
pr_info("audiodsp request irq not ready\n");
|
||||
/* err = request_irq(INT_MAILBOX_1B, audiodsp_mailbox_irq, */
|
||||
/* IRQF_SHARED, "audiodsp_mailbox", (void *)priv); */
|
||||
#endif
|
||||
if (err != 0) {
|
||||
pr_info("request IRQ for dsp mailbox failed: errno = %x\n",
|
||||
err);
|
||||
return -1;
|
||||
}
|
||||
/* WRITE_MPEG_REG(ASSIST_MBOX0_MASK, 0xffffffff); */
|
||||
priv->mailbox_reg = (struct mail_msg *)MAILBOX1_REG(0);
|
||||
priv->mailbox_reg2 = (struct mail_msg *)MAILBOX2_REG(0);
|
||||
|
||||
INIT_WORK(&audiodsp_work.audiodsp_workqueue,
|
||||
audiodsp_mailbox_work_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audiodsp_get_audioinfo(struct audiodsp_priv *priv)
|
||||
{
|
||||
int ret = -1;
|
||||
int audio_info = 0;
|
||||
|
||||
audio_info = DSP_RD(DSP_AUDIO_FORMAT_INFO);
|
||||
if (priv->frame_format.valid ==
|
||||
(CHANNEL_VALID | DATA_WIDTH_VALID | SAMPLE_RATE_VALID)) {
|
||||
ret = 0;
|
||||
goto exit;
|
||||
} else if (audio_info) {
|
||||
priv->frame_format.channel_num = audio_info & 0xf;
|
||||
if (priv->frame_format.channel_num)
|
||||
priv->frame_format.valid |= CHANNEL_VALID;
|
||||
priv->frame_format.data_width = (audio_info >> 4) & 0x3f;
|
||||
if (priv->frame_format.data_width)
|
||||
priv->frame_format.valid |= DATA_WIDTH_VALID;
|
||||
priv->frame_format.sample_rate = (audio_info >> 10);
|
||||
if (priv->frame_format.sample_rate)
|
||||
priv->frame_format.valid |= SAMPLE_RATE_VALID;
|
||||
ret = 0;
|
||||
}
|
||||
if (ret == 0) {
|
||||
DSP_PRNT(" audiodsp got audioinfo:[ch num %d],[sr %d]",
|
||||
priv->frame_format.channel_num,
|
||||
priv->frame_format.sample_rate);
|
||||
}
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int audiodsp_release_mailbox(struct audiodsp_priv *priv)
|
||||
{
|
||||
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
|
||||
free_irq(INT_ASSIST_MBOX1, (void *)priv);
|
||||
#else
|
||||
/* free_irq(INT_MAILBOX_1B,(void *)priv); */
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int hdmi_sr;
|
||||
int mailbox_send_audiodsp(int overwrite, int num, int cmd, const char *data,
|
||||
int len)
|
||||
{
|
||||
int res = -1;
|
||||
int time_out = 0;
|
||||
|
||||
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING) {
|
||||
pr_info("fatal error,dsp must be running before mailbox sent\n");
|
||||
return -1;
|
||||
}
|
||||
DSP_WD(DSP_GET_EXTRA_INFO_FINISH, 0);
|
||||
while (time_out++ < 10) {
|
||||
if ((num == M2B_IRQ0_DSP_AUDIO_EFFECT)
|
||||
&& (cmd == DSP_CMD_SET_HDMI_SR)) {
|
||||
hdmi_sr = *(unsigned int *)data;
|
||||
DSP_PRNT("<hdmi to dsp mailbox> sr changed to %d\n",
|
||||
hdmi_sr);
|
||||
DSP_WD(DSP_HDMI_SR, hdmi_sr);
|
||||
res =
|
||||
dsp_mailbox_send(audiodsp_privdata(), overwrite,
|
||||
num, cmd, (char *)&hdmi_sr,
|
||||
sizeof(unsigned int));
|
||||
break;
|
||||
}
|
||||
{
|
||||
res =
|
||||
dsp_mailbox_send(audiodsp_privdata(), overwrite,
|
||||
num, cmd, data, len);
|
||||
mdelay(10);
|
||||
}
|
||||
if (DSP_RD(DSP_GET_EXTRA_INFO_FINISH) == 0x12345678)
|
||||
break;
|
||||
}
|
||||
if (time_out == 10) {
|
||||
pr_info("error,dsp transfer mailbox time out\n");
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(mailbox_send_audiodsp);
|
||||
76
drivers/amlogic/audiodsp/dsp_mailbox.h
Normal file
76
drivers/amlogic/audiodsp/dsp_mailbox.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_mailbox.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 DSP_IRQ_HEADER
|
||||
#define DSP_IRQ_HEADER
|
||||
#include "audiodsp_module.h"
|
||||
int audiodsp_init_mailbox(struct audiodsp_priv *priv);
|
||||
int audiodsp_release_mailbox(struct audiodsp_priv *priv);
|
||||
int dsp_mailbox_send(struct audiodsp_priv *priv, int overwrite, int num,
|
||||
int cmd, const char *data, int len);
|
||||
int audiodsp_get_audioinfo(struct audiodsp_priv *priv);
|
||||
|
||||
#if 0
|
||||
#define pre_read_mailbox(reg) \
|
||||
dma_cache_inv((unsigned long)reg, sizeof(*reg))
|
||||
#define after_change_mailbox(reg) \
|
||||
dma_cache_wback((unsigned long)reg, sizeof(*reg))
|
||||
#else /*
*/
|
||||
#define pre_read_mailbox(reg)
|
||||
#define after_change_mailbox(reg)
|
||||
#endif /*
*/
|
||||
enum DSP_CMD {
|
||||
DSP_CMD_SET_EQ_PRESET = 0,
|
||||
DSP_CMD_SET_EQ_CUSTOMIZE,
|
||||
DSP_CMD_GET_EQ_VALUE_SETS,
|
||||
DSP_CMD_SET_SRS_SURROUND,
|
||||
DSP_CMD_SET_SRS_TRUBASS,
|
||||
DSP_CMD_SET_SRS_DIALOG_CLARITY,
|
||||
DSP_CMD_SET_HDMI_SR,
|
||||
DSP_CMD_SET_SRS_TRUBASS_GAIN,
|
||||
DSP_CMD_SET_SRS_DIALOG_CLARITY_GAIN,
|
||||
DSP_CMD_SET_REINIT_FILTER,
|
||||
DSP_CMD_COUNT
|
||||
};
|
||||
|
||||
extern unsigned int IEC958_bpf;
|
||||
extern unsigned int IEC958_brst;
|
||||
extern unsigned int IEC958_length;
|
||||
extern unsigned int IEC958_padsize;
|
||||
extern unsigned int IEC958_mode;
|
||||
extern unsigned int IEC958_syncword1;
|
||||
extern unsigned int IEC958_syncword2;
|
||||
extern unsigned int IEC958_syncword3;
|
||||
extern unsigned int IEC958_syncword1_mask;
|
||||
extern unsigned int IEC958_syncword2_mask;
|
||||
extern unsigned int IEC958_syncword3_mask;
|
||||
extern unsigned int IEC958_chstat0_l;
|
||||
extern unsigned int IEC958_chstat0_r;
|
||||
extern unsigned int IEC958_chstat1_l;
|
||||
extern unsigned int IEC958_chstat1_r;
|
||||
extern unsigned int IEC958_mode_raw;
|
||||
extern unsigned int IEC958_mode_codec;
|
||||
extern void set_pcminfo_data(void *pcm_encoded_info);
|
||||
|
||||
#define VDEC_ASSIST_MBOX1_IRQ_REG 0x0074
|
||||
#define VDEC_ASSIST_MBOX1_CLR_REG 0x0075
|
||||
#define VDEC_ASSIST_MBOX1_FIQ_SEL 0x0077
|
||||
#define VDEC_ASSIST_MBOX2_IRQ_REG 0x0078
|
||||
#define VDEC_ASSIST_MBOX2_MASK 0x007a
|
||||
#define VDEC_ASSIST_MBOX2_FIQ_SEL 0x007b
|
||||
|
||||
#endif /*
*/
|
||||
195
drivers/amlogic/audiodsp/dsp_microcode.c
Normal file
195
drivers/amlogic/audiodsp/dsp_microcode.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_microcode.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "audiodsp_control.h"
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/amlogic/major.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "audiodsp_module.h"
|
||||
#include "dsp_control.h"
|
||||
#include "dsp_microcode.h"
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
static int audiodsp_microcode_insert(struct audiodsp_priv *priv,
|
||||
struct audiodsp_microcode *pmcode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (pmcode == NULL)
|
||||
return -1;
|
||||
spin_lock_irqsave(&priv->mcode_lock, flags);
|
||||
list_add_tail(&pmcode->list, &priv->mcode_list);
|
||||
priv->mcode_id++;
|
||||
pmcode->id = priv->mcode_id;
|
||||
spin_unlock_irqrestore(&priv->mcode_lock, flags);
|
||||
return priv->mcode_id;
|
||||
}
|
||||
|
||||
struct audiodsp_microcode *audiodsp_find_supoort_mcode(struct audiodsp_priv
|
||||
*priv, int fmt)
|
||||
{
|
||||
struct audiodsp_microcode *pmcode = NULL;
|
||||
struct audiodsp_microcode *p = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->mcode_lock, flags);
|
||||
list_for_each_entry(p, &priv->mcode_list, list) {
|
||||
if (p->fmt & fmt) {
|
||||
pmcode = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->mcode_lock, flags);
|
||||
return pmcode;
|
||||
}
|
||||
|
||||
static struct audiodsp_microcode
|
||||
*audiodsp_find_mcode_by_name(struct audiodsp_priv *priv,
|
||||
char *name)
|
||||
{
|
||||
struct audiodsp_microcode *pmcode = NULL;
|
||||
struct audiodsp_microcode *p = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->mcode_lock, flags);
|
||||
list_for_each_entry(p, &priv->mcode_list, list) {
|
||||
if (memcmp(p->file_name, name, strlen(name)) == 0) {
|
||||
pmcode = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->mcode_lock, flags);
|
||||
return pmcode;
|
||||
}
|
||||
|
||||
int audiodsp_microcode_load(struct audiodsp_priv *priv,
|
||||
struct audiodsp_microcode *pmcode)
|
||||
{
|
||||
const struct firmware *firmware;
|
||||
int err = 0;
|
||||
unsigned int dsp_code_text_start = 0;
|
||||
|
||||
priv->micro_dev = device_create(priv->class, NULL,
|
||||
MKDEV(AUDIODSP_MAJOR, 1), NULL, "audiodsp1");
|
||||
if (priv->micro_dev == NULL)
|
||||
return -1;
|
||||
|
||||
err = request_firmware(&firmware, pmcode->file_name,
|
||||
priv->micro_dev);
|
||||
if (err < 0) {
|
||||
DSP_PRNT("can't load the %s,err=%d\n", pmcode->file_name, err);
|
||||
goto error1;
|
||||
}
|
||||
|
||||
if (firmware->size > priv->code_mem_size) {
|
||||
DSP_PRNT("not enough memory size for audiodsp code\n");
|
||||
err = ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (priv->dsp_is_started)
|
||||
#ifndef AUDIODSP_RESET
|
||||
/*
|
||||
* after dsp is running,only load from
|
||||
* the text section of the microcode.
|
||||
*/
|
||||
dsp_code_text_start = 0x1000;
|
||||
#else /*
*/
|
||||
dsp_code_text_start = 0;
|
||||
|
||||
#endif /* AUDIODSP_RESET */
|
||||
memcpy((char *)((unsigned int)priv->dsp_code_start +
|
||||
dsp_code_text_start),
|
||||
(char *)firmware->data + dsp_code_text_start,
|
||||
firmware->size - dsp_code_text_start);
|
||||
/* TODO: this cause 3.14 check patch warning */
|
||||
/* mb(); */
|
||||
pmcode->code_size = firmware->size;
|
||||
DSP_PRNT("load mcode size=%d\n,load addr 0x%lx mcode name %s",
|
||||
firmware->size, pmcode->code_start_addr, pmcode->file_name);
|
||||
release:
|
||||
release_firmware(firmware);
|
||||
error1:
|
||||
device_destroy(priv->class, MKDEV(AUDIODSP_MAJOR, 1));
|
||||
return err;
|
||||
}
|
||||
|
||||
int audiodsp_microcode_register(struct audiodsp_priv *priv, int fmt,
|
||||
char *filename)
|
||||
{
|
||||
struct audiodsp_microcode *pmcode;
|
||||
int len;
|
||||
|
||||
pmcode = audiodsp_find_mcode_by_name(priv, filename);
|
||||
if (pmcode != NULL && (fmt & pmcode->fmt)) {
|
||||
DSP_PRNT("Have register the firmware code before=%s\n",
|
||||
filename);
|
||||
DSP_PRNT("Refresh the firmware settings now\n");
|
||||
pmcode->fmt = fmt;
|
||||
pmcode->code_start_addr = priv->dsp_code_start;
|
||||
len = min_t(int, 64, strlen(filename));
|
||||
memcpy(pmcode->file_name, filename, len);
|
||||
pmcode->file_name[len] = '\0';
|
||||
return pmcode->id;
|
||||
}
|
||||
{
|
||||
pmcode =
|
||||
kmalloc(sizeof(struct audiodsp_microcode), GFP_KERNEL);
|
||||
if (pmcode == NULL)
|
||||
return -ENOMEM;
|
||||
pmcode->fmt = fmt;
|
||||
pmcode->code_start_addr = priv->dsp_code_start;
|
||||
len = min_t(int, 64, strlen(filename));
|
||||
memcpy(pmcode->file_name, filename, len);
|
||||
pmcode->file_name[len] = '\0';
|
||||
pmcode->id = audiodsp_microcode_insert(priv, pmcode);
|
||||
if (pmcode->id < 0) {
|
||||
kfree(pmcode);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audiodsp_microcode_free(struct audiodsp_priv *priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct audiodsp_microcode *pmcode;
|
||||
struct list_head *list, *head;
|
||||
|
||||
local_irq_save(flags);
|
||||
head = &priv->mcode_list;
|
||||
while (!list_empty(head)) {
|
||||
list = head->prev;
|
||||
pmcode = list_entry(list, struct audiodsp_microcode, list);
|
||||
list_del(list);
|
||||
kfree(pmcode);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
41
drivers/amlogic/audiodsp/dsp_microcode.h
Normal file
41
drivers/amlogic/audiodsp/dsp_microcode.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_microcode.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 AUDIODSP_MICROCODE_HEADER
|
||||
#define AUDIODSP_MICROCODE_HEADER
|
||||
|
||||
#include "audiodsp_module.h"
|
||||
struct audiodsp_microcode {
|
||||
int id;
|
||||
/* support format; */
|
||||
int fmt;
|
||||
struct list_head list;
|
||||
unsigned long code_start_addr;
|
||||
unsigned long code_size;
|
||||
char file_name[64];
|
||||
};
|
||||
|
||||
extern int audiodsp_microcode_register(struct audiodsp_priv *priv,
|
||||
int fmt, char *filename);
|
||||
extern struct audiodsp_microcode
|
||||
*audiodsp_find_supoort_mcode(struct audiodsp_priv *priv,
|
||||
int fmt);
|
||||
extern int audiodsp_microcode_load(struct audiodsp_priv *priv,
|
||||
struct audiodsp_microcode *pmcode);
|
||||
int audiodsp_microcode_free(struct audiodsp_priv *priv);
|
||||
|
||||
#endif /*
*/
|
||||
83
drivers/amlogic/audiodsp/dsp_monitor.c
Normal file
83
drivers/amlogic/audiodsp/dsp_monitor.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_monitor.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cache.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "dsp_microcode.h"
|
||||
#include "audiodsp_module.h"
|
||||
#include "dsp_control.h"
|
||||
/* #include <asm/dsp/dsp_register.h> */
|
||||
|
||||
static void audiodsp_monitor(unsigned long arg)
|
||||
{
|
||||
struct audiodsp_priv *priv = (struct audiodsp_priv *)arg;
|
||||
static unsigned long old_dsp_jiffies;
|
||||
static unsigned long jiffies_error;
|
||||
unsigned long dsp_jiffies;
|
||||
unsigned long dsp_status;
|
||||
|
||||
dsp_jiffies = DSP_RD(DSP_JIFFIES);
|
||||
dsp_status = DSP_RD(DSP_STATUS);
|
||||
|
||||
if (old_dsp_jiffies == dsp_jiffies)
|
||||
jiffies_error++;
|
||||
else {
|
||||
jiffies_error = 0;
|
||||
old_dsp_jiffies = dsp_jiffies;
|
||||
}
|
||||
if (jiffies_error > 5) {
|
||||
DSP_PRNT("Found audio dsp have some problem not running\n");
|
||||
DSP_PRNT("audio jiffies=%ld\n", old_dsp_jiffies);
|
||||
DSP_PRNT("audio status=%lx\n", dsp_status);
|
||||
}
|
||||
if (!dsp_check_status(priv))
|
||||
priv->dsp_abnormal_count++;
|
||||
else
|
||||
priv->dsp_abnormal_count = 0;
|
||||
|
||||
if (priv->dsp_abnormal_count > 5 || jiffies_error > 5) {
|
||||
priv->decode_fatal_err |= 0x2;
|
||||
priv->dsp_abnormal_count = 0;
|
||||
jiffies_error = 0;
|
||||
}
|
||||
priv->dsp_mointer.expires = jiffies + HZ;
|
||||
add_timer(&priv->dsp_mointer);
|
||||
}
|
||||
|
||||
void start_audiodsp_monitor(struct audiodsp_priv *priv)
|
||||
{
|
||||
priv->dsp_mointer.expires = jiffies;
|
||||
add_timer(&priv->dsp_mointer);
|
||||
}
|
||||
void stop_audiodsp_monitor(struct audiodsp_priv *priv)
|
||||
{
|
||||
del_timer_sync(&priv->dsp_mointer);
|
||||
}
|
||||
|
||||
void init_audiodsp_monitor(struct audiodsp_priv *priv)
|
||||
{
|
||||
setup_timer(&priv->dsp_mointer, audiodsp_monitor, (unsigned long)priv);
|
||||
}
|
||||
|
||||
void release_audiodsp_monitor(struct audiodsp_priv *priv)
|
||||
{
|
||||
stop_audiodsp_monitor(priv);
|
||||
}
|
||||
27
drivers/amlogic/audiodsp/dsp_monitor.h
Normal file
27
drivers/amlogic/audiodsp/dsp_monitor.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/dsp_monitor.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 DSP_MONITOR_HEADER_H
|
||||
#define DSP_MONITOR_HEADER_H
|
||||
#include "audiodsp_module.h"
|
||||
|
||||
void start_audiodsp_monitor(struct audiodsp_priv *priv);
|
||||
void stop_audiodsp_monitor(struct audiodsp_priv *priv);
|
||||
void init_audiodsp_monitor(struct audiodsp_priv *priv);
|
||||
void release_audiodsp_monitor(struct audiodsp_priv *priv);
|
||||
|
||||
#endif /*
*/
|
||||
413
drivers/amlogic/audiodsp/pcmenc_module.c
Normal file
413
drivers/amlogic/audiodsp/pcmenc_module.c
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/pcmenc_module.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 pr_fmt(fmt) "audio_dsp: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <mach/am_regs.h>
|
||||
#include "pcmenc_stream.h"
|
||||
#include <linux/amlogic/amports/dsp_register.h>
|
||||
|
||||
static int __init audiodsp_pcmenc_init_module(void);
|
||||
static void __exit audiodsp_pcmenc_exit_module(void);
|
||||
static int audiodsp_pcmenc_open(struct inode *, struct file *);
|
||||
static int audiodsp_pcmenc_release(struct inode *, struct file *);
|
||||
static ssize_t audiodsp_pcmenc_read(struct file *, char *, size_t, loff_t *);
|
||||
static ssize_t audiodsp_pcmenc_write(struct file *, const char *, size_t,
|
||||
loff_t *);
|
||||
static int audiodsp_pcmenc_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
static long audiodsp_pcmenc_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long args);
|
||||
static int audiodsp_pcmenc_create_stream_buffer(void);
|
||||
static int audiodsp_pcmenc_destroy_stream_buffer(void);
|
||||
|
||||
#define SUCCESS 0
|
||||
#define DEVICE_NAME "audiodsp_pcmenc"
|
||||
|
||||
#define MIN_CACHE_ALIGN(x) (((x-4)&(~0x1f)))
|
||||
#define MAX_CACHE_ALIGN(x) ((x+0x1f)&(~0x1f))
|
||||
|
||||
#ifdef MIN
|
||||
#undef MIN
|
||||
#endif /*
*/
|
||||
#define MIN(x, y) (((x) < (y))?(x):(y))
|
||||
static int major;
|
||||
static struct class *class_pcmenc;
|
||||
static struct device *dev_pcmenc;
|
||||
/* Is device open?
|
||||
* Used to prevent multiple access to device
|
||||
*/
|
||||
static int device_opened;
|
||||
static pcm51_encoded_info_t pcminfo = { 0 };
|
||||
|
||||
struct priv_data_t {
|
||||
void *stream_buffer_mem;
|
||||
unsigned int stream_buffer_mem_size;
|
||||
unsigned long stream_buffer_start;
|
||||
unsigned long stream_buffer_end;
|
||||
unsigned long stream_buffer_size;
|
||||
/* the offset of the stream buffer which user space reading */
|
||||
unsigned long user_read_offset;
|
||||
};
|
||||
static struct priv_data_t priv_data = { 0 };
|
||||
|
||||
static ssize_t pcmenc_ptr_show(struct class *class,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = sprintf(buf, "pcmenc runtime info:\n"
|
||||
" pcmenc rd ptr :\t%lx\n"
|
||||
" pcmenc wr ptr :\t%lx\n"
|
||||
" pcmenc level :\t%x\n",
|
||||
(DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR)),
|
||||
(DSP_RD(DSP_DECODE_51PCM_OUT_WD_ADDR)),
|
||||
pcmenc_stream_content());
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct class_attribute pcmenc_attrs[] = {
|
||||
__ATTR_RO(pcmenc_ptr),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static void create_pcmenc_attrs(struct class *class)
|
||||
{
|
||||
int i = 0, ret;
|
||||
|
||||
for (i = 0; pcmenc_attrs[i].attr.name; i++)
|
||||
ret = class_create_file(class, &pcmenc_attrs[i]);
|
||||
}
|
||||
|
||||
static void remove_amaudio_attrs(struct class *class)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; pcmenc_attrs[i].attr.name; i++)
|
||||
class_remove_file(class, &pcmenc_attrs[i]);
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.read = audiodsp_pcmenc_read,
|
||||
.unlocked_ioctl = audiodsp_pcmenc_ioctl,
|
||||
.write = audiodsp_pcmenc_write,
|
||||
.open = audiodsp_pcmenc_open,
|
||||
.mmap = audiodsp_pcmenc_mmap,
|
||||
.release = audiodsp_pcmenc_release
|
||||
};
|
||||
|
||||
static int __init audiodsp_pcmenc_init_module(void)
|
||||
{
|
||||
void *ptr_err;
|
||||
int ret = 0;
|
||||
|
||||
major = register_chrdev(0, DEVICE_NAME, &fops);
|
||||
|
||||
if (major < 0) {
|
||||
pr_info("Registering char device %s failed with %d\n",
|
||||
DEVICE_NAME, major);
|
||||
return major;
|
||||
}
|
||||
|
||||
class_pcmenc = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
ptr_err = class_pcmenc;
|
||||
if (IS_ERR(ptr_err))
|
||||
goto err0;
|
||||
|
||||
create_pcmenc_attrs(class_pcmenc);
|
||||
dev_pcmenc =
|
||||
device_create(class_pcmenc, NULL, MKDEV(major, 0), NULL,
|
||||
DEVICE_NAME);
|
||||
ptr_err = dev_pcmenc;
|
||||
if (IS_ERR(ptr_err))
|
||||
goto err1;
|
||||
|
||||
/* buffer should be page aligned for mmap operation */
|
||||
priv_data.stream_buffer_mem_size = 2 * 2 * 512 * 1024 + PAGE_SIZE;
|
||||
ret = audiodsp_pcmenc_create_stream_buffer();
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
pr_info("amlogic audio dsp pcmenc device init!\n");
|
||||
|
||||
return SUCCESS;
|
||||
err2:
|
||||
device_destroy(class_pcmenc, MKDEV(major, 0));
|
||||
err1:
|
||||
class_destroy(class_pcmenc);
|
||||
err0:
|
||||
unregister_chrdev(major, DEVICE_NAME);
|
||||
|
||||
return PTR_ERR(ptr_err);
|
||||
}
|
||||
|
||||
static void __exit audiodsp_pcmenc_exit_module(void)
|
||||
{
|
||||
device_destroy(class_pcmenc, MKDEV(major, 0));
|
||||
remove_amaudio_attrs(class_pcmenc);
|
||||
class_destroy(class_pcmenc);
|
||||
unregister_chrdev(major, DEVICE_NAME);
|
||||
audiodsp_pcmenc_destroy_stream_buffer();
|
||||
}
|
||||
|
||||
static int audiodsp_pcmenc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (device_opened)
|
||||
return -EBUSY;
|
||||
device_opened++;
|
||||
try_module_get(THIS_MODULE);
|
||||
|
||||
#if 0
|
||||
if (buf == NULL)
|
||||
buf = kmalloc(priv_data.stream_buffer_size, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
device_opened--;
|
||||
module_put(THIS_MODULE);
|
||||
pr_info("pcmenc: malloc buffer failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
/* audiodsp_pcmenc_create_stream_buffer(); //init the r/p register */
|
||||
pcmenc_stream_init();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int audiodsp_pcmenc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
device_opened--;
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
#if 0
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
#endif
|
||||
audiodsp_pcmenc_create_stream_buffer(); /* init the r/p register */
|
||||
pcmenc_stream_deinit();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static ssize_t audiodsp_pcmenc_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
#if 1
|
||||
int bytes_read = 0;
|
||||
int len = 0;
|
||||
|
||||
if (buffer == NULL)
|
||||
return 0;
|
||||
|
||||
len = MIN(length, pcmenc_stream_content());
|
||||
bytes_read = pcmenc_stream_read(buffer, len);
|
||||
return bytes_read;
|
||||
|
||||
#else /*
*/
|
||||
pr_info("Sorry, read operation isn't supported.\n");
|
||||
return -EINVAL;
|
||||
#endif /*
*/
|
||||
return length;
|
||||
}
|
||||
|
||||
static ssize_t audiodsp_pcmenc_write(struct file *filp, const char *buff,
|
||||
size_t len, loff_t *off)
|
||||
{
|
||||
pr_info("Sorry, this operation isn't supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int audiodsp_pcmenc_mmap(struct file *filp,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned int vm_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if (vm_size == 0)
|
||||
return -EAGAIN;
|
||||
|
||||
off += virt_to_phys((void *)priv_data.stream_buffer_start);
|
||||
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
|
||||
if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
|
||||
pr_info(" pcmenc : failed remap_pfn_range\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
pr_info("pcmenc: mmap finished\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long audiodsp_pcmenc_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long args)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case AUDIODSP_PCMENC_GET_RING_BUF_SIZE:
|
||||
put_user(priv_data.stream_buffer_size, (__u64 __user *) args);
|
||||
break;
|
||||
case AUDIODSP_PCMENC_GET_RING_BUF_CONTENT:
|
||||
put_user(pcmenc_stream_content(), (__s32 __user *) args);
|
||||
break;
|
||||
case AUDIODSP_PCMENC_GET_RING_BUF_SPACE:
|
||||
put_user(pcmenc_stream_space(), (__s32 __user *) args);
|
||||
break;
|
||||
case AUDIODSP_PCMENC_SET_RING_BUF_RPTR:
|
||||
priv_data.user_read_offset = (unsigned long)args;
|
||||
|
||||
/*
|
||||
* pr_info("dsp rd ptr %x\n",
|
||||
* DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
|
||||
*/
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start +
|
||||
priv_data.user_read_offset));
|
||||
|
||||
/*
|
||||
* pr_info("dsp rd ptr change to %x\n",
|
||||
* DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
|
||||
*/
|
||||
break;
|
||||
case AUDIODSP_PCMENC_GET_PCMINFO:
|
||||
if (args == 0) {
|
||||
pr_info("pcm enc: args invalid\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
ret = copy_to_user((void __user *)args, &pcminfo,
|
||||
sizeof(pcminfo));
|
||||
if (ret != 0)
|
||||
pr_info("pcm enc:copy to user error\n");
|
||||
|
||||
break;
|
||||
default:
|
||||
pr_info("pcmenc:un-implemented cmd\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audiodsp_pcmenc_create_stream_buffer(void)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
#if 0
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
|
||||
#endif /*
*/
|
||||
if (priv_data.stream_buffer_mem_size == 0)
|
||||
return 0;
|
||||
|
||||
if (priv_data.stream_buffer_mem == NULL)
|
||||
priv_data.stream_buffer_mem =
|
||||
kmalloc(priv_data.stream_buffer_mem_size,
|
||||
GFP_KERNEL);
|
||||
if (priv_data.stream_buffer_mem == NULL) {
|
||||
pr_info("kmalloc error,
|
||||
no memory for audio dsp pcmenc stream buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void *)priv_data.stream_buffer_mem, 0,
|
||||
priv_data.stream_buffer_mem_size);
|
||||
buf_map =
|
||||
dma_map_single(NULL, (void *)priv_data.stream_buffer_mem,
|
||||
priv_data.stream_buffer_mem_size, DMA_TO_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, priv_data.stream_buffer_mem_size,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/* TODO: if it's needed */
|
||||
wmb();
|
||||
priv_data.stream_buffer_start =
|
||||
PAGE_ALIGN((unsigned long)priv_data.stream_buffer_mem +
|
||||
PAGE_SIZE - 1);
|
||||
priv_data.stream_buffer_end =
|
||||
PAGE_ALIGN((unsigned long)priv_data.stream_buffer_mem +
|
||||
priv_data.stream_buffer_mem_size);
|
||||
priv_data.stream_buffer_size =
|
||||
priv_data.stream_buffer_end - priv_data.stream_buffer_start;
|
||||
if (priv_data.stream_buffer_size < 0) {
|
||||
pr_info("DSP pcmenc Stream buffer set error,
|
||||
must more larger,mensize = %d,buffer size=%ld\n",
|
||||
priv_data.stream_buffer_mem_size,
|
||||
priv_data.stream_buffer_size);
|
||||
kfree(priv_data.stream_buffer_mem);
|
||||
priv_data.stream_buffer_mem = NULL;
|
||||
return -2;
|
||||
}
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_end));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
|
||||
pr_info("DSP pcmenc stream buffer to [%#lx-%#lx]\n",
|
||||
(unsigned long int)
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start),
|
||||
(unsigned long int)
|
||||
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_end));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audiodsp_pcmenc_destroy_stream_buffer(void)
|
||||
{
|
||||
kfree(priv_data.stream_buffer_mem);
|
||||
priv_data.stream_buffer_mem = NULL;
|
||||
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_pcminfo_data(void *pcm_encoded_info)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
/*
|
||||
* as this ptr got from arc dsp side,
|
||||
* which mapping to 0 address,
|
||||
* so add this dsp start offset
|
||||
*/
|
||||
pcm51_encoded_info_t *info =
|
||||
(pcm51_encoded_info_t *) ((unsigned int)pcm_encoded_info +
|
||||
AUDIO_DSP_START_ADDR);
|
||||
|
||||
/* inv dcache as this data from device */
|
||||
buf_map =
|
||||
dma_map_single(dev_pcmenc, (void *)info,
|
||||
sizeof(pcm51_encoded_info_t), DMA_FROM_DEVICE);
|
||||
dma_unmap_single(dev_pcmenc, buf_map, sizeof(pcm51_encoded_info_t),
|
||||
DMA_FROM_DEVICE);
|
||||
memcpy(&pcminfo, info, sizeof(pcm51_encoded_info_t));
|
||||
pr_info("got pcm51 info from dsp\n");
|
||||
}
|
||||
EXPORT_SYMBOL(set_pcminfo_data);
|
||||
module_init(audiodsp_pcmenc_init_module);
|
||||
module_exit(audiodsp_pcmenc_exit_module);
|
||||
MODULE_DESCRIPTION("AMLOGIC PCM encoder interface driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("jian.xu <jian.xu@amlogic.com>");
|
||||
170
drivers/amlogic/audiodsp/pcmenc_stream.c
Normal file
170
drivers/amlogic/audiodsp/pcmenc_stream.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/pcmenc_stream.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 "pcmenc_stream.h"
|
||||
#include <linux/amlogic/amports/dsp_register.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
struct log_stream_t {
|
||||
unsigned long wpointer; /* write pointer */
|
||||
unsigned long rpointer; /* read pointer */
|
||||
unsigned long size; /* size of buffer */
|
||||
unsigned long start; /* buffer start addr */
|
||||
unsigned long end; /* buffer end addr */
|
||||
};
|
||||
|
||||
#ifdef MIN
|
||||
#undef MIN
|
||||
#endif /*
*/
|
||||
|
||||
#define MIN(x, y) (((x) < (y))?(x):(y))
|
||||
#define MIN_CACHE_LINE(x, y) (MIN(x, y) & (~0x1f))
|
||||
|
||||
#define PCM_BUFFER_RESERVED (4096)
|
||||
static struct log_stream_t log_stream = { 0 };
|
||||
|
||||
static inline int internal_read(char __user *buf, int size, int intact);
|
||||
static inline unsigned long get_read_pointer(void);
|
||||
static inline void set_read_pointer(void);
|
||||
static inline unsigned long get_write_pointer(void);
|
||||
static inline void cache_flush(unsigned int addr, unsigned int size);
|
||||
static inline void cache_invalidate(unsigned int addr, unsigned int size);
|
||||
static inline unsigned long get_read_pointer(void)
|
||||
{
|
||||
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
|
||||
}
|
||||
|
||||
static inline void set_read_pointer(void)
|
||||
{
|
||||
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
|
||||
ARM_2_ARC_ADDR_SWAP(log_stream.rpointer));
|
||||
}
|
||||
|
||||
static inline unsigned long get_write_pointer(void)
|
||||
{
|
||||
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_WD_ADDR));
|
||||
}
|
||||
|
||||
static inline void cache_flush(unsigned int addr, unsigned int size)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
buf_map = dma_map_single(NULL, (void *)addr, size, DMA_TO_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, size, DMA_TO_DEVICE);
|
||||
}
|
||||
static inline void cache_invalidate(unsigned int addr,
|
||||
unsigned int size)
|
||||
{
|
||||
dma_addr_t buf_map;
|
||||
|
||||
buf_map = dma_map_single(NULL, (void *)addr, size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, size, DMA_FROM_DEVICE);
|
||||
}
|
||||
int pcmenc_stream_space(void)
|
||||
{
|
||||
int space;
|
||||
|
||||
log_stream.rpointer = get_read_pointer();
|
||||
log_stream.wpointer = get_write_pointer();
|
||||
if (log_stream.wpointer >= log_stream.rpointer)
|
||||
space =
|
||||
log_stream.size - (log_stream.wpointer -
|
||||
log_stream.rpointer);
|
||||
else
|
||||
space = log_stream.rpointer - log_stream.wpointer;
|
||||
|
||||
space =
|
||||
space > PCM_BUFFER_RESERVED ? space - PCM_BUFFER_RESERVED : 0;
|
||||
return space;
|
||||
}
|
||||
|
||||
int pcmenc_stream_content(void)
|
||||
{
|
||||
int content;
|
||||
|
||||
log_stream.rpointer = get_read_pointer();
|
||||
log_stream.wpointer = get_write_pointer();
|
||||
if (log_stream.rpointer > log_stream.wpointer)
|
||||
content =
|
||||
log_stream.size - (log_stream.rpointer -
|
||||
log_stream.wpointer);
|
||||
else
|
||||
content = log_stream.wpointer - log_stream.rpointer;
|
||||
|
||||
content = content > PCM_BUFFER_RESERVED ?
|
||||
content - PCM_BUFFER_RESERVED : 0;
|
||||
return content;
|
||||
}
|
||||
|
||||
static inline int internal_read(char __user *buf, int size, int intact)
|
||||
{
|
||||
int len;
|
||||
int content = pcmenc_stream_content();
|
||||
int tail = 0;
|
||||
|
||||
if (intact)
|
||||
len = size > content ? -1 : size;
|
||||
else
|
||||
len =
|
||||
size <= 32 ? MIN(size, content) : MIN_CACHE_LINE(size,
|
||||
content);
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
if (log_stream.rpointer + len > log_stream.end) {
|
||||
tail = log_stream.end - log_stream.rpointer;
|
||||
cache_invalidate(log_stream.rpointer, tail);
|
||||
copy_to_user((void *)buf, (void *)(log_stream.rpointer), tail);
|
||||
log_stream.rpointer = log_stream.start;
|
||||
cache_invalidate(log_stream.rpointer, len - tail);
|
||||
copy_to_user((void *)(buf + tail),
|
||||
(void *)(log_stream.rpointer), len - tail);
|
||||
log_stream.rpointer += len - tail;
|
||||
} else {
|
||||
cache_invalidate(log_stream.rpointer, len);
|
||||
copy_to_user((void *)buf, (void *)(log_stream.rpointer), len);
|
||||
log_stream.rpointer += len;
|
||||
}
|
||||
set_read_pointer();
|
||||
return len;
|
||||
}
|
||||
|
||||
int pcmenc_stream_read(char __user *buffer, int size)
|
||||
{
|
||||
if (log_stream.start == 0)
|
||||
return 0;
|
||||
|
||||
return internal_read(buffer, size, 0);
|
||||
}
|
||||
|
||||
int pcmenc_stream_init(void)
|
||||
{
|
||||
log_stream.start =
|
||||
ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_START_ADDR));
|
||||
log_stream.end =
|
||||
ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_END_ADDR));
|
||||
log_stream.size = log_stream.end - log_stream.start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcmenc_stream_deinit(void)
|
||||
{
|
||||
log_stream.start = 0;
|
||||
log_stream.end = 0;
|
||||
log_stream.size = 0;
|
||||
return 0;
|
||||
}
|
||||
62
drivers/amlogic/audiodsp/pcmenc_stream.h
Normal file
62
drivers/amlogic/audiodsp/pcmenc_stream.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/pcmenc_stream.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 __PCMENC_STREAM_H__
|
||||
#define __PCMENC_STREAM_H__
|
||||
#include <linux/uaccess.h>
|
||||
struct pcm51_encoded_info_t {
|
||||
unsigned int InfoValidFlag;
|
||||
unsigned int SampFs;
|
||||
unsigned int NumCh;
|
||||
unsigned int AcMode;
|
||||
unsigned int LFEFlag;
|
||||
unsigned int BitsPerSamp;
|
||||
};
|
||||
|
||||
#define AUDIODSP_PCMENC_GET_RING_BUF_SIZE _IOR('l', 0x01, unsigned long)
|
||||
#define AUDIODSP_PCMENC_GET_RING_BUF_CONTENT _IOR('l', 0x02, unsigned long)
|
||||
#define AUDIODSP_PCMENC_GET_RING_BUF_SPACE _IOR('l', 0x03, unsigned long)
|
||||
#define AUDIODSP_PCMENC_SET_RING_BUF_RPTR _IOW('l', 0x04, unsigned long)
|
||||
#define AUDIODSP_PCMENC_GET_PCMINFO _IOR('l', 0x05, unsigned long)
|
||||
|
||||
/* initialize stream FIFO
|
||||
* return value: on success, zero is returned, on error, -1 is returned
|
||||
*/
|
||||
extern int pcmenc_stream_init(void);
|
||||
|
||||
/* return space of stream FIFO, unit:byte
|
||||
*/
|
||||
extern int pcmenc_stream_space(void);
|
||||
|
||||
/* return content of stream FIFO, unit:byte
|
||||
*/
|
||||
extern int pcmenc_stream_content(void);
|
||||
|
||||
/* deinit stream FIFO
|
||||
* return value: on success, zero is returned, on error, -1 is returned
|
||||
*/
|
||||
extern int pcmenc_stream_deinit(void);
|
||||
|
||||
/*
|
||||
* read data out of FIFO, the minimum of the FIFO's conten
|
||||
* and size will be read, if the FIFO is empty, read will be failed
|
||||
* return value: on success, the number of bytes read are returned,
|
||||
* othewise, 0 is returned
|
||||
*/
|
||||
extern int pcmenc_stream_read(char __user *buffer, int size);
|
||||
|
||||
#endif /*
*/
|
||||
300
drivers/amlogic/audiodsp/spdif_module.c
Normal file
300
drivers/amlogic/audiodsp/spdif_module.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/spdif_module.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 pr_fmt(fmt) "audio_dsp: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/amlogic/amports/dsp_register.h>
|
||||
#include <linux/amlogic/sound/aiu_regs.h>
|
||||
#include <linux/amlogic/iomap.h>
|
||||
|
||||
#include "spdif_module.h"
|
||||
|
||||
#define DEVICE_NAME "audio_spdif"
|
||||
static int device_opened;
|
||||
static int major_spdif;
|
||||
static struct class *class_spdif;
|
||||
static struct device *dev_spdif;
|
||||
static struct mutex mutex_spdif;
|
||||
static unsigned int iec958_wr_offset;
|
||||
|
||||
static inline int if_audio_output_iec958_enable(void)
|
||||
{
|
||||
return aml_read_cbus(AIU_MEM_IEC958_CONTROL) && (0x3 << 1);
|
||||
}
|
||||
|
||||
static inline int if_audio_output_i2s_enable(void)
|
||||
{
|
||||
return aml_read_cbus(AIU_MEM_I2S_CONTROL) && (0x3 << 1);
|
||||
}
|
||||
|
||||
static inline void audio_output_iec958_enable(unsigned int flag)
|
||||
{
|
||||
if (flag) {
|
||||
aml_write_cbus(AIU_958_FORCE_LEFT, 0);
|
||||
aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 1, 1);
|
||||
aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1,
|
||||
0x3 << 1);
|
||||
} else {
|
||||
aml_write_cbus(AIU_958_DCU_FF_CTRL, 0);
|
||||
aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_spdif_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (device_opened) {
|
||||
pr_info("error,audio_spdif device busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
device_opened++;
|
||||
try_module_get(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_spdif_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* audio_enable_output(0); */
|
||||
/* audio_output_iec958_enable(0); */
|
||||
aml_alsa_hw_reprepare();
|
||||
device_opened--;
|
||||
module_put(THIS_MODULE);
|
||||
IEC958_mode_codec = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long audio_spdif_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long args)
|
||||
{
|
||||
int err = 0;
|
||||
int tmp = 0;
|
||||
|
||||
mutex_lock(&mutex_spdif);
|
||||
switch (cmd) {
|
||||
case AUDIO_SPDIF_GET_958_BUF_RD_OFFSET:
|
||||
tmp =
|
||||
aml_read_cbus(AIU_MEM_IEC958_RD_PTR) -
|
||||
aml_read_cbus(AIU_MEM_IEC958_START_PTR);
|
||||
put_user(tmp, (__s32 __user *) args);
|
||||
break;
|
||||
case AUDIO_SPDIF_GET_958_BUF_SIZE:
|
||||
/* iec958_info.iec958_buffer_size; */
|
||||
tmp = aml_read_cbus(AIU_MEM_IEC958_END_PTR) -
|
||||
aml_read_cbus(AIU_MEM_IEC958_START_PTR) + 64;
|
||||
if (tmp == 64)
|
||||
tmp = 0;
|
||||
if (aml_read_cbus(AIU_MEM_IEC958_START_PTR) ==
|
||||
aml_read_cbus(AIU_MEM_I2S_START_PTR)) {
|
||||
tmp = tmp * 4;
|
||||
}
|
||||
put_user(tmp, (__s32 __user *) args);
|
||||
break;
|
||||
case AUDIO_SPDIF_GET_958_ENABLE_STATUS:
|
||||
put_user(if_audio_output_iec958_enable(),
|
||||
(__s32 __user *) args);
|
||||
break;
|
||||
case AUDIO_SPDIF_GET_I2S_ENABLE_STATUS:
|
||||
put_user(if_audio_output_i2s_enable(), (__s32 __user *) args);
|
||||
break;
|
||||
case AUDIO_SPDIF_SET_958_ENABLE:
|
||||
|
||||
/* IEC958_mode_raw = 1; */
|
||||
/* audio_enable_output(1); */
|
||||
audio_output_iec958_enable(args);
|
||||
break;
|
||||
case AUDIO_SPDIF_SET_958_INIT_PREPARE:
|
||||
IEC958_mode_codec = 3; /* dts pcm raw */
|
||||
DSP_WD(DSP_IEC958_INIT_READY_INFO, 0x12345678);
|
||||
aml_alsa_hw_reprepare();
|
||||
DSP_WD(DSP_IEC958_INIT_READY_INFO, 0);
|
||||
break;
|
||||
case AUDIO_SPDIF_SET_958_WR_OFFSET:
|
||||
get_user(iec958_wr_offset, (__u32 __user *) args);
|
||||
break;
|
||||
default:
|
||||
pr_info("audio spdif: cmd not implemented\n");
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mutex_spdif);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t audio_spdif_write(struct file *file,
|
||||
const char __user *userbuf, size_t len,
|
||||
loff_t *off)
|
||||
{
|
||||
char *wr_ptr;
|
||||
unsigned long wr_addr;
|
||||
dma_addr_t buf_map;
|
||||
|
||||
wr_addr = aml_read_cbus(AIU_MEM_IEC958_START_PTR) + iec958_wr_offset;
|
||||
wr_ptr = (char *)phys_to_virt(wr_addr);
|
||||
if (copy_from_user((void *)wr_ptr, (void *)userbuf, len) != 0) {
|
||||
pr_info("audio spdif: copy from user failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
buf_map = dma_map_single(NULL, (void *)wr_ptr, len, DMA_TO_DEVICE);
|
||||
dma_unmap_single(NULL, buf_map, len, DMA_TO_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t audio_spdif_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
pr_info("audio spdif: read operation isn't supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int audio_spdif_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned int vm_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if (vm_size == 0) {
|
||||
pr_info("audio spdif:vm_size 0\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* mapping the 958 dma buffer to user space to write */
|
||||
off = aml_read_cbus(AIU_MEM_IEC958_START_PTR);
|
||||
|
||||
/*|VM_MAYWRITE|VM_MAYSHARE */
|
||||
vma->vm_flags |=
|
||||
VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
|
||||
if (remap_pfn_range(vma, vma->vm_start,
|
||||
off >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot)) {
|
||||
pr_info("audio spdif : failed remap_pfn_range\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
pr_info("audio spdif: mmap finished\n");
|
||||
pr_info("audio spdif: 958 dma buf:py addr 0x%x,vir addr 0x%x\n",
|
||||
aml_read_cbus(AIU_MEM_IEC958_START_PTR), (unsigned int)
|
||||
phys_to_virt(aml_read_cbus(AIU_MEM_IEC958_START_PTR)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t audio_spdif_ptr_show(struct class *class,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = sprintf(buf, "iec958 buf runtime info:\n"
|
||||
" iec958 rd ptr :\t%x\n"
|
||||
" iec958 wr ptr :\t%x\n",
|
||||
aml_read_cbus(AIU_MEM_IEC958_RD_PTR),
|
||||
(aml_read_cbus(AIU_MEM_IEC958_START_PTR) +
|
||||
iec958_wr_offset));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t audio_spdif_buf_show(struct class *class,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
unsigned int *ptr =
|
||||
(unsigned int *)phys_to_virt(
|
||||
aml_read_cbus(AIU_MEM_IEC958_RD_PTR) +
|
||||
iec958_wr_offset);
|
||||
ret =
|
||||
sprintf(buf,
|
||||
"iec958 buf info:\n"
|
||||
" iec958 wr ptr val:\t[%x][%x][%x][%x]\n", ptr[0], ptr[1],
|
||||
ptr[2], ptr[3]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct class_attribute audio_spdif_attrs[] = {
|
||||
__ATTR_RO(audio_spdif_ptr),
|
||||
__ATTR_RO(audio_spdif_buf),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static const struct file_operations fops_spdif = {
|
||||
.read = audio_spdif_read,
|
||||
.unlocked_ioctl = audio_spdif_ioctl,
|
||||
.write = audio_spdif_write,
|
||||
.open = audio_spdif_open,
|
||||
.mmap = audio_spdif_mmap,
|
||||
.release = audio_spdif_release
|
||||
};
|
||||
|
||||
static void create_audio_spdif_attrs(struct class *class)
|
||||
{
|
||||
int i = 0, ret;
|
||||
|
||||
for (i = 0; audio_spdif_attrs[i].attr.name; i++)
|
||||
ret = class_create_file(class, &audio_spdif_attrs[i]);
|
||||
}
|
||||
|
||||
static int __init audio_spdif_init_module(void)
|
||||
{
|
||||
void *ptr_err;
|
||||
|
||||
major_spdif = register_chrdev(0, DEVICE_NAME, &fops_spdif);
|
||||
if (major_spdif < 0) {
|
||||
pr_info("Registering spdif char device %s failed with %d\n",
|
||||
DEVICE_NAME, major_spdif);
|
||||
return major_spdif;
|
||||
}
|
||||
class_spdif = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
ptr_err = class_spdif;
|
||||
if (IS_ERR(ptr_err))
|
||||
goto err0;
|
||||
|
||||
create_audio_spdif_attrs(class_spdif);
|
||||
dev_spdif =
|
||||
device_create(class_spdif, NULL, MKDEV(major_spdif, 0), NULL,
|
||||
DEVICE_NAME);
|
||||
ptr_err = dev_spdif;
|
||||
if (IS_ERR(ptr_err))
|
||||
goto err1;
|
||||
|
||||
mutex_init(&mutex_spdif);
|
||||
pr_info("amlogic audio spdif interface device init!\n");
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
err2:
|
||||
device_destroy(class_spdif, MKDEV(major_spdif, 0));
|
||||
#endif /*
*/
|
||||
err1:
|
||||
class_destroy(class_spdif);
|
||||
err0:
|
||||
unregister_chrdev(major_spdif, DEVICE_NAME);
|
||||
return PTR_ERR(ptr_err);
|
||||
}
|
||||
|
||||
static void __exit audio_spdif_exit_module(void)
|
||||
{
|
||||
device_destroy(class_spdif, MKDEV(major_spdif, 0));
|
||||
class_destroy(class_spdif);
|
||||
unregister_chrdev(major_spdif, DEVICE_NAME);
|
||||
}
|
||||
|
||||
module_init(audio_spdif_init_module);
|
||||
module_exit(audio_spdif_exit_module);
|
||||
MODULE_DESCRIPTION("AMLOGIC IEC958 output interface driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("jian.xu <jian.xu@amlogic.com>");
|
||||
51
drivers/amlogic/audiodsp/spdif_module.h
Normal file
51
drivers/amlogic/audiodsp/spdif_module.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* drivers/amlogic/audiodsp/spdif_module.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 __SPDIF_MODULE_H__
|
||||
#define __SPDIF_MODULE_H__
|
||||
struct _iec958_data_info {
|
||||
unsigned int iec958_hw_start;
|
||||
unsigned int iec958_hw_rd_offset;
|
||||
unsigned int iec958_wr_offset;
|
||||
unsigned int iec958_buffer_size;
|
||||
};
|
||||
|
||||
#define AUDIO_SPDIF_GET_958_BUF_SIZE _IOR('s', 0x01, unsigned long)
|
||||
#define AUDIO_SPDIF_GET_958_BUF_CONTENT _IOR('s', 0x02, unsigned long)
|
||||
#define AUDIO_SPDIF_GET_958_BUF_SPACE _IOR('s', 0x03, unsigned long)
|
||||
#define AUDIO_SPDIF_GET_958_BUF_RD_OFFSET _IOR('s', 0x04, unsigned long)
|
||||
#define AUDIO_SPDIF_GET_958_ENABLE_STATUS _IOR('s', 0x05, unsigned long)
|
||||
#define AUDIO_SPDIF_GET_I2S_ENABLE_STATUS _IOR('s', 0x06, unsigned long)
|
||||
#define AUDIO_SPDIF_SET_958_ENABLE _IOW('s', 0x07, unsigned long)
|
||||
#define AUDIO_SPDIF_SET_958_INIT_PREPARE _IOW('s', 0x08, unsigned long)
|
||||
#define AUDIO_SPDIF_SET_958_WR_OFFSET _IOW('s', 0x09, unsigned long)
|
||||
static int audio_spdif_open(struct inode *inode, struct file *file);
|
||||
static int audio_spdif_release(struct inode *inode, struct file *file);
|
||||
static long audio_spdif_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long args);
|
||||
static ssize_t audio_spdif_write(struct file *file,
|
||||
const char __user *userbuf, size_t len,
|
||||
loff_t *off);
|
||||
static ssize_t audio_spdif_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset);
|
||||
static int audio_spdif_mmap(struct file *file, struct vm_area_struct *vma);
|
||||
extern void aml_alsa_hw_reprepare(void);
|
||||
extern void audio_enable_output(int flag);
|
||||
extern unsigned int IEC958_mode_raw;
|
||||
extern unsigned int IEC958_mode_codec;
|
||||
|
||||
#endif /*
*/
|
||||
@@ -69,6 +69,9 @@ static int aml_DAC_Gain_get_enum(
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
u32 add, val, val1, val2;
|
||||
|
||||
/*TODO: return 0 for tmp, this wolud modified later */
|
||||
return 0;
|
||||
|
||||
if (codec == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user