| /* |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| /******************************************************************** |
| Copyright 2010 Broadcom Corporation. All rights reserved. |
| |
| Unless you and Broadcom execute a separate written software license agreement |
| governing use of this software, this software is licensed to you under the |
| terms of the GNU General Public License version 2, available at |
| http://www.gnu.org/copyleft/gpl.html (the "GPL"). |
| |
| Notwithstanding the above, under no circumstances may you combine this software |
| in any way with any other Broadcom software provided under a license other than |
| the GPL, without Broadcom's express prior written consent. |
| ***********************************************************************/ |
| #include <linux/types.h> |
| #include "mobcom_types.h" |
| #include "resultcode.h" |
| #include "audio_consts.h" |
| #include "csl_caph.h" |
| #include "bcm_fuse_sysparm_CIB.h" |
| #include "audio_tuning.h" |
| #include "audio_vdriver.h" |
| #include "audio_controller.h" |
| #include "audio_ddriver.h" |
| #include "audio_caph.h" |
| #include "caph_common.h" |
| #include "audio_pipe.h" |
| #include "csl_caph_dma.h" |
| |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| #include <linux/broadcom/knllog.h> |
| #endif |
| #include <mach/profile_timer.h> |
| #include <linux/proc_fs.h> |
| /*#include <linux/spinlock.h>*/ |
| |
| /* |
| * pipe info |
| */ |
| struct _AUDP_PIPE_INFO_t_ { |
| Boolean used; |
| int pipeID; /* aap use */ |
| void *handle; /* data driver use */ |
| PIPE_DIRECTION_t direction; /* 1-DL, 2- UL */ |
| PIPE_DEVICE_ID_t device; |
| PIPE_STATE_t state; |
| unsigned long period_ms; |
| AUDIO_NUM_OF_CHANNEL_t numChannels; |
| AUDIO_BITS_PER_SAMPLE_t bitsPerSample; |
| AUDIO_SAMPLING_RATE_t sampleRate; |
| TIDChanOfDev dev_prop; |
| AUDIO_DRIVER_Stats_t CBPrivate; |
| UInt32 dma_phy_addr; /* for debug only */ |
| void *privData; /* from aap */ |
| Boolean synced; /* updated in dma cb */ |
| }; |
| |
| #define AUDP_PIPE_INFO_t struct _AUDP_PIPE_INFO_t_ |
| static CB_PIPE aap_cb; |
| static AUDP_PIPE_INFO_t sAudpInfo[AUDIOP_MAX_PIPE_SUPPORT]; |
| static struct semaphore sAudpLock; /* critical region protection */ |
| |
| /* keep a record of how many pipes to send sync event to aap */ |
| static int sRunningPipes; |
| static atomic_t sArrivingPipes; |
| unsigned long lockFlags; |
| static spinlock_t sHwAADmaLock; |
| static uint16_t sPipeSynced; |
| |
| /* used to resync dma interrupts when shift happens*/ |
| /* timestamp when the prev sync event sent to aap */ |
| static unsigned int sPrevSyncEventMs; |
| static bool sPrevSynced = FALSE; |
| static unsigned long resync_count; |
| #define DMA_FRAME_PERIOD_MS 5 |
| #define DMA_RESYNC_MARGIN_MS 3 /* time reserved for aap per frame */ |
| #define TEST_HW_SRC 1 |
| |
| static void audp_set_pipe_hwparams(int pipeID, PIPE_DEVICE_ID_t id); |
| static void audp_debug_pipe(void); |
| |
| /* brutal force resync test |
| extern void csl_caph_dma_sync_transfer(void); */ |
| |
| /* |
| * Function Name: AUDP_AADMAC_CB |
| * |
| * Description: Callback funtion when DMA done, running at |
| * worker thread context (worker_audio_playback) |
| * |
| */ |
| /* #define AUDP_LOOPBACK */ |
| #ifdef AUDP_LOOPBACK |
| static char gLoopbackBuffer[480]; |
| #endif |
| static void AUDP_AADMAC_CB(void *pPrivate) |
| { |
| AUDIO_DRIVER_Stats_t *p = NULL; |
| unsigned int current_time_ms = 0; |
| int i; |
| |
| /* |
| unsigned long lock_flags; |
| spinlock_t dma_lock; |
| spin_lock_init (&dma_lock); |
| spin_lock_irqsave (&dma_lock, lock_flags); |
| */ |
| if (aap_cb && pPrivate) { |
| p = (AUDIO_DRIVER_Stats_t *) pPrivate; |
| #ifdef AUDP_LOOPBACK |
| if (sAudpInfo[p->pipeID].direction == AUDIOP_PIPE_DIR_UL) { |
| memcpy(&gLoopbackBuffer[0], |
| phys_to_virt(sAudpInfo[p->pipeID].dma_phy_addr + |
| p->buffer_idx * 480), 480); |
| } |
| #endif |
| |
| /* keep sending buffer done msg to aap for each pipe */ |
| aap_cb(AUDIOP_PIPE_BUFFER_DONE, p->pipeID, |
| (UInt32) (p->buffer_idx), (UInt32) p->dmach, |
| (UInt32) p->intr_counter, 0, |
| (uint32_t) sAudpInfo[p->pipeID].privData); |
| |
| current_time_ms = timer_get_msec(); |
| /* check if enough margin left for aap, |
| otherwise resync all dma transfers */ |
| if (sPrevSynced |
| && ((current_time_ms - sPrevSyncEventMs) < |
| DMA_RESYNC_MARGIN_MS)) { |
| /* uncomment to do brutal force resync test */ |
| csl_caph_dma_sync_transfer(); |
| atomic_set(&sArrivingPipes, 0); |
| sPrevSynced = FALSE; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG |
| ("xxxxxx RESYNC #%d current_time_ms=%d" |
| "sPrevSyncTime=%d xxxxxx\r\n", |
| ++resync_count, current_time_ms, |
| sPrevSyncEventMs); |
| #endif |
| return; |
| } |
| |
| /* reused the logic from aap */ |
| atomic_inc(&sArrivingPipes); |
| |
| /* Check if all ingress operations complete */ |
| if (atomic_read(&sArrivingPipes) >= sRunningPipes) { |
| aap_cb(AUDIOP_PIPE_SYNC_EVENT, sPipeSynced, |
| sRunningPipes, 0, 0, 0, 0); |
| sPrevSyncEventMs = timer_get_msec(); |
| sPrevSynced = TRUE; |
| |
| for (i = 0; i < AUDIOP_MAX_PIPE_SUPPORT; i++) |
| if ((sAudpInfo[i].synced == FALSE) |
| && (sAudpInfo[i].state == |
| AUDIOP_PIPE_STATE_PREPARED)) { |
| /* this api will need |
| rework to be called here */ |
| audp_start_pipe(i); |
| sAudpInfo[i].synced = TRUE; |
| sPipeSynced |= (1 << i); |
| /* break; */ |
| } |
| atomic_set(&sArrivingPipes, 0); |
| } |
| #ifdef AUDP_LOOPBACK |
| if (sAudpInfo[p->pipeID].direction == AUDIOP_PIPE_DIR_DL) { |
| memcpy(phys_to_virt |
| (sAudpInfo[p->pipeID].dma_phy_addr + |
| p->buffer_idx * 480), &gLoopbackBuffer[0], 480); |
| } |
| #endif |
| } else |
| DEBUG("AUDP_AADMAC_CB fail\r\n"); |
| |
| /* spin_unlock_irqrestore (&dma_lock, lock_flags); */ |
| |
| return; |
| } |
| |
| /* |
| * |
| * Function Name: audp_registerCB |
| * |
| * Description: called by AAP to register a CB |
| * |
| */ |
| int audp_registerCB(CB_PIPE from_aap) |
| { |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_registerCB\r\n"); |
| #endif |
| DEBUG("audp_registerCB\r\n"); |
| if (from_aap != NULL) { |
| aap_cb = from_aap; |
| sema_init(&sAudpLock, 1); |
| spin_lock_init(&sHwAADmaLock); |
| audp_debug_pipe(); |
| } else { |
| DEBUG("audp registerCB is NULL\r\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * |
| * Function Name: audp_deregister |
| * |
| * Description: called by AAP to deregister |
| * |
| */ |
| int audp_deregister() |
| { |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_deregister\r\n"); |
| #endif |
| DEBUG("audp_deregister\r\n"); |
| if (aap_cb != NULL) |
| aap_cb = NULL; |
| |
| return 0; |
| } |
| |
| /* |
| * |
| * Function Name: audp_create_pipe |
| * |
| * Description: called by alsa ctrl to create pipe |
| * |
| */ |
| int audp_create_pipe(PIPE_DEVICE_ID_t id) |
| { |
| int i = -1; |
| BRCM_AUDIO_Param_Open_t param_open; |
| TIDChanOfDev dev_prop, *pdev_prop = NULL; |
| PIPE_DIRECTION_t direction = AUDIOP_PIPE_DIR_NONE; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_create_pipe: pipe dev=0x%x\r\n", id); |
| #endif |
| DEBUG("audp_create_pipe: pipe dev=0x%x\r\n", id); |
| |
| memset(¶m_open, 0, sizeof(param_open)); |
| memset(&dev_prop, 0, sizeof(dev_prop)); |
| |
| param_open.pdev_prop = &dev_prop; |
| |
| switch (id) { |
| case AUDIOP_MIC_MAIN: |
| case AUDIOP_MIC_AUX: |
| case AUDIOP_MIC_DIGI1: |
| case AUDIOP_MIC_DIGI2: |
| case AUDIOP_MIC_DIGI3: |
| case AUDIOP_MIC_DIGI4: |
| case AUDIOP_MIC_BTM: |
| case AUDIOP_MIC_USB: |
| /* hardcoded drv type to obtain drv handle */ |
| param_open.pdev_prop->c.drv_type = AUDIO_DRIVER_CAPT_EPT; |
| AUDIO_Ctrl_Trigger(ACTION_AUD_OpenRecord, ¶m_open, NULL, 1); |
| direction = AUDIOP_PIPE_DIR_UL; |
| break; |
| case AUDIOP_SPK_HANDSET: |
| case AUDIOP_SPK_HEADSET: |
| case AUDIOP_SPK_LOUDSPK: |
| case AUDIOP_SPK_VIBRA: |
| case AUDIOP_SPK_BTM: |
| case AUDIOP_SPK_USB: |
| /* hardcoded drv type to obtain drv handle */ |
| param_open.pdev_prop->p[0].drv_type = AUDIO_DRIVER_PLAY_EPT; |
| AUDIO_Ctrl_Trigger(ACTION_AUD_OpenPlay, ¶m_open, NULL, 1); |
| direction = AUDIOP_PIPE_DIR_DL; |
| break; |
| default: |
| DEBUG("audp_create_pipe: unsupported pipe dev=0x%x\r\n", id); |
| break; |
| } |
| |
| if (aap_cb && param_open.drv_handle) { |
| down(&sAudpLock); |
| for (i = 0; i < AUDIOP_MAX_PIPE_SUPPORT; i++) |
| if (sAudpInfo[i].used == FALSE) |
| break; |
| up(&sAudpLock); |
| |
| if (i == AUDIOP_MAX_PIPE_SUPPORT) { |
| DEBUG("audp_create_pipe failed\r\n"); |
| return -1; |
| } |
| |
| down(&sAudpLock); |
| sAudpInfo[i].used = TRUE; |
| sAudpInfo[i].pipeID = i; |
| sAudpInfo[i].handle = param_open.drv_handle; |
| sAudpInfo[i].direction = direction; |
| sAudpInfo[i].device = id; |
| sAudpInfo[i].state = AUDIOP_PIPE_STATE_CREATED; |
| /* hard coded pipe params */ |
| #ifdef TEST_HW_SRC |
| sAudpInfo[i].sampleRate = AUDIO_SAMPLING_RATE_16000; |
| #else |
| sAudpInfo[i].sampleRate = AUDIO_SAMPLING_RATE_48000; |
| #endif |
| if (id & AUDIOP_SPK_HEADSET) |
| /*STEREO*/ |
| sAudpInfo[i].numChannels = AUDIO_CHANNEL_MONO; |
| else |
| sAudpInfo[i].numChannels = AUDIO_CHANNEL_MONO; |
| |
| sAudpInfo[i].bitsPerSample = 16; |
| sAudpInfo[i].period_ms = DMA_FRAME_PERIOD_MS; |
| sAudpInfo[i].CBPrivate.dmach = 0; |
| sAudpInfo[i].CBPrivate.intr_counter = 0; |
| sAudpInfo[i].CBPrivate.pipeID = sAudpInfo[i].pipeID; |
| /* hard coded device related info for this pipe */ |
| pdev_prop = &sAudpInfo[i].dev_prop; |
| if (direction & AUDIOP_PIPE_DIR_UL) { |
| /* hardcoded for test, these should |
| be filled when pipe is created */ |
| pdev_prop->c.drv_type = AUDIO_DRIVER_CAPT_EPT; |
| } |
| if (direction & AUDIOP_PIPE_DIR_DL) |
| pdev_prop->p[0].drv_type = AUDIO_DRIVER_PLAY_EPT; |
| up(&sAudpLock); |
| |
| audp_set_pipe_hwparams(sAudpInfo[i]. |
| pipeID, |
| sAudpInfo[i].device); |
| /* args: msgID, pipeID,devID, samplerate, |
| stereo/mono, bitspersecond, dir */ |
| aap_cb(AUDIOP_PIPE_CREATED, i, sAudpInfo[i].device, |
| sAudpInfo[i].sampleRate, sAudpInfo[i].numChannels, |
| sAudpInfo[i].bitsPerSample, sAudpInfo[i].direction); |
| } else |
| DEBUG("audp create pipe fail\r\n"); |
| |
| /* use drv handle for pipe id */ |
| return i; |
| } |
| |
| /* |
| * |
| * Function Name: audp_set_pipe_hwparams |
| * |
| * Description: fill in hw related info from audp to audio controller |
| * |
| */ |
| static void audp_set_pipe_hwparams(int pipeID, PIPE_DEVICE_ID_t id) |
| { |
| TIDChanOfDev *p = &sAudpInfo[pipeID].dev_prop; |
| |
| DEBUG("audp_set_pipe_hwparams pipeID=0x%x pipe_dev_id=0x%x\r\n", pipeID, |
| id); |
| |
| if (p == NULL) |
| return; |
| /* fill in params for this pipe */ |
| switch (id) { |
| case AUDIOP_MIC_MAIN: |
| p->c.source = AUDIO_SOURCE_ANALOG_MAIN; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_AUX: |
| p->c.source = AUDIO_SOURCE_ANALOG_AUX; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_DIGI1: |
| p->c.source = AUDIO_SOURCE_DIGI1; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_DIGI2: |
| p->c.source = AUDIO_SOURCE_DIGI2; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_DIGI3: |
| p->c.source = AUDIO_SOURCE_DIGI3; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_DIGI4: |
| p->c.source = AUDIO_SOURCE_DIGI4; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_BTM: |
| p->c.source = AUDIO_SOURCE_BTM; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_MIC_USB: |
| p->c.source = AUDIO_SOURCE_USB; |
| p->c.sink = AUDIO_SINK_MEM; |
| break; |
| case AUDIOP_SPK_HANDSET: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_HANDSET; |
| break; |
| case AUDIOP_SPK_HEADSET: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_HEADSET; |
| break; |
| case AUDIOP_SPK_LOUDSPK: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_LOUDSPK; |
| break; |
| case AUDIOP_SPK_VIBRA: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_VIBRA; |
| |
| break; |
| case AUDIOP_SPK_BTM: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_BTM; |
| break; |
| case AUDIOP_SPK_USB: |
| p->p[0].source = AUDIO_SOURCE_MEM; |
| p->p[0].sink = AUDIO_SINK_USB; |
| break; |
| default: |
| /* unsupported device */ |
| break; |
| } |
| return; |
| } |
| |
| /* |
| * |
| * Function Name: audp_set_buffer |
| * |
| * Description: called by AAP to set the pipe buffer |
| * |
| */ |
| int audp_set_buffer(int pipeID, dma_addr_t startAddr, uint32_t bufferSize, |
| uint32_t bufferNum, void *privData) |
| { |
| BRCM_AUDIO_Param_Prepare_t param_prepare; |
| |
| if ((aap_cb == NULL) || (privData == NULL)) |
| return -1; |
| |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG |
| ("audp_set_buffer: pipeID=0x%x startAddr=0x%x" |
| "bufferSize=0x%x bufferNum=0x%x\r\n", |
| pipeID, startAddr, bufferSize, bufferNum); |
| #endif |
| DEBUG |
| ("audp_set_buffer: pipeID=0x%x startAddr=0x%x" |
| "bufferSize=0x%x bufferNum=0x%x\r\n", |
| pipeID, startAddr, bufferSize, bufferNum); |
| |
| memset(¶m_prepare, 0, sizeof(param_prepare)); |
| down(&sAudpLock); |
| param_prepare.drv_handle = (void *)sAudpInfo[pipeID].handle; |
| param_prepare.cbParams.pfCallBack = AUDP_AADMAC_CB; |
| param_prepare.cbParams.pPrivateData = |
| (void *)(&sAudpInfo[pipeID].CBPrivate); |
| |
| param_prepare.buf_param.buf_size = bufferSize; |
| /* not needed, do not set as NULL */ |
| param_prepare.buf_param.pBuf = (UInt8 *) 0xffff; |
| /* physical address from aap */ |
| param_prepare.buf_param.phy_addr = (UInt32) (startAddr); |
| sAudpInfo[pipeID].dma_phy_addr = (UInt32) (startAddr); |
| param_prepare.drv_config.sample_rate = sAudpInfo[pipeID].sampleRate; |
| param_prepare.drv_config.num_channel = sAudpInfo[pipeID].numChannels; |
| param_prepare.drv_config.bits_per_sample = |
| sAudpInfo[pipeID].bitsPerSample; |
| |
| /* may not need this param. can be derived from others */ |
| param_prepare.period_bytes = |
| sAudpInfo[pipeID].period_ms * |
| (param_prepare.drv_config.sample_rate / 1000) |
| * (param_prepare.drv_config.num_channel) * 2; |
| |
| sAudpInfo[pipeID].privData = privData; |
| |
| AUDIO_Ctrl_Trigger(ACTION_AUD_SetPrePareParameters, ¶m_prepare, |
| NULL, 1); |
| sAudpInfo[pipeID].state = AUDIOP_PIPE_STATE_PREPARED; |
| up(&sAudpLock); |
| |
| /* for internal test, not needed by aap */ |
| aap_cb(AUDIOP_PIPE_SETBUFDONE, pipeID, (UInt32) startAddr, 0, 0, 0, |
| (uint32_t) privData); |
| /* start the pilot pipe first, the later pipes will be |
| started in dmacb to sync with each other */ |
| if (sRunningPipes == 0) { |
| audp_start_pipe(pipeID); |
| sAudpInfo[pipeID].synced = TRUE; |
| sPipeSynced = 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * |
| * Function Name: audp_start_pipe |
| * |
| * Description: called by aap to start pilot pipe, or |
| * by dma cb to start synced pipes |
| * |
| * |
| */ |
| void audp_start_pipe(int pipeID) |
| { |
| BRCM_AUDIO_Param_Start_t param_start; |
| PIPE_DIRECTION_t direction = AUDIOP_PIPE_DIR_NONE; |
| void *privData = NULL; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_start_pipe: pipe_id=0x%x\r\n", pipeID); |
| #endif |
| memset(¶m_start, 0, sizeof(param_start)); |
| |
| down(&sAudpLock); |
| param_start.drv_handle = sAudpInfo[pipeID].handle; |
| param_start.pdev_prop = &sAudpInfo[pipeID].dev_prop; |
| param_start.channels = sAudpInfo[pipeID].numChannels; |
| param_start.rate = sAudpInfo[pipeID].sampleRate; |
| direction = sAudpInfo[pipeID].direction; |
| sAudpInfo[pipeID].state = AUDIOP_PIPE_STATE_STARTED; |
| privData = sAudpInfo[pipeID].privData; |
| sRunningPipes++; |
| up(&sAudpLock); |
| |
| /* pump the gain for lpbk test with aap */ |
| /* for mic, it is hardcoded in Q13.2 format */ |
| if (direction == AUDIOP_PIPE_DIR_UL) { |
| param_start.vol[0] = 80; |
| param_start.vol[1] = 80; |
| } |
| if (direction == AUDIOP_PIPE_DIR_DL) { |
| param_start.vol[0] = 20; |
| param_start.vol[1] = 20; |
| } |
| |
| if (direction == AUDIOP_PIPE_DIR_UL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_StartRecord, ¶m_start, NULL, |
| 0); |
| else if (direction == AUDIOP_PIPE_DIR_DL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_StartPlay, ¶m_start, NULL, 0); |
| |
| if (aap_cb && param_start.drv_handle) |
| aap_cb(AUDIOP_PIPE_STARTED, pipeID, 0, 0, 0, 0, |
| (uint32_t) privData); |
| else |
| DEBUG("audp start pipe fail\r\n"); |
| |
| } |
| |
| /* |
| * |
| * Function Name: audp_stop_pipe |
| * |
| * Description: called by alsa ctrl to stop pipe |
| * |
| */ |
| void audp_stop_pipe(int pipeID) |
| { |
| BRCM_AUDIO_Param_Stop_t param_stop; |
| TIDChanOfDev dev_prop; |
| PIPE_DIRECTION_t direction = AUDIOP_PIPE_DIR_NONE; |
| void *privData = NULL; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_stop_pipe: pipe_id=0x%x\r\n", pipeID); |
| #endif |
| DEBUG("audp_stop_pipe: pipe_id=0x%x\r\n", pipeID); |
| |
| memset(¶m_stop, 0, sizeof(param_stop)); |
| memset(&dev_prop, 0, sizeof(dev_prop)); |
| down(&sAudpLock); |
| param_stop.drv_handle = sAudpInfo[pipeID].handle; |
| /* param_stop.pdev_prop = &sAudpInfo[pipeID].dev_prop;*/ |
| memcpy(&dev_prop, &sAudpInfo[pipeID].dev_prop, sizeof(dev_prop)); |
| param_stop.pdev_prop = &dev_prop; |
| direction = sAudpInfo[pipeID].direction; |
| sAudpInfo[pipeID].state = AUDIOP_PIPE_STATE_STOPPED; |
| privData = sAudpInfo[pipeID].privData; |
| sRunningPipes--; |
| up(&sAudpLock); |
| |
| if (direction == AUDIOP_PIPE_DIR_UL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_StopRecord, ¶m_stop, NULL, 1); |
| else if (direction == AUDIOP_PIPE_DIR_DL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_StopPlay, ¶m_stop, NULL, 1); |
| |
| if (aap_cb) |
| aap_cb(AUDIOP_PIPE_STOPPED, pipeID, 0, 0, 0, 0, |
| (uint32_t) privData); |
| else |
| DEBUG("audp stop pipe fail\r\n"); |
| down(&sAudpLock); |
| sPipeSynced &= ~(1 << pipeID); |
| up(&sAudpLock); |
| } |
| |
| /* |
| * |
| * Function Name: audp_destroy_pipe |
| * |
| * Description: called by alsa ctrl to destroy pipe |
| * |
| */ |
| void audp_destroy_pipe(int pipeID) |
| { |
| BRCM_AUDIO_Param_Close_t param_close; |
| PIPE_DIRECTION_t direction = AUDIOP_PIPE_DIR_NONE; |
| void *privData = NULL; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_destroy_pipe: pipe_id=0x%x\r\n", pipeID); |
| #endif |
| DEBUG("audp_destroy_pipe: pipe_id=0x%x\r\n", pipeID); |
| |
| memset(¶m_close, 0, sizeof(param_close)); |
| down(&sAudpLock); |
| param_close.drv_handle = sAudpInfo[pipeID].handle; |
| direction = sAudpInfo[pipeID].direction; |
| sAudpInfo[pipeID].state = AUDIOP_PIPE_STATE_DESTROYED; |
| privData = sAudpInfo[pipeID].privData; |
| up(&sAudpLock); |
| if (direction == AUDIOP_PIPE_DIR_UL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_CloseRecord, ¶m_close, NULL, |
| 1); |
| else if (direction == AUDIOP_PIPE_DIR_DL) |
| AUDIO_Ctrl_Trigger(ACTION_AUD_ClosePlay, ¶m_close, NULL, 1); |
| |
| if (aap_cb) |
| aap_cb(AUDIOP_PIPE_DESTROYED, pipeID, 0, 0, 0, 0, |
| (uint32_t) privData); |
| else |
| DEBUG("audp destroy pipe fail\r\n"); |
| |
| down(&sAudpLock); |
| memset(&sAudpInfo[pipeID], 0, sizeof(sAudpInfo[pipeID])); |
| up(&sAudpLock); |
| |
| } |
| |
| /* |
| * |
| * Function Name: audp_device_change_notify |
| * |
| * Description: called by alsa ctrl to notify aap |
| * |
| */ |
| void audp_device_change_notify(PIPE_DEVICE_ID_t new_dev, |
| PIPE_DEVICE_ID_t current_dev) |
| { |
| KNLLOG("audp_device_change_notify: new_dev=0x%x curr_dev=0x%x\r\n", |
| new_dev, current_dev); |
| |
| if (aap_cb) |
| aap_cb(AUDIOP_PIPE_DEVICE_CHANGE, 0, (UInt32) new_dev, |
| (UInt32) current_dev, 0, 0, 0); |
| else |
| DEBUG("audp cb null\r\n"); |
| } |
| |
| /* |
| * |
| * Function Name: audp_device_change_finished |
| * |
| * Description: called by alsa ctrl to notify aap |
| * |
| */ |
| void audp_device_change_finished(PIPE_DEVICE_ID_t current_dev, |
| PIPE_DEVICE_ID_t old_dev) |
| { |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_device_change_finished: curr_dev=0x%x old_dev=0x%x\r\n", |
| current_dev, old_dev); |
| #endif |
| if (aap_cb) |
| aap_cb(AUDIOP_PIPE_DEVICE_CHANGED, 0, (UInt32) current_dev, |
| (UInt32) old_dev, 0, 0, 0); |
| else |
| DEBUG("audp cb null\r\n"); |
| } |
| |
| /* |
| * |
| * Function Name: audp_get_pipeID |
| * |
| * Description: get the pipe id from audp dev |
| * |
| */ |
| int audp_get_pipeID(PIPE_DEVICE_ID_t dev) |
| { |
| int i, pipeID = -1; |
| down(&sAudpLock); |
| for (i = 0; i < AUDIOP_MAX_PIPE_SUPPORT; i++) |
| /* assume dev and pipe id are 1 to 1 mapped */ |
| if (sAudpInfo[i].device == dev) { |
| pipeID = sAudpInfo[i].pipeID; |
| break; |
| } |
| up(&sAudpLock); |
| return pipeID; |
| } |
| |
| /* |
| * |
| * Function Name: audctrl2audp |
| * |
| * Description: map audio control dev to audp |
| * |
| */ |
| static PIPE_DEVICE_ID_t audctrl2audp(long param, PIPE_DIRECTION_t direction) |
| { |
| PIPE_DEVICE_ID_t dev = AUDIOP_DEVICE_NONE; |
| if (direction & AUDIOP_PIPE_DIR_UL) |
| switch ((AUDIO_SOURCE_Enum_t) param) { |
| case AUDIO_SOURCE_ANALOG_MAIN: |
| dev = AUDIOP_MIC_MAIN; |
| break; |
| case AUDIO_SOURCE_ANALOG_AUX: |
| dev = AUDIOP_MIC_AUX; |
| break; |
| case AUDIO_SOURCE_DIGI1: |
| dev = AUDIOP_MIC_DIGI1; |
| break; |
| case AUDIO_SOURCE_DIGI2: |
| dev = AUDIOP_MIC_DIGI2; |
| break; |
| case AUDIO_SOURCE_DIGI3: |
| dev = AUDIOP_MIC_DIGI3; |
| break; |
| case AUDIO_SOURCE_DIGI4: |
| dev = AUDIOP_MIC_DIGI4; |
| break; |
| case AUDIO_SOURCE_BTM: |
| dev = AUDIOP_MIC_BTM; |
| break; |
| case AUDIO_SOURCE_USB: |
| dev = AUDIOP_MIC_USB; |
| break; |
| default: |
| break; |
| }; |
| |
| if (direction & AUDIOP_PIPE_DIR_DL) |
| switch ((AUDIO_SINK_Enum_t) param) { |
| case AUDIO_SINK_HANDSET: |
| dev = AUDIOP_SPK_HANDSET; |
| break; |
| case AUDIO_SINK_HEADSET: |
| dev = AUDIOP_SPK_HEADSET; |
| break; |
| case AUDIO_SINK_LOUDSPK: |
| dev = AUDIOP_SPK_LOUDSPK; |
| break; |
| case AUDIO_SINK_VIBRA: |
| dev = AUDIOP_SPK_VIBRA; |
| break; |
| case AUDIO_SINK_BTM: |
| dev = AUDIOP_SPK_BTM; |
| break; |
| case AUDIO_SINK_USB: |
| dev = AUDIOP_SPK_USB; |
| break; |
| default: |
| break; |
| }; |
| |
| return dev; |
| } |
| |
| /* |
| * |
| * Function Name: audp_remove_pipe |
| * |
| * Description: remove a pipe. |
| * it first stops the pipe, then closes/destroys the pipe |
| * |
| */ |
| void audp_remove_pipe(PIPE_DEVICE_ID_t id) |
| { |
| int pipeID; |
| /* need api to map from dev id to pipeID */ |
| pipeID = audp_get_pipeID(id); |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_remove_pipe dev id=0x%x pipeID=0x%x \r\n", id, pipeID); |
| #endif |
| DEBUG("audp_remove_pipe: pipe_id=0x%x\r\n", pipeID); |
| |
| audp_stop_pipe(pipeID); |
| audp_destroy_pipe(pipeID); |
| } |
| |
| /* |
| * |
| * Function Name: audp_ctrl_handler |
| * |
| * Description: entry point of audp. handles audp control from amixer control |
| * 1. support 2 kinds of control: create(add) and remove |
| * 2. switch will be done in 2 steps |
| * a. remove the current pipe b. create or add new pipe |
| * |
| */ |
| int audp_ctrl_handler(long action, long param, PIPE_DIRECTION_t direction) |
| { |
| PIPE_DEVICE_ID_t dev = AUDIOP_DEVICE_NONE; |
| #if defined(CONFIG_BCM_KNLLOG_SUPPORT) |
| KNLLOG("audp_ctrl_handler action=0x%lx param=0x%lx direction=0x%x\r\n", |
| action, param, direction); |
| #endif |
| DEBUG("audp_ctrl_handler action=0x%lx param=0x%lx direction=0x%x\r\n", |
| action, param, direction); |
| |
| dev = audctrl2audp(param, direction); |
| |
| if (action == 1) { /* create pipe */ |
| if (audp_get_pipeID(dev) < 0) |
| audp_create_pipe(dev); |
| else { |
| DEBUG |
| ("audp_ctrl_handler: pipe already" |
| "created for 0x%x\r\n", |
| dev); |
| return -1; |
| } |
| } else if (action == 2) { /* remove pipe */ |
| if (audp_get_pipeID(dev) < 0) { |
| DEBUG |
| ("audp_ctrl_handler: pipe did " |
| "not exist for 0x%x\r\n", |
| dev); |
| return -1; |
| } else |
| audp_remove_pipe(dev); |
| } else { |
| DEBUG("audp_ctrl_handler: unsupported action 0x%lx\r\n", |
| action); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * |
| * Function Name: audp_proc_read_pipes |
| * |
| * Description: read all pipe info in sAudpInfo |
| * |
| */ |
| static int audp_proc_read_pipes(char *buf, char **start, off_t offset, |
| int count, int *eof, void *data) |
| { |
| int limit = count - 200; |
| int len = 0; |
| int i = 0; |
| |
| down(&sAudpLock); |
| |
| for (i = 0; i < AUDIOP_MAX_PIPE_SUPPORT; i++) { |
| if (len >= limit) |
| break; |
| |
| len += sprintf(buf + len, "Pipe: %d ", sAudpInfo[i].pipeID); |
| |
| len += sprintf(buf + len, "Used: %d ", sAudpInfo[i].used); |
| len += sprintf(buf + len, "State: %d ", sAudpInfo[i].state); |
| len += sprintf(buf + len, "Dev: %d ", sAudpInfo[i].device); |
| len += |
| sprintf(buf + len, "Int#: %ld ", |
| sAudpInfo[i].CBPrivate.intr_counter); |
| len += |
| sprintf(buf + len, "DmaCh: %d ", |
| sAudpInfo[i].CBPrivate.dmach); |
| len += |
| sprintf(buf + len, "buf_idx: %d ", |
| sAudpInfo[i].CBPrivate.buffer_idx); |
| len += sprintf(buf + len, "resync: %ld ", resync_count); |
| len += sprintf(buf + len, "\n"); |
| } |
| up(&sAudpLock); |
| *eof = 1; |
| |
| return len; |
| |
| } |
| |
| /* |
| * |
| * Function Name: audp_debug_pipe |
| * |
| * Description: a simple debug api |
| * |
| */ |
| static void audp_debug_pipe() |
| { |
| static struct proc_dir_entry *gAudpDir; |
| |
| /* Create /proc/audp/pipes and /proc/audp/devices */ |
| gAudpDir = create_proc_entry("audp", S_IFDIR | S_IRUGO | S_IXUGO, NULL); |
| |
| if (gAudpDir == NULL) { |
| printk(KERN_ERR "Unable to create /proc/audp\n"); |
| } else { |
| create_proc_read_entry("pipes", 0, gAudpDir, |
| audp_proc_read_pipes, NULL); |
| } |
| } |