| /* |
| * 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 <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/mm.h> |
| #include <linux/interrupt.h> |
| #include <linux/mutex.h> |
| #include <linux/device.h> |
| #include <linux/timer.h> |
| #include <linux/delay.h> |
| #include <asm/cacheflush.h> |
| #include <linux/amlogic/major.h> |
| #include <linux/slab.h> |
| #include <linux/reset.h> |
| #include <linux/amlogic/media/sound/aiu_regs.h> |
| |
| /* #include <asm/dsp/audiodsp_control.h> */ |
| /* #include "audiodsp_control.h" */ |
| |
| #include <linux/uaccess.h> |
| #include <linux/amlogic/media/utils/amstream.h> |
| |
| |
| #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 <linux/dma-mapping.h> |
| #include <linux/amlogic/media/frame_sync/ptsserv.h> |
| #include <linux/amlogic/media/frame_sync/timestamp.h> |
| #include <linux/amlogic/media/frame_sync/tsync.h> |
| |
| unsigned int dsp_debug_flag = 1; |
| |
| MODULE_DESCRIPTION("AMLOGIC APOLLO Audio dsp driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Zhou Zhi <zhi.zhou@amlogic.com>"); |
| 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); |