diff --git a/MAINTAINERS b/MAINTAINERS index 92d46d159a24..789b313a76b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts index 2b1b686a9784..1d30f0c3f02b 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts @@ -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>; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts index 59ca77640ad2..ed1237a8d2dd 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts @@ -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>; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 439cb0d531f4..1f3cd43eacc3 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index bf9b6e992814..4e1debadf2ca 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -67,5 +67,6 @@ source "drivers/amlogic/key_manage/Kconfig" source "drivers/amlogic/thermal/Kconfig" +source "drivers/amlogic/audiodsp/Kconfig" endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index cac771ff3844..1ba8295efedb 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/ obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/ +obj-$(CONFIG_AMLOGIC_AUDIO_DSP) += audiodsp/ diff --git a/drivers/amlogic/audiodsp/Kconfig b/drivers/amlogic/audiodsp/Kconfig new file mode 100644 index 000000000000..3fb81cf495bf --- /dev/null +++ b/drivers/amlogic/audiodsp/Kconfig @@ -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 diff --git a/drivers/amlogic/audiodsp/Makefile b/drivers/amlogic/audiodsp/Makefile new file mode 100644 index 000000000000..b628118ea2b0 --- /dev/null +++ b/drivers/amlogic/audiodsp/Makefile @@ -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 diff --git a/drivers/amlogic/audiodsp/audiodsp_control.h b/drivers/amlogic/audiodsp/audiodsp_control.h new file mode 100644 index 000000000000..7eec80b5466c --- /dev/null +++ b/drivers/amlogic/audiodsp/audiodsp_control.h @@ -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 diff --git a/drivers/amlogic/audiodsp/audiodsp_module.c b/drivers/amlogic/audiodsp/audiodsp_module.c new file mode 100644 index 000000000000..acb9355bf160 --- /dev/null +++ b/drivers/amlogic/audiodsp/audiodsp_module.c @@ -0,0 +1,1333 @@ +/* + * drivers/amlogic/audiodsp/audiodsp_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* #include */ +/* #include "audiodsp_control.h" */ + +#include +#include + + +#include "audiodsp_module.h" +#if 0 +#include "dsp_control.h" +#include "dsp_microcode.h" +#include "dsp_mailbox.h" +#include "dsp_monitor.h" +#include "dsp_codec.h" +#endif +#include +#include +#include +#include + +unsigned int dsp_debug_flag = 1; + +MODULE_DESCRIPTION("AMLOGIC APOLLO Audio dsp driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhou Zhi "); +MODULE_VERSION("1.0.0"); +/* static int IEC958_mode_raw_last; */ +/* static int IEC958_mode_codec_last; */ +static unsigned int audio_samesource = 1; +static int decopt = 1; +/* code for DD/DD+ DRC control */ +/* Dynamic range compression mode */ +enum DDP_DEC_DRC_MODE { + GBL_COMP_CUSTOM_0 = 0, /* custom mode, analog dialnorm */ + GBL_COMP_CUSTOM_1, /* custom mode, digital dialnorm */ + GBL_COMP_LINE, /* line out mode (default) */ + GBL_COMP_RF /* RF mode */ +}; +#define DRC_MODE_BIT 0 +#define DRC_HIGH_CUT_BIT 3 +#define DRC_LOW_BST_BIT 16 +static unsigned int ac3_drc_control = +(GBL_COMP_LINE << DRC_MODE_BIT) | +(100 << DRC_HIGH_CUT_BIT) | (100 << DRC_LOW_BST_BIT); +/* code for DTS dial norm/downmix mode control */ +enum DTS_DMX_MODE { + DTS_DMX_LoRo = 0, + DTS_DMX_LtRt, +}; +#define DTS_DMX_MODE_BIT 0 +#define DTS_DIAL_NORM_BIT 1 +#define DTS_DRC_SCALE_BIT 2 +static unsigned int dts_dec_control = +(DTS_DMX_LoRo << DTS_DMX_MODE_BIT) | +(1 << DTS_DIAL_NORM_BIT) | (0 << DTS_DRC_SCALE_BIT); + +static struct audiodsp_priv *audiodsp_p; +#define DSP_DRIVER_NAME "audiodsp" +#define DSP_NAME "dsp" + +/* + * Audio codec necessary MIPS (KHz) + */ +#if 0 +static unsigned int audiodsp_mips[] = { + 200000, /* #define MCODEC_FMT_MPEG123 (1<<0) */ + 200000, /* #define MCODEC_FMT_AAC (1<<1) */ + 200000, /* #define MCODEC_FMT_AC3 (1<<2) */ + 200000, /* #define MCODEC_FMT_DTS (1<<3) */ + 200000, /* #define MCODEC_FMT_FLAC (1<<4) */ + 200000, /* #define MCODEC_FMT_COOK (1<<5) */ + 200000, /* #define MCODEC_FMT_AMR (1<<6) */ + 200000, /* #define MCODEC_FMT_RAAC (1<<7) */ + 200000, /* #define MCODEC_FMT_ADPCM (1<<8) */ + 200000, /* #define MCODEC_FMT_WMA (1<<9) */ + 200000, /* #define MCODEC_FMT_PCM (1<<10) */ + 200000, /* #define MCODEC_FMT_WMAPRO (1<<11) */ +}; +#endif +#ifdef CONFIG_PM +struct audiodsp_pm_state_t { + int event; + /* */ +}; + +/* static struct audiodsp_pm_state_t pm_state; */ + +#endif +#if 0 +static void audiodsp_prevent_sleep(void) +{ + /*struct audiodsp_priv* priv = */ audiodsp_privdata(); + pr_info("audiodsp prevent sleep\n"); + /* wake_lock(&priv->wakelock); */ +} + +static void audiodsp_allow_sleep(void) +{ + /*struct audiodsp_priv *priv= */ audiodsp_privdata(); + pr_info("audiodsp allow sleep\n"); + /* wake_unlock(&priv->wakelock); */ +} + +int audiodsp_start(void) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + struct audiodsp_microcode *pmcode; + struct audio_info *audio_info; + int ret, i; + + priv->frame_format.valid = 0; + priv->decode_error_count = 0; + priv->last_valid_pts = 0; + priv->out_len_after_last_valid_pts = 0; + priv->decode_fatal_err = 0; + priv->first_lookup_over = 0; + pmcode = audiodsp_find_supoort_mcode(priv, priv->stream_fmt); + if (pmcode == NULL) { + DSP_PRNT("have not find a valid mcode for fmt(0x%x)\n", + priv->stream_fmt); + return -1; + } + + stop_audiodsp_monitor(priv); + dsp_stop(priv); + ret = dsp_start(priv, pmcode); + if (ret == 0) { +#if 0 + if (priv->stream_fmt == MCODEC_FMT_DTS + || priv->stream_fmt == MCODEC_FMT_AC3 + || priv->stream_fmt == MCODEC_FMT_EAC3) { + dsp_get_debug_interface(1); + } +#endif + start_audiodsp_monitor(priv); + +#ifdef CONFIG_AM_VDEC_REAL + if ((pmcode->fmt == MCODEC_FMT_COOK) || + (pmcode->fmt == MCODEC_FMT_RAAC) || + (pmcode->fmt == MCODEC_FMT_AMR) || + (pmcode->fmt == MCODEC_FMT_WMA) || + (pmcode->fmt == MCODEC_FMT_ADPCM) || + (pmcode->fmt == MCODEC_FMT_PCM) || + (pmcode->fmt == MCODEC_FMT_WMAPRO) || + (pmcode->fmt == MCODEC_FMT_ALAC) || + (pmcode->fmt & MCODEC_FMT_AC3) || + (pmcode->fmt & MCODEC_FMT_EAC3) || + (pmcode->fmt == MCODEC_FMT_APE) || + (pmcode->fmt == MCODEC_FMT_FLAC)) { + DSP_PRNT("dsp send audio info\n"); + for (i = 0; i < 2000; i++) { + /* + * maybe at audiodsp side, + * INT not enabled yet,so wait a while + */ + if (DSP_RD(DSP_AUDIOINFO_STATUS) == + DSP_AUDIOINFO_READY) + break; + udelay(1000); + } + if (i == 2000) + DSP_PRNT("audiodsp not ready for info\n"); + DSP_WD(DSP_AUDIOINFO_STATUS, 0); + audio_info = get_audio_info(); + DSP_PRNT + ("kernel sent info[0x%x],[0x%x],[0x%x],[0x%x]\n\t", + audio_info->extradata[0], audio_info->extradata[1], + audio_info->extradata[2], + audio_info->extradata[3]); + DSP_WD(DSP_GET_EXTRA_INFO_FINISH, 0); + while (1) { + dsp_mailbox_send(priv, 1, M2B_IRQ4_AUDIO_INFO, + 0, (const char *)audio_info, + sizeof(struct audio_info)); + msleep(100); + + if (DSP_RD(DSP_GET_EXTRA_INFO_FINISH) == + 0x12345678) + break; + } + } +#endif + } + return ret; +} + +static int audiodsp_open(struct inode *node, struct file *file) +{ + DSP_PRNT("dsp_open\n"); + +#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + switch_mod_gate_by_type(MOD_MEDIA_CPU, 1); + /* Audio DSP firmware uses mailbox registers for communications + * with host processor. And these mailbox registers unfortunately + * falls into the assistant module, which is in vdec DOS clock domain. + * So we have to make sure the clock domain is enabled/disabled when + * audio firmware start/stop running. + * Note the module_gating has ref count so a flag 0 does + * not mean the vdec clock is gated off immediately. + */ + switch_mod_gate_by_type(MOD_VDEC, 1); +#endif + audiodsp_prevent_sleep(); + return 0; + +} + +static unsigned long audiodsp_drop_pcm(unsigned long size) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + size_t len; + int count = 0; + unsigned long drop_bytes = size; + + mutex_lock(&priv->stream_buffer_mutex); + if (priv->stream_buffer_mem == NULL || !priv->dsp_is_started) + goto err; + + while (drop_bytes > 0) { + len = dsp_codec_get_bufer_data_len(priv); + if (drop_bytes >= len) { + dsp_codec_inc_rd_addr(priv, len); + drop_bytes -= len; + msleep(50); + count++; + if (count > 20) + break; + } else { + dsp_codec_inc_rd_addr(priv, drop_bytes); + drop_bytes = 0; + } + } + + mutex_unlock(&priv->stream_buffer_mutex); + if (count > 10) + pr_info("drop pcm data timeout! count = %d\n", count); + + return size - drop_bytes; + + err: + mutex_unlock(&priv->stream_buffer_mutex); + pr_info("error, can not drop pcm data!\n"); + return 0; +} + +static long audiodsp_ioctl(struct file *file, unsigned int cmd, + unsigned long args) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + struct audiodsp_cmd *a_cmd; + char name[64]; + int len; + unsigned long pts; + int ret = 0; + unsigned long drop_size; + static int wait_format_times; + + switch (cmd) { + case AUDIODSP_SET_FMT: + priv->stream_fmt = args; + if (IEC958_mode_raw) { /* raw data pass through */ + if (args == MCODEC_FMT_DTS) + /* dts PCM/RAW mode */ + IEC958_mode_codec = ((decopt >> 5) & 1) ? 3 : 1; + else if (args == MCODEC_FMT_AC3) + IEC958_mode_codec = 2; /* ac3 */ + else if (args == MCODEC_FMT_EAC3) { + if (IEC958_mode_raw == 2) + /* 958 dd+ package */ + IEC958_mode_codec = 4; + else + /* 958 dd package */ + IEC958_mode_codec = 2; + } else + IEC958_mode_codec = 0; + } else + IEC958_mode_codec = 0; + + /* for dd+ certification */ + if (args == MCODEC_FMT_AC3 || args == MCODEC_FMT_EAC3) + DSP_WD(DSP_AC3_DRC_INFO, ac3_drc_control | (1 << 31)); + else if (args == MCODEC_FMT_DTS) + DSP_WD(DSP_DTS_DEC_INFO, dts_dec_control | (1 << 31)); + break; + case AUDIODSP_START: + if (IEC958_mode_codec + || (IEC958_mode_codec_last != IEC958_mode_codec)) { + IEC958_mode_raw_last = IEC958_mode_raw; + IEC958_mode_codec_last = IEC958_mode_codec; + aml_alsa_hw_reprepare(); + } + priv->decoded_nb_frames = 0; + priv->format_wait_count = 0; + if (priv->stream_fmt <= 0) { + DSP_PRNT("Audio dsp steam format have not set!\n"); + } else { +#if 0 + if (priv->stream_fmt == MCODEC_FMT_DTS + || priv->stream_fmt == MCODEC_FMT_AC3 + || priv->stream_fmt == MCODEC_FMT_EAC3) + dsp_get_debug_interface(0); +#endif + ret = audiodsp_start(); + } + break; + case AUDIODSP_STOP: + /* restore aiu958 setting to pcm */ + if (IEC958_mode_codec_last) + aml_alsa_hw_reprepare(); + IEC958_mode_codec = 0; + /* DSP_PRNT("audiodsp command stop\n"); */ + stop_audiodsp_monitor(priv); + dsp_stop(priv); + priv->decoded_nb_frames = 0; + priv->format_wait_count = 0; + break; + case AUDIODSP_DECODE_START: + if (priv->dsp_is_started) { + dsp_codec_start(priv); + wait_format_times = 0; + } else { + DSP_PRNT("Audio dsp have not started\n"); + } + break; + case AUDIODSP_WAIT_FORMAT: + if (priv->dsp_is_started) { + ret = audiodsp_get_audioinfo(priv); + } else { + ret = -1; + DSP_PRNT("Audio dsp have not started\n"); + } + /*Reset the PLL. Added by GK */ + tsync_pcr_recover(); + break; + case AUDIODSP_DECODE_STOP: + if (priv->dsp_is_started) + dsp_codec_stop(priv); + else + DSP_PRNT("Audio dsp have not started\n"); + + break; + case AUDIODSP_REGISTER_FIRMWARE: + a_cmd = (struct audiodsp_cmd *)args; + /* + * DSP_PRNT("register firware,%d,%s\n",a_cmd->fmt,a_cmd->data); + */ + len = a_cmd->data_len > 64 ? 64 : a_cmd->data_len; + if (copy_from_user(name, a_cmd->data, len)) + return -EFAULT; + + name[len] = '\0'; + ret = audiodsp_microcode_register(priv, a_cmd->fmt, name); + break; + case AUDIODSP_UNREGISTER_ALLFIRMWARE: + audiodsp_microcode_free(priv); + break; + case AUDIODSP_GET_CHANNELS_NUM: + /*mask data is not valid */ + put_user(-1, (__s32 __user *) args); + if (priv->frame_format.valid & CHANNEL_VALID) + put_user(priv->frame_format.channel_num, + (__s32 __user *) args); + break; + case AUDIODSP_GET_SAMPLERATE: + /*mask data is not valid */ + put_user(-1, (__s32 __user *) args); + if (priv->frame_format.valid & SAMPLE_RATE_VALID) + put_user(priv->frame_format.sample_rate, + (__s32 __user *) args); + break; + case AUDIODSP_GET_DECODED_NB_FRAMES: + put_user(priv->decoded_nb_frames, (__s32 __user *) args); + break; + case AUDIODSP_GET_BITS_PER_SAMPLE: + /*mask data is not valid */ + put_user(-1, (__s32 __user *) args); + if (priv->frame_format.valid & DATA_WIDTH_VALID) { + put_user(priv->frame_format.data_width, + (__s32 __user *) args); + } + break; + case AUDIODSP_GET_PTS: + /*val=-1 is not valid */ + put_user(dsp_codec_get_current_pts(priv), + (__u32 __user *) args); + break; + case AUDIODSP_LOOKUP_APTS: + { + u32 pts, offset; + + get_user(offset, (__u32 __user *) args); + pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, 300); + put_user(pts, (__u32 __user *) args); + } + break; + case AUDIODSP_GET_FIRST_PTS_FLAG: + if (priv->stream_fmt == MCODEC_FMT_COOK + || priv->stream_fmt == MCODEC_FMT_RAAC) + put_user(1, (__s32 __user *) args); + else + put_user(first_pts_checkin_complete(PTS_TYPE_AUDIO), + (__s32 __user *) args); + break; + + case AUDIODSP_SYNC_AUDIO_START: + + if (get_user(pts, (unsigned long __user *)args)) { + pr_info("Get start pts from user space fault!\n"); + return -EFAULT; + } + tsync_avevent(AUDIO_START, pts); + + break; + + case AUDIODSP_SYNC_AUDIO_PAUSE: + + tsync_avevent(AUDIO_PAUSE, 0); + break; + + case AUDIODSP_SYNC_AUDIO_RESUME: + + tsync_avevent(AUDIO_RESUME, 0); + break; + + case AUDIODSP_SYNC_AUDIO_TSTAMP_DISCONTINUITY: + + if (get_user(pts, (unsigned long __user *)args)) { + pr_info("Get audio discontinuity pts fault!\n"); + return -EFAULT; + } + tsync_avevent(AUDIO_TSTAMP_DISCONTINUITY, pts); + + break; + + case AUDIODSP_SYNC_GET_APTS: + + pts = timestamp_apts_get(); + + if (put_user(pts, (unsigned long __user *)args)) { + pr_info("Put audio pts to user space fault!\n"); + return -EFAULT; + } + + break; + + case AUDIODSP_SYNC_GET_PCRSCR: + + pts = timestamp_pcrscr_get(); + + if (put_user(pts, (unsigned long __user *)args)) { + pr_info("Put pcrscr to user space fault!\n"); + return -EFAULT; + } + + break; + + case AUDIODSP_SYNC_SET_APTS: + + if (get_user(pts, (unsigned long __user *)args)) { + pr_info("Get audio pts from user space fault!\n"); + return -EFAULT; + } + tsync_set_apts(pts); + + break; + + case AUDIODSP_DROP_PCMDATA: + + if (get_user(drop_size, (unsigned long __user *)args)) { + pr_info("Get pcm drop size from user space fault!\n"); + return -EFAULT; + } + audiodsp_drop_pcm(drop_size); + break; + + case AUDIODSP_AUTOMUTE_ON: + tsync_set_automute_on(1); + break; + + case AUDIODSP_AUTOMUTE_OFF: + tsync_set_automute_on(0); + break; + + case AUDIODSP_GET_PCM_LEVEL: + { + int len = dsp_codec_get_bufer_data_len(priv); + + if (put_user(len, (unsigned long __user *)args)) { + pr_info("Put pcm level to user space fault!\n"); + return -EFAULT; + } + break; + } + + case AUDIODSP_SET_PCM_BUF_SIZE: + if ((int)args > 0) + priv->stream_buffer_mem_size = args; + break; + case AUDIODSP_SKIP_BYTES: + DSP_WD(DSP_SKIP_BYTES, (unsigned int)args); + break; + default: + DSP_PRNT("unsupport cmd number%d\n", cmd); + ret = -1; + } + return ret; +} + +static int audiodsp_release(struct inode *node, struct file *file) +{ + DSP_PRNT("dsp_release\n"); +#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + switch_mod_gate_by_type(MOD_VDEC, 0); + switch_mod_gate_by_type(MOD_MEDIA_CPU, 0); +#endif + + audiodsp_allow_sleep(); + return 0; +} + +ssize_t audiodsp_read(struct file *file, char __user *ubuf, size_t size, + loff_t *loff) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + unsigned long rp, orp; + size_t len; + size_t else_len; + size_t wlen; + size_t w_else_len; + int wait = 0; + char __user *pubuf = ubuf; + dma_addr_t buf_map; + +#define MIN_READ 2 /* 1 channel * 2 bytes per sample */ +#define PCM_DATA_MIN 2 +#define PCM_DATA_ALGIN(x) (x & (~(PCM_DATA_MIN-1))) +#define MAX_WAIT (HZ/1000) + + mutex_lock(&priv->stream_buffer_mutex); + if (priv->stream_buffer_mem == NULL || !priv->dsp_is_started) + goto error_out; + + do { + len = dsp_codec_get_bufer_data_len(priv); + if (len > MIN_READ) + break; + + if (wait > 0) + break; + wait++; + init_completion(&priv->decode_completion); + wait_for_completion_timeout(&priv->decode_completion, + MAX_WAIT); + } while (len < MIN_READ); + if (len > priv->stream_buffer_size || len < 0) { + DSP_PRNT("audio stream buffer is bad len=%d\n", len); + goto error_out; + } + len = min(len, size); + len = PCM_DATA_ALGIN(len); + else_len = len; + rp = dsp_codec_get_rd_addr(priv); + orp = rp; + while (else_len > 0) { + + wlen = priv->stream_buffer_end - rp; + wlen = min(wlen, else_len); + /* dma_cache_inv((unsigned long)rp,wlen); */ + buf_map = + dma_map_single(NULL, (void *)rp, wlen, DMA_FROM_DEVICE); + w_else_len = + copy_to_user((void *)pubuf, (const char *)(rp), wlen); + if (w_else_len != 0) { + DSP_PRNT("copyed error,%d,%d,[%p]<---[%lx]\n", + w_else_len, wlen, pubuf, rp); + wlen -= w_else_len; + } + dma_unmap_single(NULL, buf_map, wlen, DMA_FROM_DEVICE); + else_len -= wlen; + pubuf += wlen; + rp = dsp_codec_inc_rd_addr(priv, wlen); + } + priv->out_len_after_last_valid_pts += len; + mutex_unlock(&priv->stream_buffer_mutex); + /* u32 timestamp_pcrscr_get(void); */ + /* + *pr_info("current pts=%ld,src=%ld\n", + *dsp_codec_get_current_pts(priv),timestamp_pcrscr_get()); + */ + return len; + error_out: + mutex_unlock(&priv->stream_buffer_mutex); + pr_info("audiodsp_read failed\n"); + return -EINVAL; +} + +ssize_t audiodsp_write(struct file *file, const char __user *ubuf, + size_t size, loff_t *loff) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + /* int dsp_codec_start( struct audiodsp_priv *priv); */ + /* int dsp_codec_stop( struct audiodsp_priv *priv); */ + audiodsp_microcode_register(priv, + MCODEC_FMT_COOK, "audiodsp_codec_cook.bin"); + priv->stream_fmt = MCODEC_FMT_COOK; + audiodsp_start(); + dsp_codec_start(priv); + /* dsp_codec_stop(priv); */ + + return size; +} + +static const struct file_operations audiodsp_fops = { + .owner = THIS_MODULE, + .open = audiodsp_open, + .read = audiodsp_read, + .write = audiodsp_write, + .release = audiodsp_release, + .unlocked_ioctl = audiodsp_ioctl, +}; + +static int audiodsp_get_status(struct adec_status *astatus) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + + if (!astatus) + return -EINVAL; + if (priv->frame_format.valid & CHANNEL_VALID) + astatus->channels = priv->frame_format.channel_num; + else + astatus->channels = 0; + if (priv->frame_format.valid & SAMPLE_RATE_VALID) + astatus->sample_rate = priv->frame_format.sample_rate; + else + astatus->sample_rate = 0; + if (priv->frame_format.valid & DATA_WIDTH_VALID) + astatus->resolution = priv->frame_format.data_width; + else + astatus->resolution = 0; + astatus->error_count = priv->decode_error_count; + astatus->status = priv->dsp_is_started ? 0 : 1; + return 0; +} + +static int audiodsp_init_mcode(struct audiodsp_priv *priv) +{ + spin_lock_init(&priv->mcode_lock); + priv->mcode_id = 0; + priv->dsp_stack_start = 0; + priv->dsp_gstack_start = 0; + priv->dsp_heap_start = 0; + priv->code_mem_size = AUDIO_DSP_MEM_SIZE - REG_MEM_SIZE; + priv->dsp_code_start = AUDIO_DSP_START_ADDR; + DSP_PRNT("DSP start addr 0x%x\n", AUDIO_DSP_START_ADDR); + priv->dsp_stack_size = 1024 * 64; + priv->dsp_gstack_size = 512; + priv->dsp_heap_size = 1024 * 1024; + priv->stream_buffer_mem = NULL; + priv->stream_buffer_mem_size = 32 * 1024; + priv->stream_fmt = -1; + INIT_LIST_HEAD(&priv->mcode_list); + init_completion(&priv->decode_completion); + mutex_init(&priv->stream_buffer_mutex); + mutex_init(&priv->dsp_mutex); + priv->last_stream_fmt = -1; + priv->last_valid_pts = 0; + priv->out_len_after_last_valid_pts = 0; + priv->dsp_work_details = (struct dsp_working_info *)DSP_WORK_INFO; + return 0; +} +#endif +static ssize_t codec_fmt_show(struct class *cla, struct class_attribute *attr, + char *buf) +{ + size_t ret = 0; + struct audiodsp_priv *priv = audiodsp_privdata(); + + ret = sprintf(buf, "The codec Format %d\n", priv->stream_fmt); + return ret; +} + +#if 0 +static ssize_t codec_mips_show(struct class *cla, struct class_attribute *attr, + char *buf) +{ + size_t ret = 0; + struct audiodsp_priv *priv = audiodsp_privdata(); + + if (priv->stream_fmt < sizeof(audiodsp_mips)) { + ret = + sprintf(buf, "%d\n", + audiodsp_mips[__builtin_ffs(priv->stream_fmt)]); + } else { + ret = sprintf(buf, "%d\n", 200000); + } + return ret; +} +#endif +static const struct file_operations audiodsp_fops = { +.owner = THIS_MODULE, +.open = NULL, +.read = NULL, +.write = NULL, +.release = NULL, +.unlocked_ioctl = NULL, +}; + +static ssize_t codec_fatal_err_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + + return sprintf(buf, "%d\n", priv->decode_fatal_err); +} + +static ssize_t codec_fatal_err_store(struct class *cla, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct audiodsp_priv *priv = audiodsp_privdata(); + + if (buf[0] == '0') + priv->decode_fatal_err = 0; + else if (buf[0] == '1') + priv->decode_fatal_err = 1; + else if (buf[0] == '2') + priv->decode_fatal_err = 2; + + pr_info("codec_fatal_err value:%d\n ", priv->decode_fatal_err); + return count; +} + +static ssize_t digital_raw_show(struct class *cla, struct class_attribute *attr, + char *buf) +{ + char *pbuf = buf; + + pbuf += sprintf(pbuf, "%d\n", IEC958_mode_raw); + return pbuf - buf; +} + +static ssize_t digital_raw_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t count) +{ + pr_info("buf=%s\n", buf); + if (buf[0] == '0') + IEC958_mode_raw = 0; /* PCM */ + else if (buf[0] == '1') + IEC958_mode_raw = 1; /* RAW without over clock */ + else if (buf[0] == '2') + IEC958_mode_raw = 2; /* RAW with over clock */ + + pr_info("IEC958_mode_raw=%d\n", IEC958_mode_raw); + return count; +} + +#define SUPPORT_TYPE_NUM 10 +static unsigned char *codec_str[SUPPORT_TYPE_NUM] = { + "2 CH PCM", "DTS RAW Mode", "Dolby Digital", "DTS", + "DD+", "DTS-HD", "8 CH PCM", "TrueHD", "DTS-HD MA", + "HIGH_SR_Stereo_PCM" +}; + +static ssize_t digital_codec_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + + pbuf += sprintf(pbuf, "%d\n", IEC958_mode_codec); + return pbuf - buf; +} + +static ssize_t digital_codec_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + unsigned int digital_codec = 0; + unsigned int mode_codec = IEC958_mode_codec; + + if (buf) { + if (kstrtoint(buf, 10, &digital_codec)) + pr_info("kstrtoint err %s\n", __func__); + if (digital_codec < SUPPORT_TYPE_NUM) { + IEC958_mode_codec = digital_codec; + pr_info("IEC958_mode_codec= %d, IEC958 type %s\n", + digital_codec, codec_str[digital_codec]); + } else { + pr_info("IEC958 type set exceed supported range\n"); + } + } + /* + * when raw output switch to pcm output, + * need trigger pcm hw prepare to re-init hw configuration + */ + pr_info("last mode %d,now %d\n", mode_codec, IEC958_mode_codec); + + return count; +} + +/* + * code to force enable none-samesource, + * to trigger alsa reset audio hw,only once available + */ +static ssize_t audio_samesource_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + if (kstrtoint(buf, 16, (unsigned int *)&audio_samesource)) { + pr_info("audio_samesource_store failed\n"); + return -EINVAL; + } + pr_info("same source set to %d\n", audio_samesource); + return count; + +} + +static ssize_t audio_samesource_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + int samesource = 0; + int i2s_enable = !!(aml_read_cbus(AIU_MEM_I2S_CONTROL) & (3 << 1)); + int iec958_enable = + !!(aml_read_cbus(AIU_MEM_IEC958_CONTROL) & (3 << 1)); + samesource = + (aml_read_cbus(AIU_MEM_IEC958_START_PTR) == + aml_read_cbus(AIU_MEM_I2S_START_PTR)); + + /* make sure i2s/958 same source.and both enabled */ + if (samesource == 0) { + if ((i2s_enable && iec958_enable) || !audio_samesource) { + samesource = 2; + if (audio_samesource == 0) + audio_samesource = 1; + } + } + pbuf += sprintf(pbuf, "%d\n", samesource); + return pbuf - buf; +} + +static ssize_t print_flag_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + static const char * const dec_format[] = { + "0 - disable arc dsp print", + "1 - enable arc dsp print", + }; + char *pbuf = buf; + + pbuf += + sprintf(pbuf, "audiodsp decode option: %s\n", + dec_format[(decopt & 0x5) >> 2]); + return pbuf - buf; +} + +static ssize_t print_flag_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t count) +{ + unsigned int dec_opt = 0x1; + + pr_info("buf=%s\n", buf); + if (buf[0] == '0') + dec_opt = 0; /* disable print flag */ + else if (buf[0] == '1') + dec_opt = 1; /* enable print flag */ + + decopt = (decopt & (~4)) | (dec_opt << 2); + pr_info("dec option=%d, decopt = %x\n", dec_opt, decopt); + return count; +} + +static ssize_t dec_option_show(struct class *cla, struct class_attribute *attr, + char *buf) +{ + static const char * const dec_format[] = { + "0 - mute dts and ac3 ", + "1 - mute dts.ac3 with noise ", + "2 - mute ac3.dts with noise", + "3 - both ac3 and dts with noise", + }; + char *pbuf = buf; + + pbuf += + sprintf(pbuf, "audiodsp decode option: %s\n", + dec_format[decopt & 0x3]); + return pbuf - buf; +} + +static ssize_t dec_option_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t count) +{ + unsigned int dec_opt = 0x3; + + pr_info("buf=%s\n", buf); + if (buf[0] == '0') { + dec_opt = 0; /* mute ac3/dts */ + } else if (buf[0] == '1') { + dec_opt = 1; /* mute dts */ + } else if (buf[0] == '2') { + dec_opt = 2; /* mute ac3 */ + } else if (buf[0] == '3') { + dec_opt = 3; /* with noise */ + } else if (buf[0] == '4') { + pr_info("digital mode :PCM-raw\n"); + decopt |= (1 << 5); + } else if (buf[0] == '5') { + pr_info("digital mode :raw\n"); + decopt &= ~(1 << 5); + } + decopt = (decopt & (~3)) | dec_opt; + pr_info("dec option=%d\n", dec_opt); + return count; +} + +static ssize_t ac3_drc_control_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + static const char * const drcmode[] = { + "CUSTOM_0", "CUSTOM_1", "LINE", "RF" }; + char *pbuf = buf; +#if 0 + pbuf += + sprintf(pbuf, "\tdd+ drc mode : %s\n", + drcmode[ac3_drc_control & 0x3]); + pbuf += + sprintf(pbuf, "\tdd+ drc high cut scale : %d%%\n", + (ac3_drc_control >> DRC_HIGH_CUT_BIT) & 0xff); + pbuf += + sprintf(pbuf, "\tdd+ drc low boost scale : %d%%\n", + (ac3_drc_control >> DRC_LOW_BST_BIT) & 0xff); +#else + pr_info("\tdd+ drc mode : %s\n", drcmode[ac3_drc_control & 0x3]); + pr_info("\tdd+ drc high cut scale : %d%%\n", + (ac3_drc_control >> DRC_HIGH_CUT_BIT) & 0xff); + pr_info("\tdd+ drc low boost scale : %d%%\n", + (ac3_drc_control >> DRC_LOW_BST_BIT) & 0xff); + pbuf += sprintf(pbuf, "%d\n", ac3_drc_control); +#endif + return pbuf - buf; +} + +static ssize_t ac3_drc_control_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + char tmpbuf[128]; + static const char * const drcmode[] = { + "CUSTOM_0", "CUSTOM_1", "LINE", "RF" }; + int i = 0; + unsigned int val; + + while ((buf[i]) && (buf[i] != ',') && (buf[i] != ' ')) { + tmpbuf[i] = buf[i]; + i++; + } + tmpbuf[i] = 0; + if (strncmp(tmpbuf, "drcmode", 7) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0x3; + pr_info("drc mode set to %s\n", drcmode[val]); + ac3_drc_control = (ac3_drc_control & (~3)) | val; + } else if (strncmp(tmpbuf, "drchighcutscale", 15) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0xff; + pr_info("drc high cut scale set to %d%%\n", val); + ac3_drc_control = + (ac3_drc_control & (~(0xff << DRC_HIGH_CUT_BIT))) | + (val << DRC_HIGH_CUT_BIT); + } else if (strncmp(tmpbuf, "drclowboostscale", 16) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0xff; + pr_info("drc low boost scale set to %d%%\n", val); + ac3_drc_control = + (ac3_drc_control & (~(0xff << DRC_LOW_BST_BIT))) | + (val << DRC_LOW_BST_BIT); + } else + pr_info("invalid args\n"); + return count; +} + +static ssize_t dts_dec_control_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + /* char *dmxmode[] = {"Lo/Ro","Lt/Rt"}; */ + /* char *dialnorm[] = {"disable","enable"}; */ + char *pbuf = buf; + + pbuf += sprintf(pbuf, "%d\n", dts_dec_control); + return pbuf - buf; +} + +static ssize_t dts_dec_control_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + char tmpbuf[128]; + static const char * const dmxmode[] = { "Lo/Ro", "Lt/Rt" }; + static const char * const dialnorm[] = { "disable", "enable" }; + int i = 0; + unsigned int val; + + while ((buf[i]) && (buf[i] != ',') && (buf[i] != ' ')) { + tmpbuf[i] = buf[i]; + i++; + } + tmpbuf[i] = 0; + if (strncmp(tmpbuf, "dtsdmxmode", 10) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0x1; + pr_info("dts dmx mode set to %s\n", dmxmode[val]); + dts_dec_control = (dts_dec_control & (~1)) | val; + } else if (strncmp(tmpbuf, "dtsdrcscale", 11) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0xff; + pr_info("dts drc scale set to %d\n", val); + dts_dec_control = + (dts_dec_control & (~(0xff << DTS_DRC_SCALE_BIT))) | + (val << DTS_DRC_SCALE_BIT); + } else if (strncmp(tmpbuf, "dtsdialnorm", 11) == 0) { + if (kstrtoint(buf + i + 1, 16, &val)) + pr_info("kstrtoint err %s\n", __func__); + val = val & 0x1; + pr_info("dts dial norm : set to %s\n", dialnorm[val]); + dts_dec_control = + (dts_dec_control & (~(0x1 << DTS_DIAL_NORM_BIT))) | + (val << DTS_DIAL_NORM_BIT); + } else { + if (kstrtoint(tmpbuf, 16, &dts_dec_control)) + pr_info("kstrtoint err %s\n", __func__); + pr_info("dts_dec_control/0x%x\n", dts_dec_control); + } + return count; + +} + +static ssize_t dsp_debug_show(struct class *cla, struct class_attribute *attr, + char *buf) +{ + char *pbuf = buf; + + pbuf += + sprintf(pbuf, "DSP Debug Flag: %s\n", + (dsp_debug_flag == 0) ? "0 - OFF" : "1 - ON"); + return pbuf - buf; +} + +static ssize_t dsp_debug_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t count) +{ + if (buf[0] == '0') + dsp_debug_flag = 0; + else if (buf[0] == '1') + dsp_debug_flag = 1; + + pr_info("DSP Debug Flag: %d\n", dsp_debug_flag); + return count; +} +#if 0 +static ssize_t skip_rawbytes_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + unsigned long bytes = DSP_RD(DSP_SKIP_BYTES); + + return sprintf(buf, "%ld\n", bytes); +} + +static ssize_t skip_rawbytes_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + unsigned int bytes = 0; + + if (kstrtoint(buf, 16, &bytes)) + return count; + + DSP_WD(DSP_SKIP_BYTES, bytes); + pr_info("audio stream SKIP when ablevel>0x%x\n", bytes); + return count; +} +#endif +#if 0 +static ssize_t pcm_left_len_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + + struct audiodsp_priv *priv = audiodsp_privdata(); + int len = dsp_codec_get_bufer_data_len(priv); + + return sprintf(buf, "%d\n", len); +} +#endif +static struct class_attribute audiodsp_attrs[] = { + __ATTR_RO(codec_fmt), +#ifdef CONFIG_ARCH_MESON1 + __ATTR_RO(codec_mips), +#endif + __ATTR(codec_fatal_err, 0664, + codec_fatal_err_show, codec_fatal_err_store), + /* __ATTR_RO(swap_buf_ptr), */ + /* __ATTR_RO(dsp_working_status), */ + __ATTR(digital_raw, 0664, digital_raw_show, + digital_raw_store), + __ATTR(digital_codec, 0664, digital_codec_show, + digital_codec_store), + __ATTR(dec_option, 0644, dec_option_show, + dec_option_store), + __ATTR(print_flag, 0644, print_flag_show, + print_flag_store), + __ATTR(ac3_drc_control, 0664, + ac3_drc_control_show, ac3_drc_control_store), + __ATTR(dsp_debug, 0644, dsp_debug_show, dsp_debug_store), + __ATTR(dts_dec_control, 0644, dts_dec_control_show, + dts_dec_control_store), + /* __ATTR(skip_rawbytes, 0644, skip_rawbytes_show, */ + /* skip_rawbytes_store), */ + /* __ATTR_RO(pcm_left_len), */ + __ATTR(audio_samesource, 0644, audio_samesource_show, + audio_samesource_store), + __ATTR_NULL +}; +#if 0 +#ifdef CONFIG_PM +static int audiodsp_suspend(struct device *dev, pm_message_t state) +{ + /* struct audiodsp_priv *priv = */ audiodsp_privdata(); +#if 0 + if (wake_lock_active(&priv->wakelock)) + return -1; /* please stop dsp first */ +#endif + pm_state.event = state.event; + if (state.event == PM_EVENT_SUSPEND) { + /* should sleep cpu2 here after RevC chip */ + /* msleep(50); */ + } + pr_info("audiodsp suspend\n"); + return 0; +} + +static int audiodsp_resume(struct device *dev) +{ + if (pm_state.event == PM_EVENT_SUSPEND) + /* wakeup cpu2 */ + pm_state.event = -1; + + pr_info("audiodsp resumed\n"); + return 0; +} +#endif +#endif +static struct class audiodsp_class = { + .name = DSP_DRIVER_NAME, + .class_attrs = audiodsp_attrs, +#if 0 + .suspend = audiodsp_suspend, + .resume = audiodsp_resume, +#else + .suspend = NULL, + .resume = NULL, +#endif +}; + +int audiodsp_probe(void) +{ + int res = 0; + struct audiodsp_priv *priv; + + dsp_debug_flag = 1; + priv = kmalloc(sizeof(struct audiodsp_priv), GFP_KERNEL); + if (priv == NULL) { + DSP_PRNT("Out of memory for audiodsp register\n"); + return -1; + } + memset(priv, 0, sizeof(struct audiodsp_priv)); + priv->dsp_is_started = 0; + /* + * priv->p = ioremap_nocache(AUDIO_DSP_START_PHY_ADDR, S_1M); + * if(priv->p) + * DSP_PRNT("DSP IOREMAP to addr 0x%x\n",(unsigned)priv->p); + * else{ + * DSP_PRNT("DSP IOREMAP error\n"); + * goto error1; + * } + */ + audiodsp_p = priv; +#if 0 + audiodsp_init_mcode(priv); +#endif + if (priv->dsp_heap_size) { + 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("no memory for audio dsp dsp_set_heap\n"); + kfree(priv); + return -ENOMEM; + } + } + res = register_chrdev(AUDIODSP_MAJOR, DSP_NAME, &audiodsp_fops); + if (res < 0) { + DSP_PRNT("Can't register char devie for " DSP_NAME "\n"); + goto error1; + } else + DSP_PRNT("register " DSP_NAME " to char divece(%d)\n", + AUDIODSP_MAJOR); + + res = class_register(&audiodsp_class); + if (res < 0) { + DSP_PRNT("Create audiodsp class failed\n"); + res = -EEXIST; + goto error2; + } + + priv->class = &audiodsp_class; + priv->dev = device_create(priv->class, + NULL, MKDEV(AUDIODSP_MAJOR, 0), + NULL, "audiodsp0"); + if (priv->dev == NULL) { + res = -EEXIST; + goto error3; + } +#if 0 + audiodsp_init_mailbox(priv); + init_audiodsp_monitor(priv); +#endif +#if 0 /* tmp_mask_for_kernel_4_4 */ + wake_lock_init(&priv->wakelock, WAKE_LOCK_SUSPEND, "audiodsp"); +#endif +#ifdef CONFIG_AM_STREAMING + /* set_adec_func(audiodsp_get_status); */ +#endif + /*memset((void *)DSP_REG_OFFSET, 0, REG_MEM_SIZE); */ +#if 0 + dsp_get_debug_interface(0); +#endif + + return res; + + device_destroy(priv->class, MKDEV(AUDIODSP_MAJOR, 0)); + error3: + class_destroy(priv->class); + error2: + unregister_chrdev(AUDIODSP_MAJOR, DSP_NAME); + error1: + kfree(priv); + return res; +} + +struct audiodsp_priv *audiodsp_privdata(void) +{ + return audiodsp_p; +} + +static int __init audiodsp_init_module(void) +{ + + return audiodsp_probe(); +} + +static void __exit audiodsp_exit_module(void) +{ + struct audiodsp_priv *priv; + + priv = audiodsp_privdata(); +#ifdef CONFIG_AM_STREAMING + set_adec_func(NULL); +#endif +#if 0 + dsp_stop(priv); + stop_audiodsp_monitor(priv); + audiodsp_release_mailbox(priv); + release_audiodsp_monitor(priv); + audiodsp_microcode_free(priv); +#endif + /* + * iounmap(priv->p); + */ + device_destroy(priv->class, MKDEV(AUDIODSP_MAJOR, 0)); + class_destroy(priv->class); + unregister_chrdev(AUDIODSP_MAJOR, DSP_NAME); + kfree(priv); + priv = NULL; +} + +module_init(audiodsp_init_module); +module_exit(audiodsp_exit_module); diff --git a/drivers/amlogic/audiodsp/audiodsp_module.h b/drivers/amlogic/audiodsp/audiodsp_module.h new file mode 100644 index 000000000000..3e6c995b62f2 --- /dev/null +++ b/drivers/amlogic/audiodsp/audiodsp_module.h @@ -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 +#include +#if 0 /* tmp_mask_for_kernel_4_4 */ +#include +#endif + +/* + * #include + * #include + */ +#include "audiodsp_control.h" +#include + +#include "codec_message.h" +#include +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 /* */ diff --git a/drivers/amlogic/audiodsp/codec_message.h b/drivers/amlogic/audiodsp/codec_message.h new file mode 100644 index 000000000000..48d482f69aa5 --- /dev/null +++ b/drivers/amlogic/audiodsp/codec_message.h @@ -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 /* */ diff --git a/drivers/amlogic/audiodsp/dsp_codec.c b/drivers/amlogic/audiodsp/dsp_codec.c new file mode 100644 index 000000000000..2b4d75231f2a --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_codec.c @@ -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 +#include +#include +#include +#include + +#include +#include +/* #include */ + +#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; +} diff --git a/drivers/amlogic/audiodsp/dsp_codec.h b/drivers/amlogic/audiodsp/dsp_codec.h new file mode 100644 index 000000000000..704fae600f60 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_codec.h @@ -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 /* */ diff --git a/drivers/amlogic/audiodsp/dsp_control.c b/drivers/amlogic/audiodsp/dsp_control.c new file mode 100644 index 000000000000..bff8056bb6d3 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_control.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include "dsp_microcode.h" +#include "audiodsp_module.h" +#include "dsp_control.h" +#include "dsp_codec.h" + +/* #include */ +#include +#include +#include + +#include "dsp_mailbox.h" +#include +#include + +#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); diff --git a/drivers/amlogic/audiodsp/dsp_control.h b/drivers/amlogic/audiodsp/dsp_control.h new file mode 100644 index 000000000000..2994034e4de6 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_control.h @@ -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 +#include "audiodsp_module.h" +#include "dsp_microcode.h" +/*#include */ +#include + +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 /* */ diff --git a/drivers/amlogic/audiodsp/dsp_mailbox.c b/drivers/amlogic/audiodsp/dsp_mailbox.c new file mode 100644 index 000000000000..0c69a9447dee --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_mailbox.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#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(" 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); diff --git a/drivers/amlogic/audiodsp/dsp_mailbox.h b/drivers/amlogic/audiodsp/dsp_mailbox.h new file mode 100644 index 000000000000..a2f5ba32c7ed --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_mailbox.h @@ -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 /* */ diff --git a/drivers/amlogic/audiodsp/dsp_microcode.c b/drivers/amlogic/audiodsp/dsp_microcode.c new file mode 100644 index 000000000000..69aa619f39ab --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_microcode.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "audiodsp_control.h" +#include +#include +#include + +#include "audiodsp_module.h" +#include "dsp_control.h" +#include "dsp_microcode.h" +#include + +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; +} diff --git a/drivers/amlogic/audiodsp/dsp_microcode.h b/drivers/amlogic/audiodsp/dsp_microcode.h new file mode 100644 index 000000000000..68b200ae45a6 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_microcode.h @@ -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 /* */ diff --git a/drivers/amlogic/audiodsp/dsp_monitor.c b/drivers/amlogic/audiodsp/dsp_monitor.c new file mode 100644 index 000000000000..38665e136c24 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_monitor.c @@ -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 +#include +#include +#include +#include + +#include "dsp_microcode.h" +#include "audiodsp_module.h" +#include "dsp_control.h" +/* #include */ + +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); +} diff --git a/drivers/amlogic/audiodsp/dsp_monitor.h b/drivers/amlogic/audiodsp/dsp_monitor.h new file mode 100644 index 000000000000..9ce3d0134e63 --- /dev/null +++ b/drivers/amlogic/audiodsp/dsp_monitor.h @@ -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 /* */ diff --git a/drivers/amlogic/audiodsp/pcmenc_module.c b/drivers/amlogic/audiodsp/pcmenc_module.c new file mode 100644 index 000000000000..419597886b0a --- /dev/null +++ b/drivers/amlogic/audiodsp/pcmenc_module.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include "pcmenc_stream.h" +#include + +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 "); diff --git a/drivers/amlogic/audiodsp/pcmenc_stream.c b/drivers/amlogic/audiodsp/pcmenc_stream.c new file mode 100644 index 000000000000..6d6765f6445d --- /dev/null +++ b/drivers/amlogic/audiodsp/pcmenc_stream.c @@ -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 +#include +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; +} diff --git a/drivers/amlogic/audiodsp/pcmenc_stream.h b/drivers/amlogic/audiodsp/pcmenc_stream.h new file mode 100644 index 000000000000..30dcd49056e6 --- /dev/null +++ b/drivers/amlogic/audiodsp/pcmenc_stream.h @@ -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 +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 /* */ diff --git a/drivers/amlogic/audiodsp/spdif_module.c b/drivers/amlogic/audiodsp/spdif_module.c new file mode 100644 index 000000000000..34d7e66d7291 --- /dev/null +++ b/drivers/amlogic/audiodsp/spdif_module.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); diff --git a/drivers/amlogic/audiodsp/spdif_module.h b/drivers/amlogic/audiodsp/spdif_module.h new file mode 100644 index 000000000000..9d42481a0d2d --- /dev/null +++ b/drivers/amlogic/audiodsp/spdif_module.h @@ -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 /* */ diff --git a/sound/soc/codecs/amlogic/aml_codec_t9015.c b/sound/soc/codecs/amlogic/aml_codec_t9015.c index c43ca2f3869a..7cd19bf2bce4 100644 --- a/sound/soc/codecs/amlogic/aml_codec_t9015.c +++ b/sound/soc/codecs/amlogic/aml_codec_t9015.c @@ -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;