audio: add audio dsp

PD#138714: add audio dsp driver

Change-Id: I289771eed7b5ec4ce6c8f657f32593461cc61770
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
This commit is contained in:
Xing Wang
2017-03-29 12:24:50 +08:00
committed by Jianxin Pan
parent 474815c2ce
commit 6780402686
28 changed files with 4532 additions and 0 deletions

View File

@@ -13712,6 +13712,7 @@ F: sound/soc/amlogic/*
F: sound/soc/codecs/Kconfig
F: sound/soc/codecs/Makefile
F: sound/soc/codecs/amlogic/*
F: drivers/amlogic/audiodsp/*
AMLOGIC PPMGR DRIVER
M: Guosong Zhou <guosong.zhou@amlogic.com>

View File

@@ -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>;

View File

@@ -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>;

View File

@@ -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

View File

@@ -67,5 +67,6 @@ source "drivers/amlogic/key_manage/Kconfig"
source "drivers/amlogic/thermal/Kconfig"
source "drivers/amlogic/audiodsp/Kconfig"
endmenu
endif

View File

@@ -63,3 +63,4 @@ obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/
obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/
obj-$(CONFIG_AMLOGIC_AUDIO_DSP) += audiodsp/

View File

@@ -0,0 +1,11 @@
# audio dsp configuration
#
menu "AMLOGIC Audio DSP process"
config AMLOGIC_AUDIO_DSP
tristate "audio dsp control support"
default n
help
support the amlogic audio dsp;
endmenu

View File

@@ -0,0 +1,20 @@
# Makefile for osd and tv
audiodsp-objs = audiodsp_module.o
# dsp_control.o \
# dsp_microcode.o \
# dsp_mailbox.o \
# dsp_monitor.o \
# dsp_codec.o
#audiodsp-objs += pcmenc_module.o pcmenc_stream.o
#audiodsp-objs += spdif_module.o
ifneq ($(KBUILD_SRC),)
TOP_KBUILD_SRC := $(KBUILD_SRC)/
endif
obj-$(CONFIG_AMLOGIC_AUDIO_DSP) +=audiodsp.o
#EXTRA_CFLAGS = -DENABLE_WAIT_FORMAT

View File

@@ -0,0 +1,86 @@
/*
* drivers/amlogic/audiodsp/audiodsp_control.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef ENABLE_WAIT_FORMAT
#define ENABLE_WAIT_FORMAT
#endif
#ifndef AUDIODSP_CONTROL_H
#define AUDIODSP_CONTROL_H
struct audiodsp_cmd {
int cmd;
int fmt;
int data_len;
char *data;
};
#define AUDIODSP_SYNC_AUDIO_PAUSE _IO('a', 0x01)
#define AUDIODSP_SYNC_AUDIO_RESUME _IO('a', 0x02)
#define AUDIODSP_SET_FMT _IOW('a', 1, long)
#define AUDIODSP_START _IOW('a', 2, long)
#define AUDIODSP_STOP _IOW('a', 3, long)
#define AUDIODSP_DECODE_START _IOW('a', 4, long)
#define AUDIODSP_DECODE_STOP _IOW('a', 5, long)
#define AUDIODSP_REGISTER_FIRMWARE _IOW('a', 6, long)
#define AUDIODSP_UNREGISTER_ALLFIRMWARE _IOW('a', 7, long)
#define AUDIODSP_SYNC_AUDIO_START _IOW('a', 8, unsigned long)
#define AUDIODSP_SYNC_AUDIO_TSTAMP_DISCONTINUITY \
_IOW('a', 9, unsigned long)
#define AUDIODSP_SYNC_SET_APTS _IOW('a', 10, unsigned long)
#ifdef ENABLE_WAIT_FORMAT
#define AUDIODSP_WAIT_FORMAT _IOW('a', 11, long)
#endif
#define AUDIODSP_DROP_PCMDATA _IOW('a', 12, unsigned long)
#define AUDIODSP_SKIP_BYTES _IOW('a', 13, unsigned long)
#define AUDIODSP_GET_CHANNELS_NUM _IOR('r', 1, long)
#define AUDIODSP_GET_SAMPLERATE _IOR('r', 2, long)
#define AUDIODSP_GET_BITS_PER_SAMPLE _IOR('r', 3, long)
#define AUDIODSP_GET_PTS _IOR('r', 4, long)
#define AUDIODSP_GET_DECODED_NB_FRAMES _IOR('r', 5, long)
#define AUDIODSP_GET_FIRST_PTS_FLAG _IOR('r', 6, long)
#define AUDIODSP_SYNC_GET_APTS _IOR('r', 7, unsigned long)
#define AUDIODSP_SYNC_GET_PCRSCR _IOR('r', 8, unsigned long)
#define AUDIODSP_AUTOMUTE_ON _IOW('r', 9, unsigned long)
#define AUDIODSP_AUTOMUTE_OFF _IOW('r', 10, unsigned long)
#define AUDIODSP_LOOKUP_APTS _IOR('r', 11, unsigned long)
#define AUDIODSP_GET_PCM_LEVEL _IOR('r', 12, unsigned long)
#define AUDIODSP_SET_PCM_BUF_SIZE _IOW('r', 13, long)
#define MCODEC_FMT_MPEG123 (1<<0)
#define MCODEC_FMT_AAC (1<<1)
#define MCODEC_FMT_AC3 (1<<2)
#define MCODEC_FMT_DTS (1<<3)
#define MCODEC_FMT_FLAC (1<<4)
#define MCODEC_FMT_COOK (1<<5)
#define MCODEC_FMT_AMR (1<<6)
#define MCODEC_FMT_RAAC (1<<7)
#define MCODEC_FMT_ADPCM (1<<8)
#define MCODEC_FMT_WMA (1<<9)
#define MCODEC_FMT_PCM (1<<10)
#define MCODEC_FMT_WMAPRO (1<<11)
#define MCODEC_FMT_ALAC (1<<12)
#define MCODEC_FMT_AAC_LATM (1<<14)
#define MCODEC_FMT_APE (1<<15)
#define MCODEC_FMT_EAC3 (1<<16)
#define MCODEC_FMT_NULL (1<<17)
#define AUDIOINFO_FROM_AUDIODSP(format) ((format == MCODEC_FMT_AAC) || \
(format == MCODEC_FMT_AAC_LATM))
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
/*
* drivers/amlogic/audiodsp/audiodsp_module.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef AUDIO_DSP_MODULES_H
#define AUDIO_DSP_MODULES_H
#include <linux/device.h>
#include <linux/timer.h>
#if 0 /* tmp_mask_for_kernel_4_4 */
#include <linux/wakelock.h>
#endif
/*
* #include <asm/dsp/audiodsp_control.h>
* #include <asm/dsp/dsp_register.h>
*/
#include "audiodsp_control.h"
#include <linux/amlogic/media/sound/dsp_register.h>
#include "codec_message.h"
#include <linux/dma-mapping.h>
struct audiodsp_priv {
struct class *class;
struct device *dev;
struct device *micro_dev;
struct timer_list dsp_mointer;
struct list_head mcode_list;
int mcode_id;
spinlock_t mcode_lock;
int code_mem_size;
struct mail_msg *mailbox_reg;
struct mail_msg *mailbox_reg2;
struct dsp_working_info *dsp_work_details;
unsigned long dsp_code_start;
unsigned long dsp_code_size;
void *dsp_stack_start;
unsigned long dsp_stack_size;
void *dsp_gstack_start;
unsigned long dsp_gstack_size;
unsigned long dsp_heap_start;
unsigned long dsp_heap_size;
struct mutex dsp_mutex;
char dsp_codename[32];
unsigned long dsp_start_time; /* system jiffies */
unsigned long dsp_end_time; /* system jiffies */
int stream_fmt;
int last_stream_fmt;
struct frame_fmt frame_format;
struct frame_info cur_frame_info;
unsigned int last_valid_pts;
int out_len_after_last_valid_pts;
int decode_error_count;
int decode_fatal_err;
int dsp_is_started;
void *stream_buffer_mem;
int stream_buffer_mem_size;
int decoded_nb_frames;
int first_lookup_over;
int format_wait_count;
unsigned long stream_buffer_start;
unsigned long stream_buffer_end;
unsigned long stream_buffer_size;
struct mutex stream_buffer_mutex;
struct completion decode_completion;
void __iomem *p;
/* for power management */
#if 0 /* tmp_mask_for_kernel_4_4 */
struct wake_lock wakelock;
#endif
unsigned int dsp_abnormal_count;
unsigned int last_ablevel;
unsigned int last_pcmlevel;
};
struct audiodsp_priv *audiodsp_privdata(void);
#define DSP_PRNT(fmt, args...) pr_info("[dsp]" fmt, ##args)
extern void tsync_pcr_recover(void);
extern void tsync_pcr_recover(void);
extern unsigned int IEC958_mode_raw;
extern unsigned int IEC958_mode_codec;
/*extern int decopt; */
extern struct audio_info *get_audio_info(void);
extern void aml_alsa_hw_reprepare(void);
extern void dsp_get_debug_interface(int flag);
/* extern void audiodsp_moniter(unsigned long); */
#endif /* */

View File

@@ -0,0 +1,93 @@
/*
* drivers/amlogic/audiodsp/codec_message.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef _CODEC_MESSAGE_HEADERS
#define _CODEC_MESSAGE_HEADERS
#define SUB_FMT_VALID (1<<1)
#define CHANNEL_VALID (1<<2)
#define SAMPLE_RATE_VALID (1<<3)
#define DATA_WIDTH_VALID (1<<4)
struct digit_raw_output_info {
int framelength;
unsigned char *framebuf;
int frame_size;
int frame_samples;
unsigned char *rawptr;
/* for AC3 */
int sampleratecode;
int bsmod;
int bpf;
int brst;
int length;
int padsize;
int mode;
unsigned int syncword1;
unsigned int syncword2;
unsigned int syncword3;
unsigned int syncword1_mask;
unsigned int syncword2_mask;
unsigned int syncword3_mask;
unsigned int chstat0_l;
unsigned int chstat0_r;
unsigned int chstat1_l;
unsigned int chstat1_r;
unsigned int can_bypass;
};
struct frame_fmt {
int valid;
int sub_fmt;
int channel_num;
int sample_rate;
int data_width;
int buffered_len; /*dsp codec,buffered origan data len */
int format;
unsigned int total_byte_parsed;
union {
unsigned int total_sample_decoded;
void *pcm_encoded_info; /* used for encoded pcm info */
} data;
unsigned int bps;
void *private_data;
struct digit_raw_output_info *digit_raw_output_info;
};
struct frame_info {
int len;
unsigned long offset; /*steam start to here */
unsigned long buffered_len; /*data buffer in dsp,pcm datalen */
int reversed[1]; /*for cache aligned 32 bytes */
};
struct dsp_working_info {
int status;
int sp;
int pc;
int ilink1;
int ilink2;
int blink;
int jiffies;
int out_wp;
int out_rp;
int buffered_len; /* pcm buffered at the dsp side */
int es_offset; /* stream read offset since start decoder */
int reserved[5];
};
#endif /* */

View File

@@ -0,0 +1,206 @@
/*
* drivers/amlogic/audiodsp/dsp_codec.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/cache.h>
#include <asm/cacheflush.h>
#include <linux/amlogic/amports/ptsserv.h>
#include <linux/amlogic/amports/timestamp.h>
/* #include <asm/dsp/dsp_register.h> */
#include "dsp_microcode.h"
#include "audiodsp_module.h"
#include "dsp_control.h"
#include "dsp_mailbox.h"
#include "dsp_codec.h"
int dsp_codec_start(struct audiodsp_priv *priv)
{
return dsp_mailbox_send(priv, 1, M2B_IRQ2_DECODE_START, 0, 0, 0);
}
int dsp_codec_stop(struct audiodsp_priv *priv)
{
return dsp_mailbox_send(priv, 1, M2B_IRQ3_DECODE_STOP, 0, 0, 0);
}
int dsp_codec_get_bufer_data_len(struct audiodsp_priv *priv)
{
#define REVERSD_BYTES 32
#define CACHE_ALIGNED(x) (x&(~0x1f))
unsigned long rp, wp, len, flags;
local_irq_save(flags);
rp = dsp_codec_get_rd_addr(priv);
wp = dsp_codec_get_wd_addr(priv);
if (rp > wp)
len = priv->stream_buffer_size - (rp - wp);
else
len = (wp - rp);
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
len = CACHE_ALIGNED(len);
local_irq_restore(flags);
return len;
}
int dsp_codec_get_bufer_data_len1(struct audiodsp_priv *priv,
unsigned long wd_ptr)
{
#define REVERSD_BYTES 32
#define CACHE_ALIGNED(x) (x&(~0x1f))
unsigned long rp, wp, len, flags;
local_irq_save(flags);
rp = dsp_codec_get_rd_addr(priv);
wp = ARC_2_ARM_ADDR_SWAP(wd_ptr);
if (rp > wp)
len = priv->stream_buffer_size - (rp - wp);
else
len = (wp - rp);
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
len = CACHE_ALIGNED(len);
local_irq_restore(flags);
return len;
}
unsigned long dsp_codec_inc_rd_addr(struct audiodsp_priv *priv, int size)
{
unsigned long rd, flags;
local_irq_save(flags);
rd = dsp_codec_get_rd_addr(priv);
rd = rd + size;
if (rd >= priv->stream_buffer_end)
rd = rd - priv->stream_buffer_size;
DSP_WD(DSP_DECODE_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP((void *)rd));
local_irq_restore(flags);
return rd;
}
u32 dsp_codec_get_current_pts(struct audiodsp_priv *priv)
{
#ifdef CONFIG_AM_PTSSERVER
u32 pts;
u32 delay_pts;
int len;
u64 frame_nums;
int res;
u32 offset, buffered_len, wp;
mutex_lock(&priv->stream_buffer_mutex);
if (priv->frame_format.channel_num == 0
|| priv->frame_format.sample_rate == 0
|| priv->frame_format.data_width == 0) {
pr_info("unvalid audio format!\n");
mutex_unlock(&priv->stream_buffer_mutex);
return -1;
}
#if 0
if (priv->stream_fmt == MCODEC_FMT_COOK) {
pts = priv->cur_frame_info.offset;
mutex_unlock(&priv->stream_buffer_mutex);
} else
#endif
{
buffered_len = DSP_RD(DSP_BUFFERED_LEN);
wp = DSP_RD(DSP_DECODE_OUT_WD_PTR);
offset = DSP_RD(DSP_AFIFO_RD_OFFSET1);
/* before audio start, the pts always be at the first index */
if (!timestamp_apts_started())
offset = 0;
if (priv->stream_fmt == MCODEC_FMT_COOK
|| priv->stream_fmt == MCODEC_FMT_RAAC) {
pts = DSP_RD(DSP_AFIFO_RD_OFFSET1);
res = 0;
} else {
res =
pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts,
300);
if (!priv->first_lookup_over) {
priv->first_lookup_over = 1;
if (first_lookup_pts_failed(PTS_TYPE_AUDIO)) {
priv->out_len_after_last_valid_pts = 0;
priv->last_valid_pts = pts;
mutex_unlock
(&priv->stream_buffer_mutex);
return pts;
}
}
}
if (res == 0) {
/* printk("check out pts == %x\n", pts); */
priv->out_len_after_last_valid_pts = 0;
len =
buffered_len + dsp_codec_get_bufer_data_len1(priv,
wp);
frame_nums =
(len * 8 /
(priv->frame_format.data_width *
priv->frame_format.channel_num));
delay_pts =
div64_u64(frame_nums * 90 * 20,
priv->frame_format.sample_rate / 50);
/* printk("cal delay pts == %x\n", delay_pts); */
if (pts > delay_pts)
pts -= delay_pts;
else
pts = 0;
priv->last_valid_pts = pts;
}
else if (priv->last_valid_pts >= 0) {
pts = priv->last_valid_pts;
len = priv->out_len_after_last_valid_pts;
frame_nums =
(len * 8 /
(priv->frame_format.data_width *
priv->frame_format.channel_num));
pts += div64_u64(frame_nums * 90 * 20,
priv->frame_format.sample_rate / 50);
}
else {
pr_info("checkout audio pts failed!\n");
pts = -1;
}
mutex_unlock(&priv->stream_buffer_mutex);
}
return pts;
#endif
return -1;
}

View File

@@ -0,0 +1,39 @@
/*
* drivers/amlogic/audiodsp/dsp_codec.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DSP_CODEC_CTL_H
#define DSP_CODEC_CTL_H
#include "dsp_control.h"
#include "codec_message.h"
int dsp_codec_start(struct audiodsp_priv *priv);
int dsp_codec_stop(struct audiodsp_priv *priv);
int dsp_codec_get_bufer_data_len(struct audiodsp_priv *priv);
static inline unsigned long dsp_codec_get_rd_addr(struct audiodsp_priv *priv)
{
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_OUT_RD_ADDR));
};
static inline unsigned long dsp_codec_get_wd_addr(struct audiodsp_priv *priv)
{
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_OUT_WD_ADDR));
};
unsigned long dsp_codec_inc_rd_addr(struct audiodsp_priv *priv, int size);
u32 dsp_codec_get_current_pts(struct audiodsp_priv *priv);
#endif /* */

View File

@@ -0,0 +1,459 @@
/*
* drivers/amlogic/audiodsp/dsp_control.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/cache.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
#include "dsp_microcode.h"
#include "audiodsp_module.h"
#include "dsp_control.h"
#include "dsp_codec.h"
/* #include <asm/dsp/dsp_register.h> */
#include <linux/amlogic/amports/dsp_register.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/sound/aiu_regs.h>
#include "dsp_mailbox.h"
#include <linux/delay.h>
#include <linux/clk.h>
#define MIN_CACHE_ALIGN(x) (((x-4)&(~0x1f)))
#define MAX_CACHE_ALIGN(x) ((x+0x1f)&(~0x1f))
#define MAX_STREAM_BUF_MEM_SIZE (32*1024)
int decopt = 0x0000fffb;
int subid;
#define RESET_AUD_ARC (1<<13)
static void enable_dsp(int flag)
{
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
/* power down the media cpu */
if (!flag)
WRITE_CBUS_REG_BITS(HHI_MEM_PD_REG0, 3, 0, 2);
/* reset */
SET_MPEG_REG_MASK(RESET1_REGISTER, RESET_AUD_ARC);
if (flag) {
/* power on media cpu */
WRITE_CBUS_REG_BITS(HHI_MEM_PD_REG0, 0, 0, 2);
/* enable */
SET_MPEG_REG_MASK(MEDIA_CPU_CTL, 3);
}
#else /* */
if (!flag)
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 0);
aml_cbus_update_bits(RESET2_REGISTER, RESET_AUD_ARC, 0);
if (flag) {
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 1);
aml_cbus_update_bits(MEDIA_CPU_CTL, 1, 0);
}
#endif /* */
}
void halt_dsp(struct audiodsp_priv *priv)
{
int i = 0;
if (DSP_RD(DSP_STATUS) == DSP_STATUS_RUNNING) {
#ifndef AUDIODSP_RESET
int i;
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_SLEEP, 0, 0, 0);
for (i = 0; i < 100; i++) {
if (DSP_RD(DSP_STATUS) == DSP_STATUS_SLEEP)
break;
udelay(1000); /*waiting arc2 sleep */
}
if (i == 100)
DSP_PRNT
("dsp isn't sleeping when call dsp_stop\n");
#else /* */
for (i = 0; i < 10; i++) {
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_HALT, 0, 0, 0);
udelay(1000); /*waiting arc2 self-halt */
if (DSP_RD(DSP_STATUS) == DSP_STATUS_HALT)
break;
}
if (i == 10)
DSP_PRNT("warning,dsp self-halt time out\n");
#endif /* */
}
#ifdef AUDIODSP_RESET
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING) {
DSP_WD(DSP_STATUS, DSP_STATUS_HALT);
return;
}
#endif /* */
if (!priv->dsp_is_started) {
enable_dsp(0); /*hardware halt the cpu */
DSP_WD(DSP_STATUS, DSP_STATUS_HALT);
/*mask the stream format is not valid */
priv->last_stream_fmt = -1;
} else
DSP_WD(DSP_STATUS, DSP_STATUS_SLEEP);
}
void reset_dsp(struct audiodsp_priv *priv)
{
halt_dsp(priv);
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
CLEAR_MPEG_REG_MASK(MEDIA_CPU_CTL, (0xfff << 20));
SET_MPEG_REG_MASK(MEDIA_CPU_CTL,
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 20);
SET_MPEG_REG_MASK(MEDIA_CPU_CTL, 1 << 16);
#else /* */
aml_cbus_update_bits(MEDIA_CPU_CTL, (0xfff << 4), 0);
aml_cbus_update_bits(MEDIA_CPU_CTL,
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 4,
((AUDIO_DSP_START_PHY_ADDR) >> 20) << 4);
#endif /* */
/* decode option */
if (audioin_mode & 2)
decopt &= ~(1 << 6);
if (IEC958_mode_codec) {
if (IEC958_mode_codec == 4) /* dd+ */
DSP_WD(DSP_DECODE_OPTION, decopt | (3 << 30));
else
DSP_WD(DSP_DECODE_OPTION, decopt | (1 << 31));
} else
DSP_WD(DSP_DECODE_OPTION, decopt & (~(1 << 31)));
DSP_WD(DSP_CHIP_SUBID, subid);
pr_info("reset dsp : dec opt=%lx, subid=%lx\n",
DSP_RD(DSP_DECODE_OPTION), DSP_RD(DSP_CHIP_SUBID));
if (!priv->dsp_is_started) {
DSP_PRNT("dsp reset now\n");
enable_dsp(1);
} else {
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_WAKEUP, 0, 0, 0);
DSP_WD(DSP_STATUS, DSP_STATUS_WAKEUP);
udelay(1000); /*waiting arc625 run again */
}
}
static inline int dsp_set_stack(struct audiodsp_priv *priv)
{
dma_addr_t buf_map;
if (priv->dsp_stack_start == NULL)
priv->dsp_stack_start =
kzalloc(priv->dsp_stack_size, GFP_KERNEL);
if (priv->dsp_stack_start == 0) {
DSP_PRNT("kmalloc error,no memory for audio dsp stack\n");
return -ENOMEM;
}
buf_map =
dma_map_single(NULL, priv->dsp_stack_start,
priv->dsp_stack_size, DMA_FROM_DEVICE);
dma_unmap_single(NULL, buf_map, priv->dsp_stack_size, DMA_FROM_DEVICE);
DSP_WD(DSP_STACK_START,
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start)));
DSP_WD(DSP_STACK_END,
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start) +
priv->dsp_stack_size));
DSP_PRNT("DSP statck start =%#lx,size=%#lx\n",
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_stack_start),
priv->dsp_stack_size);
if (priv->dsp_gstack_start == 0)
priv->dsp_gstack_start =
kzalloc(priv->dsp_gstack_size, GFP_KERNEL);
if (priv->dsp_gstack_start == 0) {
DSP_PRNT("kmalloc error,no memory for audio dsp gp stack\n");
kfree((void *)priv->dsp_stack_start);
return -ENOMEM;
}
buf_map =
dma_map_single(NULL, priv->dsp_gstack_start,
priv->dsp_gstack_size, DMA_FROM_DEVICE);
dma_unmap_single(NULL, buf_map, priv->dsp_gstack_size,
DMA_FROM_DEVICE);
DSP_WD(DSP_GP_STACK_START,
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start)));
DSP_WD(DSP_GP_STACK_END,
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start) +
priv->dsp_gstack_size));
DSP_PRNT("DSP gp statck start =%#lx,size=%#lx\n",
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_gstack_start),
priv->dsp_gstack_size);
return 0;
}
static inline int dsp_set_heap(struct audiodsp_priv *priv)
{
dma_addr_t buf_map;
if (priv->dsp_heap_size == 0)
return 0;
if (priv->dsp_heap_start == 0)
priv->dsp_heap_start =
(unsigned long)kmalloc(priv->dsp_heap_size, GFP_KERNEL);
if (priv->dsp_heap_start == 0) {
DSP_PRNT
("kmalloc error,no memory for audio dsp dsp_set_heap\n");
return -ENOMEM;
}
memset((void *)priv->dsp_heap_start, 0, priv->dsp_heap_size);
buf_map =
dma_map_single(NULL, (void *)priv->dsp_heap_start,
priv->dsp_heap_size, DMA_FROM_DEVICE);
dma_unmap_single(NULL, buf_map, priv->dsp_heap_size, DMA_FROM_DEVICE);
DSP_WD(DSP_MEM_START,
MAX_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start)));
DSP_WD(DSP_MEM_END,
MIN_CACHE_ALIGN(ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start) +
priv->dsp_heap_size));
DSP_PRNT("DSP heap start =%#lx,size=%#lx\n",
(ulong) ARM_2_ARC_ADDR_SWAP(priv->dsp_heap_start),
priv->dsp_heap_size);
return 0;
}
static inline int dsp_set_stream_buffer(struct audiodsp_priv *priv)
{
dma_addr_t buf_map;
if (priv->stream_buffer_mem_size == 0) {
DSP_WD(DSP_DECODE_OUT_START_ADDR, 0);
DSP_WD(DSP_DECODE_OUT_END_ADDR, 0);
DSP_WD(DSP_DECODE_OUT_RD_ADDR, 0);
DSP_WD(DSP_DECODE_OUT_WD_ADDR, 0);
return 0;
}
if (priv->stream_buffer_mem == NULL)
priv->stream_buffer_mem =
kmalloc(MAX_STREAM_BUF_MEM_SIZE, GFP_KERNEL);
if (priv->stream_buffer_mem == NULL) {
DSP_PRNT
("kmalloc error,no memory for audio dsp stream buffer\n");
return -ENOMEM;
}
memset((void *)priv->stream_buffer_mem, 0,
priv->stream_buffer_mem_size);
buf_map =
dma_map_single(NULL, (void *)priv->stream_buffer_mem,
priv->stream_buffer_mem_size, DMA_FROM_DEVICE);
dma_unmap_single(NULL, buf_map, priv->stream_buffer_mem_size,
DMA_FROM_DEVICE);
priv->stream_buffer_start =
MAX_CACHE_ALIGN((unsigned long)priv->stream_buffer_mem);
priv->stream_buffer_end =
MIN_CACHE_ALIGN((unsigned long)priv->stream_buffer_mem +
priv->stream_buffer_mem_size);
priv->stream_buffer_size =
priv->stream_buffer_end - priv->stream_buffer_start;
if (priv->stream_buffer_size < 0) {
DSP_PRNT
("Stream buffer set error,must more larger\n");
DSP_PRNT("mensize=%d,buffer size=%ld\n",
priv->stream_buffer_mem_size, priv->stream_buffer_size);
kfree(priv->stream_buffer_mem);
priv->stream_buffer_mem = NULL;
return -2;
}
DSP_WD(DSP_DECODE_OUT_START_ADDR,
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
DSP_WD(DSP_DECODE_OUT_END_ADDR,
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_end));
DSP_WD(DSP_DECODE_OUT_RD_ADDR,
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
DSP_WD(DSP_DECODE_OUT_WD_ADDR,
ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start));
DSP_PRNT("DSP stream buffer to [%#lx-%#lx]\n",
(ulong) ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_start),
(ulong) ARM_2_ARC_ADDR_SWAP(priv->stream_buffer_end));
return 0;
}
int dsp_start(struct audiodsp_priv *priv, struct audiodsp_microcode *mcode)
{
int i;
int res;
mutex_lock(&priv->dsp_mutex);
halt_dsp(priv);
/* remove the trick, bug fixed on dsp side */
if (priv->stream_fmt != priv->last_stream_fmt) {
if (audiodsp_microcode_load(audiodsp_privdata(), mcode) != 0) {
pr_info("load microcode error\n");
res = -1;
goto exit;
}
priv->last_stream_fmt = priv->stream_fmt;
}
res = dsp_set_stack(priv);
if (res)
goto exit;
res = dsp_set_heap(priv);
if (res)
goto exit;
res = dsp_set_stream_buffer(priv);
if (res)
goto exit;
if (!priv->dsp_is_started)
reset_dsp(priv);
else {
dsp_mailbox_send(priv, 1, M2B_IRQ0_DSP_WAKEUP, 0, 0, 0);
udelay(1000); /*waiting arc625 run again */
}
priv->dsp_start_time = jiffies;
for (i = 0; i < 1000; i++) {
if (DSP_RD(DSP_STATUS) == DSP_STATUS_RUNNING)
break;
udelay(1000);
}
if (i >= 1000) {
DSP_PRNT("dsp not running\n");
res = -1;
} else {
DSP_PRNT("dsp status=%lx\n", DSP_RD(DSP_STATUS));
priv->dsp_is_started = 1;
res = 0;
}
exit:
mutex_unlock(&priv->dsp_mutex);
return res;
}
int dsp_stop(struct audiodsp_priv *priv)
{
mutex_lock(&priv->dsp_mutex);
#ifdef AUDIODSP_RESET
priv->dsp_is_started = 0;
#endif /* */
halt_dsp(priv);
priv->dsp_end_time = jiffies;
#if 0
if (priv->dsp_stack_start != 0)
kfree((void *)priv->dsp_stack_start);
priv->dsp_stack_start = 0;
if (priv->dsp_gstack_start != 0)
kfree((void *)priv->dsp_gstack_start);
priv->dsp_gstack_start = 0;
if (priv->dsp_heap_start != 0)
kfree((void *)priv->dsp_heap_start);
priv->dsp_heap_start = 0;
if (priv->stream_buffer_mem != NULL) {
kfree(priv->stream_buffer_mem);
priv->stream_buffer_mem = NULL;
}
#endif /* */
mutex_unlock(&priv->dsp_mutex);
return 0;
}
int dsp_check_status(struct audiodsp_priv *priv)
{
/* unsigned int dsp_halt_score = 0; */
unsigned int ablevel = 0;
int pcmlevel = 0;
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING)
return 1;
ablevel = aml_read_cbus(AIU_MEM_AIFIFO_LEVEL);
pcmlevel = dsp_codec_get_bufer_data_len(priv);
if ((ablevel == priv->last_ablevel && ablevel > 50 * 1024) &&
(pcmlevel == priv->last_pcmlevel && pcmlevel < 512)) {
priv->last_ablevel = ablevel;
priv->last_pcmlevel = pcmlevel;
pr_info("dsp not working ............\n");
return 0;
}
priv->last_ablevel = ablevel;
priv->last_pcmlevel = pcmlevel;
return 1;
}
/**
* bit31 - digital raw output
* bit30 - IEC61937 pass over HDMI
* bit 6 - audio in mode.
00: spdif in mode
01: i2s in mode
* bit 5 - DTS passthrough working mode
00: AIU 958 hw search raw mode
01: PCM_RAW mode,the same as AC3/AC3+
* bit 3:4 - used for the communication of dsp and player
tansfer decoding information:
* 00: used for libplayer_end to tell dsp_end that
the file end has been notreached;
* 01: used for libplayer_end to tell dsp_end that
the file end has been reached;
* 10: used for dsp_end to tell libplayer_end that
* all the data in the dsp_end_buf
has been decoded completely;
* 11: reserved;
* bit 2 - ARC DSP print flag
* bit 1 - dts decoder policy select: 0:mute 1:noise
* bit 0 - dd/dd+ decoder policy select 0:mute 1:noise
*/
static int __init decode_option_setup(char *s)
{
unsigned long value = 0xffffffffUL;
if (kstrtoul(s, 16, &value)) {
decopt = 0x0000fffb;
return -1;
}
decopt = (int)value;
return 0;
}
__setup("decopt=", decode_option_setup);
static int __init decode_subid_setup(char *s)
{
unsigned long value = (unsigned long)(0);
if (kstrtoul(s, 16, &value)) {
subid = (unsigned long)(0);
return -1;
}
subid = (int)value;
return 0;
}
__setup("meson_id=", decode_subid_setup);

View File

@@ -0,0 +1,38 @@
/*
* drivers/amlogic/audiodsp/dsp_control.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DSP_CONTROL_HEADER
#define DSP_CONTROL_HEADER
#include <asm/cacheflush.h>
#include "audiodsp_module.h"
#include "dsp_microcode.h"
/*#include <asm/system.h>*/
#include <linux/dma-mapping.h>
void halt_dsp(struct audiodsp_priv *priv);
void reset_dsp(struct audiodsp_priv *priv);
int dsp_start(struct audiodsp_priv *priv, struct audiodsp_microcode *mcode);
int dsp_stop(struct audiodsp_priv *priv);
int dsp_check_status(struct audiodsp_priv *priv);
extern unsigned int IEC958_mode_raw;
extern unsigned int IEC958_mode_codec;
extern unsigned int audioin_mode;
#define AUDIODSP_RESET
#endif /* */

View File

@@ -0,0 +1,408 @@
/*
* drivers/amlogic/audiodsp/dsp_mailbox.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <asm/cacheflush.h>
#include <linux/amlogic/amports/tsync.h>
#include <linux/amlogic/amports/timestamp.h>
#include "dsp_mailbox.h"
#include "dsp_codec.h"
#define BASE_IRQ (32)
#define AM_IRQ(reg) (reg + BASE_IRQ)
#define INT_ASSIST_MBOX1 AM_IRQ(146)
static struct audiodsp_work_t {
char *buf;
struct work_struct audiodsp_workqueue;
} audiodsp_work;
int dsp_mailbox_send(struct audiodsp_priv *priv, int overwrite, int num,
int cmd, const char *data, int len)
{
unsigned long flags;
int res = -1;
struct mail_msg *m;
dma_addr_t buf_map;
m = &priv->mailbox_reg2[num];
local_irq_save(flags);
if (overwrite || m->status == 0) {
m->cmd = cmd;
m->data = (char *)ARM_2_ARC_ADDR_SWAP((unsigned int)data);
m->len = len;
m->status = 1;
after_change_mailbox(m);
if (data != NULL && len > 0) {
buf_map =
dma_map_single(NULL, (void *)data, len,
DMA_TO_DEVICE);
dma_unmap_single(NULL, buf_map, len, DMA_TO_DEVICE);
}
MAIBOX2_IRQ_ENABLE(num);
DSP_TRIGGER_IRQ(num);
res = 0;
}
local_irq_restore(flags);
return res;
}
int get_mailbox_data(struct audiodsp_priv *priv, int num, struct mail_msg *msg)
{
unsigned long flags;
dma_addr_t buf_map;
struct mail_msg *m;
if (num > 31 || num < 0)
return -1;
local_irq_save(flags);
m = &priv->mailbox_reg[num];
pre_read_mailbox(m);
/* dsp_addr_map =
* dma_map_single(priv->dev,(void*)m,
* sizeof(*m),DMA_FROM_DEVICE);
* dma_unmap_single(priv->dev,dsp_addr_map,
* sizeof(*m),DMA_FROM_DEVICE);
*/
msg->cmd = m->cmd;
msg->data = m->data;
msg->data = (char *)((unsigned int)msg->data + AUDIO_DSP_START_ADDR);
msg->status = m->status;
msg->len = m->len;
if (msg->len && msg->data != NULL) {
buf_map =
dma_map_single(priv->dev, (void *)msg->data, msg->len,
DMA_FROM_DEVICE);
dma_unmap_single(priv->dev, buf_map, msg->len, DMA_FROM_DEVICE);
}
m->status = 0;
after_change_mailbox(m);
local_irq_restore(flags);
return 0;
}
static irqreturn_t audiodsp_mailbox_irq(int irq, void *data)
{
struct audiodsp_priv *priv = (struct audiodsp_priv *)data;
unsigned long status;
struct mail_msg msg;
int i = 0;
unsigned long fiq_mask;
status = READ_VREG(MB1_REG);
fiq_mask = READ_VREG(MB1_SEL);
status = status & fiq_mask;
if (status & (1 << M1B_IRQ0_PRINT)) {
get_mailbox_data(priv, M1B_IRQ0_PRINT, &msg);
SYS_CLEAR_IRQ(M1B_IRQ0_PRINT);
/* inv_dcache_range((unsigned long )msg.data,
* (unsigned long)msg.data+msg.len);
*/
DSP_PRNT("%s", msg.data);
/* audiodsp_work.buf = msg.data; */
/* schedule_work(&audiodsp_work.audiodsp_workqueue); */
}
if (status & (1 << M1B_IRQ1_BUF_OVERFLOW)) {
SYS_CLEAR_IRQ(M1B_IRQ1_BUF_OVERFLOW);
DSP_PRNT("DSP BUF over flow\n");
}
if (status & (1 << M1B_IRQ2_BUF_UNDERFLOW)) {
SYS_CLEAR_IRQ(M1B_IRQ2_BUF_UNDERFLOW);
DSP_PRNT("DSP BUF over flow\n");
}
if (status & (1 << M1B_IRQ3_DECODE_ERROR)) {
SYS_CLEAR_IRQ(M1B_IRQ3_DECODE_ERROR);
priv->decode_error_count++;
}
if (status & (1 << M1B_IRQ4_DECODE_FINISH_FRAME)) {
struct frame_info *info;
SYS_CLEAR_IRQ(M1B_IRQ4_DECODE_FINISH_FRAME);
get_mailbox_data(priv, M1B_IRQ4_DECODE_FINISH_FRAME, &msg);
info = (struct frame_info *)msg.data;
if (info != NULL) {
priv->cur_frame_info.offset = info->offset;
priv->cur_frame_info.buffered_len = info->buffered_len;
}
priv->decoded_nb_frames++;
complete(&priv->decode_completion);
}
if (status & (1 << M1B_IRQ5_STREAM_FMT_CHANGED)) {
struct frame_fmt *fmt;
SYS_CLEAR_IRQ(M1B_IRQ5_STREAM_FMT_CHANGED);
get_mailbox_data(priv, M1B_IRQ5_STREAM_FMT_CHANGED, &msg);
fmt = (void *)msg.data;
/* DSP_PRNT("frame format changed"); */
if (fmt == NULL || (sizeof(struct frame_fmt) < msg.len)) {
DSP_PRNT("frame format message error\n");
} else {
DSP_PRNT("frame format changed,fmt->valid 0x%x\n",
fmt->valid);
if (fmt->valid & SUB_FMT_VALID) {
priv->frame_format.sub_fmt = fmt->sub_fmt;
priv->frame_format.valid |= SUB_FMT_VALID;
}
if (fmt->valid & CHANNEL_VALID) {
priv->frame_format.channel_num =
((fmt->channel_num >
2) ? 2 : (fmt->channel_num));
priv->frame_format.valid |= CHANNEL_VALID;
}
if (fmt->valid & SAMPLE_RATE_VALID) {
priv->frame_format.sample_rate =
fmt->sample_rate;
priv->frame_format.valid |= SAMPLE_RATE_VALID;
}
if (fmt->valid & DATA_WIDTH_VALID) {
priv->frame_format.data_width = fmt->data_width;
priv->frame_format.valid |= DATA_WIDTH_VALID;
}
}
/*
* if(fmt->data.pcm_encoded_info){
* set_pcminfo_data(fmt->data.pcm_encoded_info);
* }
*/
DSP_PRNT("audio info from dsp:sample_rate=%d channel_num=%d\n",
priv->frame_format.sample_rate,
priv->frame_format.channel_num);
}
if (status & (1 << M1B_IRQ8_IEC958_INFO)) {
struct digit_raw_output_info *info;
SYS_CLEAR_IRQ(M1B_IRQ8_IEC958_INFO);
get_mailbox_data(priv, M1B_IRQ8_IEC958_INFO, &msg);
info = (void *)msg.data;
#if 1
IEC958_bpf = info->bpf;
IEC958_brst = info->brst;
IEC958_length = info->length;
IEC958_padsize = info->padsize;
IEC958_mode = info->mode;
IEC958_syncword1 = info->syncword1;
IEC958_syncword2 = info->syncword2;
IEC958_syncword3 = info->syncword3;
IEC958_syncword1_mask = info->syncword1_mask;
IEC958_syncword2_mask = info->syncword2_mask;
IEC958_syncword3_mask = info->syncword3_mask;
IEC958_chstat0_l = info->chstat0_l;
IEC958_chstat0_r = info->chstat0_r;
IEC958_chstat1_l = info->chstat1_l;
IEC958_chstat1_r = info->chstat1_r;
#endif
/*
* IEC958_mode_codec = info->can_bypass;
*/
DSP_PRNT("MAILBOX: got IEC958 info\n");
/* schedule_work(&audiodsp_work.audiodsp_workqueue); */
}
if (status & (1 << M1B_IRQ5_STREAM_RD_WD_TEST)) {
DSP_WD((0x84100000 - 4096 + 20 * 20), 0);
SYS_CLEAR_IRQ(M1B_IRQ5_STREAM_RD_WD_TEST);
get_mailbox_data(priv, M1B_IRQ5_STREAM_RD_WD_TEST, &msg);
for (i = 0; i < 12; i++) {
if ((DSP_RD((0x84100000 - 512 * 1024 + i * 20))) !=
(0xff00 | i)) {
DSP_PRNT
("a9 read dsp err, now %#lx, should be %#x\n",
(DSP_RD((0x84100000 - 512 * 1024 + i * 20))),
12 - i);
}
/* DSP_PRNT("A9 audio dsp reg%d value 0x%x\n",
* i,DSP_RD((0x84100000-4096+i*20)));
*/
}
for (i = 0; i < 12; i++) {
DSP_WD((0x84100000 - 512 * 1024 + i * 20),
(i % 2) ? i : (0xf0 | i));
}
DSP_WD((0x84100000 - 512 * 1024 + 20 * 20), DSP_STATUS_HALT);
/* DSP_PRNT("A9 mail box handle finished\n");
* dsp_mailbox_send(priv, 1,
* M1B_IRQ5_STREAM_RD_WD_TEST, 0, NULL,0);
*/
}
if (status & (1 << M1B_IRQ7_DECODE_FATAL_ERR)) {
int err_code;
SYS_CLEAR_IRQ(M1B_IRQ7_DECODE_FATAL_ERR);
get_mailbox_data(priv, M1B_IRQ7_DECODE_FATAL_ERR, &msg);
err_code = msg.cmd;
priv->decode_fatal_err = err_code;
if (err_code & 0x01) {
timestamp_pcrscr_set(timestamp_vpts_get());
timestamp_pcrscr_enable(1);
} else if (err_code & 0x02) {
pr_info("Set decode_fatal_err flag, Reset audiodsp!\n");
}
}
return 0;
}
static void audiodsp_mailbox_work_queue(struct work_struct *work)
{
struct audiodsp_work_t *pwork =
container_of(work, struct audiodsp_work_t, audiodsp_workqueue);
char *message;
if (pwork) {
message = pwork->buf;
if (message)
pr_info("%s", message);
else
pr_info("the message from mailbox is NULL\n");
} else {
pr_info("the work queue for audiodsp mailbox is empty\n");
}
}
int audiodsp_init_mailbox(struct audiodsp_priv *priv)
{
int err = 0;
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
err = request_irq(INT_ASSIST_MBOX1, audiodsp_mailbox_irq,
IRQF_SHARED, "audiodsp_mailbox", (void *)priv);
#else
pr_info("audiodsp request irq not ready\n");
/* err = request_irq(INT_MAILBOX_1B, audiodsp_mailbox_irq, */
/* IRQF_SHARED, "audiodsp_mailbox", (void *)priv); */
#endif
if (err != 0) {
pr_info("request IRQ for dsp mailbox failed: errno = %x\n",
err);
return -1;
}
/* WRITE_MPEG_REG(ASSIST_MBOX0_MASK, 0xffffffff); */
priv->mailbox_reg = (struct mail_msg *)MAILBOX1_REG(0);
priv->mailbox_reg2 = (struct mail_msg *)MAILBOX2_REG(0);
INIT_WORK(&audiodsp_work.audiodsp_workqueue,
audiodsp_mailbox_work_queue);
return 0;
}
int audiodsp_get_audioinfo(struct audiodsp_priv *priv)
{
int ret = -1;
int audio_info = 0;
audio_info = DSP_RD(DSP_AUDIO_FORMAT_INFO);
if (priv->frame_format.valid ==
(CHANNEL_VALID | DATA_WIDTH_VALID | SAMPLE_RATE_VALID)) {
ret = 0;
goto exit;
} else if (audio_info) {
priv->frame_format.channel_num = audio_info & 0xf;
if (priv->frame_format.channel_num)
priv->frame_format.valid |= CHANNEL_VALID;
priv->frame_format.data_width = (audio_info >> 4) & 0x3f;
if (priv->frame_format.data_width)
priv->frame_format.valid |= DATA_WIDTH_VALID;
priv->frame_format.sample_rate = (audio_info >> 10);
if (priv->frame_format.sample_rate)
priv->frame_format.valid |= SAMPLE_RATE_VALID;
ret = 0;
}
if (ret == 0) {
DSP_PRNT(" audiodsp got audioinfo:[ch num %d],[sr %d]",
priv->frame_format.channel_num,
priv->frame_format.sample_rate);
}
exit:
return ret;
}
int audiodsp_release_mailbox(struct audiodsp_priv *priv)
{
#if 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
free_irq(INT_ASSIST_MBOX1, (void *)priv);
#else
/* free_irq(INT_MAILBOX_1B,(void *)priv); */
#endif
return 0;
}
static unsigned int hdmi_sr;
int mailbox_send_audiodsp(int overwrite, int num, int cmd, const char *data,
int len)
{
int res = -1;
int time_out = 0;
if (DSP_RD(DSP_STATUS) != DSP_STATUS_RUNNING) {
pr_info("fatal error,dsp must be running before mailbox sent\n");
return -1;
}
DSP_WD(DSP_GET_EXTRA_INFO_FINISH, 0);
while (time_out++ < 10) {
if ((num == M2B_IRQ0_DSP_AUDIO_EFFECT)
&& (cmd == DSP_CMD_SET_HDMI_SR)) {
hdmi_sr = *(unsigned int *)data;
DSP_PRNT("<hdmi to dsp mailbox> sr changed to %d\n",
hdmi_sr);
DSP_WD(DSP_HDMI_SR, hdmi_sr);
res =
dsp_mailbox_send(audiodsp_privdata(), overwrite,
num, cmd, (char *)&hdmi_sr,
sizeof(unsigned int));
break;
}
{
res =
dsp_mailbox_send(audiodsp_privdata(), overwrite,
num, cmd, data, len);
mdelay(10);
}
if (DSP_RD(DSP_GET_EXTRA_INFO_FINISH) == 0x12345678)
break;
}
if (time_out == 10) {
pr_info("error,dsp transfer mailbox time out\n");
return -1;
}
return res;
}
EXPORT_SYMBOL(mailbox_send_audiodsp);

View File

@@ -0,0 +1,76 @@
/*
* drivers/amlogic/audiodsp/dsp_mailbox.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DSP_IRQ_HEADER
#define DSP_IRQ_HEADER
#include "audiodsp_module.h"
int audiodsp_init_mailbox(struct audiodsp_priv *priv);
int audiodsp_release_mailbox(struct audiodsp_priv *priv);
int dsp_mailbox_send(struct audiodsp_priv *priv, int overwrite, int num,
int cmd, const char *data, int len);
int audiodsp_get_audioinfo(struct audiodsp_priv *priv);
#if 0
#define pre_read_mailbox(reg) \
dma_cache_inv((unsigned long)reg, sizeof(*reg))
#define after_change_mailbox(reg) \
dma_cache_wback((unsigned long)reg, sizeof(*reg))
#else /* */
#define pre_read_mailbox(reg)
#define after_change_mailbox(reg)
#endif /* */
enum DSP_CMD {
DSP_CMD_SET_EQ_PRESET = 0,
DSP_CMD_SET_EQ_CUSTOMIZE,
DSP_CMD_GET_EQ_VALUE_SETS,
DSP_CMD_SET_SRS_SURROUND,
DSP_CMD_SET_SRS_TRUBASS,
DSP_CMD_SET_SRS_DIALOG_CLARITY,
DSP_CMD_SET_HDMI_SR,
DSP_CMD_SET_SRS_TRUBASS_GAIN,
DSP_CMD_SET_SRS_DIALOG_CLARITY_GAIN,
DSP_CMD_SET_REINIT_FILTER,
DSP_CMD_COUNT
};
extern unsigned int IEC958_bpf;
extern unsigned int IEC958_brst;
extern unsigned int IEC958_length;
extern unsigned int IEC958_padsize;
extern unsigned int IEC958_mode;
extern unsigned int IEC958_syncword1;
extern unsigned int IEC958_syncword2;
extern unsigned int IEC958_syncword3;
extern unsigned int IEC958_syncword1_mask;
extern unsigned int IEC958_syncword2_mask;
extern unsigned int IEC958_syncword3_mask;
extern unsigned int IEC958_chstat0_l;
extern unsigned int IEC958_chstat0_r;
extern unsigned int IEC958_chstat1_l;
extern unsigned int IEC958_chstat1_r;
extern unsigned int IEC958_mode_raw;
extern unsigned int IEC958_mode_codec;
extern void set_pcminfo_data(void *pcm_encoded_info);
#define VDEC_ASSIST_MBOX1_IRQ_REG 0x0074
#define VDEC_ASSIST_MBOX1_CLR_REG 0x0075
#define VDEC_ASSIST_MBOX1_FIQ_SEL 0x0077
#define VDEC_ASSIST_MBOX2_IRQ_REG 0x0078
#define VDEC_ASSIST_MBOX2_MASK 0x007a
#define VDEC_ASSIST_MBOX2_FIQ_SEL 0x007b
#endif /* */

View File

@@ -0,0 +1,195 @@
/*
* drivers/amlogic/audiodsp/dsp_microcode.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "audiodsp_control.h"
#include <linux/firmware.h>
#include <linux/amlogic/major.h>
#include <linux/device.h>
#include "audiodsp_module.h"
#include "dsp_control.h"
#include "dsp_microcode.h"
#include <linux/dma-mapping.h>
static int audiodsp_microcode_insert(struct audiodsp_priv *priv,
struct audiodsp_microcode *pmcode)
{
unsigned long flags;
if (pmcode == NULL)
return -1;
spin_lock_irqsave(&priv->mcode_lock, flags);
list_add_tail(&pmcode->list, &priv->mcode_list);
priv->mcode_id++;
pmcode->id = priv->mcode_id;
spin_unlock_irqrestore(&priv->mcode_lock, flags);
return priv->mcode_id;
}
struct audiodsp_microcode *audiodsp_find_supoort_mcode(struct audiodsp_priv
*priv, int fmt)
{
struct audiodsp_microcode *pmcode = NULL;
struct audiodsp_microcode *p = NULL;
unsigned long flags;
spin_lock_irqsave(&priv->mcode_lock, flags);
list_for_each_entry(p, &priv->mcode_list, list) {
if (p->fmt & fmt) {
pmcode = p;
break;
}
}
spin_unlock_irqrestore(&priv->mcode_lock, flags);
return pmcode;
}
static struct audiodsp_microcode
*audiodsp_find_mcode_by_name(struct audiodsp_priv *priv,
char *name)
{
struct audiodsp_microcode *pmcode = NULL;
struct audiodsp_microcode *p = NULL;
unsigned long flags;
spin_lock_irqsave(&priv->mcode_lock, flags);
list_for_each_entry(p, &priv->mcode_list, list) {
if (memcmp(p->file_name, name, strlen(name)) == 0) {
pmcode = p;
break;
}
}
spin_unlock_irqrestore(&priv->mcode_lock, flags);
return pmcode;
}
int audiodsp_microcode_load(struct audiodsp_priv *priv,
struct audiodsp_microcode *pmcode)
{
const struct firmware *firmware;
int err = 0;
unsigned int dsp_code_text_start = 0;
priv->micro_dev = device_create(priv->class, NULL,
MKDEV(AUDIODSP_MAJOR, 1), NULL, "audiodsp1");
if (priv->micro_dev == NULL)
return -1;
err = request_firmware(&firmware, pmcode->file_name,
priv->micro_dev);
if (err < 0) {
DSP_PRNT("can't load the %s,err=%d\n", pmcode->file_name, err);
goto error1;
}
if (firmware->size > priv->code_mem_size) {
DSP_PRNT("not enough memory size for audiodsp code\n");
err = ENOMEM;
goto release;
}
if (priv->dsp_is_started)
#ifndef AUDIODSP_RESET
/*
* after dsp is running,only load from
* the text section of the microcode.
*/
dsp_code_text_start = 0x1000;
#else /* */
dsp_code_text_start = 0;
#endif /* AUDIODSP_RESET */
memcpy((char *)((unsigned int)priv->dsp_code_start +
dsp_code_text_start),
(char *)firmware->data + dsp_code_text_start,
firmware->size - dsp_code_text_start);
/* TODO: this cause 3.14 check patch warning */
/* mb(); */
pmcode->code_size = firmware->size;
DSP_PRNT("load mcode size=%d\n,load addr 0x%lx mcode name %s",
firmware->size, pmcode->code_start_addr, pmcode->file_name);
release:
release_firmware(firmware);
error1:
device_destroy(priv->class, MKDEV(AUDIODSP_MAJOR, 1));
return err;
}
int audiodsp_microcode_register(struct audiodsp_priv *priv, int fmt,
char *filename)
{
struct audiodsp_microcode *pmcode;
int len;
pmcode = audiodsp_find_mcode_by_name(priv, filename);
if (pmcode != NULL && (fmt & pmcode->fmt)) {
DSP_PRNT("Have register the firmware code before=%s\n",
filename);
DSP_PRNT("Refresh the firmware settings now\n");
pmcode->fmt = fmt;
pmcode->code_start_addr = priv->dsp_code_start;
len = min_t(int, 64, strlen(filename));
memcpy(pmcode->file_name, filename, len);
pmcode->file_name[len] = '\0';
return pmcode->id;
}
{
pmcode =
kmalloc(sizeof(struct audiodsp_microcode), GFP_KERNEL);
if (pmcode == NULL)
return -ENOMEM;
pmcode->fmt = fmt;
pmcode->code_start_addr = priv->dsp_code_start;
len = min_t(int, 64, strlen(filename));
memcpy(pmcode->file_name, filename, len);
pmcode->file_name[len] = '\0';
pmcode->id = audiodsp_microcode_insert(priv, pmcode);
if (pmcode->id < 0) {
kfree(pmcode);
return -EIO;
}
}
return 0;
}
int audiodsp_microcode_free(struct audiodsp_priv *priv)
{
unsigned long flags;
struct audiodsp_microcode *pmcode;
struct list_head *list, *head;
local_irq_save(flags);
head = &priv->mcode_list;
while (!list_empty(head)) {
list = head->prev;
pmcode = list_entry(list, struct audiodsp_microcode, list);
list_del(list);
kfree(pmcode);
}
local_irq_restore(flags);
return 0;
}

View File

@@ -0,0 +1,41 @@
/*
* drivers/amlogic/audiodsp/dsp_microcode.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef AUDIODSP_MICROCODE_HEADER
#define AUDIODSP_MICROCODE_HEADER
#include "audiodsp_module.h"
struct audiodsp_microcode {
int id;
/* support format; */
int fmt;
struct list_head list;
unsigned long code_start_addr;
unsigned long code_size;
char file_name[64];
};
extern int audiodsp_microcode_register(struct audiodsp_priv *priv,
int fmt, char *filename);
extern struct audiodsp_microcode
*audiodsp_find_supoort_mcode(struct audiodsp_priv *priv,
int fmt);
extern int audiodsp_microcode_load(struct audiodsp_priv *priv,
struct audiodsp_microcode *pmcode);
int audiodsp_microcode_free(struct audiodsp_priv *priv);
#endif /* */

View File

@@ -0,0 +1,83 @@
/*
* drivers/amlogic/audiodsp/dsp_monitor.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/cache.h>
#include <asm/cacheflush.h>
#include "dsp_microcode.h"
#include "audiodsp_module.h"
#include "dsp_control.h"
/* #include <asm/dsp/dsp_register.h> */
static void audiodsp_monitor(unsigned long arg)
{
struct audiodsp_priv *priv = (struct audiodsp_priv *)arg;
static unsigned long old_dsp_jiffies;
static unsigned long jiffies_error;
unsigned long dsp_jiffies;
unsigned long dsp_status;
dsp_jiffies = DSP_RD(DSP_JIFFIES);
dsp_status = DSP_RD(DSP_STATUS);
if (old_dsp_jiffies == dsp_jiffies)
jiffies_error++;
else {
jiffies_error = 0;
old_dsp_jiffies = dsp_jiffies;
}
if (jiffies_error > 5) {
DSP_PRNT("Found audio dsp have some problem not running\n");
DSP_PRNT("audio jiffies=%ld\n", old_dsp_jiffies);
DSP_PRNT("audio status=%lx\n", dsp_status);
}
if (!dsp_check_status(priv))
priv->dsp_abnormal_count++;
else
priv->dsp_abnormal_count = 0;
if (priv->dsp_abnormal_count > 5 || jiffies_error > 5) {
priv->decode_fatal_err |= 0x2;
priv->dsp_abnormal_count = 0;
jiffies_error = 0;
}
priv->dsp_mointer.expires = jiffies + HZ;
add_timer(&priv->dsp_mointer);
}
void start_audiodsp_monitor(struct audiodsp_priv *priv)
{
priv->dsp_mointer.expires = jiffies;
add_timer(&priv->dsp_mointer);
}
void stop_audiodsp_monitor(struct audiodsp_priv *priv)
{
del_timer_sync(&priv->dsp_mointer);
}
void init_audiodsp_monitor(struct audiodsp_priv *priv)
{
setup_timer(&priv->dsp_mointer, audiodsp_monitor, (unsigned long)priv);
}
void release_audiodsp_monitor(struct audiodsp_priv *priv)
{
stop_audiodsp_monitor(priv);
}

View File

@@ -0,0 +1,27 @@
/*
* drivers/amlogic/audiodsp/dsp_monitor.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DSP_MONITOR_HEADER_H
#define DSP_MONITOR_HEADER_H
#include "audiodsp_module.h"
void start_audiodsp_monitor(struct audiodsp_priv *priv);
void stop_audiodsp_monitor(struct audiodsp_priv *priv);
void init_audiodsp_monitor(struct audiodsp_priv *priv);
void release_audiodsp_monitor(struct audiodsp_priv *priv);
#endif /* */

View File

@@ -0,0 +1,413 @@
/*
* drivers/amlogic/audiodsp/pcmenc_module.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <mach/am_regs.h>
#include "pcmenc_stream.h"
#include <linux/amlogic/amports/dsp_register.h>
static int __init audiodsp_pcmenc_init_module(void);
static void __exit audiodsp_pcmenc_exit_module(void);
static int audiodsp_pcmenc_open(struct inode *, struct file *);
static int audiodsp_pcmenc_release(struct inode *, struct file *);
static ssize_t audiodsp_pcmenc_read(struct file *, char *, size_t, loff_t *);
static ssize_t audiodsp_pcmenc_write(struct file *, const char *, size_t,
loff_t *);
static int audiodsp_pcmenc_mmap(struct file *filp, struct vm_area_struct *vma);
static long audiodsp_pcmenc_ioctl(struct file *file, unsigned int cmd,
unsigned long args);
static int audiodsp_pcmenc_create_stream_buffer(void);
static int audiodsp_pcmenc_destroy_stream_buffer(void);
#define SUCCESS 0
#define DEVICE_NAME "audiodsp_pcmenc"
#define MIN_CACHE_ALIGN(x) (((x-4)&(~0x1f)))
#define MAX_CACHE_ALIGN(x) ((x+0x1f)&(~0x1f))
#ifdef MIN
#undef MIN
#endif /* */
#define MIN(x, y) (((x) < (y))?(x):(y))
static int major;
static struct class *class_pcmenc;
static struct device *dev_pcmenc;
/* Is device open?
* Used to prevent multiple access to device
*/
static int device_opened;
static pcm51_encoded_info_t pcminfo = { 0 };
struct priv_data_t {
void *stream_buffer_mem;
unsigned int stream_buffer_mem_size;
unsigned long stream_buffer_start;
unsigned long stream_buffer_end;
unsigned long stream_buffer_size;
/* the offset of the stream buffer which user space reading */
unsigned long user_read_offset;
};
static struct priv_data_t priv_data = { 0 };
static ssize_t pcmenc_ptr_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret = 0;
ret = sprintf(buf, "pcmenc runtime info:\n"
" pcmenc rd ptr :\t%lx\n"
" pcmenc wr ptr :\t%lx\n"
" pcmenc level :\t%x\n",
(DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR)),
(DSP_RD(DSP_DECODE_51PCM_OUT_WD_ADDR)),
pcmenc_stream_content());
return ret;
}
static struct class_attribute pcmenc_attrs[] = {
__ATTR_RO(pcmenc_ptr),
__ATTR_NULL
};
static void create_pcmenc_attrs(struct class *class)
{
int i = 0, ret;
for (i = 0; pcmenc_attrs[i].attr.name; i++)
ret = class_create_file(class, &pcmenc_attrs[i]);
}
static void remove_amaudio_attrs(struct class *class)
{
int i = 0;
for (i = 0; pcmenc_attrs[i].attr.name; i++)
class_remove_file(class, &pcmenc_attrs[i]);
}
static const struct file_operations fops = {
.read = audiodsp_pcmenc_read,
.unlocked_ioctl = audiodsp_pcmenc_ioctl,
.write = audiodsp_pcmenc_write,
.open = audiodsp_pcmenc_open,
.mmap = audiodsp_pcmenc_mmap,
.release = audiodsp_pcmenc_release
};
static int __init audiodsp_pcmenc_init_module(void)
{
void *ptr_err;
int ret = 0;
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
pr_info("Registering char device %s failed with %d\n",
DEVICE_NAME, major);
return major;
}
class_pcmenc = class_create(THIS_MODULE, DEVICE_NAME);
ptr_err = class_pcmenc;
if (IS_ERR(ptr_err))
goto err0;
create_pcmenc_attrs(class_pcmenc);
dev_pcmenc =
device_create(class_pcmenc, NULL, MKDEV(major, 0), NULL,
DEVICE_NAME);
ptr_err = dev_pcmenc;
if (IS_ERR(ptr_err))
goto err1;
/* buffer should be page aligned for mmap operation */
priv_data.stream_buffer_mem_size = 2 * 2 * 512 * 1024 + PAGE_SIZE;
ret = audiodsp_pcmenc_create_stream_buffer();
if (ret)
goto err2;
pr_info("amlogic audio dsp pcmenc device init!\n");
return SUCCESS;
err2:
device_destroy(class_pcmenc, MKDEV(major, 0));
err1:
class_destroy(class_pcmenc);
err0:
unregister_chrdev(major, DEVICE_NAME);
return PTR_ERR(ptr_err);
}
static void __exit audiodsp_pcmenc_exit_module(void)
{
device_destroy(class_pcmenc, MKDEV(major, 0));
remove_amaudio_attrs(class_pcmenc);
class_destroy(class_pcmenc);
unregister_chrdev(major, DEVICE_NAME);
audiodsp_pcmenc_destroy_stream_buffer();
}
static int audiodsp_pcmenc_open(struct inode *inode, struct file *file)
{
if (device_opened)
return -EBUSY;
device_opened++;
try_module_get(THIS_MODULE);
#if 0
if (buf == NULL)
buf = kmalloc(priv_data.stream_buffer_size, GFP_KERNEL);
if (buf == NULL) {
device_opened--;
module_put(THIS_MODULE);
pr_info("pcmenc: malloc buffer failed\n");
return -ENOMEM;
}
#endif /* 0 */
/* audiodsp_pcmenc_create_stream_buffer(); //init the r/p register */
pcmenc_stream_init();
return SUCCESS;
}
static int audiodsp_pcmenc_release(struct inode *inode, struct file *file)
{
device_opened--;
module_put(THIS_MODULE);
#if 0
kfree(buf);
buf = NULL;
#endif
audiodsp_pcmenc_create_stream_buffer(); /* init the r/p register */
pcmenc_stream_deinit();
return SUCCESS;
}
static ssize_t audiodsp_pcmenc_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
#if 1
int bytes_read = 0;
int len = 0;
if (buffer == NULL)
return 0;
len = MIN(length, pcmenc_stream_content());
bytes_read = pcmenc_stream_read(buffer, len);
return bytes_read;
#else /* */
pr_info("Sorry, read operation isn't supported.\n");
return -EINVAL;
#endif /* */
return length;
}
static ssize_t audiodsp_pcmenc_write(struct file *filp, const char *buff,
size_t len, loff_t *off)
{
pr_info("Sorry, this operation isn't supported.\n");
return -EINVAL;
}
static int audiodsp_pcmenc_mmap(struct file *filp,
struct vm_area_struct *vma)
{
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned int vm_size = vma->vm_end - vma->vm_start;
if (vm_size == 0)
return -EAGAIN;
off += virt_to_phys((void *)priv_data.stream_buffer_start);
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
pr_info(" pcmenc : failed remap_pfn_range\n");
return -EAGAIN;
}
pr_info("pcmenc: mmap finished\n");
return 0;
}
static long audiodsp_pcmenc_ioctl(struct file *file, unsigned int cmd,
unsigned long args)
{
int ret = 0;
switch (cmd) {
case AUDIODSP_PCMENC_GET_RING_BUF_SIZE:
put_user(priv_data.stream_buffer_size, (__u64 __user *) args);
break;
case AUDIODSP_PCMENC_GET_RING_BUF_CONTENT:
put_user(pcmenc_stream_content(), (__s32 __user *) args);
break;
case AUDIODSP_PCMENC_GET_RING_BUF_SPACE:
put_user(pcmenc_stream_space(), (__s32 __user *) args);
break;
case AUDIODSP_PCMENC_SET_RING_BUF_RPTR:
priv_data.user_read_offset = (unsigned long)args;
/*
* pr_info("dsp rd ptr %x\n",
* DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
*/
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start +
priv_data.user_read_offset));
/*
* pr_info("dsp rd ptr change to %x\n",
* DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
*/
break;
case AUDIODSP_PCMENC_GET_PCMINFO:
if (args == 0) {
pr_info("pcm enc: args invalid\n");
ret = -EINVAL;
}
ret = copy_to_user((void __user *)args, &pcminfo,
sizeof(pcminfo));
if (ret != 0)
pr_info("pcm enc:copy to user error\n");
break;
default:
pr_info("pcmenc:un-implemented cmd\n");
break;
}
return ret;
}
static int audiodsp_pcmenc_create_stream_buffer(void)
{
dma_addr_t buf_map;
#if 0
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
#endif /* */
if (priv_data.stream_buffer_mem_size == 0)
return 0;
if (priv_data.stream_buffer_mem == NULL)
priv_data.stream_buffer_mem =
kmalloc(priv_data.stream_buffer_mem_size,
GFP_KERNEL);
if (priv_data.stream_buffer_mem == NULL) {
pr_info("kmalloc error,
no memory for audio dsp pcmenc stream buffer\n");
return -ENOMEM;
}
memset((void *)priv_data.stream_buffer_mem, 0,
priv_data.stream_buffer_mem_size);
buf_map =
dma_map_single(NULL, (void *)priv_data.stream_buffer_mem,
priv_data.stream_buffer_mem_size, DMA_TO_DEVICE);
dma_unmap_single(NULL, buf_map, priv_data.stream_buffer_mem_size,
DMA_TO_DEVICE);
/* TODO: if it's needed */
wmb();
priv_data.stream_buffer_start =
PAGE_ALIGN((unsigned long)priv_data.stream_buffer_mem +
PAGE_SIZE - 1);
priv_data.stream_buffer_end =
PAGE_ALIGN((unsigned long)priv_data.stream_buffer_mem +
priv_data.stream_buffer_mem_size);
priv_data.stream_buffer_size =
priv_data.stream_buffer_end - priv_data.stream_buffer_start;
if (priv_data.stream_buffer_size < 0) {
pr_info("DSP pcmenc Stream buffer set error,
must more larger,mensize = %d,buffer size=%ld\n",
priv_data.stream_buffer_mem_size,
priv_data.stream_buffer_size);
kfree(priv_data.stream_buffer_mem);
priv_data.stream_buffer_mem = NULL;
return -2;
}
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR,
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR,
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_end));
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR,
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start));
pr_info("DSP pcmenc stream buffer to [%#lx-%#lx]\n",
(unsigned long int)
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_start),
(unsigned long int)
ARM_2_ARC_ADDR_SWAP(priv_data.stream_buffer_end));
return 0;
}
static int audiodsp_pcmenc_destroy_stream_buffer(void)
{
kfree(priv_data.stream_buffer_mem);
priv_data.stream_buffer_mem = NULL;
DSP_WD(DSP_DECODE_51PCM_OUT_START_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_END_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
DSP_WD(DSP_DECODE_51PCM_OUT_WD_ADDR, ARM_2_ARC_ADDR_SWAP(0));
return 0;
}
void set_pcminfo_data(void *pcm_encoded_info)
{
dma_addr_t buf_map;
/*
* as this ptr got from arc dsp side,
* which mapping to 0 address,
* so add this dsp start offset
*/
pcm51_encoded_info_t *info =
(pcm51_encoded_info_t *) ((unsigned int)pcm_encoded_info +
AUDIO_DSP_START_ADDR);
/* inv dcache as this data from device */
buf_map =
dma_map_single(dev_pcmenc, (void *)info,
sizeof(pcm51_encoded_info_t), DMA_FROM_DEVICE);
dma_unmap_single(dev_pcmenc, buf_map, sizeof(pcm51_encoded_info_t),
DMA_FROM_DEVICE);
memcpy(&pcminfo, info, sizeof(pcm51_encoded_info_t));
pr_info("got pcm51 info from dsp\n");
}
EXPORT_SYMBOL(set_pcminfo_data);
module_init(audiodsp_pcmenc_init_module);
module_exit(audiodsp_pcmenc_exit_module);
MODULE_DESCRIPTION("AMLOGIC PCM encoder interface driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jian.xu <jian.xu@amlogic.com>");

View File

@@ -0,0 +1,170 @@
/*
* drivers/amlogic/audiodsp/pcmenc_stream.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "pcmenc_stream.h"
#include <linux/amlogic/amports/dsp_register.h>
#include <linux/dma-mapping.h>
struct log_stream_t {
unsigned long wpointer; /* write pointer */
unsigned long rpointer; /* read pointer */
unsigned long size; /* size of buffer */
unsigned long start; /* buffer start addr */
unsigned long end; /* buffer end addr */
};
#ifdef MIN
#undef MIN
#endif /* */
#define MIN(x, y) (((x) < (y))?(x):(y))
#define MIN_CACHE_LINE(x, y) (MIN(x, y) & (~0x1f))
#define PCM_BUFFER_RESERVED (4096)
static struct log_stream_t log_stream = { 0 };
static inline int internal_read(char __user *buf, int size, int intact);
static inline unsigned long get_read_pointer(void);
static inline void set_read_pointer(void);
static inline unsigned long get_write_pointer(void);
static inline void cache_flush(unsigned int addr, unsigned int size);
static inline void cache_invalidate(unsigned int addr, unsigned int size);
static inline unsigned long get_read_pointer(void)
{
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_RD_ADDR));
}
static inline void set_read_pointer(void)
{
DSP_WD(DSP_DECODE_51PCM_OUT_RD_ADDR,
ARM_2_ARC_ADDR_SWAP(log_stream.rpointer));
}
static inline unsigned long get_write_pointer(void)
{
return ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_WD_ADDR));
}
static inline void cache_flush(unsigned int addr, unsigned int size)
{
dma_addr_t buf_map;
buf_map = dma_map_single(NULL, (void *)addr, size, DMA_TO_DEVICE);
dma_unmap_single(NULL, buf_map, size, DMA_TO_DEVICE);
}
static inline void cache_invalidate(unsigned int addr,
unsigned int size)
{
dma_addr_t buf_map;
buf_map = dma_map_single(NULL, (void *)addr, size, DMA_FROM_DEVICE);
dma_unmap_single(NULL, buf_map, size, DMA_FROM_DEVICE);
}
int pcmenc_stream_space(void)
{
int space;
log_stream.rpointer = get_read_pointer();
log_stream.wpointer = get_write_pointer();
if (log_stream.wpointer >= log_stream.rpointer)
space =
log_stream.size - (log_stream.wpointer -
log_stream.rpointer);
else
space = log_stream.rpointer - log_stream.wpointer;
space =
space > PCM_BUFFER_RESERVED ? space - PCM_BUFFER_RESERVED : 0;
return space;
}
int pcmenc_stream_content(void)
{
int content;
log_stream.rpointer = get_read_pointer();
log_stream.wpointer = get_write_pointer();
if (log_stream.rpointer > log_stream.wpointer)
content =
log_stream.size - (log_stream.rpointer -
log_stream.wpointer);
else
content = log_stream.wpointer - log_stream.rpointer;
content = content > PCM_BUFFER_RESERVED ?
content - PCM_BUFFER_RESERVED : 0;
return content;
}
static inline int internal_read(char __user *buf, int size, int intact)
{
int len;
int content = pcmenc_stream_content();
int tail = 0;
if (intact)
len = size > content ? -1 : size;
else
len =
size <= 32 ? MIN(size, content) : MIN_CACHE_LINE(size,
content);
if (len <= 0)
return 0;
if (log_stream.rpointer + len > log_stream.end) {
tail = log_stream.end - log_stream.rpointer;
cache_invalidate(log_stream.rpointer, tail);
copy_to_user((void *)buf, (void *)(log_stream.rpointer), tail);
log_stream.rpointer = log_stream.start;
cache_invalidate(log_stream.rpointer, len - tail);
copy_to_user((void *)(buf + tail),
(void *)(log_stream.rpointer), len - tail);
log_stream.rpointer += len - tail;
} else {
cache_invalidate(log_stream.rpointer, len);
copy_to_user((void *)buf, (void *)(log_stream.rpointer), len);
log_stream.rpointer += len;
}
set_read_pointer();
return len;
}
int pcmenc_stream_read(char __user *buffer, int size)
{
if (log_stream.start == 0)
return 0;
return internal_read(buffer, size, 0);
}
int pcmenc_stream_init(void)
{
log_stream.start =
ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_START_ADDR));
log_stream.end =
ARC_2_ARM_ADDR_SWAP(DSP_RD(DSP_DECODE_51PCM_OUT_END_ADDR));
log_stream.size = log_stream.end - log_stream.start;
return 0;
}
int pcmenc_stream_deinit(void)
{
log_stream.start = 0;
log_stream.end = 0;
log_stream.size = 0;
return 0;
}

View File

@@ -0,0 +1,62 @@
/*
* drivers/amlogic/audiodsp/pcmenc_stream.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __PCMENC_STREAM_H__
#define __PCMENC_STREAM_H__
#include <linux/uaccess.h>
struct pcm51_encoded_info_t {
unsigned int InfoValidFlag;
unsigned int SampFs;
unsigned int NumCh;
unsigned int AcMode;
unsigned int LFEFlag;
unsigned int BitsPerSamp;
};
#define AUDIODSP_PCMENC_GET_RING_BUF_SIZE _IOR('l', 0x01, unsigned long)
#define AUDIODSP_PCMENC_GET_RING_BUF_CONTENT _IOR('l', 0x02, unsigned long)
#define AUDIODSP_PCMENC_GET_RING_BUF_SPACE _IOR('l', 0x03, unsigned long)
#define AUDIODSP_PCMENC_SET_RING_BUF_RPTR _IOW('l', 0x04, unsigned long)
#define AUDIODSP_PCMENC_GET_PCMINFO _IOR('l', 0x05, unsigned long)
/* initialize stream FIFO
* return value: on success, zero is returned, on error, -1 is returned
*/
extern int pcmenc_stream_init(void);
/* return space of stream FIFO, unit:byte
*/
extern int pcmenc_stream_space(void);
/* return content of stream FIFO, unit:byte
*/
extern int pcmenc_stream_content(void);
/* deinit stream FIFO
* return value: on success, zero is returned, on error, -1 is returned
*/
extern int pcmenc_stream_deinit(void);
/*
* read data out of FIFO, the minimum of the FIFO's conten
* and size will be read, if the FIFO is empty, read will be failed
* return value: on success, the number of bytes read are returned,
* othewise, 0 is returned
*/
extern int pcmenc_stream_read(char __user *buffer, int size);
#endif /* */

View File

@@ -0,0 +1,300 @@
/*
* drivers/amlogic/audiodsp/spdif_module.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/amlogic/amports/dsp_register.h>
#include <linux/amlogic/sound/aiu_regs.h>
#include <linux/amlogic/iomap.h>
#include "spdif_module.h"
#define DEVICE_NAME "audio_spdif"
static int device_opened;
static int major_spdif;
static struct class *class_spdif;
static struct device *dev_spdif;
static struct mutex mutex_spdif;
static unsigned int iec958_wr_offset;
static inline int if_audio_output_iec958_enable(void)
{
return aml_read_cbus(AIU_MEM_IEC958_CONTROL) && (0x3 << 1);
}
static inline int if_audio_output_i2s_enable(void)
{
return aml_read_cbus(AIU_MEM_I2S_CONTROL) && (0x3 << 1);
}
static inline void audio_output_iec958_enable(unsigned int flag)
{
if (flag) {
aml_write_cbus(AIU_958_FORCE_LEFT, 0);
aml_cbus_update_bits(AIU_958_DCU_FF_CTRL, 1, 1);
aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1,
0x3 << 1);
} else {
aml_write_cbus(AIU_958_DCU_FF_CTRL, 0);
aml_cbus_update_bits(AIU_MEM_IEC958_CONTROL, 0x3 << 1, 0);
}
}
static int audio_spdif_open(struct inode *inode, struct file *file)
{
if (device_opened) {
pr_info("error,audio_spdif device busy\n");
return -EBUSY;
}
device_opened++;
try_module_get(THIS_MODULE);
return 0;
}
static int audio_spdif_release(struct inode *inode, struct file *file)
{
/* audio_enable_output(0); */
/* audio_output_iec958_enable(0); */
aml_alsa_hw_reprepare();
device_opened--;
module_put(THIS_MODULE);
IEC958_mode_codec = 0;
return 0;
}
static long audio_spdif_ioctl(struct file *file, unsigned int cmd,
unsigned long args)
{
int err = 0;
int tmp = 0;
mutex_lock(&mutex_spdif);
switch (cmd) {
case AUDIO_SPDIF_GET_958_BUF_RD_OFFSET:
tmp =
aml_read_cbus(AIU_MEM_IEC958_RD_PTR) -
aml_read_cbus(AIU_MEM_IEC958_START_PTR);
put_user(tmp, (__s32 __user *) args);
break;
case AUDIO_SPDIF_GET_958_BUF_SIZE:
/* iec958_info.iec958_buffer_size; */
tmp = aml_read_cbus(AIU_MEM_IEC958_END_PTR) -
aml_read_cbus(AIU_MEM_IEC958_START_PTR) + 64;
if (tmp == 64)
tmp = 0;
if (aml_read_cbus(AIU_MEM_IEC958_START_PTR) ==
aml_read_cbus(AIU_MEM_I2S_START_PTR)) {
tmp = tmp * 4;
}
put_user(tmp, (__s32 __user *) args);
break;
case AUDIO_SPDIF_GET_958_ENABLE_STATUS:
put_user(if_audio_output_iec958_enable(),
(__s32 __user *) args);
break;
case AUDIO_SPDIF_GET_I2S_ENABLE_STATUS:
put_user(if_audio_output_i2s_enable(), (__s32 __user *) args);
break;
case AUDIO_SPDIF_SET_958_ENABLE:
/* IEC958_mode_raw = 1; */
/* audio_enable_output(1); */
audio_output_iec958_enable(args);
break;
case AUDIO_SPDIF_SET_958_INIT_PREPARE:
IEC958_mode_codec = 3; /* dts pcm raw */
DSP_WD(DSP_IEC958_INIT_READY_INFO, 0x12345678);
aml_alsa_hw_reprepare();
DSP_WD(DSP_IEC958_INIT_READY_INFO, 0);
break;
case AUDIO_SPDIF_SET_958_WR_OFFSET:
get_user(iec958_wr_offset, (__u32 __user *) args);
break;
default:
pr_info("audio spdif: cmd not implemented\n");
break;
}
mutex_unlock(&mutex_spdif);
return err;
}
static ssize_t audio_spdif_write(struct file *file,
const char __user *userbuf, size_t len,
loff_t *off)
{
char *wr_ptr;
unsigned long wr_addr;
dma_addr_t buf_map;
wr_addr = aml_read_cbus(AIU_MEM_IEC958_START_PTR) + iec958_wr_offset;
wr_ptr = (char *)phys_to_virt(wr_addr);
if (copy_from_user((void *)wr_ptr, (void *)userbuf, len) != 0) {
pr_info("audio spdif: copy from user failed\n");
return -EINVAL;
}
buf_map = dma_map_single(NULL, (void *)wr_ptr, len, DMA_TO_DEVICE);
dma_unmap_single(NULL, buf_map, len, DMA_TO_DEVICE);
return 0;
}
static ssize_t audio_spdif_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
pr_info("audio spdif: read operation isn't supported.\n");
return -EINVAL;
}
static int audio_spdif_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned int vm_size = vma->vm_end - vma->vm_start;
if (vm_size == 0) {
pr_info("audio spdif:vm_size 0\n");
return -EAGAIN;
}
/* mapping the 958 dma buffer to user space to write */
off = aml_read_cbus(AIU_MEM_IEC958_START_PTR);
/*|VM_MAYWRITE|VM_MAYSHARE */
vma->vm_flags |=
VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
if (remap_pfn_range(vma, vma->vm_start,
off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot)) {
pr_info("audio spdif : failed remap_pfn_range\n");
return -EAGAIN;
}
pr_info("audio spdif: mmap finished\n");
pr_info("audio spdif: 958 dma buf:py addr 0x%x,vir addr 0x%x\n",
aml_read_cbus(AIU_MEM_IEC958_START_PTR), (unsigned int)
phys_to_virt(aml_read_cbus(AIU_MEM_IEC958_START_PTR)));
return 0;
}
static ssize_t audio_spdif_ptr_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret = 0;
ret = sprintf(buf, "iec958 buf runtime info:\n"
" iec958 rd ptr :\t%x\n"
" iec958 wr ptr :\t%x\n",
aml_read_cbus(AIU_MEM_IEC958_RD_PTR),
(aml_read_cbus(AIU_MEM_IEC958_START_PTR) +
iec958_wr_offset));
return ret;
}
static ssize_t audio_spdif_buf_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret = 0;
unsigned int *ptr =
(unsigned int *)phys_to_virt(
aml_read_cbus(AIU_MEM_IEC958_RD_PTR) +
iec958_wr_offset);
ret =
sprintf(buf,
"iec958 buf info:\n"
" iec958 wr ptr val:\t[%x][%x][%x][%x]\n", ptr[0], ptr[1],
ptr[2], ptr[3]);
return ret;
}
static struct class_attribute audio_spdif_attrs[] = {
__ATTR_RO(audio_spdif_ptr),
__ATTR_RO(audio_spdif_buf),
__ATTR_NULL
};
static const struct file_operations fops_spdif = {
.read = audio_spdif_read,
.unlocked_ioctl = audio_spdif_ioctl,
.write = audio_spdif_write,
.open = audio_spdif_open,
.mmap = audio_spdif_mmap,
.release = audio_spdif_release
};
static void create_audio_spdif_attrs(struct class *class)
{
int i = 0, ret;
for (i = 0; audio_spdif_attrs[i].attr.name; i++)
ret = class_create_file(class, &audio_spdif_attrs[i]);
}
static int __init audio_spdif_init_module(void)
{
void *ptr_err;
major_spdif = register_chrdev(0, DEVICE_NAME, &fops_spdif);
if (major_spdif < 0) {
pr_info("Registering spdif char device %s failed with %d\n",
DEVICE_NAME, major_spdif);
return major_spdif;
}
class_spdif = class_create(THIS_MODULE, DEVICE_NAME);
ptr_err = class_spdif;
if (IS_ERR(ptr_err))
goto err0;
create_audio_spdif_attrs(class_spdif);
dev_spdif =
device_create(class_spdif, NULL, MKDEV(major_spdif, 0), NULL,
DEVICE_NAME);
ptr_err = dev_spdif;
if (IS_ERR(ptr_err))
goto err1;
mutex_init(&mutex_spdif);
pr_info("amlogic audio spdif interface device init!\n");
return 0;
#if 0
err2:
device_destroy(class_spdif, MKDEV(major_spdif, 0));
#endif /* */
err1:
class_destroy(class_spdif);
err0:
unregister_chrdev(major_spdif, DEVICE_NAME);
return PTR_ERR(ptr_err);
}
static void __exit audio_spdif_exit_module(void)
{
device_destroy(class_spdif, MKDEV(major_spdif, 0));
class_destroy(class_spdif);
unregister_chrdev(major_spdif, DEVICE_NAME);
}
module_init(audio_spdif_init_module);
module_exit(audio_spdif_exit_module);
MODULE_DESCRIPTION("AMLOGIC IEC958 output interface driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jian.xu <jian.xu@amlogic.com>");

View File

@@ -0,0 +1,51 @@
/*
* drivers/amlogic/audiodsp/spdif_module.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __SPDIF_MODULE_H__
#define __SPDIF_MODULE_H__
struct _iec958_data_info {
unsigned int iec958_hw_start;
unsigned int iec958_hw_rd_offset;
unsigned int iec958_wr_offset;
unsigned int iec958_buffer_size;
};
#define AUDIO_SPDIF_GET_958_BUF_SIZE _IOR('s', 0x01, unsigned long)
#define AUDIO_SPDIF_GET_958_BUF_CONTENT _IOR('s', 0x02, unsigned long)
#define AUDIO_SPDIF_GET_958_BUF_SPACE _IOR('s', 0x03, unsigned long)
#define AUDIO_SPDIF_GET_958_BUF_RD_OFFSET _IOR('s', 0x04, unsigned long)
#define AUDIO_SPDIF_GET_958_ENABLE_STATUS _IOR('s', 0x05, unsigned long)
#define AUDIO_SPDIF_GET_I2S_ENABLE_STATUS _IOR('s', 0x06, unsigned long)
#define AUDIO_SPDIF_SET_958_ENABLE _IOW('s', 0x07, unsigned long)
#define AUDIO_SPDIF_SET_958_INIT_PREPARE _IOW('s', 0x08, unsigned long)
#define AUDIO_SPDIF_SET_958_WR_OFFSET _IOW('s', 0x09, unsigned long)
static int audio_spdif_open(struct inode *inode, struct file *file);
static int audio_spdif_release(struct inode *inode, struct file *file);
static long audio_spdif_ioctl(struct file *file, unsigned int cmd,
unsigned long args);
static ssize_t audio_spdif_write(struct file *file,
const char __user *userbuf, size_t len,
loff_t *off);
static ssize_t audio_spdif_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset);
static int audio_spdif_mmap(struct file *file, struct vm_area_struct *vma);
extern void aml_alsa_hw_reprepare(void);
extern void audio_enable_output(int flag);
extern unsigned int IEC958_mode_raw;
extern unsigned int IEC958_mode_codec;
#endif /* */

View File

@@ -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;