sound: Add MSM sound drivers
Signed-off-by: Patrick Lai <plai@codeaurora.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
eca1f9adfe
commit
dcb77e20ae
@@ -8,3 +8,6 @@ header-y += sb16_csp.h
|
||||
header-y += sfnt_info.h
|
||||
header-y += compress_params.h
|
||||
header-y += compress_offload.h
|
||||
header-y += tlv.h
|
||||
header-y += compress_params.h
|
||||
header-y += compress_offload.h
|
||||
|
||||
6172
include/sound/apr_audio-v2.h
Normal file
6172
include/sound/apr_audio-v2.h
Normal file
File diff suppressed because it is too large
Load Diff
1538
include/sound/apr_audio.h
Normal file
1538
include/sound/apr_audio.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -108,6 +108,7 @@
|
||||
#define CS8427_SIDEL (1<<2) /* Delay of SDIN data relative to ILRCK for left-justified data formats, 0 = first ISCLK period, 1 = second ISCLK period */
|
||||
#define CS8427_SISPOL (1<<1) /* ICLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */
|
||||
#define CS8427_SILRPOL (1<<0) /* ILRCK clock polarity, 0 = SDIN data left channel when ILRCK is high, 1 = SDIN right when ILRCK is high */
|
||||
#define CS8427_BITWIDTH_MASK 0xCF
|
||||
|
||||
/* CS8427_REG_SERIALOUTPUT */
|
||||
#define CS8427_SOMS (1<<7) /* 0 = slave, 1 = master mode */
|
||||
@@ -186,6 +187,31 @@
|
||||
#define CS8427_VERSHIFT 0
|
||||
#define CS8427_VER8427A 0x71
|
||||
|
||||
/* possible address cs8427 can take
|
||||
* based on the below combinations the upper four bits of 7bit
|
||||
* address will be fixed for 0010b, abd lower 3 bits will decide
|
||||
* the address combination based on the AD0 and AD1 and EMPH(AD2)
|
||||
* Hardware pin configuration to cs8427 chip
|
||||
*/
|
||||
#define CS8427_ADDR0 0x10
|
||||
#define CS8427_ADDR1 0x11
|
||||
#define CS8427_ADDR2 0x12
|
||||
#define CS8427_ADDR3 0x13
|
||||
#define CS8427_ADDR4 0x14
|
||||
#define CS8427_ADDR5 0x15
|
||||
#define CS8427_ADDR6 0x16
|
||||
#define CS8427_ADDR7 0x17
|
||||
|
||||
#define CHANNEL_STATUS_SIZE 24
|
||||
|
||||
struct cs8427_platform_data {
|
||||
int irq;
|
||||
int irq_base;
|
||||
int num_irqs;
|
||||
int reset_gpio;
|
||||
int (*enable) (int enable);
|
||||
};
|
||||
|
||||
struct snd_pcm_substream;
|
||||
|
||||
int snd_cs8427_create(struct snd_i2c_bus *bus, unsigned char addr,
|
||||
@@ -197,5 +223,4 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,
|
||||
struct snd_pcm_substream *capture_substream);
|
||||
int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active);
|
||||
int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate);
|
||||
|
||||
#endif /* __SOUND_CS8427_H */
|
||||
|
||||
49
include/sound/dai.h
Normal file
49
include/sound/dai.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __DAI_H__
|
||||
#define __DAI_H__
|
||||
|
||||
struct dai_dma_params {
|
||||
u8 *buffer;
|
||||
uint32_t src_start;
|
||||
uint32_t bus_id;
|
||||
int buffer_size;
|
||||
int period_size;
|
||||
int channels;
|
||||
};
|
||||
|
||||
enum {
|
||||
DAI_SPKR = 0,
|
||||
DAI_MIC,
|
||||
DAI_MI2S,
|
||||
DAI_SEC_SPKR,
|
||||
DAI_SEC_MIC,
|
||||
};
|
||||
|
||||
/* Function Prototypes */
|
||||
int dai_open(uint32_t dma_ch);
|
||||
void dai_close(uint32_t dma_ch);
|
||||
int dai_start(uint32_t dma_ch);
|
||||
int dai_stop(uint32_t dma_ch);
|
||||
int dai_set_params(uint32_t dma_ch, struct dai_dma_params *params);
|
||||
uint32_t dai_get_dma_pos(uint32_t dma_ch);
|
||||
void register_dma_irq_handler(int dma_ch,
|
||||
irqreturn_t (*callback) (int intrSrc, void *private_data),
|
||||
void *private_data);
|
||||
void unregister_dma_irq_handler(int dma_ch);
|
||||
void dai_set_master_mode(uint32_t dma_ch, int mode);
|
||||
int dai_start_hdmi(uint32_t dma_ch);
|
||||
int wait_for_dma_cnt_stop(uint32_t dma_ch);
|
||||
void dai_stop_hdmi(uint32_t dma_ch);
|
||||
|
||||
#endif
|
||||
42
include/sound/msm-dai-q6-v2.h
Normal file
42
include/sound/msm-dai-q6-v2.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __MSM_DAI_Q6_PDATA_H__
|
||||
|
||||
#define __MSM_DAI_Q6_PDATA_H__
|
||||
|
||||
#define MSM_MI2S_SD0 (1 << 0)
|
||||
#define MSM_MI2S_SD1 (1 << 1)
|
||||
#define MSM_MI2S_SD2 (1 << 2)
|
||||
#define MSM_MI2S_SD3 (1 << 3)
|
||||
#define MSM_MI2S_CAP_RX 0
|
||||
#define MSM_MI2S_CAP_TX 1
|
||||
|
||||
struct msm_dai_auxpcm_pdata {
|
||||
const char *clk;
|
||||
u16 mode;
|
||||
u16 sync;
|
||||
u16 frame;
|
||||
u16 quant;
|
||||
/* modify slot to arr[4] to specify
|
||||
* the slot number for each channel
|
||||
* in multichannel scenario */
|
||||
u16 slot;
|
||||
u16 data;
|
||||
int pcm_clk_rate;
|
||||
};
|
||||
|
||||
struct msm_i2s_data {
|
||||
u32 capability; /* RX or TX */
|
||||
u16 sd_lines;
|
||||
};
|
||||
#endif
|
||||
45
include/sound/msm-dai-q6.h
Normal file
45
include/sound/msm-dai-q6.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __MSM_DAI_Q6_PDATA_H__
|
||||
|
||||
#define __MSM_DAI_Q6_PDATA_H__
|
||||
|
||||
#define MSM_MI2S_SD0 (1 << 0)
|
||||
#define MSM_MI2S_SD1 (1 << 1)
|
||||
#define MSM_MI2S_SD2 (1 << 2)
|
||||
#define MSM_MI2S_SD3 (1 << 3)
|
||||
#define MSM_MI2S_CAP_RX 0
|
||||
#define MSM_MI2S_CAP_TX 1
|
||||
|
||||
struct msm_dai_auxpcm_config {
|
||||
u16 mode;
|
||||
u16 sync;
|
||||
u16 frame;
|
||||
u16 quant;
|
||||
u16 slot;
|
||||
u16 data;
|
||||
int pcm_clk_rate;
|
||||
};
|
||||
|
||||
struct msm_mi2s_pdata {
|
||||
u16 rx_sd_lines;
|
||||
u16 tx_sd_lines;
|
||||
};
|
||||
|
||||
struct msm_dai_auxpcm_pdata {
|
||||
const char *clk;
|
||||
struct msm_dai_auxpcm_config mode_8k;
|
||||
struct msm_dai_auxpcm_config mode_16k;
|
||||
};
|
||||
|
||||
#endif
|
||||
19
include/sound/omap-abe-dsp.h
Normal file
19
include/sound/omap-abe-dsp.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* omap-aess -- OMAP4 ABE DSP
|
||||
*
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _OMAP4_ABE_DSP_H
|
||||
#define _OMAP4_ABE_DSP_H
|
||||
|
||||
struct omap4_abe_dsp_pdata {
|
||||
/* Return context loss count due to PM states changing */
|
||||
int (*get_context_loss_count)(struct device *dev);
|
||||
};
|
||||
|
||||
#endif
|
||||
50
include/sound/q6adm-v2.h
Normal file
50
include/sound/q6adm-v2.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6_ADM_V2_H__
|
||||
#define __Q6_ADM_V2_H__
|
||||
|
||||
|
||||
#define ADM_PATH_PLAYBACK 0x1
|
||||
#define ADM_PATH_LIVE_REC 0x2
|
||||
#define ADM_PATH_NONLIVE_REC 0x3
|
||||
#include <sound/q6audio-v2.h>
|
||||
|
||||
#define Q6_AFE_MAX_PORTS 32
|
||||
|
||||
/* multiple copp per stream. */
|
||||
struct route_payload {
|
||||
unsigned int copp_ids[Q6_AFE_MAX_PORTS];
|
||||
unsigned short num_copps;
|
||||
unsigned int session_id;
|
||||
};
|
||||
|
||||
int adm_open(int port, int path, int rate, int mode, int topology);
|
||||
|
||||
int adm_multi_ch_copp_open(int port, int path, int rate, int mode,
|
||||
int topology);
|
||||
|
||||
int adm_memory_map_regions(int port_id, uint32_t *buf_add, uint32_t mempool_id,
|
||||
uint32_t *bufsz, uint32_t bufcnt);
|
||||
|
||||
int adm_memory_unmap_regions(int port_id, uint32_t *buf_add, uint32_t *bufsz,
|
||||
uint32_t bufcnt);
|
||||
|
||||
int adm_close(int port);
|
||||
|
||||
int adm_matrix_map(int session_id, int path, int num_copps,
|
||||
unsigned int *port_id, int copp_id);
|
||||
|
||||
int adm_connect_afe_port(int mode, int session_id, int port_id);
|
||||
|
||||
int adm_get_copp_id(int port_id);
|
||||
|
||||
#endif /* __Q6_ADM_V2_H__ */
|
||||
49
include/sound/q6adm.h
Normal file
49
include/sound/q6adm.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Copyright (c) 2010-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6_ADM_H__
|
||||
#define __Q6_ADM_H__
|
||||
#include <sound/q6afe.h>
|
||||
|
||||
#define ADM_PATH_PLAYBACK 0x1
|
||||
#define ADM_PATH_LIVE_REC 0x2
|
||||
#define ADM_PATH_NONLIVE_REC 0x3
|
||||
|
||||
/* multiple copp per stream. */
|
||||
struct route_payload {
|
||||
unsigned int copp_ids[AFE_MAX_PORTS];
|
||||
unsigned short num_copps;
|
||||
unsigned int session_id;
|
||||
};
|
||||
|
||||
int adm_open(int port, int path, int rate, int mode, int topology);
|
||||
|
||||
int adm_multi_ch_copp_open(int port, int path, int rate, int mode,
|
||||
int topology);
|
||||
|
||||
int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id,
|
||||
uint32_t *bufsz, uint32_t bufcnt);
|
||||
|
||||
int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz,
|
||||
uint32_t bufcnt);
|
||||
|
||||
int adm_close(int port);
|
||||
|
||||
int adm_matrix_map(int session_id, int path, int num_copps,
|
||||
unsigned int *port_id, int copp_id);
|
||||
|
||||
int adm_connect_afe_port(int mode, int session_id, int port_id);
|
||||
|
||||
#ifdef CONFIG_RTAC
|
||||
int adm_get_copp_id(int port_id);
|
||||
#endif
|
||||
|
||||
#endif /* __Q6_ADM_H__ */
|
||||
107
include/sound/q6afe-v2.h
Normal file
107
include/sound/q6afe-v2.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6AFE_V2_H__
|
||||
#define __Q6AFE_V2_H__
|
||||
#include <sound/apr_audio-v2.h>
|
||||
|
||||
#define MSM_AFE_MONO 0
|
||||
#define MSM_AFE_MONO_RIGHT 1
|
||||
#define MSM_AFE_MONO_LEFT 2
|
||||
#define MSM_AFE_STEREO 3
|
||||
#define MSM_AFE_4CHANNELS 4
|
||||
#define MSM_AFE_6CHANNELS 6
|
||||
#define MSM_AFE_8CHANNELS 8
|
||||
|
||||
#define MSM_AFE_I2S_FORMAT_LPCM 0
|
||||
#define MSM_AFE_I2S_FORMAT_COMPR 1
|
||||
#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM 2
|
||||
#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR 3
|
||||
|
||||
#define MSM_AFE_PORT_TYPE_RX 0
|
||||
#define MSM_AFE_PORT_TYPE_TX 1
|
||||
|
||||
#define RT_PROXY_DAI_001_RX 0xE0
|
||||
#define RT_PROXY_DAI_001_TX 0xF0
|
||||
#define RT_PROXY_DAI_002_RX 0xF1
|
||||
#define RT_PROXY_DAI_002_TX 0xE1
|
||||
#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000)
|
||||
|
||||
enum {
|
||||
IDX_PRIMARY_I2S_RX = 0,
|
||||
IDX_PRIMARY_I2S_TX = 1,
|
||||
IDX_PCM_RX = 2,
|
||||
IDX_PCM_TX = 3,
|
||||
IDX_SECONDARY_I2S_RX = 4,
|
||||
IDX_SECONDARY_I2S_TX = 5,
|
||||
IDX_MI2S_RX = 6,
|
||||
IDX_MI2S_TX = 7,
|
||||
IDX_HDMI_RX = 8,
|
||||
IDX_RSVD_2 = 9,
|
||||
IDX_RSVD_3 = 10,
|
||||
IDX_DIGI_MIC_TX = 11,
|
||||
IDX_VOICE_RECORD_RX = 12,
|
||||
IDX_VOICE_RECORD_TX = 13,
|
||||
IDX_VOICE_PLAYBACK_TX = 14,
|
||||
IDX_SLIMBUS_0_RX = 15,
|
||||
IDX_SLIMBUS_0_TX = 16,
|
||||
IDX_SLIMBUS_1_RX = 17,
|
||||
IDX_SLIMBUS_1_TX = 18,
|
||||
IDX_SLIMBUS_2_RX = 19,
|
||||
IDX_SLIMBUS_2_TX = 20,
|
||||
IDX_SLIMBUS_3_RX = 21,
|
||||
IDX_SLIMBUS_3_TX = 22,
|
||||
IDX_SLIMBUS_4_RX = 23,
|
||||
IDX_SLIMBUS_4_TX = 24,
|
||||
IDX_INT_BT_SCO_RX = 25,
|
||||
IDX_INT_BT_SCO_TX = 26,
|
||||
IDX_INT_BT_A2DP_RX = 27,
|
||||
IDX_INT_FM_RX = 28,
|
||||
IDX_INT_FM_TX = 29,
|
||||
IDX_RT_PROXY_PORT_001_RX = 30,
|
||||
IDX_RT_PROXY_PORT_001_TX = 31,
|
||||
AFE_MAX_PORTS
|
||||
};
|
||||
|
||||
int afe_open(u16 port_id, union afe_port_config *afe_config, int rate);
|
||||
int afe_close(int port_id);
|
||||
int afe_loopback(u16 enable, u16 rx_port, u16 tx_port);
|
||||
int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain);
|
||||
int afe_loopback_gain(u16 port_id, u16 volume);
|
||||
int afe_validate_port(u16 port_id);
|
||||
int afe_start_pseudo_port(u16 port_id);
|
||||
int afe_stop_pseudo_port(u16 port_id);
|
||||
int afe_cmd_memory_map(u32 dma_addr_p, u32 dma_buf_sz);
|
||||
int afe_cmd_memory_map_nowait(int port_id, u32 dma_addr_p, u32 dma_buf_sz);
|
||||
int afe_cmd_memory_unmap(u32 dma_addr_p);
|
||||
int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
|
||||
|
||||
int afe_register_get_events(u16 port_id,
|
||||
void (*cb) (uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv),
|
||||
void *private_data);
|
||||
int afe_unregister_get_events(u16 port_id);
|
||||
int afe_rt_proxy_port_write(u32 buf_addr_p, u32 mem_map_handle, int bytes);
|
||||
int afe_rt_proxy_port_read(u32 buf_addr_p, u32 mem_map_handle, int bytes);
|
||||
int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
|
||||
u32 rate);
|
||||
int afe_port_stop_nowait(int port_id);
|
||||
int afe_apply_gain(u16 port_id, u16 gain);
|
||||
int afe_q6_interface_prepare(void);
|
||||
int afe_get_port_type(u16 port_id);
|
||||
/* if port_id is virtual, convert to physical..
|
||||
* if port_id is already physical, return physical
|
||||
*/
|
||||
int afe_convert_virtual_to_portid(u16 port_id);
|
||||
|
||||
int afe_pseudo_port_start_nowait(u16 port_id);
|
||||
int afe_pseudo_port_stop_nowait(u16 port_id);
|
||||
#endif /* __Q6AFE_V2_H__ */
|
||||
111
include/sound/q6afe.h
Normal file
111
include/sound/q6afe.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* Copyright (c) 2010-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6AFE_H__
|
||||
#define __Q6AFE_H__
|
||||
#include <sound/apr_audio.h>
|
||||
|
||||
#define MSM_AFE_MONO 0
|
||||
#define MSM_AFE_MONO_RIGHT 1
|
||||
#define MSM_AFE_MONO_LEFT 2
|
||||
#define MSM_AFE_STEREO 3
|
||||
#define MSM_AFE_4CHANNELS 4
|
||||
#define MSM_AFE_6CHANNELS 6
|
||||
#define MSM_AFE_8CHANNELS 8
|
||||
|
||||
#define MSM_AFE_I2S_FORMAT_LPCM 0
|
||||
#define MSM_AFE_I2S_FORMAT_COMPR 1
|
||||
#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM 2
|
||||
#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR 3
|
||||
|
||||
#define MSM_AFE_PORT_TYPE_RX 0
|
||||
#define MSM_AFE_PORT_TYPE_TX 1
|
||||
|
||||
#define RT_PROXY_DAI_001_RX 0xE0
|
||||
#define RT_PROXY_DAI_001_TX 0xF0
|
||||
#define RT_PROXY_DAI_002_RX 0xF1
|
||||
#define RT_PROXY_DAI_002_TX 0xE1
|
||||
#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000)
|
||||
|
||||
enum {
|
||||
IDX_PRIMARY_I2S_RX = 0,
|
||||
IDX_PRIMARY_I2S_TX = 1,
|
||||
IDX_PCM_RX = 2,
|
||||
IDX_PCM_TX = 3,
|
||||
IDX_SECONDARY_I2S_RX = 4,
|
||||
IDX_SECONDARY_I2S_TX = 5,
|
||||
IDX_MI2S_RX = 6,
|
||||
IDX_MI2S_TX = 7,
|
||||
IDX_HDMI_RX = 8,
|
||||
IDX_RSVD_2 = 9,
|
||||
IDX_RSVD_3 = 10,
|
||||
IDX_DIGI_MIC_TX = 11,
|
||||
IDX_VOICE_RECORD_RX = 12,
|
||||
IDX_VOICE_RECORD_TX = 13,
|
||||
IDX_VOICE_PLAYBACK_TX = 14,
|
||||
IDX_SLIMBUS_0_RX = 15,
|
||||
IDX_SLIMBUS_0_TX = 16,
|
||||
IDX_SLIMBUS_1_RX = 17,
|
||||
IDX_SLIMBUS_1_TX = 18,
|
||||
IDX_SLIMBUS_2_RX = 19,
|
||||
IDX_SLIMBUS_2_TX = 20,
|
||||
IDX_SLIMBUS_3_RX = 21,
|
||||
IDX_SLIMBUS_3_TX = 22,
|
||||
IDX_SLIMBUS_4_RX = 23,
|
||||
IDX_SLIMBUS_4_TX = 24,
|
||||
IDX_INT_BT_SCO_RX = 25,
|
||||
IDX_INT_BT_SCO_TX = 26,
|
||||
IDX_INT_BT_A2DP_RX = 27,
|
||||
IDX_INT_FM_RX = 28,
|
||||
IDX_INT_FM_TX = 29,
|
||||
IDX_RT_PROXY_PORT_001_RX = 30,
|
||||
IDX_RT_PROXY_PORT_001_TX = 31,
|
||||
IDX_SECONDARY_PCM_RX = 32,
|
||||
IDX_SECONDARY_PCM_TX = 33,
|
||||
AFE_MAX_PORTS
|
||||
};
|
||||
|
||||
int afe_open(u16 port_id, union afe_port_config *afe_config, int rate);
|
||||
int afe_close(int port_id);
|
||||
int afe_loopback(u16 enable, u16 rx_port, u16 tx_port);
|
||||
int afe_loopback_cfg(u16 enable, u16 dst_port, u16 src_port, u16 mode);
|
||||
int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain);
|
||||
int afe_loopback_gain(u16 port_id, u16 volume);
|
||||
int afe_validate_port(u16 port_id);
|
||||
int afe_get_port_index(u16 port_id);
|
||||
int afe_start_pseudo_port(u16 port_id);
|
||||
int afe_stop_pseudo_port(u16 port_id);
|
||||
int afe_cmd_memory_map(u32 dma_addr_p, u32 dma_buf_sz);
|
||||
int afe_cmd_memory_map_nowait(u32 dma_addr_p, u32 dma_buf_sz);
|
||||
int afe_cmd_memory_unmap(u32 dma_addr_p);
|
||||
int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
|
||||
|
||||
int afe_register_get_events(u16 port_id,
|
||||
void (*cb) (uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv),
|
||||
void *private_data);
|
||||
int afe_unregister_get_events(u16 port_id);
|
||||
int afe_rt_proxy_port_write(u32 buf_addr_p, int bytes);
|
||||
int afe_rt_proxy_port_read(u32 buf_addr_p, int bytes);
|
||||
int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
|
||||
u32 rate);
|
||||
int afe_port_stop_nowait(int port_id);
|
||||
int afe_apply_gain(u16 port_id, u16 gain);
|
||||
int afe_q6_interface_prepare(void);
|
||||
int afe_get_port_type(u16 port_id);
|
||||
/* if port_id is virtual, convert to physical..
|
||||
* if port_id is already physical, return physical
|
||||
*/
|
||||
int afe_convert_virtual_to_portid(u16 port_id);
|
||||
|
||||
int afe_pseudo_port_start_nowait(u16 port_id);
|
||||
int afe_pseudo_port_stop_nowait(u16 port_id);
|
||||
#endif /* __Q6AFE_H__ */
|
||||
303
include/sound/q6asm-v2.h
Normal file
303
include/sound/q6asm-v2.h
Normal file
@@ -0,0 +1,303 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6_ASM_V2_H__
|
||||
#define __Q6_ASM_V2_H__
|
||||
|
||||
#include <mach/qdsp6v2/apr.h>
|
||||
#include <mach/msm_subsystem_map.h>
|
||||
#include <sound/apr_audio-v2.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/ion.h>
|
||||
|
||||
#define IN 0x000
|
||||
#define OUT 0x001
|
||||
#define CH_MODE_MONO 0x001
|
||||
#define CH_MODE_STEREO 0x002
|
||||
|
||||
#define FORMAT_LINEAR_PCM 0x0000
|
||||
#define FORMAT_DTMF 0x0001
|
||||
#define FORMAT_ADPCM 0x0002
|
||||
#define FORMAT_YADPCM 0x0003
|
||||
#define FORMAT_MP3 0x0004
|
||||
#define FORMAT_MPEG4_AAC 0x0005
|
||||
#define FORMAT_AMRNB 0x0006
|
||||
#define FORMAT_AMRWB 0x0007
|
||||
#define FORMAT_V13K 0x0008
|
||||
#define FORMAT_EVRC 0x0009
|
||||
#define FORMAT_EVRCB 0x000a
|
||||
#define FORMAT_EVRCWB 0x000b
|
||||
#define FORMAT_MIDI 0x000c
|
||||
#define FORMAT_SBC 0x000d
|
||||
#define FORMAT_WMA_V10PRO 0x000e
|
||||
#define FORMAT_WMA_V9 0x000f
|
||||
#define FORMAT_AMR_WB_PLUS 0x0010
|
||||
#define FORMAT_MPEG4_MULTI_AAC 0x0011
|
||||
#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012
|
||||
|
||||
#define ENCDEC_SBCBITRATE 0x0001
|
||||
#define ENCDEC_IMMEDIATE_DECODE 0x0002
|
||||
#define ENCDEC_CFG_BLK 0x0003
|
||||
|
||||
#define CMD_PAUSE 0x0001
|
||||
#define CMD_FLUSH 0x0002
|
||||
#define CMD_EOS 0x0003
|
||||
#define CMD_CLOSE 0x0004
|
||||
#define CMD_OUT_FLUSH 0x0005
|
||||
|
||||
/* bit 0:1 represents priority of stream */
|
||||
#define STREAM_PRIORITY_NORMAL 0x0000
|
||||
#define STREAM_PRIORITY_LOW 0x0001
|
||||
#define STREAM_PRIORITY_HIGH 0x0002
|
||||
|
||||
/* bit 4 represents META enable of encoded data buffer */
|
||||
#define BUFFER_META_ENABLE 0x0010
|
||||
|
||||
/* Enable Sample_Rate/Channel_Mode notification event from Decoder */
|
||||
#define SR_CM_NOTIFY_ENABLE 0x0004
|
||||
|
||||
#define ASYNC_IO_MODE 0x0002
|
||||
#define SYNC_IO_MODE 0x0001
|
||||
#define NO_TIMESTAMP 0xFF00
|
||||
#define SET_TIMESTAMP 0x0000
|
||||
|
||||
#define SOFT_PAUSE_ENABLE 1
|
||||
#define SOFT_PAUSE_DISABLE 0
|
||||
|
||||
#define SESSION_MAX 0x08
|
||||
|
||||
#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */
|
||||
#define SOFT_PAUSE_STEP 2000 /* Step value 2ms or 2000us */
|
||||
enum {
|
||||
SOFT_PAUSE_CURVE_LINEAR = 0,
|
||||
SOFT_PAUSE_CURVE_EXP,
|
||||
SOFT_PAUSE_CURVE_LOG,
|
||||
};
|
||||
|
||||
#define SOFT_VOLUME_PERIOD 30 /* ramp up/down for 30ms */
|
||||
#define SOFT_VOLUME_STEP 2000 /* Step value 2ms or 2000us */
|
||||
enum {
|
||||
SOFT_VOLUME_CURVE_LINEAR = 0,
|
||||
SOFT_VOLUME_CURVE_EXP,
|
||||
SOFT_VOLUME_CURVE_LOG,
|
||||
};
|
||||
|
||||
typedef void (*app_cb)(uint32_t opcode, uint32_t token,
|
||||
uint32_t *payload, void *priv);
|
||||
|
||||
struct audio_buffer {
|
||||
dma_addr_t phys;
|
||||
void *data;
|
||||
uint32_t used;
|
||||
uint32_t size;/* size of buffer */
|
||||
uint32_t actual_size; /* actual number of bytes read by DSP */
|
||||
struct ion_handle *handle;
|
||||
struct ion_client *client;
|
||||
};
|
||||
|
||||
struct audio_aio_write_param {
|
||||
unsigned long paddr;
|
||||
uint32_t len;
|
||||
uint32_t uid;
|
||||
uint32_t lsw_ts;
|
||||
uint32_t msw_ts;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct audio_aio_read_param {
|
||||
unsigned long paddr;
|
||||
uint32_t len;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct audio_port_data {
|
||||
struct audio_buffer *buf;
|
||||
uint32_t max_buf_cnt;
|
||||
uint32_t dsp_buf;
|
||||
uint32_t cpu_buf;
|
||||
struct list_head mem_map_handle;
|
||||
uint32_t tmp_hdl;
|
||||
/* read or write locks */
|
||||
struct mutex lock;
|
||||
spinlock_t dsp_lock;
|
||||
};
|
||||
|
||||
struct audio_client {
|
||||
int session;
|
||||
app_cb cb;
|
||||
atomic_t cmd_state;
|
||||
/* Relative or absolute TS */
|
||||
uint32_t time_flag;
|
||||
void *priv;
|
||||
uint32_t io_mode;
|
||||
uint64_t time_stamp;
|
||||
struct apr_svc *apr;
|
||||
struct apr_svc *mmap_apr;
|
||||
struct mutex cmd_lock;
|
||||
/* idx:1 out port, 0: in port*/
|
||||
struct audio_port_data port[2];
|
||||
wait_queue_head_t cmd_wait;
|
||||
};
|
||||
|
||||
void q6asm_audio_client_free(struct audio_client *ac);
|
||||
|
||||
struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv);
|
||||
|
||||
struct audio_client *q6asm_get_audio_client(int session_id);
|
||||
|
||||
int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */,
|
||||
struct audio_client *ac,
|
||||
unsigned int bufsz,
|
||||
unsigned int bufcnt);
|
||||
int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir
|
||||
/* 1:Out,0:In */,
|
||||
struct audio_client *ac,
|
||||
unsigned int bufsz,
|
||||
unsigned int bufcnt);
|
||||
|
||||
int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
|
||||
struct audio_client *ac);
|
||||
|
||||
int q6asm_open_read(struct audio_client *ac, uint32_t format
|
||||
/*, uint16_t bits_per_sample*/);
|
||||
|
||||
int q6asm_open_write(struct audio_client *ac, uint32_t format
|
||||
/*, uint16_t bits_per_sample*/);
|
||||
|
||||
int q6asm_open_read_write(struct audio_client *ac,
|
||||
uint32_t rd_format,
|
||||
uint32_t wr_format);
|
||||
|
||||
int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
|
||||
uint32_t lsw_ts, uint32_t flags);
|
||||
int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
|
||||
uint32_t lsw_ts, uint32_t flags);
|
||||
|
||||
int q6asm_async_write(struct audio_client *ac,
|
||||
struct audio_aio_write_param *param);
|
||||
|
||||
int q6asm_async_read(struct audio_client *ac,
|
||||
struct audio_aio_read_param *param);
|
||||
|
||||
int q6asm_read(struct audio_client *ac);
|
||||
int q6asm_read_nolock(struct audio_client *ac);
|
||||
|
||||
int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add,
|
||||
int dir, uint32_t bufsz, uint32_t bufcnt);
|
||||
|
||||
int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add,
|
||||
int dir);
|
||||
|
||||
int q6asm_run(struct audio_client *ac, uint32_t flags,
|
||||
uint32_t msw_ts, uint32_t lsw_ts);
|
||||
|
||||
int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
|
||||
uint32_t msw_ts, uint32_t lsw_ts);
|
||||
|
||||
int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable);
|
||||
|
||||
int q6asm_cmd(struct audio_client *ac, int cmd);
|
||||
|
||||
int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
|
||||
|
||||
void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac,
|
||||
uint32_t *size, uint32_t *idx);
|
||||
|
||||
void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
|
||||
uint32_t *size, uint32_t *idx);
|
||||
|
||||
int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac);
|
||||
|
||||
/* File format specific configurations to be added below */
|
||||
|
||||
int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
|
||||
uint32_t frames_per_buf,
|
||||
uint32_t sample_rate, uint32_t channels,
|
||||
uint32_t bit_rate,
|
||||
uint32_t mode, uint32_t format);
|
||||
|
||||
int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_set_encdec_chan_map(struct audio_client *ac,
|
||||
uint32_t num_channels);
|
||||
|
||||
int q6asm_enable_sbrps(struct audio_client *ac,
|
||||
uint32_t sbr_ps);
|
||||
|
||||
int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
|
||||
uint16_t sce_left, uint16_t sce_right);
|
||||
|
||||
int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t min_rate, uint16_t max_rate,
|
||||
uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
|
||||
|
||||
int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t min_rate, uint16_t max_rate,
|
||||
uint16_t rate_modulation_cmd);
|
||||
|
||||
int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t band_mode, uint16_t dtx_enable);
|
||||
|
||||
int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t band_mode, uint16_t dtx_enable);
|
||||
|
||||
int q6asm_media_format_block_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_media_format_block_aac(struct audio_client *ac,
|
||||
struct asm_aac_cfg *cfg);
|
||||
|
||||
int q6asm_media_format_block_multi_aac(struct audio_client *ac,
|
||||
struct asm_aac_cfg *cfg);
|
||||
|
||||
int q6asm_media_format_block_wma(struct audio_client *ac,
|
||||
void *cfg);
|
||||
|
||||
int q6asm_media_format_block_wmapro(struct audio_client *ac,
|
||||
void *cfg);
|
||||
|
||||
/* PP specific */
|
||||
int q6asm_equalizer(struct audio_client *ac, void *eq);
|
||||
|
||||
/* Send Volume Command */
|
||||
int q6asm_set_volume(struct audio_client *ac, int volume);
|
||||
|
||||
/* Set SoftPause Params */
|
||||
int q6asm_set_softpause(struct audio_client *ac,
|
||||
struct asm_softpause_params *param);
|
||||
|
||||
/* Set Softvolume Params */
|
||||
int q6asm_set_softvolume(struct audio_client *ac,
|
||||
struct asm_softvolume_params *param);
|
||||
|
||||
/* Send left-right channel gain */
|
||||
int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain);
|
||||
|
||||
/* Enable Mute/unmute flag */
|
||||
int q6asm_set_mute(struct audio_client *ac, int muteflag);
|
||||
|
||||
uint64_t q6asm_get_session_time(struct audio_client *ac);
|
||||
|
||||
/* Client can set the IO mode to either AIO/SIO mode */
|
||||
int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode);
|
||||
|
||||
/* Get Service ID for APR communication */
|
||||
int q6asm_get_apr_service_id(int session_id);
|
||||
|
||||
/* Common format block without any payload
|
||||
*/
|
||||
int q6asm_media_format_block(struct audio_client *ac, uint32_t format);
|
||||
|
||||
#endif /* __Q6_ASM_H__ */
|
||||
320
include/sound/q6asm.h
Normal file
320
include/sound/q6asm.h
Normal file
@@ -0,0 +1,320 @@
|
||||
/* Copyright (c) 2010-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __Q6_ASM_H__
|
||||
#define __Q6_ASM_H__
|
||||
|
||||
#include <mach/qdsp6v2/apr.h>
|
||||
#include <sound/apr_audio.h>
|
||||
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
|
||||
#include <linux/ion.h>
|
||||
#endif
|
||||
|
||||
#define IN 0x000
|
||||
#define OUT 0x001
|
||||
#define CH_MODE_MONO 0x001
|
||||
#define CH_MODE_STEREO 0x002
|
||||
|
||||
#define FORMAT_LINEAR_PCM 0x0000
|
||||
#define FORMAT_DTMF 0x0001
|
||||
#define FORMAT_ADPCM 0x0002
|
||||
#define FORMAT_YADPCM 0x0003
|
||||
#define FORMAT_MP3 0x0004
|
||||
#define FORMAT_MPEG4_AAC 0x0005
|
||||
#define FORMAT_AMRNB 0x0006
|
||||
#define FORMAT_AMRWB 0x0007
|
||||
#define FORMAT_V13K 0x0008
|
||||
#define FORMAT_EVRC 0x0009
|
||||
#define FORMAT_EVRCB 0x000a
|
||||
#define FORMAT_EVRCWB 0x000b
|
||||
#define FORMAT_MIDI 0x000c
|
||||
#define FORMAT_SBC 0x000d
|
||||
#define FORMAT_WMA_V10PRO 0x000e
|
||||
#define FORMAT_WMA_V9 0x000f
|
||||
#define FORMAT_AMR_WB_PLUS 0x0010
|
||||
#define FORMAT_MPEG4_MULTI_AAC 0x0011
|
||||
#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012
|
||||
#define FORMAT_AC3 0x0013
|
||||
#define FORMAT_DTS 0x0014
|
||||
#define FORMAT_EAC3 0x0015
|
||||
#define FORMAT_ATRAC 0x0016
|
||||
#define FORMAT_MAT 0x0017
|
||||
#define FORMAT_AAC 0x0018
|
||||
|
||||
#define ENCDEC_SBCBITRATE 0x0001
|
||||
#define ENCDEC_IMMEDIATE_DECODE 0x0002
|
||||
#define ENCDEC_CFG_BLK 0x0003
|
||||
|
||||
#define CMD_PAUSE 0x0001
|
||||
#define CMD_FLUSH 0x0002
|
||||
#define CMD_EOS 0x0003
|
||||
#define CMD_CLOSE 0x0004
|
||||
#define CMD_OUT_FLUSH 0x0005
|
||||
|
||||
/* bit 0:1 represents priority of stream */
|
||||
#define STREAM_PRIORITY_NORMAL 0x0000
|
||||
#define STREAM_PRIORITY_LOW 0x0001
|
||||
#define STREAM_PRIORITY_HIGH 0x0002
|
||||
|
||||
/* bit 4 represents META enable of encoded data buffer */
|
||||
#define BUFFER_META_ENABLE 0x0010
|
||||
|
||||
/* Enable Sample_Rate/Channel_Mode notification event from Decoder */
|
||||
#define SR_CM_NOTIFY_ENABLE 0x0004
|
||||
|
||||
#define ASYNC_IO_MODE 0x0002
|
||||
#define SYNC_IO_MODE 0x0001
|
||||
#define NO_TIMESTAMP 0xFF00
|
||||
#define SET_TIMESTAMP 0x0000
|
||||
|
||||
#define SOFT_PAUSE_ENABLE 1
|
||||
#define SOFT_PAUSE_DISABLE 0
|
||||
|
||||
#define SESSION_MAX 0x08
|
||||
|
||||
#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */
|
||||
#define SOFT_PAUSE_STEP_LINEAR 0 /* Step value 0ms or 0us */
|
||||
#define SOFT_PAUSE_STEP 2000 /* Step value 2000ms or 2000us */
|
||||
enum {
|
||||
SOFT_PAUSE_CURVE_LINEAR = 0,
|
||||
SOFT_PAUSE_CURVE_EXP,
|
||||
SOFT_PAUSE_CURVE_LOG,
|
||||
};
|
||||
|
||||
#define SOFT_VOLUME_PERIOD 30 /* ramp up/down for 30ms */
|
||||
#define SOFT_VOLUME_STEP_LINEAR 0 /* Step value 0ms or 0us */
|
||||
#define SOFT_VOLUME_STEP 2000 /* Step value 2000ms or 2000us */
|
||||
enum {
|
||||
SOFT_VOLUME_CURVE_LINEAR = 0,
|
||||
SOFT_VOLUME_CURVE_EXP,
|
||||
SOFT_VOLUME_CURVE_LOG,
|
||||
};
|
||||
|
||||
typedef void (*app_cb)(uint32_t opcode, uint32_t token,
|
||||
uint32_t *payload, void *priv);
|
||||
|
||||
struct audio_buffer {
|
||||
dma_addr_t phys;
|
||||
void *data;
|
||||
uint32_t used;
|
||||
uint32_t size;/* size of buffer */
|
||||
uint32_t actual_size; /* actual number of bytes read by DSP */
|
||||
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
|
||||
struct ion_handle *handle;
|
||||
struct ion_client *client;
|
||||
#else
|
||||
void *mem_buffer;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct audio_aio_write_param {
|
||||
unsigned long paddr;
|
||||
uint32_t uid;
|
||||
uint32_t len;
|
||||
uint32_t msw_ts;
|
||||
uint32_t lsw_ts;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct audio_aio_read_param {
|
||||
unsigned long paddr;
|
||||
uint32_t len;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct audio_port_data {
|
||||
struct audio_buffer *buf;
|
||||
uint32_t max_buf_cnt;
|
||||
uint32_t dsp_buf;
|
||||
uint32_t cpu_buf;
|
||||
/* read or write locks */
|
||||
struct mutex lock;
|
||||
spinlock_t dsp_lock;
|
||||
};
|
||||
|
||||
struct audio_client {
|
||||
int session;
|
||||
/* idx:1 out port, 0: in port*/
|
||||
struct audio_port_data port[2];
|
||||
|
||||
struct apr_svc *apr;
|
||||
struct mutex cmd_lock;
|
||||
|
||||
atomic_t cmd_state;
|
||||
atomic_t time_flag;
|
||||
wait_queue_head_t cmd_wait;
|
||||
wait_queue_head_t time_wait;
|
||||
|
||||
app_cb cb;
|
||||
void *priv;
|
||||
uint32_t io_mode;
|
||||
uint64_t time_stamp;
|
||||
};
|
||||
|
||||
void q6asm_audio_client_free(struct audio_client *ac);
|
||||
|
||||
struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv);
|
||||
|
||||
struct audio_client *q6asm_get_audio_client(int session_id);
|
||||
|
||||
int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */,
|
||||
struct audio_client *ac,
|
||||
unsigned int bufsz,
|
||||
unsigned int bufcnt);
|
||||
int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir
|
||||
/* 1:Out,0:In */,
|
||||
struct audio_client *ac,
|
||||
unsigned int bufsz,
|
||||
unsigned int bufcnt);
|
||||
|
||||
int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
|
||||
struct audio_client *ac);
|
||||
|
||||
int q6asm_open_read(struct audio_client *ac, uint32_t format);
|
||||
|
||||
int q6asm_open_write(struct audio_client *ac, uint32_t format);
|
||||
|
||||
int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format);
|
||||
|
||||
int q6asm_open_read_write(struct audio_client *ac,
|
||||
uint32_t rd_format,
|
||||
uint32_t wr_format);
|
||||
|
||||
int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
|
||||
uint32_t lsw_ts, uint32_t flags);
|
||||
int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
|
||||
uint32_t lsw_ts, uint32_t flags);
|
||||
|
||||
int q6asm_async_write(struct audio_client *ac,
|
||||
struct audio_aio_write_param *param);
|
||||
|
||||
int q6asm_async_read(struct audio_client *ac,
|
||||
struct audio_aio_read_param *param);
|
||||
|
||||
int q6asm_read(struct audio_client *ac);
|
||||
int q6asm_read_nolock(struct audio_client *ac);
|
||||
|
||||
int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add,
|
||||
int dir, uint32_t bufsz, uint32_t bufcnt);
|
||||
|
||||
int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add,
|
||||
int dir);
|
||||
|
||||
int q6asm_run(struct audio_client *ac, uint32_t flags,
|
||||
uint32_t msw_ts, uint32_t lsw_ts);
|
||||
|
||||
int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
|
||||
uint32_t msw_ts, uint32_t lsw_ts);
|
||||
|
||||
int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable);
|
||||
|
||||
int q6asm_cmd(struct audio_client *ac, int cmd);
|
||||
|
||||
int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
|
||||
|
||||
void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac,
|
||||
uint32_t *size, uint32_t *idx);
|
||||
|
||||
void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
|
||||
uint32_t *size, uint32_t *idx);
|
||||
|
||||
int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac);
|
||||
|
||||
/* File format specific configurations to be added below */
|
||||
|
||||
int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
|
||||
uint32_t frames_per_buf,
|
||||
uint32_t sample_rate, uint32_t channels,
|
||||
uint32_t bit_rate,
|
||||
uint32_t mode, uint32_t format);
|
||||
|
||||
int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_enc_cfg_blk_multi_ch_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_enable_sbrps(struct audio_client *ac,
|
||||
uint32_t sbr_ps);
|
||||
|
||||
int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
|
||||
uint16_t sce_left, uint16_t sce_right);
|
||||
|
||||
int q6asm_set_encdec_chan_map(struct audio_client *ac,
|
||||
uint32_t num_channels);
|
||||
|
||||
int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t min_rate, uint16_t max_rate,
|
||||
uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
|
||||
|
||||
int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t min_rate, uint16_t max_rate,
|
||||
uint16_t rate_modulation_cmd);
|
||||
|
||||
int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t band_mode, uint16_t dtx_enable);
|
||||
|
||||
int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
|
||||
uint16_t band_mode, uint16_t dtx_enable);
|
||||
|
||||
int q6asm_media_format_block_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
|
||||
uint32_t rate, uint32_t channels);
|
||||
|
||||
int q6asm_media_format_block_aac(struct audio_client *ac,
|
||||
struct asm_aac_cfg *cfg);
|
||||
|
||||
int q6asm_media_format_block_multi_aac(struct audio_client *ac,
|
||||
struct asm_aac_cfg *cfg);
|
||||
|
||||
int q6asm_media_format_block_wma(struct audio_client *ac,
|
||||
void *cfg);
|
||||
|
||||
int q6asm_media_format_block_wmapro(struct audio_client *ac,
|
||||
void *cfg);
|
||||
|
||||
/* PP specific */
|
||||
int q6asm_equalizer(struct audio_client *ac, void *eq);
|
||||
|
||||
/* Send Volume Command */
|
||||
int q6asm_set_volume(struct audio_client *ac, int volume);
|
||||
|
||||
/* Set SoftPause Params */
|
||||
int q6asm_set_softpause(struct audio_client *ac,
|
||||
struct asm_softpause_params *param);
|
||||
|
||||
/* Set Softvolume Params */
|
||||
int q6asm_set_softvolume(struct audio_client *ac,
|
||||
struct asm_softvolume_params *param);
|
||||
|
||||
/* Send left-right channel gain */
|
||||
int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain);
|
||||
|
||||
/* Enable Mute/unmute flag */
|
||||
int q6asm_set_mute(struct audio_client *ac, int muteflag);
|
||||
|
||||
uint64_t q6asm_get_session_time(struct audio_client *ac);
|
||||
|
||||
/* Client can set the IO mode to either AIO/SIO mode */
|
||||
int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode);
|
||||
|
||||
#ifdef CONFIG_RTAC
|
||||
/* Get Service ID for APR communication */
|
||||
int q6asm_get_apr_service_id(int session_id);
|
||||
#endif
|
||||
|
||||
/* Common format block without any payload
|
||||
*/
|
||||
int q6asm_media_format_block(struct audio_client *ac, uint32_t format);
|
||||
|
||||
#endif /* __Q6_ASM_H__ */
|
||||
26
include/sound/q6audio-v2.h
Normal file
26
include/sound/q6audio-v2.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _Q6_AUDIO_H_
|
||||
#define _Q6_AUDIO_H_
|
||||
|
||||
#include <mach/qdsp6v2/apr.h>
|
||||
|
||||
int q6audio_get_port_index(u16 port_id);
|
||||
|
||||
int q6audio_convert_virtual_to_portid(u16 port_id);
|
||||
|
||||
int q6audio_validate_port(u16 port_id);
|
||||
|
||||
int q6audio_get_port_id(u16 port_id);
|
||||
|
||||
#endif
|
||||
@@ -149,6 +149,7 @@ config SND_SEQ_RTCTIMER_DEFAULT
|
||||
|
||||
config SND_DYNAMIC_MINORS
|
||||
bool "Dynamic device file minor numbers"
|
||||
default y if SND_OMAP_SOC_ABE_DSP
|
||||
help
|
||||
If you say Y here, the minor numbers of ALSA device files in
|
||||
/dev/snd/ are allocated dynamically. This allows you to have
|
||||
|
||||
@@ -41,6 +41,7 @@ source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/mid-x86/Kconfig"
|
||||
source "sound/soc/msm/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/samsung/Kconfig"
|
||||
|
||||
@@ -15,6 +15,7 @@ obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += imx/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += mid-x86/
|
||||
obj-$(CONFIG_SND_SOC) += msm/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
obj-$(CONFIG_SND_SOC) += nuc900/
|
||||
obj-$(CONFIG_SND_SOC) += omap/
|
||||
|
||||
@@ -40,7 +40,6 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_MAX98088 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
select SND_SOC_MAX9850 if I2C
|
||||
select SND_SOC_MAX9768 if I2C
|
||||
select SND_SOC_MAX9877 if I2C
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_RT5631 if I2C
|
||||
@@ -63,7 +62,6 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_WL1273 if MFD_WL1273_CORE
|
||||
select SND_SOC_WM1250_EV1 if I2C
|
||||
select SND_SOC_WM2000 if I2C
|
||||
select SND_SOC_WM2200 if I2C
|
||||
select SND_SOC_WM5100 if I2C
|
||||
select SND_SOC_WM8350 if MFD_WM8350
|
||||
select SND_SOC_WM8400 if MFD_WM8400
|
||||
@@ -107,6 +105,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_TIMPANI if MARIMBA_CORE
|
||||
help
|
||||
Normally ASoC codec drivers are only built if a machine driver which
|
||||
uses them is also built since they are only usable with a machine
|
||||
@@ -284,6 +283,15 @@ config SND_SOC_UDA134X
|
||||
config SND_SOC_UDA1380
|
||||
tristate
|
||||
|
||||
config SND_SOC_WCD9304
|
||||
tristate
|
||||
|
||||
config SND_SOC_WCD9310
|
||||
tristate
|
||||
|
||||
config SND_SOC_CS8427
|
||||
tristate
|
||||
|
||||
config SND_SOC_WL1273
|
||||
tristate
|
||||
|
||||
@@ -429,11 +437,11 @@ config SND_SOC_WM9713
|
||||
config SND_SOC_LM4857
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9768
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9877
|
||||
tristate
|
||||
|
||||
config SND_SOC_TPA6130A2
|
||||
tristate
|
||||
|
||||
config SND_SOC_MSM_STUB
|
||||
tristate
|
||||
|
||||
@@ -25,7 +25,6 @@ snd-soc-dmic-objs := dmic.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
snd-soc-lm4857-objs := lm4857.o
|
||||
snd-soc-max9768-objs := max9768.o
|
||||
snd-soc-max98088-objs := max98088.o
|
||||
snd-soc-max98095-objs := max98095.o
|
||||
snd-soc-max9850-objs := max9850.o
|
||||
@@ -49,10 +48,12 @@ snd-soc-twl4030-objs := twl4030.o
|
||||
snd-soc-twl6040-objs := twl6040.o
|
||||
snd-soc-uda134x-objs := uda134x.o
|
||||
snd-soc-uda1380-objs := uda1380.o
|
||||
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
|
||||
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
|
||||
snd-soc-cs8427-objs := cs8427.o
|
||||
snd-soc-wl1273-objs := wl1273.o
|
||||
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
|
||||
snd-soc-wm2000-objs := wm2000.o
|
||||
snd-soc-wm2200-objs := wm2200.o
|
||||
snd-soc-wm5100-objs := wm5100.o wm5100-tables.o
|
||||
snd-soc-wm8350-objs := wm8350.o
|
||||
snd-soc-wm8400-objs := wm8400.o
|
||||
@@ -98,6 +99,8 @@ snd-soc-wm9712-objs := wm9712.o
|
||||
snd-soc-wm9713-objs := wm9713.o
|
||||
snd-soc-wm-hubs-objs := wm_hubs.o
|
||||
|
||||
snd-soc-timpani-objs := timpani.o
|
||||
snd-soc-msm-stub-objs := msm_stub.o
|
||||
# Amp
|
||||
snd-soc-max9877-objs := max9877.o
|
||||
snd-soc-tpa6130a2-objs := tpa6130a2.o
|
||||
@@ -131,7 +134,6 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
|
||||
@@ -153,10 +155,12 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
|
||||
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
|
||||
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_WCD9304) += snd-soc-wcd9304.o
|
||||
obj-$(CONFIG_SND_SOC_WCD9310) += snd-soc-wcd9310.o
|
||||
obj-$(CONFIG_SND_SOC_CS8427) += snd-soc-cs8427.o
|
||||
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
|
||||
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
|
||||
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
|
||||
obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o
|
||||
obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o
|
||||
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
|
||||
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
|
||||
@@ -201,6 +205,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
|
||||
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
|
||||
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
|
||||
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
|
||||
obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o
|
||||
|
||||
# Amp
|
||||
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
|
||||
|
||||
904
sound/soc/codecs/cs8427.c
Normal file
904
sound/soc/codecs/cs8427.c
Normal file
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
* Routines for control of the CS8427 via i2c bus
|
||||
* IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
//#include <linux/export.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/cs8427.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#define CS8427_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define CS8427_FORMATS (SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FORMAT_S16_LE |\
|
||||
SNDRV_PCM_FORMAT_S20_3LE)
|
||||
|
||||
struct cs8427_stream {
|
||||
struct snd_pcm_substream *substream;
|
||||
char hw_status[CHANNEL_STATUS_SIZE]; /* hardware status */
|
||||
char def_status[CHANNEL_STATUS_SIZE]; /* default status */
|
||||
char pcm_status[CHANNEL_STATUS_SIZE]; /* PCM private status */
|
||||
char hw_udata[32];
|
||||
struct snd_kcontrol *pcm_ctl;
|
||||
};
|
||||
|
||||
struct cs8427 {
|
||||
struct i2c_client *client;
|
||||
struct i2c_msg xfer_msg[2];
|
||||
unsigned char regmap[0x14]; /* map of first 1 + 13 registers */
|
||||
unsigned int reset_timeout;
|
||||
struct cs8427_stream playback;
|
||||
};
|
||||
|
||||
static int cs8427_i2c_write_device(struct cs8427 *cs8427_i2c,
|
||||
u16 reg, u8 *value, u32 bytes)
|
||||
{
|
||||
struct i2c_msg *msg;
|
||||
int ret = 0;
|
||||
u8 reg_addr = 0;
|
||||
u8 data[bytes + 1];
|
||||
|
||||
if (cs8427_i2c->client == NULL) {
|
||||
pr_err("%s: failed to get device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
reg_addr = (u8)reg;
|
||||
msg = &cs8427_i2c->xfer_msg[0];
|
||||
msg->addr = cs8427_i2c->client->addr;
|
||||
msg->len = bytes + 1;
|
||||
msg->flags = 0;
|
||||
data[0] = reg_addr;
|
||||
data[1] = *value;
|
||||
msg->buf = data;
|
||||
ret = i2c_transfer(cs8427_i2c->client->adapter,
|
||||
cs8427_i2c->xfer_msg, 1);
|
||||
/* Try again if the write fails
|
||||
* checking with ebusy and number of bytes executed
|
||||
* for write ret value should be 1
|
||||
*/
|
||||
if ((ret != 1) || (ret == -EBUSY)) {
|
||||
ret = i2c_transfer(
|
||||
cs8427_i2c->client->adapter,
|
||||
cs8427_i2c->xfer_msg, 1);
|
||||
if ((ret != 1) || (ret < 0)) {
|
||||
dev_err(&cs8427_i2c->client->dev,
|
||||
"failed to write the"
|
||||
" device reg %d\n", reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs8427_i2c_write(struct cs8427 *chip, unsigned short reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
return cs8427_i2c_write_device(chip, reg, src, bytes);
|
||||
}
|
||||
static int cs8427_i2c_read_device(struct cs8427 *cs8427_i2c,
|
||||
unsigned short reg,
|
||||
int bytes, unsigned char *dest)
|
||||
{
|
||||
struct i2c_msg *msg;
|
||||
int ret = 0;
|
||||
u8 reg_addr = 0;
|
||||
u8 i = 0;
|
||||
|
||||
if (cs8427_i2c->client == NULL) {
|
||||
pr_err("%s: failed to get device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
for (i = 0; i < bytes; i++) {
|
||||
reg_addr = (u8)reg++;
|
||||
msg = &cs8427_i2c->xfer_msg[0];
|
||||
msg->addr = cs8427_i2c->client->addr;
|
||||
msg->len = 1;
|
||||
msg->flags = 0;
|
||||
msg->buf = ®_addr;
|
||||
|
||||
msg = &cs8427_i2c->xfer_msg[1];
|
||||
msg->addr = cs8427_i2c->client->addr;
|
||||
msg->len = 1;
|
||||
msg->flags = I2C_M_RD;
|
||||
msg->buf = dest++;
|
||||
ret = i2c_transfer(cs8427_i2c->client->adapter,
|
||||
cs8427_i2c->xfer_msg, 2);
|
||||
|
||||
/* Try again if read fails first time
|
||||
checking with ebusy and number of bytes executed
|
||||
for read ret value should be 2*/
|
||||
if ((ret != 2) || (ret == -EBUSY)) {
|
||||
ret = i2c_transfer(
|
||||
cs8427_i2c->client->adapter,
|
||||
cs8427_i2c->xfer_msg, 2);
|
||||
if ((ret != 2) || (ret < 0)) {
|
||||
dev_err(&cs8427_i2c->client->dev,
|
||||
"failed to read cs8427"
|
||||
" register %d\n", reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs8427_i2c_read(struct cs8427 *chip,
|
||||
unsigned short reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
return cs8427_i2c_read_device(chip, reg,
|
||||
bytes, dest);
|
||||
}
|
||||
|
||||
static int cs8427_i2c_sendbytes(struct cs8427 *chip,
|
||||
char *reg_addr, char *data,
|
||||
int bytes)
|
||||
{
|
||||
u32 ret = 0;
|
||||
u8 i = 0;
|
||||
|
||||
if (!chip) {
|
||||
pr_err("%s, invalid device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!data) {
|
||||
dev_err(&chip->client->dev, "%s:"
|
||||
"invalid data pointer\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = cs8427_i2c_write_device(chip, (*reg_addr + i),
|
||||
&data[i], 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s: failed to send the data to"
|
||||
" cs8427 chip\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the chip using run bit, also lock PLL using ILRCK and
|
||||
* put back AES3INPUT. This workaround is described in latest
|
||||
* CS8427 datasheet, otherwise TXDSERIAL will not work.
|
||||
*/
|
||||
static void snd_cs8427_reset(struct cs8427 *chip)
|
||||
{
|
||||
unsigned long end_time;
|
||||
int data, aes3input = 0;
|
||||
unsigned char val = 0;
|
||||
|
||||
if (snd_BUG_ON(!chip))
|
||||
return;
|
||||
if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) ==
|
||||
CS8427_RXDAES3INPUT) /* AES3 bit is set */
|
||||
aes3input = 1;
|
||||
chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~(CS8427_RUN | CS8427_RXDMASK);
|
||||
cs8427_i2c_write(chip, CS8427_REG_CLOCKSOURCE,
|
||||
1, &chip->regmap[CS8427_REG_CLOCKSOURCE]);
|
||||
udelay(200);
|
||||
chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RUN | CS8427_RXDILRCK;
|
||||
cs8427_i2c_write(chip, CS8427_REG_CLOCKSOURCE,
|
||||
1, &chip->regmap[CS8427_REG_CLOCKSOURCE]);
|
||||
udelay(200);
|
||||
end_time = jiffies + chip->reset_timeout;
|
||||
while (time_after_eq(end_time, jiffies)) {
|
||||
data = cs8427_i2c_read(chip, CS8427_REG_RECVERRORS,
|
||||
1, &val);
|
||||
if (!(val & CS8427_UNLOCK))
|
||||
break;
|
||||
schedule_timeout_uninterruptible(1);
|
||||
}
|
||||
chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~CS8427_RXDMASK;
|
||||
if (aes3input)
|
||||
chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RXDAES3INPUT;
|
||||
cs8427_i2c_write(chip, CS8427_REG_CLOCKSOURCE,
|
||||
1, &chip->regmap[CS8427_REG_CLOCKSOURCE]);
|
||||
}
|
||||
|
||||
static int snd_cs8427_in_status_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_in_status_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct cs8427 *chip = kcontrol->private_data;
|
||||
unsigned char val = 0;
|
||||
int err = 0;
|
||||
|
||||
err = cs8427_i2c_read(chip, kcontrol->private_value, 1, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_qsubcode_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_qsubcode_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct cs8427 *chip = kcontrol->private_data;
|
||||
unsigned char reg = CS8427_REG_QSUBCODE;
|
||||
int err;
|
||||
unsigned char val[20];
|
||||
|
||||
if (!chip) {
|
||||
pr_err("%s: invalid device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = cs8427_i2c_write(chip, reg, 1, &val[0]);
|
||||
if (err != 1) {
|
||||
dev_err(&chip->client->dev, "unable to send register"
|
||||
" 0x%x byte to CS8427\n", reg);
|
||||
return err < 0 ? err : -EIO;
|
||||
}
|
||||
err = cs8427_i2c_read(chip, *ucontrol->value.bytes.data, 10, &val);
|
||||
if (err != 10) {
|
||||
dev_err(&chip->client->dev, "unable to read"
|
||||
" Q-subcode bytes from CS8427\n");
|
||||
return err < 0 ? err : -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_spdif_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_select_corudata(struct cs8427 *cs8427_i2c, int udata)
|
||||
{
|
||||
struct cs8427 *chip = cs8427_i2c;
|
||||
int err;
|
||||
|
||||
udata = udata ? CS8427_BSEL : 0;
|
||||
if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) {
|
||||
chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL;
|
||||
chip->regmap[CS8427_REG_CSDATABUF] |= udata;
|
||||
err = cs8427_i2c_write(cs8427_i2c, CS8427_REG_CSDATABUF,
|
||||
1, &chip->regmap[CS8427_REG_CSDATABUF]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_send_corudata(struct cs8427 *obj,
|
||||
int udata,
|
||||
unsigned char *ndata,
|
||||
int count)
|
||||
{
|
||||
struct cs8427 *chip = obj;
|
||||
char *hw_data = udata ?
|
||||
chip->playback.hw_udata : chip->playback.hw_status;
|
||||
char data[32];
|
||||
int err, idx;
|
||||
unsigned char addr = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!memcmp(hw_data, ndata, count))
|
||||
return 0;
|
||||
err = snd_cs8427_select_corudata(chip, udata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
memcpy(hw_data, ndata, count);
|
||||
if (udata) {
|
||||
memset(data, 0, sizeof(data));
|
||||
if (memcmp(hw_data, data, count) == 0) {
|
||||
chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK;
|
||||
chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS |
|
||||
CS8427_EFTUI;
|
||||
err = cs8427_i2c_write(chip, CS8427_REG_UDATABUF,
|
||||
1, &chip->regmap[CS8427_REG_UDATABUF]);
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
}
|
||||
idx = 0;
|
||||
memcpy(data, ndata, CHANNEL_STATUS_SIZE);
|
||||
/* address from where the bufferhas to write*/
|
||||
addr = 0x20;
|
||||
ret = cs8427_i2c_sendbytes(chip, &addr, data, count);
|
||||
if (ret != count)
|
||||
return -EIO;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int snd_cs8427_spdif_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct cs8427 *chip = kcontrol->private_data;
|
||||
if (!chip) {
|
||||
pr_err("%s: invalid device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
memcpy(ucontrol->value.iec958.status,
|
||||
chip->playback.def_status, CHANNEL_STATUS_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_spdif_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct cs8427 *chip = kcontrol->private_data;
|
||||
unsigned char *status;
|
||||
int err, change;
|
||||
|
||||
if (!chip) {
|
||||
pr_err("%s: invalid device info\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
status = kcontrol->private_value ?
|
||||
chip->playback.pcm_status : chip->playback.def_status;
|
||||
|
||||
change = memcmp(ucontrol->value.iec958.status, status,
|
||||
CHANNEL_STATUS_SIZE) != 0;
|
||||
|
||||
if (!change) {
|
||||
memcpy(status, ucontrol->value.iec958.status,
|
||||
CHANNEL_STATUS_SIZE);
|
||||
err = snd_cs8427_send_corudata(chip, 0, status,
|
||||
CHANNEL_STATUS_SIZE);
|
||||
if (err < 0)
|
||||
change = err;
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static int snd_cs8427_spdif_mask_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_cs8427_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
memset(ucontrol->value.iec958.status, 0xff, CHANNEL_STATUS_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.info = snd_cs8427_in_status_info,
|
||||
.name = "IEC958 CS8427 Input Status",
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
|
||||
.get = snd_cs8427_in_status_get,
|
||||
.private_value = 15,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.info = snd_cs8427_in_status_info,
|
||||
.name = "IEC958 CS8427 Error Status",
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
|
||||
.get = snd_cs8427_in_status_get,
|
||||
.private_value = 16,
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
|
||||
.info = snd_cs8427_spdif_mask_info,
|
||||
.get = snd_cs8427_spdif_mask_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK,
|
||||
DEFAULT),
|
||||
.info = snd_cs8427_spdif_info,
|
||||
.get = snd_cs8427_spdif_get,
|
||||
.put = snd_cs8427_spdif_put,
|
||||
.private_value = 0
|
||||
},
|
||||
{
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
|
||||
.info = snd_cs8427_spdif_info,
|
||||
.get = snd_cs8427_spdif_get,
|
||||
.put = snd_cs8427_spdif_put,
|
||||
.private_value = 1
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.info = snd_cs8427_qsubcode_info,
|
||||
.name = "IEC958 Q-subcode Capture Default",
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
|
||||
.get = snd_cs8427_qsubcode_get
|
||||
}
|
||||
};
|
||||
|
||||
static int cs8427_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct cs8427 *chip = dev_get_drvdata(codec->dev);
|
||||
int ret = 0;
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
chip->regmap[CS8427_REG_SERIALINPUT] &= CS8427_BITWIDTH_MASK;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
chip->regmap[CS8427_REG_SERIALINPUT] |= CS8427_SIRES16;
|
||||
ret = cs8427_i2c_write(chip, CS8427_REG_SERIALINPUT, 1,
|
||||
&chip->regmap[CS8427_REG_SERIALINPUT]);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
chip->regmap[CS8427_REG_SERIALINPUT] |= CS8427_SIRES20;
|
||||
ret = cs8427_i2c_write(chip, CS8427_REG_SERIALINPUT, 1,
|
||||
&chip->regmap[CS8427_REG_SERIALINPUT]);
|
||||
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
chip->regmap[CS8427_REG_SERIALINPUT] |= CS8427_SIRES24;
|
||||
ret = cs8427_i2c_write(chip, CS8427_REG_SERIALINPUT, 1,
|
||||
&chip->regmap[CS8427_REG_SERIALINPUT]);
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid format\n");
|
||||
break;
|
||||
}
|
||||
dev_dbg(&chip->client->dev,
|
||||
"%s(): substream = %s stream = %d\n" , __func__,
|
||||
substream->name, substream->stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_cs8427_iec958_register_kcontrol(struct cs8427 *cs8427,
|
||||
struct snd_card *card)
|
||||
{
|
||||
struct cs8427 *chip = cs8427;
|
||||
struct snd_kcontrol *kctl;
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_cs8427_iec958_controls); idx++) {
|
||||
kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], chip);
|
||||
if (kctl == NULL)
|
||||
return -ENOMEM;
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to add the kcontrol\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cs8427_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct cs8427 *chip = dev_get_drvdata(dai->codec->dev);
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* we need to make the pll lock for the I2S tranfers
|
||||
* reset the cs8427 chip for this.
|
||||
*/
|
||||
snd_cs8427_reset(chip);
|
||||
dev_dbg(&chip->client->dev,
|
||||
"%s(): substream = %s stream = %d\n" , __func__,
|
||||
substream->name, substream->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs8427_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct cs8427 *chip = dev_get_drvdata(dai->codec->dev);
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return;
|
||||
}
|
||||
dev_dbg(&chip->client->dev,
|
||||
"%s(): substream = %s stream = %d\n" , __func__,
|
||||
substream->name, substream->stream);
|
||||
}
|
||||
|
||||
static int cs8427_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct cs8427 *chip = dev_get_drvdata(dai->codec->dev);
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(&chip->client->dev, "%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops cs8427_dai_ops = {
|
||||
.startup = cs8427_startup,
|
||||
.shutdown = cs8427_shutdown,
|
||||
.hw_params = cs8427_hw_params,
|
||||
.set_fmt = cs8427_set_dai_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver cs8427_dai[] = {
|
||||
{
|
||||
.name = "spdif_rx",
|
||||
.id = 1,
|
||||
.playback = {
|
||||
.stream_name = "AIF1 Playback",
|
||||
.rates = CS8427_RATES,
|
||||
.formats = CS8427_FORMATS,
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &cs8427_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static unsigned int cs8427_soc_i2c_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct cs8427 *chip = dev_get_drvdata(codec->dev);
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(&chip->client->dev, "cs8427 soc i2c read\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs8427_soc_i2c_write(struct snd_soc_codec *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct cs8427 *chip = dev_get_drvdata(codec->dev);
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(&chip->client->dev, "cs8427 soc i2c write\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs8427_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cs8427 *chip;
|
||||
codec->control_data = dev_get_drvdata(codec->dev);
|
||||
chip = codec->control_data;
|
||||
|
||||
if (chip == NULL) {
|
||||
pr_err("invalid device private data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
snd_cs8427_iec958_register_kcontrol(chip, codec->card->snd_card);
|
||||
dev_set_drvdata(codec->dev, chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_cs8427 = {
|
||||
.read = cs8427_soc_i2c_read,
|
||||
.write = cs8427_soc_i2c_write,
|
||||
.probe = cs8427_soc_probe,
|
||||
};
|
||||
|
||||
int poweron_cs8427(struct cs8427 *chip)
|
||||
{
|
||||
struct cs8427_platform_data *pdata = chip->client->dev.platform_data;
|
||||
int ret = 0;
|
||||
|
||||
/*enable the 100KHz level shifter*/
|
||||
if (pdata->enable) {
|
||||
ret = pdata->enable(1);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to enable the level shifter\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gpio_request(pdata->reset_gpio, "cs8427 reset");
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to request the gpio %d\n",
|
||||
pdata->reset_gpio);
|
||||
return ret;
|
||||
}
|
||||
/*bring the chip out of reset*/
|
||||
gpio_direction_output(pdata->reset_gpio, 1);
|
||||
msleep(20);
|
||||
gpio_direction_output(pdata->reset_gpio, 0);
|
||||
msleep(20);
|
||||
gpio_direction_output(pdata->reset_gpio, 1);
|
||||
msleep(20);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __devinit int cs8427_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
static unsigned char initvals1[] = {
|
||||
CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,
|
||||
/* CS8427_REG_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes,
|
||||
* TCBL=output
|
||||
*/
|
||||
CS8427_SWCLK | CS8427_TCBLDIR,
|
||||
/* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs,
|
||||
* normal stereo operation
|
||||
*/
|
||||
0x08,
|
||||
/* CS8427_REG_DATAFLOW:
|
||||
* AES3 Transmitter data source => Serial Audio input port
|
||||
* Serial audio output port data source => reserved
|
||||
*/
|
||||
CS8427_TXDSERIAL,
|
||||
/* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs,
|
||||
* output time base = OMCK, input time base = recovered input clock,
|
||||
* recovered input clock source is ILRCK changed to AES3INPUT
|
||||
* (workaround, see snd_cs8427_reset)
|
||||
*/
|
||||
CS8427_RXDILRCK | CS8427_OUTC,
|
||||
/* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S,
|
||||
* 24-bit, 64*Fsi
|
||||
*/
|
||||
CS8427_SIDEL | CS8427_SILRPOL | CS8427_SORES16,
|
||||
/* CS8427_REG_SERIALOUTPUT: Serial audio output port data format
|
||||
* = I2S, 24-bit, 64*Fsi
|
||||
*/
|
||||
CS8427_SODEL | CS8427_SOLRPOL | CS8427_SIRES16,
|
||||
};
|
||||
static unsigned char initvals2[] = {
|
||||
CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC,
|
||||
/* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence,
|
||||
* biphase, parity status bits
|
||||
* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,
|
||||
*/
|
||||
0xff, /* set everything */
|
||||
/* CS8427_REG_CSDATABUF:
|
||||
* Registers 32-55 window to CS buffer
|
||||
* Inhibit D->E transfers from overwriting first 5 bytes of CS data.
|
||||
* Inhibit D->E transfers (all) of CS data.
|
||||
* Allow E->F transfer of CS data.
|
||||
* One byte mode; both A/B channels get same written CB data.
|
||||
* A channel info is output to chip's EMPH* pin.
|
||||
*/
|
||||
CS8427_CBMR | CS8427_DETCI,
|
||||
/* CS8427_REG_UDATABUF:
|
||||
* Use internal buffer to transmit User (U) data.
|
||||
* Chip's U pin is an output.
|
||||
* Transmit all O's for user data.
|
||||
* Inhibit D->E transfers.
|
||||
* Inhibit E->F transfers.
|
||||
*/
|
||||
CS8427_UD | CS8427_EFTUI | CS8427_DETUI,
|
||||
};
|
||||
int err;
|
||||
unsigned char buf[CHANNEL_STATUS_SIZE];
|
||||
unsigned char val = 0;
|
||||
char addr = 0;
|
||||
unsigned int reset_timeout = 100;
|
||||
int ret = 0;
|
||||
struct cs8427 *chip;
|
||||
|
||||
if (!client) {
|
||||
pr_err("%s: invalid device info\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = kzalloc(sizeof(struct cs8427), GFP_KERNEL);
|
||||
if (chip == NULL) {
|
||||
dev_err(&client->dev,
|
||||
"%s: error, allocation failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip->client = client;
|
||||
|
||||
dev_set_drvdata(&chip->client->dev, chip);
|
||||
|
||||
ret = poweron_cs8427(chip);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to bring chip out of reset\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = cs8427_i2c_read(chip, CS8427_REG_ID_AND_VER, 1, &val);
|
||||
if (err < 0) {
|
||||
/* give second chance */
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to read cs8427 trying once again\n");
|
||||
err = cs8427_i2c_read(chip, CS8427_REG_ID_AND_VER,
|
||||
1, &val);
|
||||
if (err < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to read version number\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(&chip->client->dev,
|
||||
"version number read = %x\n", val);
|
||||
}
|
||||
if (val != CS8427_VER8427A) {
|
||||
dev_err(&chip->client->dev,
|
||||
"unable to find CS8427 signature "
|
||||
"(expected 0x%x, read 0x%x),\n",
|
||||
CS8427_VER8427A, val);
|
||||
dev_err(&chip->client->dev,
|
||||
" initialization is not completed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
val = 0;
|
||||
/* turn off run bit while making changes to configuration */
|
||||
err = cs8427_i2c_write(chip, CS8427_REG_CLOCKSOURCE, 1, &val);
|
||||
if (err < 0)
|
||||
goto __fail;
|
||||
/* send initial values */
|
||||
memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
|
||||
addr = 1;
|
||||
err = cs8427_i2c_sendbytes(chip, &addr, &initvals1[1], 6);
|
||||
if (err != 6) {
|
||||
err = err < 0 ? err : -EIO;
|
||||
goto __fail;
|
||||
}
|
||||
/* Turn off CS8427 interrupt stuff that is not used in hardware */
|
||||
memset(buf, 0, 7);
|
||||
/* from address 9 to 15 */
|
||||
addr = 9;
|
||||
err = cs8427_i2c_sendbytes(chip, &addr, buf, 7);
|
||||
if (err != 7)
|
||||
goto __fail;
|
||||
/* send transfer initialization sequence */
|
||||
addr = 0x11;
|
||||
memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
|
||||
err = cs8427_i2c_sendbytes(chip, &addr, &initvals2[1], 3);
|
||||
if (err != 3) {
|
||||
err = err < 0 ? err : -EIO;
|
||||
goto __fail;
|
||||
}
|
||||
/* write default channel status bytes */
|
||||
put_unaligned_le32(SNDRV_PCM_DEFAULT_CON_SPDIF, buf);
|
||||
memset(buf + 4, 0, CHANNEL_STATUS_SIZE - 4);
|
||||
if (snd_cs8427_send_corudata(chip, 0, buf, CHANNEL_STATUS_SIZE) < 0)
|
||||
goto __fail;
|
||||
memcpy(chip->playback.def_status, buf, CHANNEL_STATUS_SIZE);
|
||||
memcpy(chip->playback.pcm_status, buf, CHANNEL_STATUS_SIZE);
|
||||
|
||||
/* turn on run bit and rock'n'roll */
|
||||
if (reset_timeout < 1)
|
||||
reset_timeout = 1;
|
||||
chip->reset_timeout = reset_timeout;
|
||||
snd_cs8427_reset(chip);
|
||||
|
||||
ret = snd_soc_register_codec(&chip->client->dev, &soc_codec_dev_cs8427,
|
||||
cs8427_dai, ARRAY_SIZE(cs8427_dai));
|
||||
|
||||
return 0;
|
||||
|
||||
__fail:
|
||||
kfree(chip);
|
||||
return err < 0 ? err : -EIO;
|
||||
}
|
||||
|
||||
static int __devexit cs8427_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cs8427 *chip;
|
||||
struct cs8427_platform_data *pdata;
|
||||
chip = dev_get_drvdata(&client->dev);
|
||||
if (!chip) {
|
||||
pr_err("invalid device info\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pdata = chip->client->dev.platform_data;
|
||||
gpio_free(pdata->reset_gpio);
|
||||
if (pdata->enable)
|
||||
pdata->enable(0);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id cs8427_id_table[] = {
|
||||
{"cs8427", CS8427_ADDR0},
|
||||
{"cs8427", CS8427_ADDR2},
|
||||
{"cs8427", CS8427_ADDR3},
|
||||
{"cs8427", CS8427_ADDR4},
|
||||
{"cs8427", CS8427_ADDR5},
|
||||
{"cs8427", CS8427_ADDR6},
|
||||
{"cs8427", CS8427_ADDR7},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs8427_id_table);
|
||||
|
||||
static struct i2c_driver cs8427_i2c_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cs8427-spdif",
|
||||
},
|
||||
.id_table = cs8427_id_table,
|
||||
.probe = cs8427_i2c_probe,
|
||||
.remove = __devexit_p(cs8427_remove),
|
||||
};
|
||||
|
||||
static int __init cs8427_module_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = i2c_add_driver(&cs8427_i2c_driver);
|
||||
if (ret != 0)
|
||||
pr_err("failed to add the I2C driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cs8427_module_exit(void)
|
||||
{
|
||||
pr_info("module exit\n");
|
||||
}
|
||||
|
||||
module_init(cs8427_module_init)
|
||||
module_exit(cs8427_module_exit)
|
||||
|
||||
MODULE_DESCRIPTION("CS8427 interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
80
sound/soc/codecs/msm_stub.c
Normal file
80
sound/soc/codecs/msm_stub.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/* Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
/* A dummy driver useful only to advertise hardware parameters */
|
||||
static struct snd_soc_dai_driver msm_stub_dais[] = {
|
||||
{
|
||||
.name = "msm-stub-rx",
|
||||
.playback = { /* Support maximum range */
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "msm-stub-tx",
|
||||
.capture = { /* Support maximum range */
|
||||
.stream_name = "Record",
|
||||
.channels_min = 1,
|
||||
.channels_max = 4,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_msm_stub = {};
|
||||
|
||||
static int __devinit msm_stub_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_codec(&pdev->dev,
|
||||
&soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais));
|
||||
}
|
||||
|
||||
static int __devexit msm_stub_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_stub_driver = {
|
||||
.driver = {
|
||||
.name = "msm-stub-codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_stub_dev_probe,
|
||||
.remove = __devexit_p(msm_stub_dev_remove),
|
||||
};
|
||||
|
||||
static int __init msm_stub_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_stub_driver);
|
||||
}
|
||||
module_init(msm_stub_init);
|
||||
|
||||
static void __exit msm_stub_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_stub_driver);
|
||||
}
|
||||
module_exit(msm_stub_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Generic MSM CODEC driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
482
sound/soc/codecs/timpani.c
Normal file
482
sound/soc/codecs/timpani.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/marimba.h>
|
||||
#include <linux/mfd/timpani-audio.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
/* Debug purpose */
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <mach/mpp.h>
|
||||
/* End of debug purpose */
|
||||
|
||||
#define ADIE_CODEC_MAX 2
|
||||
|
||||
struct adie_codec_register {
|
||||
u8 reg;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
static struct adie_codec_register dmic_on[] = {
|
||||
{0x80, 0x05, 0x05},
|
||||
{0x80, 0x05, 0x00},
|
||||
{0x83, 0x0C, 0x00},
|
||||
{0x8A, 0xF0, 0x30},
|
||||
{0x86, 0xFF, 0xAC},
|
||||
{0x87, 0xFF, 0xAC},
|
||||
{0x8A, 0xF0, 0xF0},
|
||||
{0x82, 0x1F, 0x1E},
|
||||
{0x83, 0x0C, 0x0C},
|
||||
{0x92, 0x3F, 0x21},
|
||||
{0x94, 0x3F, 0x24},
|
||||
{0xA3, 0x39, 0x01},
|
||||
{0xA8, 0x0F, 0x00},
|
||||
{0xAB, 0x3F, 0x00},
|
||||
{0x86, 0xFF, 0x00},
|
||||
{0x87, 0xFF, 0x00},
|
||||
{0x8A, 0xF0, 0xC0},
|
||||
};
|
||||
|
||||
static struct adie_codec_register dmic_off[] = {
|
||||
{0x8A, 0xF0, 0xF0},
|
||||
{0x83, 0x0C, 0x00},
|
||||
{0x92, 0xFF, 0x00},
|
||||
{0x94, 0xFF, 0x1B},
|
||||
};
|
||||
|
||||
static struct adie_codec_register spk_on[] = {
|
||||
{0x80, 0x02, 0x02},
|
||||
{0x80, 0x02, 0x00},
|
||||
{0x83, 0x03, 0x00},
|
||||
{0x8A, 0x0F, 0x03},
|
||||
{0xA3, 0x02, 0x02},
|
||||
{0x84, 0xFF, 0x00},
|
||||
{0x85, 0xFF, 0x00},
|
||||
{0x8A, 0x0F, 0x0C},
|
||||
{0x81, 0xFF, 0x0E},
|
||||
{0x83, 0x03, 0x03},
|
||||
{0x24, 0x6F, 0x6C},
|
||||
{0xB7, 0x01, 0x01},
|
||||
{0x31, 0x01, 0x01},
|
||||
{0x32, 0xF8, 0x08},
|
||||
{0x32, 0xF8, 0x48},
|
||||
{0x32, 0xF8, 0xF8},
|
||||
{0xE0, 0xFE, 0xAC},
|
||||
{0xE1, 0xFE, 0xAC},
|
||||
{0x3A, 0x24, 0x24},
|
||||
{0xE0, 0xFE, 0x3C},
|
||||
{0xE1, 0xFE, 0x3C},
|
||||
{0xE0, 0xFE, 0x1C},
|
||||
{0xE1, 0xFE, 0x1C},
|
||||
{0xE0, 0xFE, 0x10},
|
||||
{0xE1, 0xFE, 0x10},
|
||||
};
|
||||
|
||||
static struct adie_codec_register spk_off[] = {
|
||||
{0x8A, 0x0F, 0x0F},
|
||||
{0xE0, 0xFE, 0x1C},
|
||||
{0xE1, 0xFE, 0x1C},
|
||||
{0xE0, 0xFE, 0x3C},
|
||||
{0xE1, 0xFE, 0x3C},
|
||||
{0xE0, 0xFC, 0xAC},
|
||||
{0xE1, 0xFC, 0xAC},
|
||||
{0x32, 0xF8, 0x00},
|
||||
{0x31, 0x05, 0x00},
|
||||
{0x3A, 0x24, 0x00},
|
||||
};
|
||||
|
||||
static struct adie_codec_register spk_mute[] = {
|
||||
{0x84, 0xFF, 0xAC},
|
||||
{0x85, 0xFF, 0xAC},
|
||||
{0x8A, 0x0F, 0x0C},
|
||||
};
|
||||
|
||||
static struct adie_codec_register spk_unmute[] = {
|
||||
{0x84, 0xFF, 0x00},
|
||||
{0x85, 0xFF, 0x00},
|
||||
{0x8A, 0x0F, 0x0C},
|
||||
};
|
||||
|
||||
struct adie_codec_path {
|
||||
int rate; /* sample rate of path */
|
||||
u32 reg_owner;
|
||||
};
|
||||
|
||||
struct timpani_drv_data { /* member undecided */
|
||||
struct snd_soc_codec codec;
|
||||
struct adie_codec_path path[ADIE_CODEC_MAX];
|
||||
u32 ref_cnt;
|
||||
struct marimba_codec_platform_data *codec_pdata;
|
||||
};
|
||||
|
||||
static struct snd_soc_codec *timpani_codec;
|
||||
|
||||
enum /* regaccess blk id */
|
||||
{
|
||||
RA_BLOCK_RX1 = 0,
|
||||
RA_BLOCK_RX2,
|
||||
RA_BLOCK_TX1,
|
||||
RA_BLOCK_TX2,
|
||||
RA_BLOCK_LB,
|
||||
RA_BLOCK_SHARED_RX_LB,
|
||||
RA_BLOCK_SHARED_TX,
|
||||
RA_BLOCK_TXFE1,
|
||||
RA_BLOCK_TXFE2,
|
||||
RA_BLOCK_PA_COMMON,
|
||||
RA_BLOCK_PA_EAR,
|
||||
RA_BLOCK_PA_HPH,
|
||||
RA_BLOCK_PA_LINE,
|
||||
RA_BLOCK_PA_AUX,
|
||||
RA_BLOCK_ADC,
|
||||
RA_BLOCK_DMIC,
|
||||
RA_BLOCK_TX_I2S,
|
||||
RA_BLOCK_DRV,
|
||||
RA_BLOCK_TEST,
|
||||
RA_BLOCK_RESERVED,
|
||||
RA_BLOCK_NUM,
|
||||
};
|
||||
|
||||
enum /* regaccess onwer ID */
|
||||
{
|
||||
RA_OWNER_NONE = 0,
|
||||
RA_OWNER_PATH_RX1,
|
||||
RA_OWNER_PATH_RX2,
|
||||
RA_OWNER_PATH_TX1,
|
||||
RA_OWNER_PATH_TX2,
|
||||
RA_OWNER_PATH_LB,
|
||||
RA_OWNER_DRV,
|
||||
RA_OWNER_NUM,
|
||||
};
|
||||
|
||||
struct reg_acc_blk_cfg {
|
||||
u8 valid_owners[RA_OWNER_NUM];
|
||||
};
|
||||
|
||||
struct timpani_regaccess {
|
||||
u8 reg_addr;
|
||||
u8 blk_mask[RA_BLOCK_NUM];
|
||||
u8 reg_mask;
|
||||
u8 reg_default;
|
||||
};
|
||||
|
||||
static unsigned int timpani_codec_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct marimba *pdrv = codec->control_data;
|
||||
int rc;
|
||||
u8 val;
|
||||
|
||||
rc = marimba_read(pdrv, reg, &val, 1);
|
||||
if (IS_ERR_VALUE(rc)) {
|
||||
pr_err("%s: fail to write reg %x\n", __func__, reg);
|
||||
return 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int timpani_codec_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
struct marimba *pdrv = codec->control_data;
|
||||
int rc;
|
||||
|
||||
rc = marimba_write_bit_mask(pdrv, reg, (u8 *)&value, 1, 0xFF);
|
||||
if (IS_ERR_VALUE(rc)) {
|
||||
pr_err("%s: fail to write reg %x\n", __func__, reg);
|
||||
return -EIO;
|
||||
}
|
||||
pr_debug("%s: write reg %x val %x\n", __func__, reg, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timpani_codec_bring_up(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
|
||||
int rc;
|
||||
|
||||
if (timpani->codec_pdata &&
|
||||
timpani->codec_pdata->marimba_codec_power) {
|
||||
if (timpani->ref_cnt)
|
||||
return;
|
||||
/* Codec power up sequence */
|
||||
rc = timpani->codec_pdata->marimba_codec_power(1);
|
||||
if (rc)
|
||||
pr_err("%s: could not power up timpani "
|
||||
"codec\n", __func__);
|
||||
else {
|
||||
timpani_codec_write(codec, 0xFF, 0x08);
|
||||
timpani_codec_write(codec, 0xFF, 0x0A);
|
||||
timpani_codec_write(codec, 0xFF, 0x0E);
|
||||
timpani_codec_write(codec, 0xFF, 0x07);
|
||||
timpani_codec_write(codec, 0xFF, 0x17);
|
||||
timpani_codec_write(codec, TIMPANI_A_MREF, 0x22);
|
||||
msleep(15);
|
||||
timpani->ref_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void timpani_codec_bring_down(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
|
||||
int rc;
|
||||
|
||||
if (timpani->codec_pdata &&
|
||||
timpani->codec_pdata->marimba_codec_power) {
|
||||
timpani->ref_cnt--;
|
||||
if (timpani->ref_cnt >= 1)
|
||||
return;
|
||||
timpani_codec_write(codec, TIMPANI_A_MREF, TIMPANI_MREF_POR);
|
||||
timpani_codec_write(codec, 0xFF, 0x07);
|
||||
timpani_codec_write(codec, 0xFF, 0x06);
|
||||
timpani_codec_write(codec, 0xFF, 0x0E);
|
||||
timpani_codec_write(codec, 0xFF, 0x08);
|
||||
rc = timpani->codec_pdata->marimba_codec_power(0);
|
||||
if (rc)
|
||||
pr_err("%s: could not power down timpani "
|
||||
"codec\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void timpani_dmic_config(struct snd_soc_codec *codec, int on)
|
||||
{
|
||||
struct adie_codec_register *regs;
|
||||
int regs_sz, i;
|
||||
|
||||
if (on) {
|
||||
regs = dmic_on;
|
||||
regs_sz = ARRAY_SIZE(dmic_on);
|
||||
} else {
|
||||
regs = dmic_off;
|
||||
regs_sz = ARRAY_SIZE(dmic_off);
|
||||
}
|
||||
|
||||
for (i = 0; i < regs_sz; i++)
|
||||
timpani_codec_write(codec, regs[i].reg,
|
||||
(regs[i].mask & regs[i].val));
|
||||
}
|
||||
|
||||
static void timpani_spk_config(struct snd_soc_codec *codec, int on)
|
||||
{
|
||||
struct adie_codec_register *regs;
|
||||
int regs_sz, i;
|
||||
|
||||
if (on) {
|
||||
regs = spk_on;
|
||||
regs_sz = ARRAY_SIZE(spk_on);
|
||||
} else {
|
||||
regs = spk_off;
|
||||
regs_sz = ARRAY_SIZE(spk_off);
|
||||
}
|
||||
|
||||
for (i = 0; i < regs_sz; i++)
|
||||
timpani_codec_write(codec, regs[i].reg,
|
||||
(regs[i].mask & regs[i].val));
|
||||
}
|
||||
|
||||
static int timpani_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->card->codec;
|
||||
|
||||
pr_info("%s()\n", __func__);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
pr_info("%s: playback\n", __func__);
|
||||
timpani_codec_bring_up(codec);
|
||||
timpani_spk_config(codec, 1);
|
||||
} else {
|
||||
pr_info("%s: Capture\n", __func__);
|
||||
timpani_codec_bring_up(codec);
|
||||
timpani_dmic_config(codec, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timpani_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->card->codec;
|
||||
|
||||
pr_info("%s()\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
timpani_codec_bring_down(codec);
|
||||
timpani_spk_config(codec, 0);
|
||||
} else {
|
||||
timpani_codec_bring_down(codec);
|
||||
timpani_dmic_config(codec, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adie_codec_register *regs;
|
||||
int regs_sz, i;
|
||||
|
||||
if (mute) {
|
||||
regs = spk_mute;
|
||||
regs_sz = ARRAY_SIZE(spk_mute);
|
||||
} else {
|
||||
regs = spk_unmute;
|
||||
regs_sz = ARRAY_SIZE(spk_unmute);
|
||||
}
|
||||
|
||||
for (i = 0; i < regs_sz; i++) {
|
||||
timpani_codec_write(codec, regs[i].reg,
|
||||
(regs[i].mask & regs[i].val));
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops timpani_dai_ops = {
|
||||
.startup = timpani_startup,
|
||||
.shutdown = timpani_shutdown,
|
||||
};
|
||||
|
||||
struct snd_soc_dai timpani_codec_dai[] = {
|
||||
{
|
||||
.name = "TIMPANI Rx",
|
||||
.playback = {
|
||||
.stream_name = "Handset Playback",
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rate_max = 96000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &timpani_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "TIMPANI Tx",
|
||||
.capture = {
|
||||
.stream_name = "Handset Capture",
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rate_max = 96000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &timpani_dai_ops,
|
||||
}
|
||||
};
|
||||
|
||||
static int timpani_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec;
|
||||
int ret = 0;
|
||||
|
||||
if (!timpani_codec) {
|
||||
dev_err(&pdev->dev, "core driver not yet probed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
socdev->card->codec = timpani_codec;
|
||||
codec = timpani_codec;
|
||||
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0)
|
||||
dev_err(codec->dev, "failed to create pcms\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* power down chip */
|
||||
static int timpani_soc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_free_pcms(socdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_codec_device soc_codec_dev_timpani = {
|
||||
.probe = timpani_soc_probe,
|
||||
.remove = timpani_soc_remove,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_timpani);
|
||||
|
||||
static int timpani_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
struct timpani_drv_data *priv;
|
||||
|
||||
pr_info("%s()\n", __func__);
|
||||
priv = kzalloc(sizeof(struct timpani_drv_data), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = &priv->codec;
|
||||
snd_soc_codec_set_drvdata(codec, priv);
|
||||
priv->codec_pdata = pdev->dev.platform_data;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
codec->name = "TIMPANI";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->read = timpani_codec_read;
|
||||
codec->write = timpani_codec_write;
|
||||
codec->dai = timpani_codec_dai;
|
||||
codec->num_dai = ARRAY_SIZE(timpani_codec_dai);
|
||||
codec->control_data = platform_get_drvdata(pdev);
|
||||
timpani_codec = codec;
|
||||
|
||||
snd_soc_register_dais(timpani_codec_dai, ARRAY_SIZE(timpani_codec_dai));
|
||||
snd_soc_register_codec(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver timpani_codec_driver = {
|
||||
.probe = timpani_codec_probe,
|
||||
.driver = {
|
||||
.name = "timpani_codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init timpani_codec_init(void)
|
||||
{
|
||||
return platform_driver_register(&timpani_codec_driver);
|
||||
}
|
||||
|
||||
static void __exit timpani_codec_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&timpani_codec_driver);
|
||||
}
|
||||
|
||||
module_init(timpani_codec_init);
|
||||
module_exit(timpani_codec_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Timpani codec driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
15
sound/soc/codecs/timpani.h
Normal file
15
sound/soc/codecs/timpani.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 NUM_I2S 2
|
||||
extern struct snd_soc_dai timpani_codec_dai[NUM_I2S];
|
||||
extern struct snd_soc_codec_device soc_codec_dev_timpani;
|
||||
722
sound/soc/codecs/wcd9304-tables.c
Normal file
722
sound/soc/codecs/wcd9304-tables.c
Normal file
@@ -0,0 +1,722 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/mfd/wcd9xxx/wcd9xxx_registers.h>
|
||||
#include <linux/mfd/wcd9xxx/wcd9304_registers.h>
|
||||
#include "wcd9304.h"
|
||||
|
||||
const u8 sitar_reg_defaults[SITAR_CACHE_SIZE] = {
|
||||
[WCD9XXX_A_CHIP_CTL] = WCD9XXX_A_CHIP_CTL__POR,
|
||||
[WCD9XXX_A_CHIP_STATUS] = WCD9XXX_A_CHIP_STATUS__POR,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_0] = WCD9XXX_A_CHIP_ID_BYTE_0__POR,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_1] = WCD9XXX_A_CHIP_ID_BYTE_1__POR,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_2] = WCD9XXX_A_CHIP_ID_BYTE_2__POR,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_3] = WCD9XXX_A_CHIP_ID_BYTE_3__POR,
|
||||
[WCD9XXX_A_CHIP_VERSION] = WCD9XXX_A_CHIP_VERSION__POR,
|
||||
[WCD9XXX_A_SB_VERSION] = WCD9XXX_A_SB_VERSION__POR,
|
||||
[WCD9XXX_A_SLAVE_ID_1] = WCD9XXX_A_SLAVE_ID_1__POR,
|
||||
[WCD9XXX_A_SLAVE_ID_2] = WCD9XXX_A_SLAVE_ID_2__POR,
|
||||
[WCD9XXX_A_SLAVE_ID_3] = WCD9XXX_A_SLAVE_ID_3__POR,
|
||||
[SITAR_A_PIN_CTL_OE0] = SITAR_A_PIN_CTL_OE0__POR,
|
||||
[SITAR_A_PIN_CTL_OE1] = SITAR_A_PIN_CTL_OE1__POR,
|
||||
[SITAR_A_PIN_CTL_DATA0] = SITAR_A_PIN_CTL_DATA0__POR,
|
||||
[SITAR_A_PIN_CTL_DATA1] = SITAR_A_PIN_CTL_DATA1__POR,
|
||||
[SITAR_A_HDRIVE_GENERIC] = SITAR_A_HDRIVE_GENERIC__POR,
|
||||
[SITAR_A_HDRIVE_OVERRIDE] = SITAR_A_HDRIVE_OVERRIDE__POR,
|
||||
[SITAR_A_ANA_CSR_WAIT_STATE] = SITAR_A_ANA_CSR_WAIT_STATE__POR,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL0] = SITAR_A_PROCESS_MONITOR_CTL0__POR,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL1] = SITAR_A_PROCESS_MONITOR_CTL1__POR,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL2] = SITAR_A_PROCESS_MONITOR_CTL2__POR,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL3] = SITAR_A_PROCESS_MONITOR_CTL3__POR,
|
||||
[SITAR_A_QFUSE_CTL] = SITAR_A_QFUSE_CTL__POR,
|
||||
[SITAR_A_QFUSE_STATUS] = SITAR_A_QFUSE_STATUS__POR,
|
||||
[SITAR_A_QFUSE_DATA_OUT0] = SITAR_A_QFUSE_DATA_OUT0__POR,
|
||||
[SITAR_A_QFUSE_DATA_OUT1] = SITAR_A_QFUSE_DATA_OUT1__POR,
|
||||
[SITAR_A_QFUSE_DATA_OUT2] = SITAR_A_QFUSE_DATA_OUT2__POR,
|
||||
[SITAR_A_QFUSE_DATA_OUT3] = SITAR_A_QFUSE_DATA_OUT3__POR,
|
||||
[SITAR_A_CDC_CTL] = SITAR_A_CDC_CTL__POR,
|
||||
[SITAR_A_LEAKAGE_CTL] = SITAR_A_LEAKAGE_CTL__POR,
|
||||
[SITAR_A_INTR_MODE] = SITAR_A_INTR_MODE__POR,
|
||||
[SITAR_A_INTR_MASK0] = SITAR_A_INTR_MASK0__POR,
|
||||
[SITAR_A_INTR_MASK1] = SITAR_A_INTR_MASK1__POR,
|
||||
[SITAR_A_INTR_MASK2] = SITAR_A_INTR_MASK2__POR,
|
||||
[SITAR_A_INTR_STATUS0] = SITAR_A_INTR_STATUS0__POR,
|
||||
[SITAR_A_INTR_STATUS1] = SITAR_A_INTR_STATUS1__POR,
|
||||
[SITAR_A_INTR_STATUS2] = SITAR_A_INTR_STATUS2__POR,
|
||||
[SITAR_A_INTR_CLEAR0] = SITAR_A_INTR_CLEAR0__POR,
|
||||
[SITAR_A_INTR_CLEAR1] = SITAR_A_INTR_CLEAR1__POR,
|
||||
[SITAR_A_INTR_CLEAR2] = SITAR_A_INTR_CLEAR2__POR,
|
||||
[SITAR_A_INTR_LEVEL0] = SITAR_A_INTR_LEVEL0__POR,
|
||||
[SITAR_A_INTR_LEVEL1] = SITAR_A_INTR_LEVEL1__POR,
|
||||
[SITAR_A_INTR_LEVEL2] = SITAR_A_INTR_LEVEL2__POR,
|
||||
[SITAR_A_INTR_TEST0] = SITAR_A_INTR_TEST0__POR,
|
||||
[SITAR_A_INTR_TEST1] = SITAR_A_INTR_TEST1__POR,
|
||||
[SITAR_A_INTR_TEST2] = SITAR_A_INTR_TEST2__POR,
|
||||
[SITAR_A_INTR_SET0] = SITAR_A_INTR_SET0__POR,
|
||||
[SITAR_A_INTR_SET1] = SITAR_A_INTR_SET1__POR,
|
||||
[SITAR_A_INTR_SET2] = SITAR_A_INTR_SET2__POR,
|
||||
[SITAR_A_CDC_TX_I2S_SCK_MODE] = SITAR_A_CDC_TX_I2S_SCK_MODE__POR,
|
||||
[SITAR_A_CDC_TX_I2S_WS_MODE] = SITAR_A_CDC_TX_I2S_WS_MODE__POR,
|
||||
[SITAR_A_CDC_DMIC_DATA0_MODE] = SITAR_A_CDC_DMIC_DATA0_MODE__POR,
|
||||
[SITAR_A_CDC_DMIC_CLK0_MODE] = SITAR_A_CDC_DMIC_CLK0_MODE__POR,
|
||||
[SITAR_A_CDC_DMIC_DATA1_MODE] = SITAR_A_CDC_DMIC_DATA1_MODE__POR,
|
||||
[SITAR_A_CDC_DMIC_CLK1_MODE] = SITAR_A_CDC_DMIC_CLK1_MODE__POR,
|
||||
[SITAR_A_CDC_TX_I2S_SD0_MODE] = SITAR_A_CDC_TX_I2S_SD0_MODE__POR,
|
||||
[SITAR_A_CDC_INTR_MODE] = SITAR_A_CDC_INTR_MODE__POR,
|
||||
[SITAR_A_CDC_RX_I2S_SD0_MODE] = SITAR_A_CDC_RX_I2S_SD0_MODE__POR,
|
||||
[SITAR_A_CDC_RX_I2S_SD1_MODE] = SITAR_A_CDC_RX_I2S_SD1_MODE__POR,
|
||||
[SITAR_A_BIAS_REF_CTL] = SITAR_A_BIAS_REF_CTL__POR,
|
||||
[SITAR_A_BIAS_CENTRAL_BG_CTL] = SITAR_A_BIAS_CENTRAL_BG_CTL__POR,
|
||||
[SITAR_A_BIAS_PRECHRG_CTL] = SITAR_A_BIAS_PRECHRG_CTL__POR,
|
||||
[SITAR_A_BIAS_CURR_CTL_1] = SITAR_A_BIAS_CURR_CTL_1__POR,
|
||||
[SITAR_A_BIAS_CURR_CTL_2] = SITAR_A_BIAS_CURR_CTL_2__POR,
|
||||
[SITAR_A_BIAS_OSC_BG_CTL] = SITAR_A_BIAS_OSC_BG_CTL__POR,
|
||||
[SITAR_A_CLK_BUFF_EN1] = SITAR_A_CLK_BUFF_EN1__POR,
|
||||
[SITAR_A_CLK_BUFF_EN2] = SITAR_A_CLK_BUFF_EN2__POR,
|
||||
[SITAR_A_LDO_H_MODE_1] = SITAR_A_LDO_H_MODE_1__POR,
|
||||
[SITAR_A_LDO_H_MODE_2] = SITAR_A_LDO_H_MODE_2__POR,
|
||||
[SITAR_A_LDO_H_LOOP_CTL] = SITAR_A_LDO_H_LOOP_CTL__POR,
|
||||
[SITAR_A_LDO_H_COMP_1] = SITAR_A_LDO_H_COMP_1__POR,
|
||||
[SITAR_A_LDO_H_COMP_2] = SITAR_A_LDO_H_COMP_2__POR,
|
||||
[SITAR_A_LDO_H_BIAS_1] = SITAR_A_LDO_H_BIAS_1__POR,
|
||||
[SITAR_A_LDO_H_BIAS_2] = SITAR_A_LDO_H_BIAS_2__POR,
|
||||
[SITAR_A_LDO_H_BIAS_3] = SITAR_A_LDO_H_BIAS_3__POR,
|
||||
[SITAR_A_MICB_CFILT_1_CTL] = SITAR_A_MICB_CFILT_1_CTL__POR,
|
||||
[SITAR_A_MICB_CFILT_1_VAL] = SITAR_A_MICB_CFILT_1_VAL__POR,
|
||||
[SITAR_A_MICB_CFILT_1_PRECHRG] = SITAR_A_MICB_CFILT_1_PRECHRG__POR,
|
||||
[SITAR_A_MICB_1_CTL] = SITAR_A_MICB_1_CTL__POR,
|
||||
[SITAR_A_MICB_1_INT_RBIAS] = SITAR_A_MICB_1_INT_RBIAS__POR,
|
||||
[SITAR_A_MICB_1_MBHC] = SITAR_A_MICB_1_MBHC__POR,
|
||||
[SITAR_A_MICB_CFILT_2_CTL] = SITAR_A_MICB_CFILT_2_CTL__POR,
|
||||
[SITAR_A_MICB_CFILT_2_VAL] = SITAR_A_MICB_CFILT_2_VAL__POR,
|
||||
[SITAR_A_MICB_CFILT_2_PRECHRG] = SITAR_A_MICB_CFILT_2_PRECHRG__POR,
|
||||
[SITAR_A_MICB_2_CTL] = SITAR_A_MICB_2_CTL__POR,
|
||||
[SITAR_A_MICB_2_INT_RBIAS] = SITAR_A_MICB_2_INT_RBIAS__POR,
|
||||
[SITAR_A_MICB_2_MBHC] = SITAR_A_MICB_2_MBHC__POR,
|
||||
[SITAR_A_TX_COM_BIAS] = SITAR_A_TX_COM_BIAS__POR,
|
||||
[SITAR_A_MBHC_SCALING_MUX_1] = SITAR_A_MBHC_SCALING_MUX_1__POR,
|
||||
[SITAR_A_MBHC_SCALING_MUX_2] = SITAR_A_MBHC_SCALING_MUX_2__POR,
|
||||
[SITAR_A_TX_SUP_SWITCH_CTRL_1] = SITAR_A_TX_SUP_SWITCH_CTRL_1__POR,
|
||||
[SITAR_A_TX_SUP_SWITCH_CTRL_2] = SITAR_A_TX_SUP_SWITCH_CTRL_2__POR,
|
||||
[SITAR_A_TX_1_2_EN] = SITAR_A_TX_1_2_EN__POR,
|
||||
[SITAR_A_TX_1_2_TEST_EN] = SITAR_A_TX_1_2_TEST_EN__POR,
|
||||
[SITAR_A_TX_1_2_ADC_CH1] = SITAR_A_TX_1_2_ADC_CH1__POR,
|
||||
[SITAR_A_TX_1_2_ADC_CH2] = SITAR_A_TX_1_2_ADC_CH2__POR,
|
||||
[SITAR_A_TX_1_2_ATEST_REFCTRL] = SITAR_A_TX_1_2_ATEST_REFCTRL__POR,
|
||||
[SITAR_A_TX_1_2_TEST_CTL] = SITAR_A_TX_1_2_TEST_CTL__POR,
|
||||
[SITAR_A_TX_1_2_TEST_BLOCK_EN] = SITAR_A_TX_1_2_TEST_BLOCK_EN__POR,
|
||||
[SITAR_A_TX_1_2_TXFE_CLKDIV] = SITAR_A_TX_1_2_TXFE_CLKDIV__POR,
|
||||
[SITAR_A_TX_1_2_SAR_ERR_CH1] = SITAR_A_TX_1_2_SAR_ERR_CH1__POR,
|
||||
[SITAR_A_TX_1_2_SAR_ERR_CH2] = SITAR_A_TX_1_2_SAR_ERR_CH2__POR,
|
||||
[SITAR_A_TX_3_EN] = SITAR_A_TX_3_EN__POR,
|
||||
[SITAR_A_TX_3_TEST_EN] = SITAR_A_TX_3_TEST_EN__POR,
|
||||
[SITAR_A_TX_3_ADC] = SITAR_A_TX_3_ADC__POR,
|
||||
[SITAR_A_TX_3_MBHC_ATEST_REFCTRL] =
|
||||
SITAR_A_TX_3_MBHC_ATEST_REFCTRL__POR,
|
||||
[SITAR_A_TX_3_TEST_CTL] = SITAR_A_TX_3_TEST_CTL__POR,
|
||||
[SITAR_A_TX_3_TEST_BLOCK_EN] = SITAR_A_TX_3_TEST_BLOCK_EN__POR,
|
||||
[SITAR_A_TX_3_TXFE_CKDIV] = SITAR_A_TX_3_TXFE_CKDIV__POR,
|
||||
[SITAR_A_TX_3_SAR_ERR] = SITAR_A_TX_3_SAR_ERR__POR,
|
||||
[SITAR_A_TX_4_MBHC_EN] = SITAR_A_TX_4_MBHC_EN__POR,
|
||||
[SITAR_A_TX_4_MBHC_ADC] = SITAR_A_TX_4_MBHC_ADC__POR,
|
||||
[SITAR_A_TX_4_MBHC_TEST_CTL] = SITAR_A_TX_4_MBHC_TEST_CTL__POR,
|
||||
[SITAR_A_TX_4_MBHC_SAR_ERR] = SITAR_A_TX_4_MBHC_SAR_ERR__POR,
|
||||
[SITAR_A_TX_4_TXFE_CLKDIV] = SITAR_A_TX_4_TXFE_CLKDIV__POR,
|
||||
[SITAR_A_AUX_COM_CTL] = SITAR_A_AUX_COM_CTL__POR,
|
||||
[SITAR_A_AUX_COM_ATEST] = SITAR_A_AUX_COM_ATEST__POR,
|
||||
[SITAR_A_AUX_L_EN] = SITAR_A_AUX_L_EN__POR,
|
||||
[SITAR_A_AUX_L_GAIN] = SITAR_A_AUX_L_GAIN__POR,
|
||||
[SITAR_A_AUX_L_PA_CONN] = SITAR_A_AUX_L_PA_CONN__POR,
|
||||
[SITAR_A_AUX_L_PA_CONN_INV] = SITAR_A_AUX_L_PA_CONN_INV__POR,
|
||||
[SITAR_A_AUX_R_EN] = SITAR_A_AUX_R_EN__POR,
|
||||
[SITAR_A_AUX_R_GAIN] = SITAR_A_AUX_R_GAIN__POR,
|
||||
[SITAR_A_AUX_R_PA_CONN] = SITAR_A_AUX_R_PA_CONN__POR,
|
||||
[SITAR_A_AUX_R_PA_CONN_INV] = SITAR_A_AUX_R_PA_CONN_INV__POR,
|
||||
[SITAR_A_CP_EN] = SITAR_A_CP_EN__POR,
|
||||
[SITAR_A_CP_CLK] = SITAR_A_CP_CLK__POR,
|
||||
[SITAR_A_CP_STATIC] = SITAR_A_CP_STATIC__POR,
|
||||
[SITAR_A_CP_DCC1] = SITAR_A_CP_DCC1__POR,
|
||||
[SITAR_A_CP_DCC3] = SITAR_A_CP_DCC3__POR,
|
||||
[SITAR_A_CP_ATEST] = SITAR_A_CP_ATEST__POR,
|
||||
[SITAR_A_CP_DTEST] = SITAR_A_CP_DTEST__POR,
|
||||
[SITAR_A_RX_COM_TIMER_DIV] = SITAR_A_RX_COM_TIMER_DIV__POR,
|
||||
[SITAR_A_RX_COM_OCP_CTL] = SITAR_A_RX_COM_OCP_CTL__POR,
|
||||
[SITAR_A_RX_COM_OCP_COUNT] = SITAR_A_RX_COM_OCP_COUNT__POR,
|
||||
[SITAR_A_RX_COM_DAC_CTL] = SITAR_A_RX_COM_DAC_CTL__POR,
|
||||
[SITAR_A_RX_COM_BIAS] = SITAR_A_RX_COM_BIAS__POR,
|
||||
[SITAR_A_RX_HPH_BIAS_PA] = SITAR_A_RX_HPH_BIAS_PA__POR,
|
||||
[SITAR_A_RX_HPH_BIAS_LDO] = SITAR_A_RX_HPH_BIAS_LDO__POR,
|
||||
[SITAR_A_RX_HPH_BIAS_CNP] = SITAR_A_RX_HPH_BIAS_CNP__POR,
|
||||
[SITAR_A_RX_HPH_BIAS_WG] = SITAR_A_RX_HPH_BIAS_WG__POR,
|
||||
[SITAR_A_RX_HPH_OCP_CTL] = SITAR_A_RX_HPH_OCP_CTL__POR,
|
||||
[SITAR_A_RX_HPH_CNP_EN] = SITAR_A_RX_HPH_CNP_EN__POR,
|
||||
[SITAR_A_RX_HPH_CNP_WG_CTL] = SITAR_A_RX_HPH_CNP_WG_CTL__POR,
|
||||
[SITAR_A_RX_HPH_CNP_WG_TIME] = SITAR_A_RX_HPH_CNP_WG_TIME__POR,
|
||||
[SITAR_A_RX_HPH_L_GAIN] = SITAR_A_RX_HPH_L_GAIN__POR,
|
||||
[SITAR_A_RX_HPH_L_TEST] = SITAR_A_RX_HPH_L_TEST__POR,
|
||||
[SITAR_A_RX_HPH_L_PA_CTL] = SITAR_A_RX_HPH_L_PA_CTL__POR,
|
||||
[SITAR_A_RX_HPH_L_DAC_CTL] = SITAR_A_RX_HPH_L_DAC_CTL__POR,
|
||||
[SITAR_A_RX_HPH_L_ATEST] = SITAR_A_RX_HPH_L_ATEST__POR,
|
||||
[SITAR_A_RX_HPH_L_STATUS] = SITAR_A_RX_HPH_L_STATUS__POR,
|
||||
[SITAR_A_RX_HPH_R_GAIN] = SITAR_A_RX_HPH_R_GAIN__POR,
|
||||
[SITAR_A_RX_HPH_R_TEST] = SITAR_A_RX_HPH_R_TEST__POR,
|
||||
[SITAR_A_RX_HPH_R_PA_CTL] = SITAR_A_RX_HPH_R_PA_CTL__POR,
|
||||
[SITAR_A_RX_HPH_R_DAC_CTL] = SITAR_A_RX_HPH_R_DAC_CTL__POR,
|
||||
[SITAR_A_RX_HPH_R_ATEST] = SITAR_A_RX_HPH_R_ATEST__POR,
|
||||
[SITAR_A_RX_HPH_R_STATUS] = SITAR_A_RX_HPH_R_STATUS__POR,
|
||||
[SITAR_A_RX_EAR_BIAS_PA] = SITAR_A_RX_EAR_BIAS_PA__POR,
|
||||
[SITAR_A_RX_EAR_BIAS_CMBUFF] = SITAR_A_RX_EAR_BIAS_CMBUFF__POR,
|
||||
[SITAR_A_RX_EAR_EN] = SITAR_A_RX_EAR_EN__POR,
|
||||
[SITAR_A_RX_EAR_GAIN] = SITAR_A_RX_EAR_GAIN__POR,
|
||||
[SITAR_A_RX_EAR_CMBUFF] = SITAR_A_RX_EAR_CMBUFF__POR,
|
||||
[SITAR_A_RX_EAR_ICTL] = SITAR_A_RX_EAR_ICTL__POR,
|
||||
[SITAR_A_RX_EAR_CCOMP] = SITAR_A_RX_EAR_CCOMP__POR,
|
||||
[SITAR_A_RX_EAR_VCM] = SITAR_A_RX_EAR_VCM__POR,
|
||||
[SITAR_A_RX_EAR_CNP] = SITAR_A_RX_EAR_CNP__POR,
|
||||
[SITAR_A_RX_EAR_ATEST] = SITAR_A_RX_EAR_ATEST__POR,
|
||||
[SITAR_A_RX_EAR_STATUS] = SITAR_A_RX_EAR_STATUS__POR,
|
||||
[SITAR_A_RX_LINE_BIAS_PA] = SITAR_A_RX_LINE_BIAS_PA__POR,
|
||||
[SITAR_A_RX_LINE_BIAS_LDO] = SITAR_A_RX_LINE_BIAS_LDO__POR,
|
||||
[SITAR_A_RX_LINE_BIAS_CNP1] = SITAR_A_RX_LINE_BIAS_CNP1__POR,
|
||||
[SITAR_A_RX_LINE_COM] = SITAR_A_RX_LINE_COM__POR,
|
||||
[SITAR_A_RX_LINE_CNP_EN] = SITAR_A_RX_LINE_CNP_EN__POR,
|
||||
[SITAR_A_RX_LINE_CNP_WG_CTL] = SITAR_A_RX_LINE_CNP_WG_CTL__POR,
|
||||
[SITAR_A_RX_LINE_CNP_WG_TIME] = SITAR_A_RX_LINE_CNP_WG_TIME__POR,
|
||||
[SITAR_A_RX_LINE_1_GAIN] = SITAR_A_RX_LINE_1_GAIN__POR,
|
||||
[SITAR_A_RX_LINE_1_TEST] = SITAR_A_RX_LINE_1_TEST__POR,
|
||||
[SITAR_A_RX_LINE_1_DAC_CTL] = SITAR_A_RX_LINE_1_DAC_CTL__POR,
|
||||
[SITAR_A_RX_LINE_1_STATUS] = SITAR_A_RX_LINE_1_STATUS__POR,
|
||||
[SITAR_A_RX_LINE_2_GAIN] = SITAR_A_RX_LINE_2_GAIN__POR,
|
||||
[SITAR_A_RX_LINE_2_TEST] = SITAR_A_RX_LINE_2_TEST__POR,
|
||||
[SITAR_A_RX_LINE_2_DAC_CTL] = SITAR_A_RX_LINE_2_DAC_CTL__POR,
|
||||
[SITAR_A_RX_LINE_2_STATUS] = SITAR_A_RX_LINE_2_STATUS__POR,
|
||||
[SITAR_A_RX_LINE_BIAS_CNP2] = SITAR_A_RX_LINE_BIAS_CNP2__POR,
|
||||
[SITAR_A_RX_LINE_OCP_CTL] = SITAR_A_RX_LINE_OCP_CTL__POR,
|
||||
[SITAR_A_RX_LINE_1_PA_CTL] = SITAR_A_RX_LINE_1_PA_CTL__POR,
|
||||
[SITAR_A_RX_LINE_2_PA_CTL] = SITAR_A_RX_LINE_2_PA_CTL__POR,
|
||||
[SITAR_A_RX_LINE_CNP_DBG] = SITAR_A_RX_LINE_CNP_DBG__POR,
|
||||
[SITAR_A_MBHC_HPH] = SITAR_A_MBHC_HPH__POR,
|
||||
[SITAR_A_RC_OSC_FREQ] = SITAR_A_RC_OSC_FREQ__POR,
|
||||
[SITAR_A_RC_OSC_TEST] = SITAR_A_RC_OSC_TEST__POR,
|
||||
[SITAR_A_RC_OSC_STATUS] = SITAR_A_RC_OSC_STATUS__POR,
|
||||
[SITAR_A_RC_OSC_TUNER] = SITAR_A_RC_OSC_TUNER__POR,
|
||||
[SITAR_A_CDC_ANC1_CTL] = SITAR_A_CDC_ANC1_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_SHIFT] = SITAR_A_CDC_ANC1_SHIFT__POR,
|
||||
[SITAR_A_CDC_ANC1_IIR_B1_CTL] = SITAR_A_CDC_ANC1_IIR_B1_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_IIR_B2_CTL] = SITAR_A_CDC_ANC1_IIR_B2_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_IIR_B3_CTL] = SITAR_A_CDC_ANC1_IIR_B3_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_IIR_B4_CTL] = SITAR_A_CDC_ANC1_IIR_B4_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_LPF_B1_CTL] = SITAR_A_CDC_ANC1_LPF_B1_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_LPF_B2_CTL] = SITAR_A_CDC_ANC1_LPF_B2_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_LPF_B3_CTL] = SITAR_A_CDC_ANC1_LPF_B3_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_SPARE] = SITAR_A_CDC_ANC1_SPARE__POR,
|
||||
[SITAR_A_CDC_ANC1_SMLPF_CTL] = SITAR_A_CDC_ANC1_SMLPF_CTL__POR,
|
||||
[SITAR_A_CDC_ANC1_DCFLT_CTL] = SITAR_A_CDC_ANC1_DCFLT_CTL__POR,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_TIMER] = SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_GAIN] = SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_CFG] = SITAR_A_CDC_TX1_VOL_CTL_CFG__POR,
|
||||
[SITAR_A_CDC_TX1_MUX_CTL] = SITAR_A_CDC_TX1_MUX_CTL__POR,
|
||||
[SITAR_A_CDC_TX1_CLK_FS_CTL] = SITAR_A_CDC_TX1_CLK_FS_CTL__POR,
|
||||
[SITAR_A_CDC_TX1_DMIC_CTL] = SITAR_A_CDC_TX1_DMIC_CTL__POR,
|
||||
[SITAR_A_CDC_SRC1_PDA_CFG] = SITAR_A_CDC_SRC1_PDA_CFG__POR,
|
||||
[SITAR_A_CDC_SRC1_FS_CTL] = SITAR_A_CDC_SRC1_FS_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B1_CTL] = SITAR_A_CDC_RX1_B1_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B2_CTL] = SITAR_A_CDC_RX1_B2_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B3_CTL] = SITAR_A_CDC_RX1_B3_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B4_CTL] = SITAR_A_CDC_RX1_B4_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B5_CTL] = SITAR_A_CDC_RX1_B5_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_B6_CTL] = SITAR_A_CDC_RX1_B6_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR,
|
||||
[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_ANC_RESET_CTL] = SITAR_A_CDC_CLK_ANC_RESET_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_RX_RESET_CTL] = SITAR_A_CDC_CLK_RX_RESET_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] =
|
||||
SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_TX_RESET_B2_CTL] =
|
||||
SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_DMIC_CTL] = SITAR_A_CDC_CLK_DMIC_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_RX_I2S_CTL] = SITAR_A_CDC_CLK_RX_I2S_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_TX_I2S_CTL] = SITAR_A_CDC_CLK_TX_I2S_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_OTHR_RESET_CTL] = SITAR_A_CDC_CLK_OTHR_RESET_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_TX_CLK_EN_B1_CTL] =
|
||||
SITAR_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_OTHR_CTL] = SITAR_A_CDC_CLK_OTHR_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_RDAC_CLK_EN_CTL] =
|
||||
SITAR_A_CDC_CLK_RDAC_CLK_EN_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_ANC_CLK_EN_CTL] = SITAR_A_CDC_CLK_ANC_CLK_EN_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_RX_B1_CTL] = SITAR_A_CDC_CLK_RX_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_RX_B2_CTL] = SITAR_A_CDC_CLK_RX_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_MCLK_CTL] = SITAR_A_CDC_CLK_MCLK_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_PDM_CTL] = SITAR_A_CDC_CLK_PDM_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_SD_CTL] = SITAR_A_CDC_CLK_SD_CTL__POR,
|
||||
[SITAR_A_CDC_CLK_LP_CTL] = SITAR_A_CDC_CLK_LP_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B1_CTL] =
|
||||
SITAR_A_CDC_CLSG_FREQ_THRESH_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B2_CTL] =
|
||||
SITAR_A_CDC_CLSG_FREQ_THRESH_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B3_CTL] =
|
||||
SITAR_A_CDC_CLSG_FREQ_THRESH_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B4_CTL] =
|
||||
SITAR_A_CDC_CLSG_FREQ_THRESH_B4_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_GAIN_THRESH_CTL] =
|
||||
SITAR_A_CDC_CLSG_GAIN_THRESH_CTL__POR,
|
||||
[SITAR_A_CDC_CLSG_TIMER_B1_CFG] = SITAR_A_CDC_CLSG_TIMER_B1_CFG__POR,
|
||||
[SITAR_A_CDC_CLSG_TIMER_B2_CFG] = SITAR_A_CDC_CLSG_TIMER_B2_CFG__POR,
|
||||
[SITAR_A_CDC_CLSG_CTL] = SITAR_A_CDC_CLSG_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B1_CTL] = SITAR_A_CDC_IIR1_GAIN_B1_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B2_CTL] = SITAR_A_CDC_IIR1_GAIN_B2_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B3_CTL] = SITAR_A_CDC_IIR1_GAIN_B3_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B4_CTL] = SITAR_A_CDC_IIR1_GAIN_B4_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B5_CTL] = SITAR_A_CDC_IIR1_GAIN_B5_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B6_CTL] = SITAR_A_CDC_IIR1_GAIN_B6_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B7_CTL] = SITAR_A_CDC_IIR1_GAIN_B7_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B8_CTL] = SITAR_A_CDC_IIR1_GAIN_B8_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_CTL] = SITAR_A_CDC_IIR1_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_GAIN_TIMER_CTL] =
|
||||
SITAR_A_CDC_IIR1_GAIN_TIMER_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_COEF_B1_CTL] = SITAR_A_CDC_IIR1_COEF_B1_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_COEF_B2_CTL] = SITAR_A_CDC_IIR1_COEF_B2_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_COEF_B3_CTL] = SITAR_A_CDC_IIR1_COEF_B3_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_COEF_B4_CTL] = SITAR_A_CDC_IIR1_COEF_B4_CTL__POR,
|
||||
[SITAR_A_CDC_IIR1_COEF_B5_CTL] = SITAR_A_CDC_IIR1_COEF_B5_CTL__POR,
|
||||
[SITAR_A_CDC_TOP_GAIN_UPDATE] = SITAR_A_CDC_TOP_GAIN_UPDATE__POR,
|
||||
[SITAR_A_CDC_TOP_RDAC_DOUT_CTL] = SITAR_A_CDC_TOP_RDAC_DOUT_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B1_CTL] = SITAR_A_CDC_DEBUG_B1_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B2_CTL] = SITAR_A_CDC_DEBUG_B2_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B3_CTL] = SITAR_A_CDC_DEBUG_B3_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B4_CTL] = SITAR_A_CDC_DEBUG_B4_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B5_CTL] = SITAR_A_CDC_DEBUG_B5_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B6_CTL] = SITAR_A_CDC_DEBUG_B6_CTL__POR,
|
||||
[SITAR_A_CDC_DEBUG_B7_CTL] = SITAR_A_CDC_DEBUG_B7_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B1_CTL] = SITAR_A_CDC_COMP1_B1_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B2_CTL] = SITAR_A_CDC_COMP1_B2_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B3_CTL] = SITAR_A_CDC_COMP1_B3_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B4_CTL] = SITAR_A_CDC_COMP1_B4_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B5_CTL] = SITAR_A_CDC_COMP1_B5_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_B6_CTL] = SITAR_A_CDC_COMP1_B6_CTL__POR,
|
||||
[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] =
|
||||
SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS__POR,
|
||||
[SITAR_A_CDC_COMP1_FS_CFG] = SITAR_A_CDC_COMP1_FS_CFG__POR,
|
||||
[SITAR_A_CDC_CONN_RX1_B1_CTL] = SITAR_A_CDC_CONN_RX1_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX1_B2_CTL] = SITAR_A_CDC_CONN_RX1_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX1_B3_CTL] = SITAR_A_CDC_CONN_RX1_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX2_B1_CTL] = SITAR_A_CDC_CONN_RX2_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX2_B2_CTL] = SITAR_A_CDC_CONN_RX2_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX2_B3_CTL] = SITAR_A_CDC_CONN_RX2_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX3_B1_CTL] = SITAR_A_CDC_CONN_RX3_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX3_B2_CTL] = SITAR_A_CDC_CONN_RX3_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX3_B3_CTL] = SITAR_A_CDC_CONN_RX3_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_ANC_B1_CTL] = SITAR_A_CDC_CONN_ANC_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_ANC_B2_CTL] = SITAR_A_CDC_CONN_ANC_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_B1_CTL] = SITAR_A_CDC_CONN_TX_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_B2_CTL] = SITAR_A_CDC_CONN_TX_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ1_B1_CTL] = SITAR_A_CDC_CONN_EQ1_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ1_B2_CTL] = SITAR_A_CDC_CONN_EQ1_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ1_B3_CTL] = SITAR_A_CDC_CONN_EQ1_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ1_B4_CTL] = SITAR_A_CDC_CONN_EQ1_B4_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ2_B1_CTL] = SITAR_A_CDC_CONN_EQ2_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ2_B2_CTL] = SITAR_A_CDC_CONN_EQ2_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ2_B3_CTL] = SITAR_A_CDC_CONN_EQ2_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_EQ2_B4_CTL] = SITAR_A_CDC_CONN_EQ2_B4_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_SRC1_B1_CTL] = SITAR_A_CDC_CONN_SRC1_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_SRC1_B2_CTL] = SITAR_A_CDC_CONN_SRC1_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_SRC2_B1_CTL] = SITAR_A_CDC_CONN_SRC2_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_SRC2_B2_CTL] = SITAR_A_CDC_CONN_SRC2_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B1_CTL] = SITAR_A_CDC_CONN_TX_SB_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B2_CTL] = SITAR_A_CDC_CONN_TX_SB_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B3_CTL] = SITAR_A_CDC_CONN_TX_SB_B3_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B4_CTL] = SITAR_A_CDC_CONN_TX_SB_B4_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B5_CTL] = SITAR_A_CDC_CONN_TX_SB_B5_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX_SB_B1_CTL] = SITAR_A_CDC_CONN_RX_SB_B1_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_RX_SB_B2_CTL] = SITAR_A_CDC_CONN_RX_SB_B2_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_CLSG_CTL] = SITAR_A_CDC_CONN_CLSG_CTL__POR,
|
||||
[SITAR_A_CDC_CONN_SPARE] = SITAR_A_CDC_CONN_SPARE__POR,
|
||||
[SITAR_A_CDC_MBHC_EN_CTL] = SITAR_A_CDC_MBHC_EN_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_FIR_B1_CFG] = SITAR_A_CDC_MBHC_FIR_B1_CFG__POR,
|
||||
[SITAR_A_CDC_MBHC_FIR_B2_CFG] = SITAR_A_CDC_MBHC_FIR_B2_CFG__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B1_CTL] = SITAR_A_CDC_MBHC_TIMER_B1_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B2_CTL] = SITAR_A_CDC_MBHC_TIMER_B2_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B3_CTL] = SITAR_A_CDC_MBHC_TIMER_B3_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B4_CTL] = SITAR_A_CDC_MBHC_TIMER_B4_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B5_CTL] = SITAR_A_CDC_MBHC_TIMER_B5_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B6_CTL] = SITAR_A_CDC_MBHC_TIMER_B6_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_B1_STATUS] = SITAR_A_CDC_MBHC_B1_STATUS__POR,
|
||||
[SITAR_A_CDC_MBHC_B2_STATUS] = SITAR_A_CDC_MBHC_B2_STATUS__POR,
|
||||
[SITAR_A_CDC_MBHC_B3_STATUS] = SITAR_A_CDC_MBHC_B3_STATUS__POR,
|
||||
[SITAR_A_CDC_MBHC_B4_STATUS] = SITAR_A_CDC_MBHC_B4_STATUS__POR,
|
||||
[SITAR_A_CDC_MBHC_B5_STATUS] = SITAR_A_CDC_MBHC_B5_STATUS__POR,
|
||||
[SITAR_A_CDC_MBHC_B1_CTL] = SITAR_A_CDC_MBHC_B1_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_B2_CTL] = SITAR_A_CDC_MBHC_B2_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B1_CTL] = SITAR_A_CDC_MBHC_VOLT_B1_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B2_CTL] = SITAR_A_CDC_MBHC_VOLT_B2_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B3_CTL] = SITAR_A_CDC_MBHC_VOLT_B3_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B4_CTL] = SITAR_A_CDC_MBHC_VOLT_B4_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B5_CTL] = SITAR_A_CDC_MBHC_VOLT_B5_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B6_CTL] = SITAR_A_CDC_MBHC_VOLT_B6_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B7_CTL] = SITAR_A_CDC_MBHC_VOLT_B7_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B8_CTL] = SITAR_A_CDC_MBHC_VOLT_B8_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B9_CTL] = SITAR_A_CDC_MBHC_VOLT_B9_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B10_CTL] = SITAR_A_CDC_MBHC_VOLT_B10_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B11_CTL] = SITAR_A_CDC_MBHC_VOLT_B11_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B12_CTL] = SITAR_A_CDC_MBHC_VOLT_B12_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_CLK_CTL] = SITAR_A_CDC_MBHC_CLK_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_INT_CTL] = SITAR_A_CDC_MBHC_INT_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_DEBUG_CTL] = SITAR_A_CDC_MBHC_DEBUG_CTL__POR,
|
||||
[SITAR_A_CDC_MBHC_SPARE] = SITAR_A_CDC_MBHC_SPARE__POR,
|
||||
};
|
||||
|
||||
const u8 sitar_reg_readable[SITAR_CACHE_SIZE] = {
|
||||
[WCD9XXX_A_CHIP_CTL] = 1,
|
||||
[WCD9XXX_A_CHIP_STATUS] = 1,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_0] = 1,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_1] = 1,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_2] = 1,
|
||||
[WCD9XXX_A_CHIP_ID_BYTE_3] = 1,
|
||||
[WCD9XXX_A_CHIP_VERSION] = 1,
|
||||
[WCD9XXX_A_SB_VERSION] = 1,
|
||||
[WCD9XXX_A_SLAVE_ID_1] = 1,
|
||||
[WCD9XXX_A_SLAVE_ID_2] = 1,
|
||||
[WCD9XXX_A_SLAVE_ID_3] = 1,
|
||||
[SITAR_A_PIN_CTL_OE0] = 1,
|
||||
[SITAR_A_PIN_CTL_OE1] = 1,
|
||||
[SITAR_A_PIN_CTL_DATA0] = 1,
|
||||
[SITAR_A_PIN_CTL_DATA1] = 1,
|
||||
[SITAR_A_HDRIVE_GENERIC] = 1,
|
||||
[SITAR_A_HDRIVE_OVERRIDE] = 1,
|
||||
[SITAR_A_ANA_CSR_WAIT_STATE] = 1,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL0] = 1,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL1] = 1,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL2] = 1,
|
||||
[SITAR_A_PROCESS_MONITOR_CTL3] = 1,
|
||||
[SITAR_A_QFUSE_CTL] = 1,
|
||||
[SITAR_A_QFUSE_STATUS] = 1,
|
||||
[SITAR_A_QFUSE_DATA_OUT0] = 1,
|
||||
[SITAR_A_QFUSE_DATA_OUT1] = 1,
|
||||
[SITAR_A_QFUSE_DATA_OUT2] = 1,
|
||||
[SITAR_A_QFUSE_DATA_OUT3] = 1,
|
||||
[SITAR_A_CDC_CTL] = 1,
|
||||
[SITAR_A_LEAKAGE_CTL] = 1,
|
||||
[SITAR_A_INTR_MODE] = 1,
|
||||
[SITAR_A_INTR_MASK0] = 1,
|
||||
[SITAR_A_INTR_MASK1] = 1,
|
||||
[SITAR_A_INTR_MASK2] = 1,
|
||||
[SITAR_A_INTR_STATUS0] = 1,
|
||||
[SITAR_A_INTR_STATUS1] = 1,
|
||||
[SITAR_A_INTR_STATUS2] = 1,
|
||||
[SITAR_A_INTR_LEVEL0] = 1,
|
||||
[SITAR_A_INTR_LEVEL1] = 1,
|
||||
[SITAR_A_INTR_LEVEL2] = 1,
|
||||
[SITAR_A_INTR_TEST0] = 1,
|
||||
[SITAR_A_INTR_TEST1] = 1,
|
||||
[SITAR_A_INTR_TEST2] = 1,
|
||||
[SITAR_A_INTR_SET0] = 1,
|
||||
[SITAR_A_INTR_SET1] = 1,
|
||||
[SITAR_A_INTR_SET2] = 1,
|
||||
[SITAR_A_CDC_TX_I2S_SCK_MODE] = 1,
|
||||
[SITAR_A_CDC_TX_I2S_WS_MODE] = 1,
|
||||
[SITAR_A_CDC_DMIC_DATA0_MODE] = 1,
|
||||
[SITAR_A_CDC_DMIC_CLK0_MODE] = 1,
|
||||
[SITAR_A_CDC_DMIC_DATA1_MODE] = 1,
|
||||
[SITAR_A_CDC_DMIC_CLK1_MODE] = 1,
|
||||
[SITAR_A_CDC_TX_I2S_SD0_MODE] = 1,
|
||||
[SITAR_A_CDC_INTR_MODE] = 1,
|
||||
[SITAR_A_CDC_RX_I2S_SD0_MODE] = 1,
|
||||
[SITAR_A_CDC_RX_I2S_SD1_MODE] = 1,
|
||||
[SITAR_A_BIAS_REF_CTL] = 1,
|
||||
[SITAR_A_BIAS_CENTRAL_BG_CTL] = 1,
|
||||
[SITAR_A_BIAS_PRECHRG_CTL] = 1,
|
||||
[SITAR_A_BIAS_CURR_CTL_1] = 1,
|
||||
[SITAR_A_BIAS_CURR_CTL_2] = 1,
|
||||
[SITAR_A_BIAS_OSC_BG_CTL] = 1,
|
||||
[SITAR_A_CLK_BUFF_EN1] = 1,
|
||||
[SITAR_A_CLK_BUFF_EN2] = 1,
|
||||
[SITAR_A_LDO_H_MODE_1] = 1,
|
||||
[SITAR_A_LDO_H_MODE_2] = 1,
|
||||
[SITAR_A_LDO_H_LOOP_CTL] = 1,
|
||||
[SITAR_A_LDO_H_COMP_1] = 1,
|
||||
[SITAR_A_LDO_H_COMP_2] = 1,
|
||||
[SITAR_A_LDO_H_BIAS_1] = 1,
|
||||
[SITAR_A_LDO_H_BIAS_2] = 1,
|
||||
[SITAR_A_LDO_H_BIAS_3] = 1,
|
||||
[SITAR_A_MICB_CFILT_1_CTL] = 1,
|
||||
[SITAR_A_MICB_CFILT_1_VAL] = 1,
|
||||
[SITAR_A_MICB_CFILT_1_PRECHRG] = 1,
|
||||
[SITAR_A_MICB_1_CTL] = 1,
|
||||
[SITAR_A_MICB_1_INT_RBIAS] = 1,
|
||||
[SITAR_A_MICB_1_MBHC] = 1,
|
||||
[SITAR_A_MICB_CFILT_2_CTL] = 1,
|
||||
[SITAR_A_MICB_CFILT_2_VAL] = 1,
|
||||
[SITAR_A_MICB_CFILT_2_PRECHRG] = 1,
|
||||
[SITAR_A_MICB_2_CTL] = 1,
|
||||
[SITAR_A_MICB_2_INT_RBIAS] = 1,
|
||||
[SITAR_A_MICB_2_MBHC] = 1,
|
||||
[SITAR_A_TX_COM_BIAS] = 1,
|
||||
[SITAR_A_MBHC_SCALING_MUX_1] = 1,
|
||||
[SITAR_A_MBHC_SCALING_MUX_2] = 1,
|
||||
[SITAR_A_TX_SUP_SWITCH_CTRL_1] = 1,
|
||||
[SITAR_A_TX_SUP_SWITCH_CTRL_2] = 1,
|
||||
[SITAR_A_TX_1_2_EN] = 1,
|
||||
[SITAR_A_TX_1_2_TEST_EN] = 1,
|
||||
[SITAR_A_TX_1_2_ADC_CH1] = 1,
|
||||
[SITAR_A_TX_1_2_ADC_CH2] = 1,
|
||||
[SITAR_A_TX_1_2_ATEST_REFCTRL] = 1,
|
||||
[SITAR_A_TX_1_2_TEST_CTL] = 1,
|
||||
[SITAR_A_TX_1_2_TEST_BLOCK_EN] = 1,
|
||||
[SITAR_A_TX_1_2_TXFE_CLKDIV] = 1,
|
||||
[SITAR_A_TX_1_2_SAR_ERR_CH1] = 1,
|
||||
[SITAR_A_TX_1_2_SAR_ERR_CH2] = 1,
|
||||
[SITAR_A_TX_3_EN] = 1,
|
||||
[SITAR_A_TX_3_TEST_EN] = 1,
|
||||
[SITAR_A_TX_3_ADC] = 1,
|
||||
[SITAR_A_TX_3_MBHC_ATEST_REFCTRL] = 1,
|
||||
[SITAR_A_TX_3_TEST_CTL] = 1,
|
||||
[SITAR_A_TX_3_TEST_BLOCK_EN] = 1,
|
||||
[SITAR_A_TX_3_TXFE_CKDIV] = 1,
|
||||
[SITAR_A_TX_3_SAR_ERR] = 1,
|
||||
[SITAR_A_TX_4_MBHC_EN] = 1,
|
||||
[SITAR_A_TX_4_MBHC_ADC] = 1,
|
||||
[SITAR_A_TX_4_MBHC_TEST_CTL] = 1,
|
||||
[SITAR_A_TX_4_MBHC_SAR_ERR] = 1,
|
||||
[SITAR_A_TX_4_TXFE_CLKDIV] = 1,
|
||||
[SITAR_A_AUX_COM_CTL] = 1,
|
||||
[SITAR_A_AUX_COM_ATEST] = 1,
|
||||
[SITAR_A_AUX_L_EN] = 1,
|
||||
[SITAR_A_AUX_L_GAIN] = 1,
|
||||
[SITAR_A_AUX_L_PA_CONN] = 1,
|
||||
[SITAR_A_AUX_L_PA_CONN_INV] = 1,
|
||||
[SITAR_A_AUX_R_EN] = 1,
|
||||
[SITAR_A_AUX_R_GAIN] = 1,
|
||||
[SITAR_A_AUX_R_PA_CONN] = 1,
|
||||
[SITAR_A_AUX_R_PA_CONN_INV] = 1,
|
||||
[SITAR_A_CP_EN] = 1,
|
||||
[SITAR_A_CP_CLK] = 1,
|
||||
[SITAR_A_CP_STATIC] = 1,
|
||||
[SITAR_A_CP_DCC1] = 1,
|
||||
[SITAR_A_CP_DCC3] = 1,
|
||||
[SITAR_A_CP_ATEST] = 1,
|
||||
[SITAR_A_CP_DTEST] = 1,
|
||||
[SITAR_A_RX_COM_TIMER_DIV] = 1,
|
||||
[SITAR_A_RX_COM_OCP_CTL] = 1,
|
||||
[SITAR_A_RX_COM_OCP_COUNT] = 1,
|
||||
[SITAR_A_RX_COM_DAC_CTL] = 1,
|
||||
[SITAR_A_RX_COM_BIAS] = 1,
|
||||
[SITAR_A_RX_HPH_BIAS_PA] = 1,
|
||||
[SITAR_A_RX_HPH_BIAS_LDO] = 1,
|
||||
[SITAR_A_RX_HPH_BIAS_CNP] = 1,
|
||||
[SITAR_A_RX_HPH_BIAS_WG] = 1,
|
||||
[SITAR_A_RX_HPH_OCP_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_CNP_EN] = 1,
|
||||
[SITAR_A_RX_HPH_CNP_WG_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_CNP_WG_TIME] = 1,
|
||||
[SITAR_A_RX_HPH_L_GAIN] = 1,
|
||||
[SITAR_A_RX_HPH_L_TEST] = 1,
|
||||
[SITAR_A_RX_HPH_L_PA_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_L_DAC_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_L_ATEST] = 1,
|
||||
[SITAR_A_RX_HPH_L_STATUS] = 1,
|
||||
[SITAR_A_RX_HPH_R_GAIN] = 1,
|
||||
[SITAR_A_RX_HPH_R_TEST] = 1,
|
||||
[SITAR_A_RX_HPH_R_PA_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_R_DAC_CTL] = 1,
|
||||
[SITAR_A_RX_HPH_R_ATEST] = 1,
|
||||
[SITAR_A_RX_HPH_R_STATUS] = 1,
|
||||
[SITAR_A_RX_EAR_BIAS_PA] = 1,
|
||||
[SITAR_A_RX_EAR_BIAS_CMBUFF] = 1,
|
||||
[SITAR_A_RX_EAR_EN] = 1,
|
||||
[SITAR_A_RX_EAR_GAIN] = 1,
|
||||
[SITAR_A_RX_EAR_CMBUFF] = 1,
|
||||
[SITAR_A_RX_EAR_ICTL] = 1,
|
||||
[SITAR_A_RX_EAR_CCOMP] = 1,
|
||||
[SITAR_A_RX_EAR_VCM] = 1,
|
||||
[SITAR_A_RX_EAR_CNP] = 1,
|
||||
[SITAR_A_RX_EAR_ATEST] = 1,
|
||||
[SITAR_A_RX_EAR_STATUS] = 1,
|
||||
[SITAR_A_RX_LINE_BIAS_PA] = 1,
|
||||
[SITAR_A_RX_LINE_BIAS_LDO] = 1,
|
||||
[SITAR_A_RX_LINE_BIAS_CNP1] = 1,
|
||||
[SITAR_A_RX_LINE_COM] = 1,
|
||||
[SITAR_A_RX_LINE_CNP_EN] = 1,
|
||||
[SITAR_A_RX_LINE_CNP_WG_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_CNP_WG_TIME] = 1,
|
||||
[SITAR_A_RX_LINE_1_GAIN] = 1,
|
||||
[SITAR_A_RX_LINE_1_TEST] = 1,
|
||||
[SITAR_A_RX_LINE_1_DAC_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_1_STATUS] = 1,
|
||||
[SITAR_A_RX_LINE_2_GAIN] = 1,
|
||||
[SITAR_A_RX_LINE_2_TEST] = 1,
|
||||
[SITAR_A_RX_LINE_2_DAC_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_2_STATUS] = 1,
|
||||
[SITAR_A_RX_LINE_BIAS_CNP2] = 1,
|
||||
[SITAR_A_RX_LINE_OCP_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_1_PA_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_2_PA_CTL] = 1,
|
||||
[SITAR_A_RX_LINE_CNP_DBG] = 1,
|
||||
[SITAR_A_MBHC_HPH] = 1,
|
||||
[SITAR_A_RC_OSC_FREQ] = 1,
|
||||
[SITAR_A_RC_OSC_TEST] = 1,
|
||||
[SITAR_A_RC_OSC_STATUS] = 1,
|
||||
[SITAR_A_RC_OSC_TUNER] = 1,
|
||||
[SITAR_A_CDC_ANC1_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_SHIFT] = 1,
|
||||
[SITAR_A_CDC_ANC1_IIR_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_IIR_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_IIR_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_IIR_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_LPF_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_LPF_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_LPF_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_SPARE] = 1,
|
||||
[SITAR_A_CDC_ANC1_SMLPF_CTL] = 1,
|
||||
[SITAR_A_CDC_ANC1_DCFLT_CTL] = 1,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_TIMER] = 1,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_GAIN] = 1,
|
||||
[SITAR_A_CDC_TX1_VOL_CTL_CFG] = 1,
|
||||
[SITAR_A_CDC_TX1_MUX_CTL] = 1,
|
||||
[SITAR_A_CDC_TX1_CLK_FS_CTL] = 1,
|
||||
[SITAR_A_CDC_TX1_DMIC_CTL] = 1,
|
||||
[SITAR_A_CDC_SRC1_PDA_CFG] = 1,
|
||||
[SITAR_A_CDC_SRC1_FS_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_RX2_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_RX3_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_ANC_RESET_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_RX_RESET_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_TX_RESET_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_DMIC_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_RX_I2S_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_TX_I2S_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_OTHR_RESET_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_OTHR_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_RDAC_CLK_EN_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_ANC_CLK_EN_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_RX_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_RX_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_MCLK_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_PDM_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_SD_CTL] = 1,
|
||||
[SITAR_A_CDC_CLK_LP_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_FREQ_THRESH_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_GAIN_THRESH_CTL] = 1,
|
||||
[SITAR_A_CDC_CLSG_TIMER_B1_CFG] = 1,
|
||||
[SITAR_A_CDC_CLSG_TIMER_B2_CFG] = 1,
|
||||
[SITAR_A_CDC_CLSG_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B7_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_B8_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_GAIN_TIMER_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_COEF_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_COEF_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_COEF_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_COEF_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_IIR1_COEF_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_TOP_GAIN_UPDATE] = 1,
|
||||
[SITAR_A_CDC_TOP_RDAC_DOUT_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_DEBUG_B7_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
|
||||
[SITAR_A_CDC_COMP1_FS_CFG] = 1,
|
||||
[SITAR_A_CDC_CONN_RX1_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX1_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX1_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX2_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX2_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX2_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX3_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX3_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX3_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_ANC_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_ANC_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ1_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ1_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ1_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ1_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ2_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ2_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ2_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_EQ2_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_SRC1_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_SRC1_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_SRC2_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_SRC2_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_TX_SB_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX_SB_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_RX_SB_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_CLSG_CTL] = 1,
|
||||
[SITAR_A_CDC_CONN_SPARE] = 1,
|
||||
[SITAR_A_CDC_MBHC_EN_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_FIR_B1_CFG] = 1,
|
||||
[SITAR_A_CDC_MBHC_FIR_B2_CFG] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_TIMER_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_B1_STATUS] = 1,
|
||||
[SITAR_A_CDC_MBHC_B2_STATUS] = 1,
|
||||
[SITAR_A_CDC_MBHC_B3_STATUS] = 1,
|
||||
[SITAR_A_CDC_MBHC_B4_STATUS] = 1,
|
||||
[SITAR_A_CDC_MBHC_B5_STATUS] = 1,
|
||||
[SITAR_A_CDC_MBHC_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B1_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B2_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B3_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B4_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B5_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B6_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B7_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B8_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B9_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B10_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B11_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_VOLT_B12_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_CLK_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_INT_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_DEBUG_CTL] = 1,
|
||||
[SITAR_A_CDC_MBHC_SPARE] = 1,
|
||||
};
|
||||
5013
sound/soc/codecs/wcd9304.c
Normal file
5013
sound/soc/codecs/wcd9304.c
Normal file
File diff suppressed because it is too large
Load Diff
251
sound/soc/codecs/wcd9304.h
Normal file
251
sound/soc/codecs/wcd9304.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <sound/soc.h>
|
||||
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
|
||||
|
||||
#define SITAR_NUM_REGISTERS 0x400
|
||||
#define SITAR_MAX_REGISTER (SITAR_NUM_REGISTERS-1)
|
||||
#define SITAR_CACHE_SIZE SITAR_NUM_REGISTERS
|
||||
#define SITAR_1_X_ONLY_REGISTERS 3
|
||||
#define SITAR_2_HIGHER_ONLY_REGISTERS 3
|
||||
|
||||
#define SITAR_REG_VAL(reg, val) {reg, 0, val}
|
||||
|
||||
#define DEFAULT_DCE_STA_WAIT 55
|
||||
#define DEFAULT_DCE_WAIT 60000
|
||||
#define DEFAULT_STA_WAIT 5000
|
||||
|
||||
#define STA 0
|
||||
#define DCE 1
|
||||
|
||||
#define SITAR_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
|
||||
SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
|
||||
SND_JACK_BTN_6 | SND_JACK_BTN_7)
|
||||
|
||||
extern const u8 sitar_reg_readable[SITAR_CACHE_SIZE];
|
||||
extern const u32 sitar_1_reg_readable[SITAR_1_X_ONLY_REGISTERS];
|
||||
extern const u32 sitar_2_reg_readable[SITAR_2_HIGHER_ONLY_REGISTERS];
|
||||
extern const u8 sitar_reg_defaults[SITAR_CACHE_SIZE];
|
||||
|
||||
enum sitar_micbias_num {
|
||||
SITAR_MICBIAS1,
|
||||
SITAR_MICBIAS2,
|
||||
SITAR_MICBIAS3,
|
||||
SITAR_MICBIAS4,
|
||||
};
|
||||
|
||||
enum sitar_pid_current {
|
||||
SITAR_PID_MIC_2P5_UA,
|
||||
SITAR_PID_MIC_5_UA,
|
||||
SITAR_PID_MIC_10_UA,
|
||||
SITAR_PID_MIC_20_UA,
|
||||
};
|
||||
|
||||
struct sitar_reg_mask_val {
|
||||
u16 reg;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum sitar_mbhc_clk_freq {
|
||||
SITAR_MCLK_12P2MHZ = 0,
|
||||
SITAR_MCLK_9P6MHZ,
|
||||
SITAR_NUM_CLK_FREQS,
|
||||
};
|
||||
|
||||
enum sitar_mbhc_analog_pwr_cfg {
|
||||
SITAR_ANALOG_PWR_COLLAPSED = 0,
|
||||
SITAR_ANALOG_PWR_ON,
|
||||
SITAR_NUM_ANALOG_PWR_CONFIGS,
|
||||
};
|
||||
|
||||
enum sitar_mbhc_btn_det_mem {
|
||||
SITAR_BTN_DET_V_BTN_LOW,
|
||||
SITAR_BTN_DET_V_BTN_HIGH,
|
||||
SITAR_BTN_DET_N_READY,
|
||||
SITAR_BTN_DET_N_CIC,
|
||||
SITAR_BTN_DET_GAIN
|
||||
};
|
||||
|
||||
struct sitar_mbhc_general_cfg {
|
||||
u8 t_ldoh;
|
||||
u8 t_bg_fast_settle;
|
||||
u8 t_shutdown_plug_rem;
|
||||
u8 mbhc_nsa;
|
||||
u8 mbhc_navg;
|
||||
u8 v_micbias_l;
|
||||
u8 v_micbias;
|
||||
u8 mbhc_reserved;
|
||||
u16 settle_wait;
|
||||
u16 t_micbias_rampup;
|
||||
u16 t_micbias_rampdown;
|
||||
u16 t_supply_bringup;
|
||||
} __packed;
|
||||
|
||||
struct sitar_mbhc_plug_detect_cfg {
|
||||
u32 mic_current;
|
||||
u32 hph_current;
|
||||
u16 t_mic_pid;
|
||||
u16 t_ins_complete;
|
||||
u16 t_ins_retry;
|
||||
u16 v_removal_delta;
|
||||
u8 micbias_slow_ramp;
|
||||
u8 reserved0;
|
||||
u8 reserved1;
|
||||
u8 reserved2;
|
||||
} __packed;
|
||||
|
||||
struct sitar_mbhc_plug_type_cfg {
|
||||
u8 av_detect;
|
||||
u8 mono_detect;
|
||||
u8 num_ins_tries;
|
||||
u8 reserved0;
|
||||
s16 v_no_mic;
|
||||
s16 v_av_min;
|
||||
s16 v_av_max;
|
||||
s16 v_hs_min;
|
||||
s16 v_hs_max;
|
||||
u16 reserved1;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct sitar_mbhc_btn_detect_cfg {
|
||||
s8 c[8];
|
||||
u8 nc;
|
||||
u8 n_meas;
|
||||
u8 mbhc_nsc;
|
||||
u8 n_btn_meas;
|
||||
u8 n_btn_con;
|
||||
u8 num_btn;
|
||||
u8 reserved0;
|
||||
u8 reserved1;
|
||||
u16 t_poll;
|
||||
u16 t_bounce_wait;
|
||||
u16 t_rel_timeout;
|
||||
s16 v_btn_press_delta_sta;
|
||||
s16 v_btn_press_delta_cic;
|
||||
u16 t_btn0_timeout;
|
||||
s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
|
||||
s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
|
||||
u8 _n_ready[SITAR_NUM_CLK_FREQS];
|
||||
u8 _n_cic[SITAR_NUM_CLK_FREQS];
|
||||
u8 _gain[SITAR_NUM_CLK_FREQS];
|
||||
} __packed;
|
||||
|
||||
struct sitar_mbhc_imped_detect_cfg {
|
||||
u8 _hs_imped_detect;
|
||||
u8 _n_rload;
|
||||
u8 _hph_keep_on;
|
||||
u8 _repeat_rload_calc;
|
||||
u16 _t_dac_ramp_time;
|
||||
u16 _rhph_high;
|
||||
u16 _rhph_low;
|
||||
u16 _rload[0]; /* rload[n_rload] */
|
||||
u16 _alpha[0]; /* alpha[n_rload] */
|
||||
u16 _beta[3];
|
||||
} __packed;
|
||||
|
||||
struct sitar_mbhc_config {
|
||||
struct snd_soc_jack *headset_jack;
|
||||
struct snd_soc_jack *button_jack;
|
||||
bool read_fw_bin;
|
||||
/* void* calibration contains:
|
||||
* struct tabla_mbhc_general_cfg generic;
|
||||
* struct tabla_mbhc_plug_detect_cfg plug_det;
|
||||
* struct tabla_mbhc_plug_type_cfg plug_type;
|
||||
* struct tabla_mbhc_btn_detect_cfg btn_det;
|
||||
* struct tabla_mbhc_imped_detect_cfg imped_det;
|
||||
* Note: various size depends on btn_det->num_btn
|
||||
*/
|
||||
void *calibration;
|
||||
enum sitar_micbias_num micbias;
|
||||
int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
|
||||
unsigned int mclk_rate;
|
||||
unsigned int gpio;
|
||||
unsigned int gpio_irq;
|
||||
int gpio_level_insert;
|
||||
};
|
||||
|
||||
extern int sitar_hs_detect(struct snd_soc_codec *codec,
|
||||
const struct sitar_mbhc_config *cfg);
|
||||
|
||||
#ifndef anc_header_dec
|
||||
struct anc_header {
|
||||
u32 reserved[3];
|
||||
u32 num_anc_slots;
|
||||
};
|
||||
#define anc_header_dec
|
||||
#endif
|
||||
|
||||
extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
|
||||
bool dapm);
|
||||
|
||||
extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
|
||||
*btn_det,
|
||||
const enum sitar_mbhc_btn_det_mem mem);
|
||||
|
||||
#define SITAR_MBHC_CAL_SIZE(buttons, rload) ( \
|
||||
sizeof(enum sitar_micbias_num) + \
|
||||
sizeof(struct sitar_mbhc_general_cfg) + \
|
||||
sizeof(struct sitar_mbhc_plug_detect_cfg) + \
|
||||
((sizeof(s16) + sizeof(s16)) * buttons) + \
|
||||
sizeof(struct sitar_mbhc_plug_type_cfg) + \
|
||||
sizeof(struct sitar_mbhc_btn_detect_cfg) + \
|
||||
sizeof(struct sitar_mbhc_imped_detect_cfg) + \
|
||||
((sizeof(u16) + sizeof(u16)) * rload) \
|
||||
)
|
||||
|
||||
#define SITAR_MBHC_CAL_GENERAL_PTR(cali) ( \
|
||||
(struct sitar_mbhc_general_cfg *) cali)
|
||||
#define SITAR_MBHC_CAL_PLUG_DET_PTR(cali) ( \
|
||||
(struct sitar_mbhc_plug_detect_cfg *) \
|
||||
&(SITAR_MBHC_CAL_GENERAL_PTR(cali)[1]))
|
||||
#define SITAR_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
|
||||
(struct sitar_mbhc_plug_type_cfg *) \
|
||||
&(SITAR_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
|
||||
#define SITAR_MBHC_CAL_BTN_DET_PTR(cali) ( \
|
||||
(struct sitar_mbhc_btn_detect_cfg *) \
|
||||
&(SITAR_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
|
||||
#define SITAR_MBHC_CAL_IMPED_DET_PTR(cali) ( \
|
||||
(struct sitar_mbhc_imped_detect_cfg *) \
|
||||
(((void *)&SITAR_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
|
||||
(SITAR_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
|
||||
(sizeof(SITAR_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
|
||||
sizeof(SITAR_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
|
||||
)
|
||||
|
||||
/* minimum size of calibration data assuming there is only one button and
|
||||
* one rload.
|
||||
*/
|
||||
#define SITAR_MBHC_CAL_MIN_SIZE ( \
|
||||
sizeof(struct sitar_mbhc_general_cfg) + \
|
||||
sizeof(struct sitar_mbhc_plug_detect_cfg) + \
|
||||
sizeof(struct sitar_mbhc_plug_type_cfg) + \
|
||||
sizeof(struct sitar_mbhc_btn_detect_cfg) + \
|
||||
sizeof(struct sitar_mbhc_imped_detect_cfg) + \
|
||||
(sizeof(u16) * 2))
|
||||
|
||||
#define SITAR_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
|
||||
sizeof(struct sitar_mbhc_btn_detect_cfg) + \
|
||||
(cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
|
||||
sizeof(cfg_ptr->_v_btn_high[0]))))
|
||||
|
||||
#define SITAR_MBHC_CAL_IMPED_MIN_SZ ( \
|
||||
sizeof(struct sitar_mbhc_imped_detect_cfg) + \
|
||||
sizeof(u16) * 2)
|
||||
|
||||
#define SITAR_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
|
||||
sizeof(struct sitar_mbhc_imped_detect_cfg) + \
|
||||
(cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
|
||||
sizeof(cfg_ptr->_alpha[0]))))
|
||||
1114
sound/soc/codecs/wcd9310-tables.c
Normal file
1114
sound/soc/codecs/wcd9310-tables.c
Normal file
File diff suppressed because it is too large
Load Diff
7792
sound/soc/codecs/wcd9310.c
Normal file
7792
sound/soc/codecs/wcd9310.c
Normal file
File diff suppressed because it is too large
Load Diff
253
sound/soc/codecs/wcd9310.h
Normal file
253
sound/soc/codecs/wcd9310.h
Normal file
@@ -0,0 +1,253 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
|
||||
|
||||
#define TABLA_NUM_REGISTERS 0x400
|
||||
#define TABLA_MAX_REGISTER (TABLA_NUM_REGISTERS-1)
|
||||
#define TABLA_CACHE_SIZE TABLA_NUM_REGISTERS
|
||||
#define TABLA_1_X_ONLY_REGISTERS 3
|
||||
#define TABLA_2_HIGHER_ONLY_REGISTERS 3
|
||||
|
||||
#define TABLA_REG_VAL(reg, val) {reg, 0, val}
|
||||
|
||||
#define DEFAULT_DCE_STA_WAIT 55
|
||||
#define DEFAULT_DCE_WAIT 60000
|
||||
#define DEFAULT_STA_WAIT 5000
|
||||
#define VDDIO_MICBIAS_MV 1800
|
||||
|
||||
#define STA 0
|
||||
#define DCE 1
|
||||
|
||||
#define TABLA_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
|
||||
SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
|
||||
SND_JACK_BTN_6 | SND_JACK_BTN_7)
|
||||
|
||||
extern const u8 tabla_reg_readable[TABLA_CACHE_SIZE];
|
||||
extern const u32 tabla_1_reg_readable[TABLA_1_X_ONLY_REGISTERS];
|
||||
extern const u32 tabla_2_reg_readable[TABLA_2_HIGHER_ONLY_REGISTERS];
|
||||
extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
|
||||
|
||||
enum tabla_micbias_num {
|
||||
TABLA_MICBIAS1 = 0,
|
||||
TABLA_MICBIAS2,
|
||||
TABLA_MICBIAS3,
|
||||
TABLA_MICBIAS4,
|
||||
};
|
||||
|
||||
enum tabla_pid_current {
|
||||
TABLA_PID_MIC_2P5_UA,
|
||||
TABLA_PID_MIC_5_UA,
|
||||
TABLA_PID_MIC_10_UA,
|
||||
TABLA_PID_MIC_20_UA,
|
||||
};
|
||||
|
||||
struct tabla_reg_mask_val {
|
||||
u16 reg;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum tabla_mbhc_clk_freq {
|
||||
TABLA_MCLK_12P2MHZ = 0,
|
||||
TABLA_MCLK_9P6MHZ,
|
||||
TABLA_NUM_CLK_FREQS,
|
||||
};
|
||||
|
||||
enum tabla_mbhc_analog_pwr_cfg {
|
||||
TABLA_ANALOG_PWR_COLLAPSED = 0,
|
||||
TABLA_ANALOG_PWR_ON,
|
||||
TABLA_NUM_ANALOG_PWR_CONFIGS,
|
||||
};
|
||||
|
||||
enum tabla_mbhc_btn_det_mem {
|
||||
TABLA_BTN_DET_V_BTN_LOW,
|
||||
TABLA_BTN_DET_V_BTN_HIGH,
|
||||
TABLA_BTN_DET_N_READY,
|
||||
TABLA_BTN_DET_N_CIC,
|
||||
TABLA_BTN_DET_GAIN
|
||||
};
|
||||
|
||||
struct tabla_mbhc_general_cfg {
|
||||
u8 t_ldoh;
|
||||
u8 t_bg_fast_settle;
|
||||
u8 t_shutdown_plug_rem;
|
||||
u8 mbhc_nsa;
|
||||
u8 mbhc_navg;
|
||||
u8 v_micbias_l;
|
||||
u8 v_micbias;
|
||||
u8 mbhc_reserved;
|
||||
u16 settle_wait;
|
||||
u16 t_micbias_rampup;
|
||||
u16 t_micbias_rampdown;
|
||||
u16 t_supply_bringup;
|
||||
} __packed;
|
||||
|
||||
struct tabla_mbhc_plug_detect_cfg {
|
||||
u32 mic_current;
|
||||
u32 hph_current;
|
||||
u16 t_mic_pid;
|
||||
u16 t_ins_complete;
|
||||
u16 t_ins_retry;
|
||||
u16 v_removal_delta;
|
||||
u8 micbias_slow_ramp;
|
||||
u8 reserved0;
|
||||
u8 reserved1;
|
||||
u8 reserved2;
|
||||
} __packed;
|
||||
|
||||
struct tabla_mbhc_plug_type_cfg {
|
||||
u8 av_detect;
|
||||
u8 mono_detect;
|
||||
u8 num_ins_tries;
|
||||
u8 reserved0;
|
||||
s16 v_no_mic;
|
||||
s16 v_av_min;
|
||||
s16 v_av_max;
|
||||
s16 v_hs_min;
|
||||
s16 v_hs_max;
|
||||
u16 reserved1;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct tabla_mbhc_btn_detect_cfg {
|
||||
s8 c[8];
|
||||
u8 nc;
|
||||
u8 n_meas;
|
||||
u8 mbhc_nsc;
|
||||
u8 n_btn_meas;
|
||||
u8 n_btn_con;
|
||||
u8 num_btn;
|
||||
u8 reserved0;
|
||||
u8 reserved1;
|
||||
u16 t_poll;
|
||||
u16 t_bounce_wait;
|
||||
u16 t_rel_timeout;
|
||||
s16 v_btn_press_delta_sta;
|
||||
s16 v_btn_press_delta_cic;
|
||||
u16 t_btn0_timeout;
|
||||
s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
|
||||
s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
|
||||
u8 _n_ready[TABLA_NUM_CLK_FREQS];
|
||||
u8 _n_cic[TABLA_NUM_CLK_FREQS];
|
||||
u8 _gain[TABLA_NUM_CLK_FREQS];
|
||||
} __packed;
|
||||
|
||||
struct tabla_mbhc_imped_detect_cfg {
|
||||
u8 _hs_imped_detect;
|
||||
u8 _n_rload;
|
||||
u8 _hph_keep_on;
|
||||
u8 _repeat_rload_calc;
|
||||
u16 _t_dac_ramp_time;
|
||||
u16 _rhph_high;
|
||||
u16 _rhph_low;
|
||||
u16 _rload[0]; /* rload[n_rload] */
|
||||
u16 _alpha[0]; /* alpha[n_rload] */
|
||||
u16 _beta[3];
|
||||
} __packed;
|
||||
|
||||
struct tabla_mbhc_config {
|
||||
struct snd_soc_jack *headset_jack;
|
||||
struct snd_soc_jack *button_jack;
|
||||
bool read_fw_bin;
|
||||
/* void* calibration contains:
|
||||
* struct tabla_mbhc_general_cfg generic;
|
||||
* struct tabla_mbhc_plug_detect_cfg plug_det;
|
||||
* struct tabla_mbhc_plug_type_cfg plug_type;
|
||||
* struct tabla_mbhc_btn_detect_cfg btn_det;
|
||||
* struct tabla_mbhc_imped_detect_cfg imped_det;
|
||||
* Note: various size depends on btn_det->num_btn
|
||||
*/
|
||||
void *calibration;
|
||||
enum tabla_micbias_num micbias;
|
||||
int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
|
||||
unsigned int mclk_rate;
|
||||
unsigned int gpio;
|
||||
unsigned int gpio_irq;
|
||||
int gpio_level_insert;
|
||||
/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
|
||||
bool (*swap_gnd_mic) (struct snd_soc_codec *);
|
||||
};
|
||||
|
||||
extern int tabla_hs_detect(struct snd_soc_codec *codec,
|
||||
const struct tabla_mbhc_config *cfg);
|
||||
|
||||
struct anc_header {
|
||||
u32 reserved[3];
|
||||
u32 num_anc_slots;
|
||||
};
|
||||
|
||||
extern int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
|
||||
bool dapm);
|
||||
|
||||
extern void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg
|
||||
*btn_det,
|
||||
const enum tabla_mbhc_btn_det_mem mem);
|
||||
|
||||
#define TABLA_MBHC_CAL_SIZE(buttons, rload) ( \
|
||||
sizeof(enum tabla_micbias_num) + \
|
||||
sizeof(struct tabla_mbhc_general_cfg) + \
|
||||
sizeof(struct tabla_mbhc_plug_detect_cfg) + \
|
||||
((sizeof(s16) + sizeof(s16)) * buttons) + \
|
||||
sizeof(struct tabla_mbhc_plug_type_cfg) + \
|
||||
sizeof(struct tabla_mbhc_btn_detect_cfg) + \
|
||||
sizeof(struct tabla_mbhc_imped_detect_cfg) + \
|
||||
((sizeof(u16) + sizeof(u16)) * rload) \
|
||||
)
|
||||
|
||||
#define TABLA_MBHC_CAL_GENERAL_PTR(cali) ( \
|
||||
(struct tabla_mbhc_general_cfg *) cali)
|
||||
#define TABLA_MBHC_CAL_PLUG_DET_PTR(cali) ( \
|
||||
(struct tabla_mbhc_plug_detect_cfg *) \
|
||||
&(TABLA_MBHC_CAL_GENERAL_PTR(cali)[1]))
|
||||
#define TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
|
||||
(struct tabla_mbhc_plug_type_cfg *) \
|
||||
&(TABLA_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
|
||||
#define TABLA_MBHC_CAL_BTN_DET_PTR(cali) ( \
|
||||
(struct tabla_mbhc_btn_detect_cfg *) \
|
||||
&(TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
|
||||
#define TABLA_MBHC_CAL_IMPED_DET_PTR(cali) ( \
|
||||
(struct tabla_mbhc_imped_detect_cfg *) \
|
||||
(((void *)&TABLA_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
|
||||
(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
|
||||
(sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
|
||||
sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
|
||||
)
|
||||
|
||||
/* minimum size of calibration data assuming there is only one button and
|
||||
* one rload.
|
||||
*/
|
||||
#define TABLA_MBHC_CAL_MIN_SIZE ( \
|
||||
sizeof(struct tabla_mbhc_general_cfg) + \
|
||||
sizeof(struct tabla_mbhc_plug_detect_cfg) + \
|
||||
sizeof(struct tabla_mbhc_plug_type_cfg) + \
|
||||
sizeof(struct tabla_mbhc_btn_detect_cfg) + \
|
||||
sizeof(struct tabla_mbhc_imped_detect_cfg) + \
|
||||
(sizeof(u16) * 2))
|
||||
|
||||
#define TABLA_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
|
||||
sizeof(struct tabla_mbhc_btn_detect_cfg) + \
|
||||
(cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
|
||||
sizeof(cfg_ptr->_v_btn_high[0]))))
|
||||
|
||||
#define TABLA_MBHC_CAL_IMPED_MIN_SZ ( \
|
||||
sizeof(struct tabla_mbhc_imped_detect_cfg) + \
|
||||
sizeof(u16) * 2)
|
||||
|
||||
#define TABLA_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
|
||||
sizeof(struct tabla_mbhc_imped_detect_cfg) + \
|
||||
(cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
|
||||
sizeof(cfg_ptr->_alpha[0]))))
|
||||
|
||||
|
||||
@@ -643,6 +643,8 @@ SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
|
||||
SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("LINEOUT_VMID_BUF", WM8993_ANTIPOP1, 7, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
|
||||
in1l_pga, ARRAY_SIZE(in1l_pga)),
|
||||
SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
|
||||
@@ -867,9 +869,11 @@ static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
|
||||
{ "LINEOUT1N Mixer", NULL, "LINEOUT_VMID_BUF" },
|
||||
{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
{ "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
|
||||
|
||||
{ "LINEOUT1P Mixer", NULL, "LINEOUT_VMID_BUF" },
|
||||
{ "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
|
||||
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
|
||||
@@ -886,9 +890,11 @@ static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
|
||||
{ "LINEOUT2N Mixer", NULL, "LINEOUT_VMID_BUF" },
|
||||
{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
{ "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
|
||||
|
||||
{ "LINEOUT2P Mixer", NULL, "LINEOUT_VMID_BUF" },
|
||||
{ "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
|
||||
|
||||
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
|
||||
|
||||
158
sound/soc/msm/Kconfig
Normal file
158
sound/soc/msm/Kconfig
Normal file
@@ -0,0 +1,158 @@
|
||||
menu "MSM SoC Audio support"
|
||||
|
||||
#7201 7625 variants
|
||||
config SND_MSM_DAI_SOC
|
||||
tristate
|
||||
|
||||
config SND_MSM_SOC_MSM7K
|
||||
tristate
|
||||
|
||||
config SND_MSM_SOC
|
||||
tristate "SoC Audio for the MSM series chips"
|
||||
depends on ARCH_MSM7X27
|
||||
select SND_MSM_DAI_SOC
|
||||
select SND_MSM_SOC_MSM7K
|
||||
default n
|
||||
help
|
||||
To add support for ALSA PCM driver for MSM board.
|
||||
|
||||
#7630 Variants
|
||||
config SND_MSM7KV2_DAI_SOC
|
||||
tristate
|
||||
|
||||
config SND_MSM_SOC_MSM7KV2
|
||||
tristate
|
||||
|
||||
config SND_MSM7KV2_SOC
|
||||
tristate "SoC Audio for the MSM7KV2 chip"
|
||||
depends on ARCH_MSM7X30 && SND_SOC && MSM7KV2_AUDIO
|
||||
select SND_MSM_SOC_MSM7KV2
|
||||
select SND_MSM7KV2_DAI_SOC
|
||||
default n
|
||||
help
|
||||
To add support for ALSA PCM driver for QSD8k board.
|
||||
|
||||
config SND_MSM_MVS7x30_SOC
|
||||
tristate
|
||||
|
||||
config SND_MSM_MVS_DAI_SOC
|
||||
tristate
|
||||
|
||||
config SND_MVS_SOC
|
||||
tristate "SoC Mvs support for MSM7X30"
|
||||
depends on SND_MSM7KV2_SOC
|
||||
select SND_MSM_MVS7x30_SOC
|
||||
select SND_MSM_MVS_DAI_SOC
|
||||
default n
|
||||
help
|
||||
To support Mvs packet capture/playback
|
||||
|
||||
#8660 Variants
|
||||
config SND_SOC_MSM8X60_PCM
|
||||
tristate
|
||||
|
||||
config SND_SOC_MSM8X60_DAI
|
||||
tristate
|
||||
|
||||
config SND_SOC_MSM8X60
|
||||
tristate "SoC Audio over DSP support for MSM8660"
|
||||
depends on ARCH_MSM8X60 && SND_SOC && MSM8X60_AUDIO
|
||||
select SND_SOC_MSM8X60_PCM
|
||||
select SND_SOC_MSM8X60_DAI
|
||||
select SND_SOC_MSM_QDSP6_INTF
|
||||
default y
|
||||
help
|
||||
To add support for SoC audio on MSM8X60. This driver
|
||||
Adds support for audio over DSP. The driver adds Kcontrols
|
||||
to do device switch/routing and volume control support for all
|
||||
audio sessions. The kcontols also does sesion management for
|
||||
voice calls
|
||||
|
||||
config SND_SOC_MSM_HOSTLESS_PCM
|
||||
tristate
|
||||
|
||||
config SND_SOC_LPASS_PCM
|
||||
tristate
|
||||
|
||||
config SND_SOC_MSM8660_LPAIF
|
||||
tristate
|
||||
|
||||
config SND_VOIP_PCM
|
||||
tristate
|
||||
|
||||
config SND_SOC_MSM_QDSP6_HDMI_AUDIO
|
||||
tristate "Soc QDSP6 HDMI Audio DAI driver"
|
||||
depends on FB_MSM_HDMI_MSM_PANEL
|
||||
default n
|
||||
help
|
||||
To support HDMI Audio on MSM8960 over QDSP6.
|
||||
|
||||
config MSM_8x60_VOIP
|
||||
tristate "SoC Machine driver for voip"
|
||||
depends on SND_SOC_MSM8X60
|
||||
select SND_MSM_MVS_DAI_SOC
|
||||
select SND_VOIP_PCM
|
||||
default n
|
||||
help
|
||||
To support ALSA VOIP driver for MSM8x60 target.
|
||||
This driver communicates with QDSP6, for getting
|
||||
uplink and downlink voice packets.
|
||||
|
||||
config SND_SOC_MSM_QDSP6_INTF
|
||||
bool "SoC Q6 audio driver for MSM8960"
|
||||
depends on MSM_QDSP6_APR
|
||||
default n
|
||||
help
|
||||
To add support for SoC audio on MSM8960.
|
||||
|
||||
config SND_SOC_VOICE
|
||||
bool "SoC Q6 voice driver for MSM8960"
|
||||
depends on SND_SOC_MSM_QDSP6_INTF
|
||||
default n
|
||||
help
|
||||
To add support for SoC voice on MSM8960.
|
||||
|
||||
config SND_SOC_QDSP6
|
||||
tristate "SoC ALSA audio driver for QDSP6"
|
||||
select SND_SOC_MSM_QDSP6_INTF
|
||||
default n
|
||||
help
|
||||
To add support for MSM QDSP6 Soc Audio.
|
||||
|
||||
config SND_SOC_MSM8960
|
||||
tristate "SoC Machine driver for MSM8960 and APQ8064 boards"
|
||||
depends on ARCH_MSM8960 || ARCH_APQ8064
|
||||
select SND_SOC_VOICE
|
||||
select SND_SOC_QDSP6
|
||||
select SND_SOC_MSM_STUB
|
||||
select SND_SOC_WCD9310
|
||||
select SND_SOC_WCD9304
|
||||
select SND_SOC_MSM_HOSTLESS_PCM
|
||||
select SND_SOC_MSM_QDSP6_HDMI_AUDIO
|
||||
select SND_SOC_CS8427
|
||||
default n
|
||||
help
|
||||
To add support for SoC audio on MSM8960 and APQ8064 boards
|
||||
|
||||
config SND_SOC_MDM9615
|
||||
tristate "SoC Machine driver for MDM9615 boards"
|
||||
depends on ARCH_MSM9615
|
||||
select SND_SOC_VOICE
|
||||
select SND_SOC_QDSP6
|
||||
select SND_SOC_MSM_STUB
|
||||
select SND_SOC_WCD9310
|
||||
select SND_SOC_MSM_HOSTLESS_PCM
|
||||
select SND_DYNAMIC_MINORS
|
||||
help
|
||||
To add support for SoC audio on MDM9615 boards
|
||||
|
||||
config SND_SOC_MSM8660_APQ
|
||||
tristate "Soc Machine driver for APQ8060 WM8903 codec"
|
||||
depends on ARCH_MSM8X60
|
||||
select SND_SOC_QDSP6
|
||||
select SND_SOC_WM8903
|
||||
select SND_SOC_MSM_STUB
|
||||
default n
|
||||
help
|
||||
To add support for SoC audio on APQ8060 board
|
||||
endmenu
|
||||
86
sound/soc/msm/Makefile
Normal file
86
sound/soc/msm/Makefile
Normal file
@@ -0,0 +1,86 @@
|
||||
# MSM CPU/CODEC DAI Support
|
||||
snd-soc-msm-dai-objs := msm-dai.o
|
||||
obj-$(CONFIG_SND_MSM_DAI_SOC) += snd-soc-msm-dai.o
|
||||
|
||||
snd-soc-msm7kv2-dai-objs := msm7kv2-dai.o
|
||||
obj-$(CONFIG_SND_MSM7KV2_DAI_SOC) += snd-soc-msm7kv2-dai.o
|
||||
|
||||
# MSM Platform Support
|
||||
snd-soc-msm-objs := msm-pcm.o msm7k-pcm.o
|
||||
obj-$(CONFIG_SND_MSM_SOC) += snd-soc-msm.o
|
||||
|
||||
snd-soc-msmv2-objs := msm7kv2-dsp.o msm7kv2-pcm.o
|
||||
obj-$(CONFIG_SND_MSM7KV2_SOC) += snd-soc-msmv2.o
|
||||
|
||||
# MSM Machine Support
|
||||
snd-soc-msm7k-objs := msm7201.o
|
||||
obj-$(CONFIG_SND_MSM_SOC_MSM7K) += snd-soc-msm7k.o
|
||||
|
||||
snd-soc-msm7kv2-objs := msm7x30.o
|
||||
obj-$(CONFIG_SND_MSM_SOC_MSM7KV2) += snd-soc-msm7kv2.o
|
||||
|
||||
# 8660 ALSA Support
|
||||
snd-soc-msm8x60-dai-objs := msm8x60-dai.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8X60_DAI) += snd-soc-msm8x60-dai.o
|
||||
|
||||
snd-soc-msm8x60-pcm-objs := msm8x60-pcm.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8X60_PCM) += snd-soc-msm8x60-pcm.o
|
||||
|
||||
snd-soc-msm8x60-objs := msm8x60.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8X60) += snd-soc-msm8x60.o
|
||||
|
||||
|
||||
#MVS Support
|
||||
snd-soc-msm-mvs-dai-objs := mvs-dai.o
|
||||
obj-$(CONFIG_SND_MSM_MVS_DAI_SOC) += snd-soc-msm-mvs-dai.o
|
||||
|
||||
snd-soc-msm-mvs-objs := msm-mvs.o
|
||||
obj-$(CONFIG_SND_MVS_SOC) += snd-soc-msm-mvs.o
|
||||
|
||||
# 8660 ALSA Support
|
||||
snd-soc-lpass-objs := lpass-i2s.o lpass-dma.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8660_LPAIF) += snd-soc-lpass.o
|
||||
|
||||
snd-soc-lpass-pcm-objs := lpass-pcm.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_PCM) += snd-soc-lpass-pcm.o
|
||||
|
||||
#8660 VOIP Driver Support
|
||||
|
||||
snd-soc-msm-voip-objs := msm-voip.o
|
||||
obj-$(CONFIG_SND_VOIP_PCM) += snd-soc-msm-voip.o
|
||||
|
||||
snd-soc-lpass-dma-objs := lpass-dma.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8X60) += snd-soc-lpass-dma.o
|
||||
|
||||
# for MSM 8960 sound card driver
|
||||
|
||||
obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
|
||||
|
||||
snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
|
||||
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o
|
||||
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
|
||||
obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
|
||||
|
||||
snd-soc-msm8960-objs := msm8960.o apq8064.o msm8930.o mpq8064.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8960) += snd-soc-msm8960.o
|
||||
|
||||
# Generic MSM drivers
|
||||
snd-soc-hostless-pcm-objs := msm-pcm-hostless.o
|
||||
obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o
|
||||
|
||||
snd-soc-msm8660-apq-objs := msm8660-apq-wm8903.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8660_APQ) += snd-soc-msm8660-apq.o
|
||||
|
||||
# for MDM 9615 sound card driver
|
||||
snd-soc-mdm9615-objs := mdm9615.o
|
||||
obj-$(CONFIG_SND_SOC_MDM9615) += snd-soc-mdm9615.o
|
||||
|
||||
# for MSM 8974 sound card driver
|
||||
obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/
|
||||
snd-soc-msm8974-objs := msm8974.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8974) += snd-soc-msm8974.o
|
||||
|
||||
snd-soc-qdsp6v2-objs := msm-dai-fe.o msm-dai-stub.o
|
||||
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
|
||||
|
||||
2031
sound/soc/msm/apq8064.c
Normal file
2031
sound/soc/msm/apq8064.c
Normal file
File diff suppressed because it is too large
Load Diff
488
sound/soc/msm/lpass-dma.c
Normal file
488
sound/soc/msm/lpass-dma.c
Normal file
@@ -0,0 +1,488 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/msm_audio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <mach/msm_iomap-8x60.h>
|
||||
#include <mach/audio_dma_msm8k.h>
|
||||
#include <sound/dai.h>
|
||||
#include "lpass-pcm.h"
|
||||
|
||||
struct dai_baseinfo {
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static struct dai_baseinfo dai_info;
|
||||
|
||||
struct dai_drv {
|
||||
u8 *buffer;
|
||||
u32 buffer_phys;
|
||||
int channels;
|
||||
irqreturn_t (*callback) (int intrsrc, void *private_data);
|
||||
void *private_data;
|
||||
int in_use;
|
||||
u32 buffer_len;
|
||||
u32 period_len;
|
||||
u32 master_mode;
|
||||
};
|
||||
|
||||
static struct dai_drv *dai[MAX_CHANNELS];
|
||||
static spinlock_t dai_lock;
|
||||
|
||||
static int dai_find_dma_channel(uint32_t intrsrc)
|
||||
{
|
||||
int i, dma_channel = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
for (i = 0; i <= 27; i += 3) {
|
||||
if (intrsrc & (1 << i)) {
|
||||
dma_channel = i / 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dma_channel;
|
||||
}
|
||||
|
||||
void register_dma_irq_handler(int dma_ch,
|
||||
irqreturn_t (*callback) (int intrsrc, void *private_data),
|
||||
void *private_data)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
dai[dma_ch]->callback = callback;
|
||||
dai[dma_ch]->private_data = private_data;
|
||||
}
|
||||
|
||||
void unregister_dma_irq_handler(int dma_ch)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
dai[dma_ch]->callback = NULL;
|
||||
dai[dma_ch]->private_data = NULL;
|
||||
}
|
||||
|
||||
static irqreturn_t dai_irq_handler(int irq, void *data)
|
||||
{
|
||||
unsigned long flag;
|
||||
uint32_t intrsrc;
|
||||
uint32_t dma_ch = 0;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&dai_lock, flag);
|
||||
intrsrc = readl(dai_info.base + LPAIF_IRQ_STAT(0));
|
||||
writel(intrsrc, dai_info.base + LPAIF_IRQ_CLEAR(0));
|
||||
mb();
|
||||
while (intrsrc) {
|
||||
dma_ch = dai_find_dma_channel(intrsrc);
|
||||
|
||||
if (!dai[dma_ch]->callback)
|
||||
goto handled;
|
||||
if (!dai[dma_ch]->private_data)
|
||||
goto handled;
|
||||
ret = dai[dma_ch]->callback(intrsrc,
|
||||
dai[dma_ch]->private_data);
|
||||
intrsrc &= ~(0x7 << (dma_ch * 3));
|
||||
}
|
||||
handled:
|
||||
spin_unlock_irqrestore(&dai_lock, flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dai_print_state(uint32_t dma_ch)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned long *ptrmem = (unsigned long *)dai_info.base;
|
||||
|
||||
for (i = 0; i < 4; i++, ++ptrmem)
|
||||
pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
|
||||
(unsigned int)*ptrmem);
|
||||
|
||||
ptrmem = (unsigned long *)(dai_info.base
|
||||
+ DMA_CH_CTL_BASE + DMA_CH_INDEX(dma_ch));
|
||||
for (i = 0; i < 10; i++, ++ptrmem)
|
||||
pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
|
||||
(unsigned int) *ptrmem);
|
||||
}
|
||||
|
||||
static int dai_enable_irq(uint32_t dma_ch)
|
||||
{
|
||||
int ret;
|
||||
pr_debug("%s\n", __func__);
|
||||
ret = request_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, dai_irq_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_SHARED, "msm-i2s",
|
||||
(void *) (dma_ch+1));
|
||||
if (ret < 0) {
|
||||
pr_debug("Request Irq Failed err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dai_config_dma(uint32_t dma_ch)
|
||||
{
|
||||
pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
|
||||
|
||||
writel(dai[dma_ch]->buffer_phys,
|
||||
dai_info.base + LPAIF_DMA_BASE(dma_ch));
|
||||
writel(((dai[dma_ch]->buffer_len >> 2) - 1),
|
||||
dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
|
||||
writel(((dai[dma_ch]->period_len >> 2) - 1),
|
||||
dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
|
||||
mb();
|
||||
}
|
||||
|
||||
static void dai_enable_codec(uint32_t dma_ch, int codec)
|
||||
{
|
||||
uint32_t intrVal;
|
||||
uint32_t i2sctl;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
|
||||
intrVal = intrVal | (7 << (dma_ch * 3));
|
||||
writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
|
||||
if (codec == DAI_SPKR) {
|
||||
writel(0x0813, dai_info.base + LPAIF_DMA_CTL(dma_ch));
|
||||
i2sctl = 0x4400;
|
||||
i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
|
||||
writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_SPKR));
|
||||
} else if (codec == DAI_MIC) {
|
||||
writel(0x81b, dai_info.base + LPAIF_DMA_CTL(dma_ch));
|
||||
i2sctl = 0x0110;
|
||||
i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
|
||||
writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_MIC));
|
||||
}
|
||||
}
|
||||
|
||||
static void dai_disable_codec(uint32_t dma_ch, int codec)
|
||||
{
|
||||
uint32_t intrVal = 0;
|
||||
uint32_t intrVal1 = 0;
|
||||
unsigned long flag = 0x0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&dai_lock, flag);
|
||||
|
||||
intrVal1 = readl(dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
|
||||
|
||||
if (codec == DAI_SPKR)
|
||||
intrVal1 = intrVal1 & ~(1 << 14);
|
||||
else if (codec == DAI_MIC)
|
||||
intrVal1 = intrVal1 & ~(1 << 8);
|
||||
|
||||
writel(intrVal1, dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
|
||||
intrVal = 0x0;
|
||||
writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
|
||||
|
||||
spin_unlock_irqrestore(&dai_lock, flag);
|
||||
}
|
||||
|
||||
int dai_open(uint32_t dma_ch)
|
||||
{
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (!dai_info.base) {
|
||||
pr_debug("%s failed as no msm-dai device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (dma_ch >= MAX_CHANNELS) {
|
||||
pr_debug("%s over max channesl %d\n", __func__, dma_ch);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dai_close(uint32_t dma_ch)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
if ((dma_ch >= 0) && (dma_ch < 5))
|
||||
dai_disable_codec(dma_ch, DAI_SPKR);
|
||||
else
|
||||
dai_disable_codec(dma_ch, DAI_MIC);
|
||||
free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
|
||||
}
|
||||
|
||||
void dai_set_master_mode(uint32_t dma_ch, int mode)
|
||||
{
|
||||
if (dma_ch < MAX_CHANNELS)
|
||||
dai[dma_ch]->master_mode = mode;
|
||||
else
|
||||
pr_err("%s: invalid dma channel\n", __func__);
|
||||
}
|
||||
|
||||
int dai_set_params(uint32_t dma_ch, struct dai_dma_params *params)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
dai[dma_ch]->buffer = params->buffer;
|
||||
dai[dma_ch]->buffer_phys = params->src_start;
|
||||
dai[dma_ch]->channels = params->channels;
|
||||
dai[dma_ch]->buffer_len = params->buffer_size;
|
||||
dai[dma_ch]->period_len = params->period_size;
|
||||
mb();
|
||||
dai_config_dma(dma_ch);
|
||||
return dma_ch;
|
||||
}
|
||||
|
||||
int dai_start(uint32_t dma_ch)
|
||||
{
|
||||
unsigned long flag = 0x0;
|
||||
|
||||
spin_lock_irqsave(&dai_lock, flag);
|
||||
dai_enable_irq(dma_ch);
|
||||
if ((dma_ch >= 0) && (dma_ch < 5))
|
||||
dai_enable_codec(dma_ch, DAI_SPKR);
|
||||
else
|
||||
dai_enable_codec(dma_ch, DAI_MIC);
|
||||
spin_unlock_irqrestore(&dai_lock, flag);
|
||||
dai_print_state(dma_ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HDMI_BURST_INCR4 (1 << 11)
|
||||
#define HDMI_WPSCNT (1 << 8)
|
||||
#define HDMI_AUDIO_INTF (5 << 4)
|
||||
#define HDMI_FIFO_WATER_MARK (7 << 1)
|
||||
#define HDMI_ENABLE (1)
|
||||
|
||||
int dai_start_hdmi(uint32_t dma_ch)
|
||||
{
|
||||
unsigned long flag = 0x0;
|
||||
uint32_t val;
|
||||
|
||||
pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
|
||||
|
||||
spin_lock_irqsave(&dai_lock, flag);
|
||||
|
||||
dai_enable_irq(dma_ch);
|
||||
|
||||
if ((dma_ch >= 0) && (dma_ch < 5)) {
|
||||
|
||||
val = readl(dai_info.base + LPAIF_IRQ_EN(0));
|
||||
val = val | (7 << (dma_ch * 3));
|
||||
writel(val, dai_info.base + LPAIF_IRQ_EN(0));
|
||||
mb();
|
||||
|
||||
|
||||
val = (HDMI_BURST_INCR4 | HDMI_WPSCNT | HDMI_AUDIO_INTF |
|
||||
HDMI_FIFO_WATER_MARK | HDMI_ENABLE);
|
||||
|
||||
writel(val, dai_info.base + LPAIF_DMA_CTL(dma_ch));
|
||||
}
|
||||
spin_unlock_irqrestore(&dai_lock, flag);
|
||||
|
||||
mb();
|
||||
dai_print_state(dma_ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wait_for_dma_cnt_stop(uint32_t dma_ch)
|
||||
{
|
||||
uint32_t dma_per_cnt_reg_val, dma_per_cnt, prev_dma_per_cnt;
|
||||
uint32_t i;
|
||||
|
||||
pr_info("%s dma_ch %u\n", __func__, dma_ch);
|
||||
|
||||
dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
|
||||
LPAIF_DMA_PER_CNT(dma_ch));
|
||||
|
||||
dma_per_cnt =
|
||||
((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
|
||||
LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
|
||||
((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
|
||||
LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
|
||||
|
||||
prev_dma_per_cnt = dma_per_cnt;
|
||||
|
||||
i = 1;
|
||||
pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
|
||||
__func__, i, dma_per_cnt_reg_val, dma_per_cnt);
|
||||
|
||||
while (i <= 50) {
|
||||
msleep(50);
|
||||
|
||||
dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
|
||||
LPAIF_DMA_PER_CNT(dma_ch));
|
||||
|
||||
dma_per_cnt =
|
||||
((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
|
||||
LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
|
||||
((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
|
||||
LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
|
||||
|
||||
i++;
|
||||
|
||||
pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
|
||||
__func__, i, dma_per_cnt_reg_val, dma_per_cnt);
|
||||
|
||||
if (prev_dma_per_cnt == dma_per_cnt)
|
||||
break;
|
||||
|
||||
prev_dma_per_cnt = dma_per_cnt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dai_stop_hdmi(uint32_t dma_ch)
|
||||
{
|
||||
unsigned long flag = 0x0;
|
||||
uint32_t intrVal;
|
||||
uint32_t int_mask = 0x00000007;
|
||||
|
||||
pr_debug("%s dma_ch %u\n", __func__, dma_ch);
|
||||
|
||||
spin_lock_irqsave(&dai_lock, flag);
|
||||
|
||||
free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
|
||||
|
||||
|
||||
intrVal = 0x0;
|
||||
writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
|
||||
|
||||
mb();
|
||||
|
||||
intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
|
||||
|
||||
int_mask = ((int_mask) << (dma_ch * 3));
|
||||
int_mask = ~int_mask;
|
||||
|
||||
intrVal = intrVal & int_mask;
|
||||
writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
|
||||
|
||||
mb();
|
||||
|
||||
spin_unlock_irqrestore(&dai_lock, flag);
|
||||
}
|
||||
|
||||
int dai_stop(uint32_t dma_ch)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dai_get_dma_pos(uint32_t dma_ch)
|
||||
{
|
||||
|
||||
uint32_t addr;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
addr = readl(dai_info.base + LPAIF_DMA_CURR_ADDR(dma_ch));
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int __devinit dai_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
int i = 0;
|
||||
struct resource *src;
|
||||
src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msm-dai");
|
||||
if (!src) {
|
||||
rc = -ENODEV;
|
||||
pr_debug("%s Error rc=%d\n", __func__, rc);
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i <= MAX_CHANNELS; i++) {
|
||||
dai[i] = kzalloc(sizeof(struct dai_drv), GFP_KERNEL);
|
||||
if (!dai[0]) {
|
||||
pr_debug("Allocation failed for dma_channel = 0\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
dai_info.base = ioremap(src->start, (src->end - src->start) + 1);
|
||||
pr_debug("%s: msm-dai: 0x%08x\n", __func__,
|
||||
(unsigned int)dai_info.base);
|
||||
spin_lock_init(&dai_lock);
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dai_remove(struct platform_device *pdev)
|
||||
{
|
||||
iounmap(dai_info.base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dai_driver = {
|
||||
.probe = dai_probe,
|
||||
.remove = dai_remove,
|
||||
.driver = {
|
||||
.name = "msm-dai",
|
||||
.owner = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msm_lpa_resources[] = {
|
||||
{
|
||||
.start = MSM_LPA_PHYS,
|
||||
.end = MSM_LPA_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.name = "msm-dai",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *codec_device;
|
||||
|
||||
static int msm_dai_dev_register(const char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s : called\n", __func__);
|
||||
codec_device = platform_device_alloc(name, -1);
|
||||
if (codec_device == NULL) {
|
||||
pr_debug("Failed to allocate %s\n", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
platform_set_drvdata(codec_device, (void *)&dai_info);
|
||||
platform_device_add_resources(codec_device, &msm_lpa_resources[0],
|
||||
ARRAY_SIZE(msm_lpa_resources));
|
||||
ret = platform_device_add(codec_device);
|
||||
if (ret != 0) {
|
||||
pr_debug("Failed to register %s: %d\n", name, ret);
|
||||
platform_device_put(codec_device);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init dai_init(void)
|
||||
{
|
||||
if (msm_dai_dev_register("msm-dai")) {
|
||||
pr_notice("dai_init: msm-dai Failed");
|
||||
return -ENODEV;
|
||||
}
|
||||
return platform_driver_register(&dai_driver);
|
||||
}
|
||||
|
||||
static void __exit dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dai_driver);
|
||||
platform_device_put(codec_device);
|
||||
}
|
||||
|
||||
module_init(dai_init);
|
||||
module_exit(dai_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MSM I2S driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
139
sound/soc/msm/lpass-i2s.c
Normal file
139
sound/soc/msm/lpass-i2s.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dai.h>
|
||||
|
||||
static int msm_cpu_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
uint32_t dma_ch = dai->id;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
ret = dai_open(dma_ch);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void msm_cpu_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
uint32_t dma_ch = dai->id;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
dai_close(dma_ch);
|
||||
}
|
||||
|
||||
static int msm_cpu_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_cpu_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
uint32_t dma_ch = dai->id;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
dai_set_master_mode(dma_ch, 1); /* CPU is master */
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
dai_set_master_mode(dma_ch, 0); /* CPU is slave */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops msm_cpu_dai_ops = {
|
||||
.startup = msm_cpu_dai_startup,
|
||||
.shutdown = msm_cpu_dai_shutdown,
|
||||
.trigger = msm_cpu_dai_trigger,
|
||||
.set_fmt = msm_cpu_dai_fmt,
|
||||
|
||||
};
|
||||
|
||||
|
||||
#define MSM_DAI_SPEAKER_BUILDER(link_id) \
|
||||
{ \
|
||||
.name = "msm-speaker-dai-"#link_id, \
|
||||
.id = (link_id), \
|
||||
.playback = { \
|
||||
.rates = SNDRV_PCM_RATE_8000_96000, \
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
|
||||
.channels_min = 1, \
|
||||
.channels_max = 2, \
|
||||
.rate_max = 96000, \
|
||||
.rate_min = 8000, \
|
||||
}, \
|
||||
.ops = &msm_cpu_dai_ops, \
|
||||
}
|
||||
|
||||
|
||||
#define MSM_DAI_MIC_BUILDER(link_id) \
|
||||
{ \
|
||||
.name = "msm-mic-dai-"#link_id, \
|
||||
.id = (link_id), \
|
||||
.capture = { \
|
||||
.rates = SNDRV_PCM_RATE_8000_96000, \
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
|
||||
.rate_min = 8000, \
|
||||
.rate_max = 96000, \
|
||||
.channels_min = 1, \
|
||||
.channels_max = 2, \
|
||||
}, \
|
||||
.ops = &msm_cpu_dai_ops, \
|
||||
}
|
||||
|
||||
|
||||
struct snd_soc_dai msm_cpu_dai[] = {
|
||||
MSM_DAI_SPEAKER_BUILDER(0),
|
||||
MSM_DAI_SPEAKER_BUILDER(1),
|
||||
MSM_DAI_SPEAKER_BUILDER(2),
|
||||
MSM_DAI_SPEAKER_BUILDER(3),
|
||||
MSM_DAI_SPEAKER_BUILDER(4),
|
||||
MSM_DAI_MIC_BUILDER(5),
|
||||
MSM_DAI_MIC_BUILDER(6),
|
||||
MSM_DAI_MIC_BUILDER(7),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(msm_cpu_dai);
|
||||
|
||||
static int __init msm_cpu_dai_init(void)
|
||||
{
|
||||
return snd_soc_register_dais(msm_cpu_dai, ARRAY_SIZE(msm_cpu_dai));
|
||||
}
|
||||
module_init(msm_cpu_dai_init);
|
||||
|
||||
static void __exit msm_cpu_dai_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dais(msm_cpu_dai, ARRAY_SIZE(msm_cpu_dai));
|
||||
}
|
||||
module_exit(msm_cpu_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM CPU DAI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
369
sound/soc/msm/lpass-pcm.c
Normal file
369
sound/soc/msm/lpass-pcm.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <mach/audio_dma_msm8k.h>
|
||||
#include <sound/dai.h>
|
||||
#include "lpass-pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = DMASZ/4,
|
||||
.buffer_bytes_max = DMASZ,
|
||||
.rate_max = 96000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.periods_min = 4,
|
||||
.periods_max = 512,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
struct msm_pcm_data {
|
||||
spinlock_t lock;
|
||||
int ch;
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t msm_pcm_irq(int intrsrc, void *data)
|
||||
{
|
||||
struct snd_pcm_substream *substream = data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
|
||||
int dma_ch = 0;
|
||||
unsigned int has_xrun, pending;
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
if (prtd)
|
||||
dma_ch = prtd->dma_ch;
|
||||
else
|
||||
return ret;
|
||||
|
||||
pr_debug("msm8660-pcm: msm_pcm_irq called\n");
|
||||
pending = (intrsrc
|
||||
& (UNDER_CH(dma_ch) | PER_CH(dma_ch) | ERR_CH(dma_ch)));
|
||||
has_xrun = (pending & UNDER_CH(dma_ch));
|
||||
|
||||
if (unlikely(has_xrun) &&
|
||||
substream->runtime &&
|
||||
snd_pcm_running(substream)) {
|
||||
pr_err("xrun\n");
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
|
||||
ret = IRQ_HANDLED;
|
||||
pending &= ~UNDER_CH(dma_ch);
|
||||
}
|
||||
|
||||
|
||||
if (pending & PER_CH(dma_ch)) {
|
||||
ret = IRQ_HANDLED;
|
||||
if (likely(substream->runtime &&
|
||||
snd_pcm_running(substream))) {
|
||||
/* end of buffer missed? loop back */
|
||||
if (++prtd->period_index >= runtime->periods)
|
||||
prtd->period_index = 0;
|
||||
snd_pcm_period_elapsed(substream);
|
||||
pr_debug("period elapsed\n");
|
||||
}
|
||||
pending &= ~PER_CH(dma_ch);
|
||||
}
|
||||
|
||||
if (unlikely(pending
|
||||
& (UNDER_CH(dma_ch) & PER_CH(dma_ch) & ERR_CH(dma_ch)))) {
|
||||
if (pending & UNDER_CH(dma_ch))
|
||||
pr_err("msm8660-pcm: DMA %x Underflow\n",
|
||||
dma_ch);
|
||||
if (pending & ERR_CH(dma_ch))
|
||||
pr_err("msm8660-pcm: DMA %x Master Error\n",
|
||||
dma_ch);
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
|
||||
struct dai_dma_params dma_params;
|
||||
int dma_ch = 0;
|
||||
|
||||
if (prtd)
|
||||
dma_ch = prtd->dma_ch;
|
||||
else
|
||||
return 0;
|
||||
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
pr_debug("%s:prtd->pcm_size = %d\n", __func__, prtd->pcm_size);
|
||||
pr_debug("%s:prtd->pcm_count = %d\n", __func__, prtd->pcm_count);
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
dma_params.src_start = runtime->dma_addr;
|
||||
dma_params.buffer = (u8 *)runtime->dma_area;
|
||||
dma_params.buffer_size = prtd->pcm_size;
|
||||
dma_params.period_size = prtd->pcm_count;
|
||||
dma_params.channels = runtime->channels;
|
||||
|
||||
dai_set_params(dma_ch, &dma_params);
|
||||
register_dma_irq_handler(dma_ch, msm_pcm_irq, (void *)substream);
|
||||
|
||||
prtd->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
dai_start(prtd->dma_ch);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
dai_stop(prtd->dma_ch);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
|
||||
snd_pcm_uframes_t offset = 0;
|
||||
|
||||
pr_debug("%s: period_index =%d\n", __func__, prtd->period_index);
|
||||
offset = prtd->period_index * runtime->period_size;
|
||||
if (offset >= runtime->buffer_size)
|
||||
offset = 0;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai_link *machine = rtd->dai;
|
||||
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
|
||||
struct msm_audio *prtd = NULL;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
snd_soc_set_runtime_hwparams(substream, &msm_pcm_hardware);
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Error setting hw_constraint\n");
|
||||
goto err;
|
||||
}
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_err("Error snd_pcm_hw_constraint_list failed\n");
|
||||
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
|
||||
if (prtd == NULL) {
|
||||
pr_err("Error allocating prtd\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
prtd->dma_ch = cpu_dai->id;
|
||||
prtd->enabled = 0;
|
||||
runtime->dma_bytes = msm_pcm_hardware.buffer_bytes_max;
|
||||
runtime->private_data = prtd;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
|
||||
int dma_ch = 0;
|
||||
|
||||
if (prtd)
|
||||
dma_ch = prtd->dma_ch;
|
||||
else
|
||||
return 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
unregister_dma_irq_handler(dma_ch);
|
||||
kfree(runtime->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vms)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
pr_debug("%s: snd_msm_audio_hw_params runtime->dma_addr 0x(%x)\n",
|
||||
__func__, (unsigned int)runtime->dma_addr);
|
||||
pr_debug("%s: snd_msm_audio_hw_params runtime->dma_area 0x(%x)\n",
|
||||
__func__, (unsigned int)runtime->dma_area);
|
||||
pr_debug("%s: snd_msm_audio_hw_params runtime->dma_bytes 0x(%x)\n",
|
||||
__func__, (unsigned int)runtime->dma_bytes);
|
||||
|
||||
return dma_mmap_coherent(substream->pcm->card->dev, vms,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
}
|
||||
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int pcm_preallocate_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size = msm_pcm_hardware.buffer_bytes_max;
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_pcm_free_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!stream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
static u64 msm_pcm_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int msm_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->dma_mask)
|
||||
card->dev->dma_mask = &msm_pcm_dmamask;
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
if (dai->playback.channels_min) {
|
||||
ret = pcm_preallocate_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (dai->capture.channels_min) {
|
||||
ret = pcm_preallocate_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_platform msm8660_soc_platform = {
|
||||
.name = "msm8660-pcm-audio",
|
||||
.pcm_ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
.pcm_free = msm_pcm_free_buffers,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(msm8660_soc_platform);
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&msm8660_soc_platform);
|
||||
}
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&msm8660_soc_platform);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MSM PCM module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
45
sound/soc/msm/lpass-pcm.h
Normal file
45
sound/soc/msm/lpass-pcm.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
|
||||
#define USE_CHANNELS_MIN 1
|
||||
#define USE_CHANNELS_MAX 2
|
||||
#define NUM_DMAS 9
|
||||
#define DMASZ 16384
|
||||
#define MAX_CHANNELS 9
|
||||
|
||||
#define MSM_LPA_PHYS 0x28100000
|
||||
#define MSM_LPA_END 0x2810DFFF
|
||||
|
||||
|
||||
struct msm_audio {
|
||||
struct snd_pcm_substream *substream;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
int enabled;
|
||||
int period;
|
||||
int dma_ch;
|
||||
int period_index;
|
||||
int start;
|
||||
};
|
||||
|
||||
extern struct snd_soc_dai msm_cpu_dai[NUM_DMAS];
|
||||
extern struct snd_soc_platform msm8660_soc_platform;
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
2203
sound/soc/msm/mdm9615.c
Normal file
2203
sound/soc/msm/mdm9615.c
Normal file
File diff suppressed because it is too large
Load Diff
1470
sound/soc/msm/mpq8064.c
Normal file
1470
sound/soc/msm/mpq8064.c
Normal file
File diff suppressed because it is too large
Load Diff
753
sound/soc/msm/msm-compr-q6.c
Normal file
753
sound/soc/msm/msm-compr-q6.c
Normal file
@@ -0,0 +1,753 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/q6asm.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
#include "msm-compr-q6.h"
|
||||
#include "msm-pcm-routing.h"
|
||||
|
||||
struct snd_msm {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm compressed_audio = {NULL, 0x2000} ;
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
static struct snd_pcm_hardware msm_compr_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 1200 * 1024 * 2,
|
||||
.period_bytes_min = 4800,
|
||||
.period_bytes_max = 1200 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 512,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void compr_event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct compr_audio *compr = priv;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_aio_write_param param;
|
||||
struct audio_buffer *buf = NULL;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s opcode =%08x\n", __func__, opcode);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE: {
|
||||
uint32_t *ptrmem = (uint32_t *)¶m;
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
else
|
||||
if (substream->timer_running)
|
||||
snd_timer_interrupt(substream->timer, 1);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start)) {
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
break;
|
||||
} else
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
|
||||
if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
|
||||
break;
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
|
||||
__func__, prtd->pcm_count, prtd->out_head);
|
||||
pr_debug("%s:writing buffer[%d] from 0x%08x\n",
|
||||
__func__, prtd->out_head,
|
||||
((unsigned int)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count)));
|
||||
|
||||
param.paddr = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
|
||||
i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1) & (runtime->periods - 1);
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_CMDRSP_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN: {
|
||||
if (!atomic_read(&prtd->pending_buffer))
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer[%d] to dsp\n",
|
||||
__func__, prtd->pcm_count, prtd->out_head);
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
pr_debug("%s:writing buffer[%d] from 0x%08x\n",
|
||||
__func__, prtd->out_head,
|
||||
((unsigned int)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count)));
|
||||
param.paddr = (unsigned long)buf[prtd->out_head].phys;
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[prtd->out_head].phys;
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1)
|
||||
& (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
}
|
||||
break;
|
||||
case ASM_STREAM_CMD_FLUSH:
|
||||
pr_debug("ASM_STREAM_CMD_FLUSH\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct asm_aac_cfg aac_cfg;
|
||||
struct asm_wma_cfg wma_cfg;
|
||||
struct asm_wmapro_cfg wma_pro_cfg;
|
||||
int ret;
|
||||
|
||||
pr_debug("compressed stream prepare\n");
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
prtd->out_head = 0;
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
switch (compr->info.codec_param.codec.id) {
|
||||
case SND_AUDIOCODEC_MP3:
|
||||
ret = q6asm_media_format_block(prtd->audio_client,
|
||||
compr->codec);
|
||||
if (ret < 0)
|
||||
pr_info("%s: CMD Format block failed\n", __func__);
|
||||
break;
|
||||
case SND_AUDIOCODEC_AAC:
|
||||
pr_debug("SND_AUDIOCODEC_AAC\n");
|
||||
memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
|
||||
aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
|
||||
aac_cfg.format = 0x03;
|
||||
aac_cfg.ch_cfg = runtime->channels;
|
||||
aac_cfg.sample_rate = runtime->rate;
|
||||
ret = q6asm_media_format_block_aac(prtd->audio_client,
|
||||
&aac_cfg);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD Format block failed\n", __func__);
|
||||
break;
|
||||
case SND_AUDIOCODEC_AC3_PASS_THROUGH:
|
||||
pr_debug("compressd playback, no need to send"
|
||||
" the decoder params\n");
|
||||
break;
|
||||
case SND_AUDIOCODEC_WMA:
|
||||
pr_debug("SND_AUDIOCODEC_WMA\n");
|
||||
memset(&wma_cfg, 0x0, sizeof(struct asm_wma_cfg));
|
||||
wma_cfg.format_tag = compr->info.codec_param.codec.format;
|
||||
wma_cfg.ch_cfg = runtime->channels;
|
||||
wma_cfg.sample_rate = runtime->rate;
|
||||
wma_cfg.avg_bytes_per_sec =
|
||||
compr->info.codec_param.codec.bit_rate/8;
|
||||
wma_cfg.block_align = compr->info.codec_param.codec.align;
|
||||
wma_cfg.valid_bits_per_sample =
|
||||
compr->info.codec_param.codec.options.wma.bits_per_sample;
|
||||
wma_cfg.ch_mask =
|
||||
compr->info.codec_param.codec.options.wma.channelmask;
|
||||
wma_cfg.encode_opt =
|
||||
compr->info.codec_param.codec.options.wma.encodeopt;
|
||||
ret = q6asm_media_format_block_wma(prtd->audio_client,
|
||||
&wma_cfg);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD Format block failed\n", __func__);
|
||||
break;
|
||||
case SND_AUDIOCODEC_WMA_PRO:
|
||||
pr_debug("SND_AUDIOCODEC_WMA_PRO\n");
|
||||
memset(&wma_pro_cfg, 0x0, sizeof(struct asm_wmapro_cfg));
|
||||
wma_pro_cfg.format_tag = compr->info.codec_param.codec.format;
|
||||
wma_pro_cfg.ch_cfg = compr->info.codec_param.codec.ch_in;
|
||||
wma_pro_cfg.sample_rate = runtime->rate;
|
||||
wma_pro_cfg.avg_bytes_per_sec =
|
||||
compr->info.codec_param.codec.bit_rate/8;
|
||||
wma_pro_cfg.block_align = compr->info.codec_param.codec.align;
|
||||
wma_pro_cfg.valid_bits_per_sample =
|
||||
compr->info.codec_param.codec.options.wma.bits_per_sample;
|
||||
wma_pro_cfg.ch_mask =
|
||||
compr->info.codec_param.codec.options.wma.channelmask;
|
||||
wma_pro_cfg.encode_opt =
|
||||
compr->info.codec_param.codec.options.wma.encodeopt;
|
||||
ret = q6asm_media_format_block_wmapro(prtd->audio_client,
|
||||
&wma_pro_cfg);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD Format block failed\n", __func__);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->pcm_irq_pos = 0;
|
||||
if (compr->info.codec_param.codec.id ==
|
||||
SND_AUDIOCODEC_AC3_PASS_THROUGH) {
|
||||
msm_pcm_routing_reg_psthr_stream(
|
||||
soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
}
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void populate_codec_list(struct compr_audio *compr,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
/* MP3 Block */
|
||||
compr->info.compr_cap.num_codecs = 1;
|
||||
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
|
||||
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
|
||||
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
|
||||
compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
|
||||
compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
|
||||
compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
|
||||
compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3_PASS_THROUGH;
|
||||
compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_WMA;
|
||||
compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_WMA_PRO;
|
||||
/* Add new codecs here */
|
||||
}
|
||||
|
||||
static int msm_compr_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
|
||||
if (compr == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd = &compr->prtd;
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)compr_event_handler, compr);
|
||||
if (!prtd->audio_client) {
|
||||
pr_info("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
runtime->hw = msm_compr_hardware_playback;
|
||||
|
||||
pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
compr->codec = FORMAT_MP3;
|
||||
populate_codec_list(compr, runtime);
|
||||
runtime->private_data = compr;
|
||||
compressed_audio.prtd = &compr->prtd;
|
||||
ret = compressed_set_volume(compressed_audio.volume);
|
||||
if (ret < 0)
|
||||
pr_err("%s : Set Volume failed : %d", __func__, ret);
|
||||
|
||||
ret = q6asm_set_softpause(compressed_audio.prtd->audio_client,
|
||||
&softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(compressed_audio.prtd->audio_client,
|
||||
&softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compressed_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
|
||||
rc = q6asm_set_volume(compressed_audio.prtd->audio_client,
|
||||
volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
compressed_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_compr_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
int dir = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
compressed_audio.prtd = NULL;
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_compr_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
static int msm_compr_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_compr_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_compr_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_compr_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
switch (compr->info.codec_param.codec.id) {
|
||||
case SND_AUDIOCODEC_AC3_PASS_THROUGH:
|
||||
ret = q6asm_open_write_compressed(prtd->audio_client,
|
||||
compr->codec);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: compressed Session out open failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = q6asm_open_write(prtd->audio_client, compr->codec);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Session out open failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
break;
|
||||
}
|
||||
ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Set IO mode failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed "
|
||||
"rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
uint64_t timestamp;
|
||||
uint64_t temp;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_COMPRESS_TSTAMP: {
|
||||
struct snd_compr_tstamp tstamp;
|
||||
pr_debug("SNDRV_COMPRESS_TSTAMP\n");
|
||||
|
||||
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
|
||||
timestamp = q6asm_get_session_time(prtd->audio_client);
|
||||
if (timestamp < 0) {
|
||||
pr_err("%s: Get Session Time return value =%lld\n",
|
||||
__func__, timestamp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
temp = (timestamp * 2 * runtime->channels);
|
||||
temp = temp * (runtime->rate/1000);
|
||||
temp = div_u64(temp, 1000);
|
||||
tstamp.sampling_rate = runtime->rate;
|
||||
tstamp.timestamp = timestamp;
|
||||
pr_debug("%s: bytes_consumed:,"
|
||||
"timestamp = %lld,\n", __func__,
|
||||
tstamp.timestamp);
|
||||
if (copy_to_user((void *) arg, &tstamp,
|
||||
sizeof(struct snd_compr_tstamp)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_COMPRESS_GET_CAPS:
|
||||
pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
|
||||
if (copy_to_user((void *) arg, &compr->info.compr_cap,
|
||||
sizeof(struct snd_compr_caps))) {
|
||||
rc = -EFAULT;
|
||||
pr_err("%s: ERROR: copy to user\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
case SNDRV_COMPRESS_SET_PARAMS:
|
||||
pr_debug("SNDRV_COMPRESS_SET_PARAMS: ");
|
||||
if (copy_from_user(&compr->info.codec_param, (void *) arg,
|
||||
sizeof(struct snd_compr_params))) {
|
||||
rc = -EFAULT;
|
||||
pr_err("%s: ERROR: copy from user\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
switch (compr->info.codec_param.codec.id) {
|
||||
case SND_AUDIOCODEC_MP3:
|
||||
/* For MP3 we dont need any other parameter */
|
||||
pr_debug("SND_AUDIOCODEC_MP3\n");
|
||||
compr->codec = FORMAT_MP3;
|
||||
break;
|
||||
case SND_AUDIOCODEC_AAC:
|
||||
pr_debug("SND_AUDIOCODEC_AAC\n");
|
||||
compr->codec = FORMAT_MPEG4_AAC;
|
||||
break;
|
||||
case SND_AUDIOCODEC_AC3_PASS_THROUGH:
|
||||
pr_debug("SND_AUDIOCODEC_AC3_PASS_THROUGH\n");
|
||||
compr->codec = FORMAT_AC3;
|
||||
break;
|
||||
case SND_AUDIOCODEC_WMA:
|
||||
pr_debug("SND_AUDIOCODEC_WMA\n");
|
||||
compr->codec = FORMAT_WMA_V9;
|
||||
break;
|
||||
case SND_AUDIOCODEC_WMA_PRO:
|
||||
pr_debug("SND_AUDIOCODEC_WMA_PRO\n");
|
||||
compr->codec = FORMAT_WMA_V10PRO;
|
||||
break;
|
||||
default:
|
||||
pr_debug("FORMAT_LINEAR_PCM\n");
|
||||
compr->codec = FORMAT_LINEAR_PCM;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case SNDRV_PCM_IOCTL1_RESET:
|
||||
prtd->cmd_ack = 0;
|
||||
rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
|
||||
if (rc < 0)
|
||||
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("Flush cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_compr_ops = {
|
||||
.open = msm_compr_open,
|
||||
.hw_params = msm_compr_hw_params,
|
||||
.close = msm_compr_close,
|
||||
.ioctl = msm_compr_ioctl,
|
||||
.prepare = msm_compr_prepare,
|
||||
.trigger = msm_compr_trigger,
|
||||
.pointer = msm_compr_pointer,
|
||||
.mmap = msm_compr_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_compr_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_compr_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_compr_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_compr_driver = {
|
||||
.driver = {
|
||||
.name = "msm-compr-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_compr_probe,
|
||||
.remove = __devexit_p(msm_compr_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_compr_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_compr_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
36
sound/soc/msm/msm-compr-q6.h
Normal file
36
sound/soc/msm/msm-compr-q6.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_COMPR_H
|
||||
#define _MSM_COMPR_H
|
||||
#include <sound/apr_audio.h>
|
||||
#include <sound/q6asm.h>
|
||||
#include <sound/compress_params.h>
|
||||
#include <sound/compress_offload.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "msm-pcm-q6.h"
|
||||
|
||||
struct compr_info {
|
||||
struct snd_compr_caps compr_cap;
|
||||
struct snd_compr_codec_caps codec_caps;
|
||||
struct snd_compr_params codec_param;
|
||||
};
|
||||
|
||||
struct compr_audio {
|
||||
struct msm_audio prtd;
|
||||
struct compr_info info;
|
||||
uint32_t codec;
|
||||
};
|
||||
|
||||
#endif /*_MSM_COMPR_H*/
|
||||
412
sound/soc/msm/msm-dai-fe.c
Normal file
412
sound/soc/msm/msm-dai-fe.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
static struct snd_soc_dai_ops msm_fe_dai_ops = {};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int multimedia_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = {
|
||||
.startup = multimedia_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver msm_fe_dais[] = {
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "Multimedia1 Playback",
|
||||
.aif_name = "MM_DL1",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000|
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Multimedia1 Capture",
|
||||
.aif_name = "MM_UL1",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000|
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 4,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_Multimedia_dai_ops,
|
||||
.name = "MultiMedia1",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "Multimedia2 Playback",
|
||||
.aif_name = "MM_DL2",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000|
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 6,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Multimedia2 Capture",
|
||||
.aif_name = "MM_UL2",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000|
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_Multimedia_dai_ops,
|
||||
.name = "MultiMedia2",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "Voice Playback",
|
||||
.aif_name = "CS-VOICE_DL1",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Voice Capture",
|
||||
.aif_name = "CS-VOICE_UL1",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "CS-VOICE",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "VoIP Playback",
|
||||
.aif_name = "VOIP_DL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_SPECIAL,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "VoIP Capture",
|
||||
.aif_name = "VOIP_UL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_SPECIAL,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "VoIP",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "MultiMedia3 Playback",
|
||||
.aif_name = "MM_DL3",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000 |
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_Multimedia_dai_ops,
|
||||
.name = "MultiMedia3",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "MultiMedia4 Playback",
|
||||
.aif_name = "MM_DL4",
|
||||
.rates = (SNDRV_PCM_RATE_8000_48000 |
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_Multimedia_dai_ops,
|
||||
.name = "MultiMedia4",
|
||||
},
|
||||
/* FE DAIs created for hostless operation purpose */
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "SLIMBUS0 Hostless Playback",
|
||||
.aif_name = "SLIM0_DL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "SLIMBUS0 Hostless Capture",
|
||||
.aif_name = "SLIM0_UL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "SLIMBUS0_HOSTLESS",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "INT_FM Hostless Playback",
|
||||
.aif_name = "INTFM_DL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "INT_FM Hostless Capture",
|
||||
.aif_name = "INTFM_UL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "INT_FM_HOSTLESS",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "AFE-PROXY Playback",
|
||||
.aif_name = "PCM_RX",
|
||||
.rates = (SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "AFE-PROXY Capture",
|
||||
.aif_name = "PCM_TX",
|
||||
.rates = (SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "AFE-PROXY",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "HDMI_Rx Hostless Playback",
|
||||
.aif_name = "HDMI_DL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "HDMI_HOSTLESS"
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "AUXPCM Hostless Playback",
|
||||
.aif_name = "AUXPCM_DL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 16000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "AUXPCM Hostless Capture",
|
||||
.aif_name = "AUXPCM_UL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 16000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "AUXPCM_HOSTLESS",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "Voice Stub Playback",
|
||||
.aif_name = "VOICE_STUB_DL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Voice Stub Capture",
|
||||
.aif_name = "VOICE_STUB_UL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "VOICE_STUB",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "VoLTE Playback",
|
||||
.aif_name = "VoLTE_DL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "VoLTE Capture",
|
||||
.aif_name = "VoLTE_UL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "VoLTE",
|
||||
},
|
||||
{
|
||||
.capture = {
|
||||
.stream_name = "MI2S_TX Hostless Capture",
|
||||
.aif_name = "MI2S_UL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "MI2S_TX_HOSTLESS",
|
||||
},
|
||||
{
|
||||
.playback = {
|
||||
.stream_name = "SEC_I2S_RX Hostless Playback",
|
||||
.aif_name = "SEC_I2S_DL_HL",
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_fe_dai_ops,
|
||||
.name = "SEC_I2S_RX_HOSTLESS",
|
||||
},
|
||||
};
|
||||
|
||||
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__,
|
||||
dev_name(&pdev->dev));
|
||||
return snd_soc_register_dais(&pdev->dev, msm_fe_dais,
|
||||
ARRAY_SIZE(msm_fe_dais));
|
||||
}
|
||||
|
||||
static __devexit int msm_fe_dai_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_fe_dai_driver = {
|
||||
.probe = msm_fe_dai_dev_probe,
|
||||
.remove = msm_fe_dai_dev_remove,
|
||||
.driver = {
|
||||
.name = "msm-dai-fe",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_fe_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_fe_dai_driver);
|
||||
}
|
||||
module_init(msm_fe_dai_init);
|
||||
|
||||
static void __exit msm_fe_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_fe_dai_driver);
|
||||
}
|
||||
module_exit(msm_fe_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Frontend DAI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
326
sound/soc/msm/msm-dai-q6-hdmi.c
Normal file
326
sound/soc/msm/msm-dai-q6-hdmi.c
Normal file
@@ -0,0 +1,326 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/apr_audio.h>
|
||||
#include <sound/q6afe.h>
|
||||
#include <sound/q6adm.h>
|
||||
#include <sound/msm-dai-q6.h>
|
||||
#include <mach/msm_hdmi_audio.h>
|
||||
|
||||
|
||||
enum {
|
||||
STATUS_PORT_STARTED, /* track if AFE port has started */
|
||||
STATUS_MAX
|
||||
};
|
||||
|
||||
struct msm_dai_q6_hdmi_dai_data {
|
||||
DECLARE_BITMAP(status_mask, STATUS_MAX);
|
||||
u32 rate;
|
||||
u32 channels;
|
||||
union afe_port_config port_config;
|
||||
};
|
||||
|
||||
static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
|
||||
int value = ucontrol->value.integer.value[0];
|
||||
dai_data->port_config.hdmi_multi_ch.data_type = value;
|
||||
pr_debug("%s: value = %d\n", __func__, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
|
||||
ucontrol->value.integer.value[0] =
|
||||
dai_data->port_config.hdmi_multi_ch.data_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
|
||||
* 0: linear PCM
|
||||
* 1: non-linear PCM
|
||||
*/
|
||||
static const char *hdmi_format[] = {
|
||||
"LPCM",
|
||||
"Compr"
|
||||
};
|
||||
|
||||
static const struct soc_enum hdmi_config_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, hdmi_format),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_config_controls[] = {
|
||||
SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
|
||||
msm_dai_q6_hdmi_format_get,
|
||||
msm_dai_q6_hdmi_format_put),
|
||||
};
|
||||
|
||||
/* Current implementation assumes hw_param is called once
|
||||
* This may not be the case but what to do when ADM and AFE
|
||||
* port are already opened and parameter changes
|
||||
*/
|
||||
static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
|
||||
u32 channel_allocation = 0;
|
||||
u32 level_shift = 0; /* 0dB */
|
||||
bool down_mix = FALSE;
|
||||
|
||||
dai_data->channels = params_channels(params);
|
||||
dai_data->rate = params_rate(params);
|
||||
dai_data->port_config.hdmi_multi_ch.reserved = 0;
|
||||
|
||||
switch (dai_data->channels) {
|
||||
case 2:
|
||||
channel_allocation = 0;
|
||||
hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
|
||||
channel_allocation, level_shift, down_mix);
|
||||
dai_data->port_config.hdmi_multi_ch.channel_allocation =
|
||||
channel_allocation;
|
||||
break;
|
||||
case 6:
|
||||
channel_allocation = 0x0B;
|
||||
hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
|
||||
channel_allocation, level_shift, down_mix);
|
||||
dai_data->port_config.hdmi_multi_ch.channel_allocation =
|
||||
channel_allocation;
|
||||
break;
|
||||
default:
|
||||
dev_err(dai->dev, "invalid Channels = %u\n",
|
||||
dai_data->channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(dai->dev, "%s() num_ch = %u rate =%u"
|
||||
" channel_allocation = %u data type = %d\n", __func__,
|
||||
dai_data->channels,
|
||||
dai_data->rate,
|
||||
dai_data->port_config.hdmi_multi_ch.channel_allocation,
|
||||
dai_data->port_config.hdmi_multi_ch.data_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
|
||||
int rc = 0;
|
||||
|
||||
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
|
||||
pr_info("%s: afe port not started. dai_data->status_mask"
|
||||
" = %ld\n", __func__, *dai_data->status_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = afe_close(dai->id); /* can block */
|
||||
|
||||
if (IS_ERR_VALUE(rc))
|
||||
dev_err(dai->dev, "fail to close AFE port\n");
|
||||
|
||||
pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
|
||||
*dai_data->status_mask);
|
||||
|
||||
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
|
||||
}
|
||||
|
||||
|
||||
static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
|
||||
int rc = 0;
|
||||
|
||||
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
|
||||
/* PORT START should be set if prepare called in active state */
|
||||
rc = afe_q6_interface_prepare();
|
||||
if (IS_ERR_VALUE(rc))
|
||||
dev_err(dai->dev, "fail to open AFE APR\n");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_dai_q6_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
|
||||
|
||||
/* Start/stop port without waiting for Q6 AFE response. Need to have
|
||||
* native q6 AFE driver propagates AFE response in order to handle
|
||||
* port start/stop command error properly if error does arise.
|
||||
*/
|
||||
pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
|
||||
__func__, dai->id, cmd, *dai_data->status_mask);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
|
||||
afe_port_start_nowait(dai->id, &dai_data->port_config,
|
||||
dai_data->rate);
|
||||
|
||||
set_bit(STATUS_PORT_STARTED, dai_data->status_mask);
|
||||
}
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
|
||||
afe_port_stop_nowait(dai->id);
|
||||
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dai->dev, "invalid Trigger command = %d\n", cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data;
|
||||
const struct snd_kcontrol_new *kcontrol;
|
||||
int rc = 0;
|
||||
|
||||
dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!dai_data) {
|
||||
dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
|
||||
dai->id);
|
||||
rc = -ENOMEM;
|
||||
} else
|
||||
dev_set_drvdata(dai->dev, dai_data);
|
||||
|
||||
kcontrol = &hdmi_config_controls[0];
|
||||
|
||||
rc = snd_ctl_add(dai->card->snd_card,
|
||||
snd_ctl_new1(kcontrol, dai_data));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct msm_dai_q6_hdmi_dai_data *dai_data;
|
||||
int rc;
|
||||
|
||||
dai_data = dev_get_drvdata(dai->dev);
|
||||
|
||||
/* If AFE port is still up, close it */
|
||||
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
|
||||
rc = afe_close(dai->id); /* can block */
|
||||
|
||||
if (IS_ERR_VALUE(rc))
|
||||
dev_err(dai->dev, "fail to close AFE port\n");
|
||||
|
||||
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
|
||||
}
|
||||
kfree(dai_data);
|
||||
snd_soc_unregister_dai(dai->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
|
||||
.prepare = msm_dai_q6_hdmi_prepare,
|
||||
.trigger = msm_dai_q6_hdmi_trigger,
|
||||
.hw_params = msm_dai_q6_hdmi_hw_params,
|
||||
.shutdown = msm_dai_q6_hdmi_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
|
||||
.playback = {
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 6,
|
||||
.rate_max = 48000,
|
||||
.rate_min = 48000,
|
||||
},
|
||||
.ops = &msm_dai_q6_hdmi_ops,
|
||||
.probe = msm_dai_q6_hdmi_dai_probe,
|
||||
.remove = msm_dai_q6_hdmi_dai_remove,
|
||||
};
|
||||
|
||||
|
||||
/* To do: change to register DAIs as batch */
|
||||
static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
dev_dbg(&pdev->dev, "dev name %s dev-id %d\n",
|
||||
dev_name(&pdev->dev), pdev->id);
|
||||
|
||||
switch (pdev->id) {
|
||||
case HDMI_RX:
|
||||
rc = snd_soc_register_dai(&pdev->dev,
|
||||
&msm_dai_q6_hdmi_hdmi_rx_dai);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
|
||||
rc = -ENODEV;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_dai_q6_hdmi_driver = {
|
||||
.probe = msm_dai_q6_hdmi_dev_probe,
|
||||
.remove = msm_dai_q6_hdmi_dev_remove,
|
||||
.driver = {
|
||||
.name = "msm-dai-q6-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_dai_q6_hdmi_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_dai_q6_hdmi_driver);
|
||||
}
|
||||
module_init(msm_dai_q6_hdmi_init);
|
||||
|
||||
static void __exit msm_dai_q6_hdmi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_dai_q6_hdmi_driver);
|
||||
}
|
||||
module_exit(msm_dai_q6_hdmi_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
2024
sound/soc/msm/msm-dai-q6.c
Normal file
2024
sound/soc/msm/msm-dai-q6.c
Normal file
File diff suppressed because it is too large
Load Diff
102
sound/soc/msm/msm-dai-stub.c
Normal file
102
sound/soc/msm/msm-dai-stub.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
static int msm_dai_stub_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops msm_dai_stub_ops = {
|
||||
.set_channel_map = msm_dai_stub_set_channel_map,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver msm_dai_stub_dai = {
|
||||
.playback = {
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.capture = {
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &msm_dai_stub_ops,
|
||||
};
|
||||
|
||||
static __devinit int msm_dai_stub_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
|
||||
|
||||
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_stub_dai);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static __devexit int msm_dai_stub_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_dai_stub_driver = {
|
||||
.probe = msm_dai_stub_dev_probe,
|
||||
.remove = msm_dai_stub_dev_remove,
|
||||
.driver = {
|
||||
.name = "msm-dai-stub",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_dai_stub_init(void)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
return platform_driver_register(&msm_dai_stub_driver);
|
||||
}
|
||||
module_init(msm_dai_stub_init);
|
||||
|
||||
static void __exit msm_dai_stub_exit(void)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
platform_driver_unregister(&msm_dai_stub_driver);
|
||||
}
|
||||
module_exit(msm_dai_stub_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Stub DSP DAI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
150
sound/soc/msm/msm-dai.c
Normal file
150
sound/soc/msm/msm-dai.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/* sound/soc/msm/msm-dai.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Derived from msm-pcm.c and msm7201.c.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include "msm-pcm.h"
|
||||
|
||||
static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
|
||||
{
|
||||
.name = "msm-codec-dai",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.formats = USE_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rates = USE_RATE,
|
||||
.formats = USE_FORMATS,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
|
||||
{
|
||||
.name = "msm-cpu-dai",
|
||||
.playback = {
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.formats = USE_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rates = USE_RATE,
|
||||
.formats = USE_FORMATS,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_msm = {
|
||||
.compress_type = SND_SOC_FLAT_COMPRESSION,
|
||||
};
|
||||
|
||||
static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
|
||||
msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
|
||||
}
|
||||
|
||||
static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __devinit int asoc_pcm_cpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
|
||||
}
|
||||
|
||||
static int __devexit asoc_pcm_cpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver asoc_codec_dai_driver = {
|
||||
.probe = asoc_msm_codec_probe,
|
||||
.remove = __devexit_p(asoc_msm_codec_remove),
|
||||
.driver = {
|
||||
.name = "msm-codec-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver asoc_cpu_dai_driver = {
|
||||
.probe = asoc_pcm_cpu_probe,
|
||||
.remove = __devexit_p(asoc_pcm_cpu_remove),
|
||||
.driver = {
|
||||
.name = "msm-cpu-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_codec_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_codec_dai_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_codec_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_codec_dai_driver);
|
||||
}
|
||||
|
||||
static int __init msm_cpu_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_cpu_dai_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_cpu_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_cpu_dai_driver);
|
||||
}
|
||||
|
||||
module_init(msm_codec_dai_init);
|
||||
module_exit(msm_codec_dai_exit);
|
||||
module_init(msm_cpu_dai_init);
|
||||
module_exit(msm_cpu_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
804
sound/soc/msm/msm-multi-ch-pcm-q6.c
Normal file
804
sound/soc/msm/msm-multi-ch-pcm-q6.c
Normal file
@@ -0,0 +1,804 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <asm/dma.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "msm-pcm-q6.h"
|
||||
#include "msm-pcm-routing.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
struct snd_msm_volume {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm_volume multi_ch_pcm_audio = {NULL, 0x2000};
|
||||
|
||||
#define PLAYBACK_NUM_PERIODS 8
|
||||
#define PLAYBACK_MAX_PERIOD_SIZE 4032
|
||||
#define PLAYBACK_MIN_PERIOD_SIZE 256
|
||||
#define CAPTURE_NUM_PERIODS 16
|
||||
#define CAPTURE_PERIOD_SIZE 320
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_capture = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_min = CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_max = CAPTURE_PERIOD_SIZE,
|
||||
.periods_min = CAPTURE_NUM_PERIODS,
|
||||
.periods_max = CAPTURE_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 6,
|
||||
.buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
|
||||
.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
|
||||
.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
|
||||
.periods_min = PLAYBACK_NUM_PERIODS,
|
||||
.periods_max = PLAYBACK_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
uint32_t *ptrmem = (uint32_t *)payload;
|
||||
int i = 0;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start))
|
||||
break;
|
||||
if (!prtd->mmap_flag)
|
||||
break;
|
||||
if (q6asm_is_cpu_buf_avail_nolock(IN,
|
||||
prtd->audio_client,
|
||||
&size, &idx)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count, 0, 0, NO_TIMESTAMP);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_CMDRSP_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case ASM_DATA_EVENT_READ_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
|
||||
pr_debug("token = 0x%08x\n", token);
|
||||
for (i = 0; i < 8; i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
in_frame_info[token][0] = payload[2];
|
||||
in_frame_info[token][1] = payload[3];
|
||||
prtd->pcm_irq_pos += in_frame_info[token][0];
|
||||
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (atomic_read(&prtd->in_count) <= prtd->periods)
|
||||
atomic_inc(&prtd->in_count);
|
||||
wake_up(&the_locks.read_wait);
|
||||
if (prtd->mmap_flag
|
||||
&& q6asm_is_cpu_buf_avail_nolock(OUT,
|
||||
prtd->audio_client,
|
||||
&size, &idx))
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN:
|
||||
if (substream->stream
|
||||
!= SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
}
|
||||
if (prtd->mmap_flag) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
} else {
|
||||
while (atomic_read(&prtd->out_needed)) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
atomic_dec(&prtd->out_needed);
|
||||
wake_up(&the_locks.write_wait);
|
||||
};
|
||||
}
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
|
||||
runtime->rate, runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_info("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
|
||||
pr_debug("Channel = %d\n", prtd->channel_mode);
|
||||
ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
|
||||
prtd->channel_mode);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: cmd cfg pcm was block failed", __func__);
|
||||
|
||||
for (i = 0; i < runtime->periods; i++)
|
||||
q6asm_read(prtd->audio_client);
|
||||
prtd->periods = runtime->periods;
|
||||
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_err("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw = msm_pcm_hardware_playback;
|
||||
ret = q6asm_open_write(prtd->audio_client,
|
||||
FORMAT_MULTI_CHANNEL_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_hardware_capture;
|
||||
ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm in open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
PLAYBACK_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
|
||||
PLAYBACK_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
|
||||
if (ret < 0) {
|
||||
pr_err("constraint for buffer bytes min max ret = %d\n",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
runtime->private_data = prtd;
|
||||
pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
|
||||
pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
|
||||
multi_ch_pcm_audio.prtd = prtd;
|
||||
ret = multi_ch_pcm_set_volume(multi_ch_pcm_audio.volume);
|
||||
if (ret < 0)
|
||||
pr_err("%s : Set Volume failed : %d", __func__, ret);
|
||||
|
||||
ret = q6asm_set_softpause(multi_ch_pcm_audio.prtd->audio_client,
|
||||
&softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(multi_ch_pcm_audio.prtd->audio_client,
|
||||
&softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multi_ch_pcm_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
pr_err("multi_ch_pcm_set_volume\n");
|
||||
|
||||
if (multi_ch_pcm_audio.prtd && multi_ch_pcm_audio.prtd->audio_client) {
|
||||
pr_err("%s q6asm_set_volume\n", __func__);
|
||||
rc = q6asm_set_volume(multi_ch_pcm_audio.prtd->audio_client,
|
||||
volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
multi_ch_pcm_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer = 0;
|
||||
char *bufptr = NULL;
|
||||
void *data = NULL;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
pr_debug("%s: prtd->out_count = %d\n",
|
||||
__func__, atomic_read(&prtd->out_count));
|
||||
ret = wait_event_timeout(the_locks.write_wait,
|
||||
(atomic_read(&prtd->out_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!atomic_read(&prtd->out_count)) {
|
||||
pr_err("%s: pcm stopped out_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
if (bufptr) {
|
||||
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
|
||||
__func__, fbytes, xfer, size);
|
||||
xfer = fbytes;
|
||||
if (copy_from_user(bufptr, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
buf += xfer;
|
||||
fbytes -= xfer;
|
||||
pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
|
||||
if (atomic_read(&prtd->start)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp\n",
|
||||
__func__, xfer);
|
||||
ret = q6asm_write(prtd->audio_client, xfer,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
if (ret < 0) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
atomic_inc(&prtd->out_needed);
|
||||
atomic_dec(&prtd->out_count);
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
ret = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD_EOS failed\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
multi_ch_pcm_audio.prtd = NULL;
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer;
|
||||
char *bufptr;
|
||||
void *data = NULL;
|
||||
static uint32_t idx;
|
||||
static uint32_t size;
|
||||
uint32_t offset = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
|
||||
pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
|
||||
pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
|
||||
pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
|
||||
|
||||
ret = wait_event_timeout(the_locks.read_wait,
|
||||
(atomic_read(&prtd->in_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (!atomic_read(&prtd->in_count)) {
|
||||
pr_debug("%s: pcm stopped in_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("Checking if valid buffer is available...%08x\n",
|
||||
(unsigned int) data);
|
||||
data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
pr_debug("Size = %d\n", size);
|
||||
pr_debug("fbytes = %d\n", fbytes);
|
||||
pr_debug("idx = %d\n", idx);
|
||||
if (bufptr) {
|
||||
xfer = fbytes;
|
||||
if (xfer > size)
|
||||
xfer = size;
|
||||
offset = in_frame_info[idx][1];
|
||||
pr_debug("Offset value = %d\n", offset);
|
||||
if (copy_to_user(buf, bufptr+offset, xfer)) {
|
||||
pr_err("Failed to copy buf to user\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
fbytes -= xfer;
|
||||
size -= xfer;
|
||||
in_frame_info[idx][1] += xfer;
|
||||
pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
|
||||
__func__, fbytes, size, xfer);
|
||||
pr_debug(" Sending next buffer to dsp\n");
|
||||
memset(&in_frame_info[idx], 0,
|
||||
sizeof(uint32_t) * 2);
|
||||
atomic_dec(&prtd->in_count);
|
||||
ret = q6asm_read(prtd->audio_client);
|
||||
if (ret < 0) {
|
||||
pr_err("q6asm read failed\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
pr_err("No valid buffer\n");
|
||||
|
||||
pr_debug("Returning from capture_copy... %d\n", ret);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = OUT;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
dir = OUT;
|
||||
|
||||
if (dir == OUT) {
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
} else {
|
||||
/*
|
||||
*TODO : Need to Add Async IO changes. All period
|
||||
* size might not be supported.
|
||||
*/
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
(params_buffer_bytes(params) / params_periods(params)),
|
||||
params_periods(params));
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
if (dir == OUT)
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
else
|
||||
dma_buf->bytes = params_buffer_bytes(params);
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-multi-ch-pcm-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Multi channel PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
936
sound/soc/msm/msm-mvs.c
Normal file
936
sound/soc/msm/msm-mvs.c
Normal file
@@ -0,0 +1,936 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
#include <mach/debug_mm.h>
|
||||
#include "msm_audio_mvs.h"
|
||||
|
||||
|
||||
static struct audio_mvs_info_type audio_mvs_info;
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN,
|
||||
.period_bytes_min = MVS_MAX_VOC_PKT_SIZE,
|
||||
.period_bytes_max = MVS_MAX_VOC_PKT_SIZE,
|
||||
.periods_min = MVS_MAX_Q_LEN,
|
||||
.periods_max = MVS_MAX_Q_LEN,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static void snd_pcm_mvs_timer(unsigned long data)
|
||||
{
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
MM_DBG("%s\n", __func__);
|
||||
if (audio->playback_start) {
|
||||
if (audio->ack_dl_count) {
|
||||
audio->pcm_playback_irq_pos += audio->pcm_count;
|
||||
audio->ack_dl_count--;
|
||||
snd_pcm_period_elapsed(audio->playback_substream);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->capture_start) {
|
||||
if (audio->ack_ul_count) {
|
||||
audio->pcm_capture_irq_pos += audio->pcm_capture_count;
|
||||
audio->ack_ul_count--;
|
||||
snd_pcm_period_elapsed(audio->capture_substream);
|
||||
}
|
||||
}
|
||||
audio->timer.expires += audio->expiry_delta;
|
||||
add_timer(&audio->timer);
|
||||
}
|
||||
|
||||
static int audio_mvs_setup_mvs(struct audio_mvs_info_type *audio)
|
||||
{
|
||||
int rc = 0;
|
||||
struct audio_mvs_enable_msg enable_msg;
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
/* Enable MVS. */
|
||||
|
||||
memset(&enable_msg, 0, sizeof(enable_msg));
|
||||
audio->rpc_status = RPC_STATUS_FAILURE;
|
||||
enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
|
||||
enable_msg.enable_args.mode = cpu_to_be32(MVS_MODE_LINEAR_PCM);
|
||||
enable_msg.enable_args.ul_cb_func_id = (int) NULL;
|
||||
enable_msg.enable_args.dl_cb_func_id = (int) NULL;
|
||||
enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR);
|
||||
|
||||
msm_rpc_setup_req(&enable_msg.rpc_hdr, MVS_PROG,
|
||||
MVS_VERS, MVS_ENABLE_PROC);
|
||||
|
||||
rc = msm_rpc_write(audio->rpc_endpt,
|
||||
&enable_msg, sizeof(enable_msg));
|
||||
|
||||
if (rc >= 0) {
|
||||
MM_DBG("RPC write for enable done\n");
|
||||
|
||||
rc = wait_event_timeout(audio->wait,
|
||||
(audio->rpc_status !=
|
||||
RPC_STATUS_FAILURE), 1 * HZ);
|
||||
|
||||
if (rc > 0) {
|
||||
MM_DBG("Wait event for enable succeeded\n");
|
||||
|
||||
mutex_lock(&audio->lock);
|
||||
audio->mvs_mode = MVS_MODE_LINEAR_PCM;
|
||||
audio->frame_mode = MVS_FRAME_MODE_PCM_DL;
|
||||
audio->pcm_frame = 0;
|
||||
mutex_unlock(&audio->lock);
|
||||
rc = 0;
|
||||
|
||||
} else
|
||||
MM_ERR("Wait event for enable failed %d\n", rc);
|
||||
} else
|
||||
MM_ERR("RPC write for enable failed %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void audio_mvs_rpc_reply(struct msm_rpc_endpoint *endpoint,
|
||||
uint32_t xid)
|
||||
{
|
||||
int rc = 0;
|
||||
struct rpc_reply_hdr reply_hdr;
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
memset(&reply_hdr, 0, sizeof(reply_hdr));
|
||||
reply_hdr.xid = cpu_to_be32(xid);
|
||||
reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
|
||||
reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
|
||||
reply_hdr.data.acc_hdr.accept_stat =
|
||||
cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
|
||||
reply_hdr.data.acc_hdr.verf_flavor = 0;
|
||||
reply_hdr.data.acc_hdr.verf_length = 0;
|
||||
|
||||
rc = msm_rpc_write(endpoint, &reply_hdr, sizeof(reply_hdr));
|
||||
|
||||
if (rc < 0)
|
||||
MM_ERR("RPC write for response failed %d\n", rc);
|
||||
}
|
||||
|
||||
static void audio_mvs_process_rpc_request(uint32_t procedure, uint32_t xid,
|
||||
void *data, uint32_t length,
|
||||
struct audio_mvs_info_type *audio)
|
||||
{
|
||||
|
||||
int rc = 0;
|
||||
uint32_t index;
|
||||
MM_DBG("%s\n", __func__);
|
||||
switch (procedure) {
|
||||
case MVS_EVENT_CB_TYPE_PROC:{
|
||||
struct audio_mvs_cb_func_args *args = data;
|
||||
uint32_t event_type = be32_to_cpu(args->event);
|
||||
uint32_t cmd_status =
|
||||
be32_to_cpu(args->
|
||||
event_data.mvs_ev_command_type.cmd_status);
|
||||
uint32_t mode_status =
|
||||
be32_to_cpu(args->
|
||||
event_data.mvs_ev_mode_type.mode_status);
|
||||
audio_mvs_rpc_reply(audio->rpc_endpt, xid);
|
||||
if (be32_to_cpu(args->valid_ptr)) {
|
||||
if (event_type == AUDIO_MVS_COMMAND) {
|
||||
if (cmd_status == AUDIO_MVS_CMD_SUCCESS)
|
||||
audio->rpc_status = RPC_STATUS_SUCCESS;
|
||||
wake_up(&audio->wait);
|
||||
} else if (event_type == AUDIO_MVS_MODE) {
|
||||
if (mode_status != AUDIO_MVS_MODE_NOT_AVAIL) {
|
||||
audio->rpc_status =
|
||||
RPC_STATUS_SUCCESS;
|
||||
}
|
||||
audio->prepare_ack++;
|
||||
wake_up(&audio->wait);
|
||||
wake_up(&audio->prepare_wait);
|
||||
} else {
|
||||
/*nothing to do */
|
||||
}
|
||||
} else
|
||||
MM_ERR("ALSA: CB event pointer not valid\n");
|
||||
break;
|
||||
}
|
||||
case MVS_PACKET_UL_FN_TYPE_PROC:{
|
||||
uint32_t *cb_data = data;
|
||||
uint32_t pkt_len ;
|
||||
struct audio_mvs_ul_reply ul_reply;
|
||||
MM_DBG("MVS_PACKET_UL_FN_TYPE_PROC\n");
|
||||
|
||||
memset(&ul_reply, 0, sizeof(ul_reply));
|
||||
cb_data++;
|
||||
pkt_len = be32_to_cpu(*cb_data);
|
||||
cb_data++;
|
||||
if (audio->capture_enable) {
|
||||
audio_mvs_info.ack_ul_count++;
|
||||
mutex_lock(&audio->out_lock);
|
||||
index = audio->out_write % MVS_MAX_Q_LEN;
|
||||
memcpy(audio->out[index].voc_pkt, cb_data,
|
||||
pkt_len);
|
||||
audio->out[index].len = pkt_len;
|
||||
audio->out_write++;
|
||||
mutex_unlock(&audio->out_lock);
|
||||
}
|
||||
MM_DBG(" audio->out_read = %d audio->out write = %d\n",
|
||||
audio->out_read, audio->out_write);
|
||||
ul_reply.reply_hdr.xid = cpu_to_be32(xid);
|
||||
ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
|
||||
ul_reply.reply_hdr.reply_stat =
|
||||
cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
|
||||
ul_reply.reply_hdr.data.acc_hdr.accept_stat =
|
||||
cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
|
||||
ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
|
||||
ul_reply.reply_hdr.data.acc_hdr.verf_length = 0;
|
||||
ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
|
||||
ul_reply.pkt_status = cpu_to_be32(0x00000000);
|
||||
rc = msm_rpc_write(audio->rpc_endpt, &ul_reply,
|
||||
sizeof(ul_reply));
|
||||
wake_up(&audio->out_wait);
|
||||
if (rc < 0)
|
||||
MM_ERR("RPC write for UL response failed %d\n",
|
||||
rc);
|
||||
break;
|
||||
}
|
||||
case MVS_PACKET_DL_FN_TYPE_PROC:{
|
||||
struct audio_mvs_dl_reply dl_reply;
|
||||
MM_DBG("MVS_PACKET_DL_FN_TYPE_PROC\n");
|
||||
memset(&dl_reply, 0, sizeof(dl_reply));
|
||||
dl_reply.reply_hdr.xid = cpu_to_be32(xid);
|
||||
dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
|
||||
dl_reply.reply_hdr.reply_stat =
|
||||
cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
|
||||
dl_reply.reply_hdr.data.acc_hdr.accept_stat =
|
||||
cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
|
||||
dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
|
||||
dl_reply.reply_hdr.data.acc_hdr.verf_length = 0;
|
||||
mutex_lock(&audio->in_lock);
|
||||
if (audio->in_read < audio->in_write
|
||||
&& audio->dl_play) {
|
||||
index = audio->in_read % MVS_MAX_Q_LEN;
|
||||
memcpy(&dl_reply.voc_pkt,
|
||||
audio->in[index].voc_pkt,
|
||||
audio->in[index].len);
|
||||
audio->in_read++;
|
||||
audio_mvs_info.ack_dl_count++;
|
||||
dl_reply.pkt_status =
|
||||
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
|
||||
wake_up(&audio->in_wait);
|
||||
} else {
|
||||
dl_reply.pkt_status =
|
||||
cpu_to_be32(AUDIO_MVS_PKT_SLOW);
|
||||
}
|
||||
mutex_unlock(&audio->in_lock);
|
||||
MM_DBG(" audio->in_read = %d audio->in write = %d\n",
|
||||
audio->in_read, audio->in_write);
|
||||
dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001);
|
||||
dl_reply.frame_mode = cpu_to_be32(audio->frame_mode);
|
||||
dl_reply.frame_mode_again =
|
||||
cpu_to_be32(audio->frame_mode);
|
||||
dl_reply.frame_info_hdr.frame_mode =
|
||||
cpu_to_be32(audio->frame_mode);
|
||||
dl_reply.frame_info_hdr.mvs_mode =
|
||||
cpu_to_be32(audio->mvs_mode);
|
||||
dl_reply.frame_info_hdr.buf_free_cnt = 0;
|
||||
dl_reply.pcm_frame = cpu_to_be32(audio->pcm_frame);
|
||||
dl_reply.pcm_mode = cpu_to_be32(audio->pcm_mode);
|
||||
dl_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
|
||||
rc = msm_rpc_write(audio->rpc_endpt, &dl_reply,
|
||||
sizeof(dl_reply));
|
||||
if (rc < 0)
|
||||
MM_ERR("RPC write for DL response failed %d\n",
|
||||
rc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MM_ERR("Unknown CB type %d\n", procedure);
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_mvs_thread(void *data)
|
||||
{
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
struct rpc_request_hdr *rpc_hdr = NULL;
|
||||
struct rpc_reply_hdr *rpc_reply = NULL;
|
||||
uint32_t reply_status = 0;
|
||||
uint32_t rpc_type;
|
||||
int rpc_hdr_len;
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
rpc_hdr_len =
|
||||
msm_rpc_read(audio->rpc_endpt, (void **)&rpc_hdr, -1, -1);
|
||||
if (rpc_hdr_len < 0) {
|
||||
MM_ERR("RPC read failed %d\n", rpc_hdr_len);
|
||||
break;
|
||||
} else if (rpc_hdr_len < RPC_COMMON_HDR_SZ)
|
||||
continue;
|
||||
else {
|
||||
rpc_type = be32_to_cpu(rpc_hdr->type);
|
||||
if (rpc_type == RPC_TYPE_REPLY) {
|
||||
if (rpc_hdr_len < RPC_REPLY_HDR_SZ)
|
||||
continue;
|
||||
rpc_reply = (void *)rpc_hdr;
|
||||
reply_status = be32_to_cpu(rpc_reply->
|
||||
reply_stat);
|
||||
if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) {
|
||||
/* If the command is not accepted,
|
||||
* there will be no response callback.
|
||||
* Wake the caller and report error. */
|
||||
audio->rpc_status = RPC_STATUS_REJECT;
|
||||
wake_up(&audio->wait);
|
||||
MM_ERR("RPC reply status denied\n");
|
||||
}
|
||||
} else if (rpc_type == RPC_TYPE_REQUEST) {
|
||||
if (rpc_hdr_len < RPC_REQUEST_HDR_SZ)
|
||||
continue;
|
||||
MM_DBG("ALSA: kthread call procedure\n");
|
||||
audio_mvs_process_rpc_request(
|
||||
be32_to_cpu(rpc_hdr->procedure),
|
||||
be32_to_cpu(rpc_hdr->xid),
|
||||
(void *)(rpc_hdr + 1),
|
||||
(rpc_hdr_len - sizeof(*rpc_hdr)),
|
||||
audio);
|
||||
} else
|
||||
MM_ERR("Unexpected RPC type %d\n", rpc_type);
|
||||
}
|
||||
kfree(rpc_hdr);
|
||||
rpc_hdr = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
audio->playback_start = 1;
|
||||
else
|
||||
audio->capture_start = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
audio->playback_start = 0;
|
||||
else
|
||||
audio->capture_start = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
|
||||
MM_DBG("%s\n", __func__);
|
||||
mutex_lock(&audio->lock);
|
||||
if (audio->state < AUDIO_MVS_OPENED) {
|
||||
audio->rpc_endpt =
|
||||
msm_rpc_connect_compatible(MVS_PROG,
|
||||
MVS_VERS,
|
||||
MSM_RPC_UNINTERRUPTIBLE);
|
||||
audio->state = AUDIO_MVS_OPENED;
|
||||
}
|
||||
|
||||
if (IS_ERR(audio->rpc_endpt)) {
|
||||
MM_ERR("ALSA MVS RPC connect failed with version 0x%x\n",
|
||||
MVS_VERS);
|
||||
ret = PTR_ERR(audio->rpc_endpt);
|
||||
audio->rpc_endpt = NULL;
|
||||
goto err;
|
||||
} else {
|
||||
MM_DBG("ALSA MVS RPC connect succeeded\n");
|
||||
if (audio->playback_substream == NULL ||
|
||||
audio->capture_substream == NULL) {
|
||||
if (substream->stream ==
|
||||
SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
audio->playback_substream =
|
||||
substream;
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
} else if (substream->stream ==
|
||||
SNDRV_PCM_STREAM_CAPTURE) {
|
||||
audio->capture_substream =
|
||||
substream;
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
}
|
||||
} else {
|
||||
ret = -EPERM;
|
||||
goto err;
|
||||
}
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
MM_ERR("snd_pcm_hw_constraint_integer failed\n");
|
||||
if (!audio->instance) {
|
||||
msm_rpc_close(audio->rpc_endpt);
|
||||
audio->rpc_endpt = NULL;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
audio->instance++;
|
||||
}
|
||||
err:
|
||||
mutex_unlock(&audio->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 0;
|
||||
int count = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
uint32_t index;
|
||||
MM_DBG("%s\n", __func__);
|
||||
if (audio->dl_play == 1) {
|
||||
rc = wait_event_interruptible_timeout(audio->in_wait,
|
||||
(audio->in_write - audio->in_read <= 3),
|
||||
100 * HZ);
|
||||
if (!rc) {
|
||||
MM_ERR("MVS: write time out\n");
|
||||
return -ETIMEDOUT;
|
||||
} else if (rc < 0) {
|
||||
MM_ERR("MVS: write was interrupted\n");
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
mutex_lock(&audio->in_lock);
|
||||
if (audio->state == AUDIO_MVS_ENABLED) {
|
||||
index = audio->in_write % MVS_MAX_Q_LEN;
|
||||
count = frames_to_bytes(runtime, frames);
|
||||
if (count <= MVS_MAX_VOC_PKT_SIZE) {
|
||||
rc = copy_from_user(audio->in[index].voc_pkt, buf,
|
||||
count);
|
||||
} else
|
||||
rc = -ENOMEM;
|
||||
if (!rc) {
|
||||
audio->in[index].len = count;
|
||||
audio->in_write++;
|
||||
rc = count;
|
||||
if (audio->in_write >= 3)
|
||||
audio->dl_play = 1;
|
||||
} else {
|
||||
MM_ERR("Copy from user returned %d\n", rc);
|
||||
rc = -EFAULT;
|
||||
}
|
||||
|
||||
} else {
|
||||
MM_ERR("Write performed in invalid state %d\n",
|
||||
audio->state);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&audio->in_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff,
|
||||
void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 0;
|
||||
int count = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
uint32_t index = 0;
|
||||
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
/* Ensure the driver has been enabled. */
|
||||
if (audio->state != AUDIO_MVS_ENABLED) {
|
||||
MM_ERR("Read performed in invalid state %d\n", audio->state);
|
||||
return -EPERM;
|
||||
}
|
||||
rc = wait_event_interruptible_timeout(audio->out_wait,
|
||||
(audio->out_read < audio->out_write ||
|
||||
audio->state == AUDIO_MVS_CLOSING ||
|
||||
audio->state == AUDIO_MVS_CLOSED),
|
||||
100 * HZ);
|
||||
if (!rc) {
|
||||
MM_ERR("MVS: No UL data available\n");
|
||||
return -ETIMEDOUT;
|
||||
} else if (rc < 0) {
|
||||
MM_ERR("MVS: Read was interrupted\n");
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
mutex_lock(&audio->out_lock);
|
||||
if (audio->state == AUDIO_MVS_CLOSING
|
||||
|| audio->state == AUDIO_MVS_CLOSED) {
|
||||
rc = -EBUSY;
|
||||
} else {
|
||||
count = frames_to_bytes(runtime, frames);
|
||||
index = audio->out_read % MVS_MAX_Q_LEN;
|
||||
if (audio->out[index].len <= count) {
|
||||
rc = copy_to_user(buf,
|
||||
audio->out[index].voc_pkt,
|
||||
audio->out[index].len);
|
||||
if (rc == 0) {
|
||||
rc = audio->out[index].len;
|
||||
audio->out_read++;
|
||||
} else {
|
||||
MM_ERR("Copy to user %d\n", rc);
|
||||
rc = -EFAULT;
|
||||
}
|
||||
} else
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
mutex_unlock(&audio->out_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
MM_DBG("%s\n", __func__);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
struct audio_mvs_release_msg release_msg;
|
||||
MM_DBG("%s\n", __func__);
|
||||
memset(&release_msg, 0, sizeof(release_msg));
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
audio->instance--;
|
||||
wake_up(&audio->out_wait);
|
||||
|
||||
if (!audio->instance) {
|
||||
if (audio->state == AUDIO_MVS_ENABLED) {
|
||||
audio->state = AUDIO_MVS_CLOSING;
|
||||
/* Release MVS. */
|
||||
release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
|
||||
msm_rpc_setup_req(&release_msg.rpc_hdr, audio->rpc_prog,
|
||||
audio->rpc_ver,
|
||||
MVS_RELEASE_PROC);
|
||||
audio->rpc_status = RPC_STATUS_FAILURE;
|
||||
rc = msm_rpc_write(audio->rpc_endpt, &release_msg,
|
||||
sizeof(release_msg));
|
||||
if (rc >= 0) {
|
||||
MM_DBG("RPC write for release done\n");
|
||||
rc = wait_event_timeout(audio->wait,
|
||||
(audio->rpc_status !=
|
||||
RPC_STATUS_FAILURE), 1 * HZ);
|
||||
if (rc != 0) {
|
||||
MM_DBG
|
||||
("Wait event for release succeeded\n");
|
||||
rc = 0;
|
||||
kthread_stop(audio->task);
|
||||
audio->prepare_ack = 0;
|
||||
audio->task = NULL;
|
||||
del_timer_sync(&audio->timer);
|
||||
} else {
|
||||
MM_ERR
|
||||
("Wait event for release failed %d\n",
|
||||
rc);
|
||||
}
|
||||
} else {
|
||||
MM_ERR("RPC write for release failed %d\n", rc);
|
||||
}
|
||||
}
|
||||
audio->state = AUDIO_MVS_CLOSED;
|
||||
msm_rpc_close(audio->rpc_endpt);
|
||||
audio->rpc_endpt = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&audio->lock);
|
||||
|
||||
wake_unlock(&audio->suspend_lock);
|
||||
pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE);
|
||||
/* Release the IO buffers. */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
mutex_lock(&audio->in_lock);
|
||||
audio->in_write = 0;
|
||||
audio->in_read = 0;
|
||||
audio->playback_enable = 0;
|
||||
audio->dl_play = 0;
|
||||
audio->ack_dl_count = 0;
|
||||
memset(audio->in[0].voc_pkt, 0,
|
||||
MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN);
|
||||
audio->in->len = 0;
|
||||
audio->playback_substream = NULL;
|
||||
mutex_unlock(&audio->in_lock);
|
||||
} else {
|
||||
mutex_lock(&audio->out_lock);
|
||||
audio->out_write = 0;
|
||||
audio->out_read = 0;
|
||||
audio->capture_enable = 0;
|
||||
audio->ack_ul_count = 0;
|
||||
memset(audio->out[0].voc_pkt, 0,
|
||||
MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN);
|
||||
audio->out->len = 0;
|
||||
audio->capture_substream = NULL;
|
||||
mutex_unlock(&audio->out_lock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_mvs_pcm_setup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct audio_mvs_acquire_msg acquire_msg;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
memset(&acquire_msg, 0, sizeof(acquire_msg));
|
||||
|
||||
/*Create an Kthread */
|
||||
MM_DBG("ALSA MVS thread creating\n");
|
||||
if (!IS_ERR(audio->rpc_endpt)) {
|
||||
audio->task =
|
||||
kthread_run(audio_mvs_thread, audio,
|
||||
"audio_alsa_mvs_thread");
|
||||
if (!IS_ERR(audio->task)) {
|
||||
MM_DBG("ALSA MVS thread create succeeded\n");
|
||||
audio->rpc_prog = MVS_PROG;
|
||||
audio->rpc_ver = MVS_VERS;
|
||||
/* Acquire MVS. */
|
||||
acquire_msg.acquire_args.client_id =
|
||||
cpu_to_be32(MVS_CLIENT_ID_VOIP);
|
||||
acquire_msg.acquire_args.cb_func_id =
|
||||
cpu_to_be32(MVS_CB_FUNC_ID);
|
||||
msm_rpc_setup_req(&acquire_msg.rpc_hdr,
|
||||
audio->rpc_prog,
|
||||
audio->rpc_ver,
|
||||
MVS_ACQUIRE_PROC);
|
||||
audio->rpc_status = RPC_STATUS_FAILURE;
|
||||
rc = msm_rpc_write(audio->rpc_endpt,
|
||||
&acquire_msg, sizeof(acquire_msg));
|
||||
if (rc >= 0) {
|
||||
MM_DBG("RPC write for acquire done\n");
|
||||
|
||||
rc = wait_event_timeout(audio->wait,
|
||||
(audio->rpc_status !=
|
||||
RPC_STATUS_FAILURE),
|
||||
1 * HZ);
|
||||
if (rc != 0) {
|
||||
audio->state =
|
||||
AUDIO_MVS_ACQUIRE;
|
||||
rc = 0;
|
||||
MM_DBG
|
||||
("MVS driver in acquire state\n");
|
||||
} else {
|
||||
MM_ERR
|
||||
("acquire Wait event failed %d\n",
|
||||
rc);
|
||||
rc = -EBUSY;
|
||||
}
|
||||
} else {
|
||||
MM_ERR("RPC write for acquire failed %d\n",
|
||||
rc);
|
||||
rc = -EBUSY;
|
||||
}
|
||||
} else {
|
||||
MM_ERR("ALSA MVS thread create failed\n");
|
||||
rc = PTR_ERR(audio->task);
|
||||
audio->task = NULL;
|
||||
msm_rpc_close(audio->rpc_endpt);
|
||||
audio->rpc_endpt = NULL;
|
||||
}
|
||||
} else {
|
||||
MM_ERR("RPC connect is not setup with version 0x%x\n",
|
||||
MVS_VERS);
|
||||
rc = PTR_ERR(audio->rpc_endpt);
|
||||
audio->rpc_endpt = NULL;
|
||||
}
|
||||
/*mvs mode setup */
|
||||
if (audio->state == AUDIO_MVS_ACQUIRE)
|
||||
rc = audio_mvs_setup_mvs(audio);
|
||||
else
|
||||
rc = -EBUSY;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct audio_mvs_info_type *prtd = &audio_mvs_info;
|
||||
MM_DBG("%s\n", __func__);
|
||||
prtd->pcm_playback_irq_pos = 0;
|
||||
prtd->pcm_playback_buf_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->playback_enable = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct audio_mvs_info_type *prtd = &audio_mvs_info;
|
||||
prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_capture_irq_pos = 0;
|
||||
prtd->pcm_capture_buf_pos = 0;
|
||||
prtd->capture_enable = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *prtd = &audio_mvs_info;
|
||||
unsigned long expiry = 0;
|
||||
MM_DBG("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
|
||||
mutex_lock(&prtd->prepare_lock);
|
||||
if (prtd->state == AUDIO_MVS_ENABLED)
|
||||
goto enabled;
|
||||
else if (prtd->state == AUDIO_MVS_PREPARING)
|
||||
goto prepairing;
|
||||
else if (prtd->state == AUDIO_MVS_OPENED) {
|
||||
prtd->state = AUDIO_MVS_PREPARING;
|
||||
rc = msm_mvs_pcm_setup(substream);
|
||||
}
|
||||
if (!rc) {
|
||||
expiry = ((unsigned long)((prtd->pcm_count * 1000)
|
||||
/(runtime->rate * runtime->channels * 2)));
|
||||
expiry -= (expiry % 10);
|
||||
prtd->timer.expires = jiffies + (msecs_to_jiffies(expiry));
|
||||
prtd->expiry_delta = (msecs_to_jiffies(expiry));
|
||||
if (prtd->expiry_delta <= 2)
|
||||
prtd->expiry_delta = 1;
|
||||
setup_timer(&prtd->timer, snd_pcm_mvs_timer,
|
||||
(unsigned long)prtd);
|
||||
prtd->ack_ul_count = 0;
|
||||
prtd->ack_dl_count = 0;
|
||||
add_timer(&prtd->timer);
|
||||
|
||||
} else {
|
||||
MM_ERR("ALSA MVS setup is not done");
|
||||
rc = -EPERM;
|
||||
prtd->state = AUDIO_MVS_OPENED;
|
||||
goto err;
|
||||
}
|
||||
|
||||
prepairing:
|
||||
rc = wait_event_interruptible(prtd->prepare_wait,
|
||||
(prtd->prepare_ack == 2));
|
||||
if (rc < 0) {
|
||||
MM_ERR("Wait event for prepare faild rc %d", rc);
|
||||
rc = -EINTR;
|
||||
prtd->state = AUDIO_MVS_OPENED;
|
||||
goto err;
|
||||
} else
|
||||
MM_DBG("Wait event for prepare succeeded\n");
|
||||
|
||||
prtd->state = AUDIO_MVS_ENABLED;
|
||||
enabled:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rc = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
rc = msm_pcm_capture_prepare(substream);
|
||||
err:
|
||||
mutex_unlock(&prtd->prepare_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int msm_mvs_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
MM_DBG("%s\n", __func__);
|
||||
if (substream->pcm->device & 1) {
|
||||
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
|
||||
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
|
||||
if (audio->pcm_playback_irq_pos >= audio->pcm_size)
|
||||
audio->pcm_playback_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (audio->pcm_playback_irq_pos));
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_mvs_info_type *audio = &audio_mvs_info;
|
||||
|
||||
if (audio->pcm_capture_irq_pos >= audio->pcm_capture_size)
|
||||
audio->pcm_capture_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (audio->pcm_capture_irq_pos));
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_uframes_t ret = 0;
|
||||
MM_DBG("%s\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_pointer(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_pointer(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_mvs_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_mvs_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
|
||||
};
|
||||
|
||||
static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret, offset = 0;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
audio_mvs_info.mem_chunk = kmalloc(
|
||||
2 * MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN, GFP_KERNEL);
|
||||
if (audio_mvs_info.mem_chunk != NULL) {
|
||||
audio_mvs_info.in_read = 0;
|
||||
audio_mvs_info.in_write = 0;
|
||||
audio_mvs_info.out_read = 0;
|
||||
audio_mvs_info.out_write = 0;
|
||||
for (i = 0; i < MVS_MAX_Q_LEN; i++) {
|
||||
audio_mvs_info.in[i].voc_pkt =
|
||||
audio_mvs_info.mem_chunk + offset;
|
||||
offset = offset + MVS_MAX_VOC_PKT_SIZE;
|
||||
}
|
||||
for (i = 0; i < MVS_MAX_Q_LEN; i++) {
|
||||
audio_mvs_info.out[i].voc_pkt =
|
||||
audio_mvs_info.mem_chunk + offset;
|
||||
offset = offset + MVS_MAX_VOC_PKT_SIZE;
|
||||
}
|
||||
audio_mvs_info.playback_substream = NULL;
|
||||
audio_mvs_info.capture_substream = NULL;
|
||||
} else {
|
||||
MM_ERR("MSM MVS kmalloc failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_mvs_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_mvs_pcm_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver msm_mvs_soc_platform = {
|
||||
.ops = &msm_mvs_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
};
|
||||
EXPORT_SYMBOL(msm_mvs_soc_platform);
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_mvs_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-mvs-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_mvs_soc_platform_init(void)
|
||||
{
|
||||
memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
|
||||
mutex_init(&audio_mvs_info.lock);
|
||||
mutex_init(&audio_mvs_info.prepare_lock);
|
||||
mutex_init(&audio_mvs_info.in_lock);
|
||||
mutex_init(&audio_mvs_info.out_lock);
|
||||
init_waitqueue_head(&audio_mvs_info.wait);
|
||||
init_waitqueue_head(&audio_mvs_info.prepare_wait);
|
||||
init_waitqueue_head(&audio_mvs_info.out_wait);
|
||||
init_waitqueue_head(&audio_mvs_info.in_wait);
|
||||
wake_lock_init(&audio_mvs_info.suspend_lock, WAKE_LOCK_SUSPEND,
|
||||
"audio_mvs_suspend");
|
||||
pm_qos_add_request(&audio_mvs_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_mvs_soc_platform_init);
|
||||
|
||||
static void __exit msm_mvs_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_mvs_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MVS PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
621
sound/soc/msm/msm-pcm-afe.c
Normal file
621
sound/soc/msm/msm-pcm-afe.c
Normal file
@@ -0,0 +1,621 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/q6adm.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/memory_alloc.h>
|
||||
#include <mach/msm_subsystem_map.h>
|
||||
#include "msm-pcm-afe.h"
|
||||
|
||||
#define MIN_PERIOD_SIZE (128 * 2)
|
||||
#define MAX_PERIOD_SIZE (128 * 2 * 2 * 6)
|
||||
static struct snd_pcm_hardware msm_afe_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MAX_PERIOD_SIZE * 32,
|
||||
.period_bytes_min = MIN_PERIOD_SIZE,
|
||||
.period_bytes_max = MAX_PERIOD_SIZE,
|
||||
.periods_min = 32,
|
||||
.periods_max = 384,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
|
||||
static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
|
||||
|
||||
static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct pcm_afe_info *prtd =
|
||||
container_of(hrt, struct pcm_afe_info, hrt);
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
if (prtd->start) {
|
||||
pr_debug("sending frame to DSP: poll_time: %d\n",
|
||||
prtd->poll_time);
|
||||
if (prtd->dsp_cnt == runtime->periods)
|
||||
prtd->dsp_cnt = 0;
|
||||
afe_rt_proxy_port_write(
|
||||
(prtd->dma_addr +
|
||||
(prtd->dsp_cnt *
|
||||
snd_pcm_lib_period_bytes(prtd->substream))),
|
||||
snd_pcm_lib_period_bytes(prtd->substream));
|
||||
prtd->dsp_cnt++;
|
||||
hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
|
||||
* 1000));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
} else
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct pcm_afe_info *prtd =
|
||||
container_of(hrt, struct pcm_afe_info, hrt);
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
if (prtd->start) {
|
||||
if (prtd->dsp_cnt == runtime->periods)
|
||||
prtd->dsp_cnt = 0;
|
||||
afe_rt_proxy_port_read(
|
||||
(prtd->dma_addr + (prtd->dsp_cnt
|
||||
* snd_pcm_lib_period_bytes(prtd->substream))),
|
||||
snd_pcm_lib_period_bytes(prtd->substream));
|
||||
prtd->dsp_cnt++;
|
||||
pr_debug("sending frame rec to DSP: poll_time: %d\n",
|
||||
prtd->poll_time);
|
||||
hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
|
||||
* 1000));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
} else
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
static void pcm_afe_process_tx_pkt(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload,
|
||||
void *priv)
|
||||
{
|
||||
struct pcm_afe_info *prtd = priv;
|
||||
unsigned long dsp_flags;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
uint16_t event;
|
||||
|
||||
if (prtd == NULL)
|
||||
return;
|
||||
substream = prtd->substream;
|
||||
runtime = substream->runtime;
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
|
||||
switch (opcode) {
|
||||
case AFE_EVENT_RT_PROXY_PORT_STATUS: {
|
||||
event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
|
||||
switch (event) {
|
||||
case AFE_EVENT_RTPORT_START: {
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->poll_time = ((unsigned long)((
|
||||
snd_pcm_lib_period_bytes
|
||||
(prtd->substream) *
|
||||
1000 * 1000)/
|
||||
(runtime->rate *
|
||||
runtime->channels * 2)));
|
||||
pr_debug("prtd->poll_time: %d",
|
||||
prtd->poll_time);
|
||||
hrtimer_start(&prtd->hrt,
|
||||
ns_to_ktime(0),
|
||||
HRTIMER_MODE_REL);
|
||||
break;
|
||||
}
|
||||
case AFE_EVENT_RTPORT_STOP:
|
||||
pr_debug("%s: event!=0\n", __func__);
|
||||
prtd->start = 0;
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_LOW_WM:
|
||||
pr_debug("%s: Underrun\n", __func__);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_HI_WM:
|
||||
pr_debug("%s: Overrun\n", __func__);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case AFE_SERVICE_CMD_RTPORT_WR:
|
||||
pr_debug("write done\n");
|
||||
prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
|
||||
(prtd->substream);
|
||||
snd_pcm_period_elapsed(prtd->substream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
|
||||
}
|
||||
|
||||
static void pcm_afe_process_rx_pkt(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload,
|
||||
void *priv)
|
||||
{
|
||||
struct pcm_afe_info *prtd = priv;
|
||||
unsigned long dsp_flags;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
uint16_t event;
|
||||
|
||||
if (prtd == NULL)
|
||||
return;
|
||||
substream = prtd->substream;
|
||||
runtime = substream->runtime;
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
|
||||
switch (opcode) {
|
||||
case AFE_EVENT_RT_PROXY_PORT_STATUS: {
|
||||
event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
|
||||
switch (event) {
|
||||
case AFE_EVENT_RTPORT_START: {
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->poll_time = ((unsigned long)((
|
||||
snd_pcm_lib_period_bytes(prtd->substream)
|
||||
* 1000 * 1000)/(runtime->rate
|
||||
* runtime->channels * 2)));
|
||||
hrtimer_start(&prtd->hrt,
|
||||
ns_to_ktime(0),
|
||||
HRTIMER_MODE_REL);
|
||||
pr_debug("prtd->poll_time : %d", prtd->poll_time);
|
||||
break;
|
||||
}
|
||||
case AFE_EVENT_RTPORT_STOP:
|
||||
pr_debug("%s: event!=0\n", __func__);
|
||||
prtd->start = 0;
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_LOW_WM:
|
||||
pr_debug("%s: Underrun\n", __func__);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_HI_WM:
|
||||
pr_debug("%s: Overrun\n", __func__);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case AFE_SERVICE_CMD_RTPORT_RD:
|
||||
pr_debug("Read done\n");
|
||||
prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
|
||||
(prtd->substream);
|
||||
snd_pcm_period_elapsed(prtd->substream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
|
||||
}
|
||||
|
||||
static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
|
||||
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
ret = afe_register_get_events(dai->id,
|
||||
pcm_afe_process_tx_pkt, prtd);
|
||||
if (ret < 0) {
|
||||
pr_err("afe-pcm:register for events failed\n");
|
||||
return ret;
|
||||
}
|
||||
pr_debug("%s:success\n", __func__);
|
||||
prtd->prepared++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
ret = afe_register_get_events(dai->id,
|
||||
pcm_afe_process_rx_pkt, prtd);
|
||||
if (ret < 0) {
|
||||
pr_err("afe-pcm:register for events failed\n");
|
||||
return ret;
|
||||
}
|
||||
pr_debug("%s:success\n", __func__);
|
||||
prtd->prepared++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 16000, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int msm_afe_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = NULL;
|
||||
int ret = 0;
|
||||
|
||||
prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
} else
|
||||
pr_debug("prtd %x\n", (unsigned int)prtd);
|
||||
|
||||
mutex_init(&prtd->lock);
|
||||
spin_lock_init(&prtd->dsp_lock);
|
||||
prtd->dsp_cnt = 0;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
runtime->hw = msm_afe_hardware;
|
||||
prtd->substream = substream;
|
||||
runtime->private_data = prtd;
|
||||
mutex_unlock(&prtd->lock);
|
||||
hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->hrt.function = afe_hrtimer_callback;
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
prtd->hrt.function = afe_hrtimer_rec_callback;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_afe_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_dma_buffer *dma_buf;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct pcm_afe_info *prtd;
|
||||
struct snd_soc_pcm_runtime *rtd = NULL;
|
||||
struct snd_soc_dai *dai = NULL;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (substream == NULL) {
|
||||
pr_err("substream is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rtd = substream->private_data;
|
||||
dai = rtd->cpu_dai;
|
||||
runtime = substream->runtime;
|
||||
prtd = runtime->private_data;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = afe_unregister_get_events(dai->id);
|
||||
if (ret < 0)
|
||||
pr_err("AFE unregister for events failed\n");
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
ret = afe_unregister_get_events(dai->id);
|
||||
if (ret < 0)
|
||||
pr_err("AFE unregister for events failed\n");
|
||||
}
|
||||
hrtimer_cancel(&prtd->hrt);
|
||||
|
||||
rc = afe_cmd_memory_unmap(runtime->dma_addr);
|
||||
if (rc < 0)
|
||||
pr_err("AFE memory unmap failed\n");
|
||||
|
||||
pr_debug("release all buffer\n");
|
||||
dma_buf = &substream->dma_buffer;
|
||||
if (dma_buf == NULL) {
|
||||
pr_debug("dma_buf is NULL\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (dma_buf->area) {
|
||||
if (msm_subsystem_unmap_buffer(prtd->mem_buffer) < 0) {
|
||||
pr_err("%s: unmap buffer failed\n", __func__);
|
||||
prtd->mem_buffer = NULL;
|
||||
dma_buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dma_buf->addr)
|
||||
free_contiguous_memory_by_paddr(dma_buf->addr);
|
||||
done:
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
mutex_unlock(&prtd->lock);
|
||||
prtd->prepared--;
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
static int msm_afe_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
prtd->pcm_irq_pos = 0;
|
||||
if (prtd->prepared)
|
||||
return 0;
|
||||
mutex_lock(&prtd->lock);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_afe_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_afe_capture_prepare(substream);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return ret;
|
||||
}
|
||||
static int msm_afe_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
int result = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
|
||||
prtd->start = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
|
||||
prtd->start = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int msm_afe_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
int rc;
|
||||
unsigned int flags = 0;
|
||||
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
|
||||
dma_buf->addr = allocate_contiguous_ebi_nomap(
|
||||
runtime->hw.buffer_bytes_max, SZ_4K);
|
||||
if (!dma_buf->addr) {
|
||||
pr_err("%s:MSM AFE physical memory allocation failed\n",
|
||||
__func__);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
flags = MSM_SUBSYSTEM_MAP_KADDR | MSM_SUBSYSTEM_MAP_CACHED;
|
||||
|
||||
prtd->mem_buffer = msm_subsystem_map_buffer(dma_buf->addr,
|
||||
runtime->hw.buffer_bytes_max, flags,
|
||||
NULL, 0);
|
||||
if (IS_ERR((void *) prtd->mem_buffer)) {
|
||||
pr_err("%s: map_buffer failed error = %ld\n", __func__,
|
||||
PTR_ERR((void *)prtd->mem_buffer));
|
||||
free_contiguous_memory_by_paddr(dma_buf->addr);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_buf->area = prtd->mem_buffer->vaddr;
|
||||
|
||||
pr_debug("%s: dma_buf->area: 0x%p, dma_buf->addr: 0x%x", __func__,
|
||||
(unsigned int *) dma_buf->area, dma_buf->addr);
|
||||
|
||||
if (!dma_buf->area) {
|
||||
pr_err("%s: Invalid Virtual address\n", __func__);
|
||||
if (prtd->mem_buffer) {
|
||||
msm_subsystem_unmap_buffer(prtd->mem_buffer);
|
||||
prtd->mem_buffer = NULL;
|
||||
dma_buf->area = NULL;
|
||||
}
|
||||
free_contiguous_memory_by_paddr(dma_buf->addr);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
|
||||
prtd->dma_addr = (u32) dma_buf->addr;
|
||||
|
||||
mutex_unlock(&prtd->lock);
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
|
||||
rc = afe_cmd_memory_map(dma_buf->addr, dma_buf->bytes);
|
||||
if (rc < 0)
|
||||
pr_err("fail to map memory to DSP\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_afe_ops = {
|
||||
.open = msm_afe_open,
|
||||
.hw_params = msm_afe_hw_params,
|
||||
.trigger = msm_afe_trigger,
|
||||
.close = msm_afe_close,
|
||||
.prepare = msm_afe_prepare,
|
||||
.mmap = msm_afe_mmap,
|
||||
.pointer = msm_afe_pointer,
|
||||
};
|
||||
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_afe_afe_probe(struct snd_soc_platform *platform)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_afe_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
.probe = msm_afe_afe_probe,
|
||||
};
|
||||
|
||||
static __devinit int msm_afe_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_afe_remove(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_afe_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-afe",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_afe_probe,
|
||||
.remove = __devexit_p(msm_afe_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return platform_driver_register(&msm_afe_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
platform_driver_unregister(&msm_afe_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AFE PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
46
sound/soc/msm/msm-pcm-afe.h
Normal file
46
sound/soc/msm/msm-pcm-afe.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_AFE_H
|
||||
#define _MSM_PCM_AFE_H
|
||||
#include <sound/apr_audio.h>
|
||||
#include <sound/q6afe.h>
|
||||
|
||||
|
||||
struct pcm_afe_info {
|
||||
unsigned long dma_addr;
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
struct mutex lock;
|
||||
spinlock_t dsp_lock;
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint8_t start;
|
||||
uint32_t dsp_cnt;
|
||||
uint32_t buf_phys;
|
||||
int32_t mmap_flag;
|
||||
int prepared;
|
||||
struct hrtimer hrt;
|
||||
int poll_time;
|
||||
struct msm_mapped_buffer *mem_buffer;
|
||||
};
|
||||
|
||||
|
||||
#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
|
||||
{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
||||
.name = xname, \
|
||||
.info = fp_info,\
|
||||
.get = fp_get, .put = fp_put, \
|
||||
.private_value = addr, \
|
||||
}
|
||||
|
||||
#endif /*_MSM_PCM_AFE_H*/
|
||||
61
sound/soc/msm/msm-pcm-hostless.c
Normal file
61
sound/soc/msm/msm-pcm-hostless.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_hostless_ops = {};
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_hostless_platform = {
|
||||
.ops = &msm_pcm_hostless_ops,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_hostless_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_hostless_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_hostless_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_hostless_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-hostless",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_hostless_probe,
|
||||
.remove = __devexit_p(msm_pcm_hostless_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_pcm_hostless_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_hostless_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Hostless platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
610
sound/soc/msm/msm-pcm-lpa.c
Normal file
610
sound/soc/msm/msm-pcm-lpa.c
Normal file
@@ -0,0 +1,610 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <sound/compress_params.h>
|
||||
#include <sound/compress_offload.h>
|
||||
#include <sound/compress_driver.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
#include "msm-pcm-q6.h"
|
||||
#include "msm-pcm-routing.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm lpa_audio;
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 1024 * 1024,
|
||||
/* TODO: Check on the lowest period size we can support */
|
||||
.period_bytes_min = 128 * 1024,
|
||||
.period_bytes_max = 256 * 1024,
|
||||
.periods_min = 4,
|
||||
.periods_max = 8,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_aio_write_param param;
|
||||
struct audio_buffer *buf = NULL;
|
||||
unsigned long flag = 0;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&the_locks.event_lock, flag);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE: {
|
||||
uint32_t *ptrmem = (uint32_t *)¶m;
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
else
|
||||
if (substream->timer_running)
|
||||
snd_timer_interrupt(substream->timer, 1);
|
||||
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start)) {
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
break;
|
||||
} else
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
|
||||
memset((void *)buf[0].phys +
|
||||
(prtd->out_head * prtd->pcm_count),
|
||||
0, prtd->pcm_count);
|
||||
}
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
param.paddr = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
|
||||
i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1) & (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_CMDRSP_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN: {
|
||||
if (!atomic_read(&prtd->pending_buffer))
|
||||
break;
|
||||
if (runtime->status->hw_ptr >=
|
||||
runtime->control->appl_ptr)
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__, prtd->pcm_count);
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
param.paddr = (unsigned long)buf[prtd->out_head].phys;
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[prtd->out_head].phys;
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1)
|
||||
& (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
}
|
||||
break;
|
||||
case ASM_STREAM_CMD_FLUSH:
|
||||
pr_debug("ASM_STREAM_CMD_FLUSH\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.event_lock, flag);
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
prtd->out_head = 0;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
|
||||
runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->pcm_irq_pos = 0;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_START\n");
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_debug("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Set IO mode failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return -EPERM;
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
runtime->private_data = prtd;
|
||||
lpa_audio.prtd = prtd;
|
||||
lpa_set_volume(lpa_audio.volume);
|
||||
ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lpa_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
|
||||
rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
lpa_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
If routing is still enabled, we need to issue EOS to
|
||||
the DSP
|
||||
To issue EOS to dsp, we need to be run state otherwise
|
||||
EOS is not honored.
|
||||
*/
|
||||
if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
|
||||
rc = q6asm_run(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
pr_debug("%s\n", __func__);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("EOS cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
}
|
||||
|
||||
dir = IN;
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
lpa_audio.prtd = NULL;
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
return -EPERM;
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed \
|
||||
rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
if (buf == NULL || buf[0].data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
uint64_t timestamp;
|
||||
uint64_t temp;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_COMPRESS_TSTAMP: {
|
||||
struct snd_compr_tstamp tstamp;
|
||||
pr_debug("SNDRV_COMPRESS_TSTAMP\n");
|
||||
|
||||
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
|
||||
timestamp = q6asm_get_session_time(prtd->audio_client);
|
||||
if (timestamp < 0) {
|
||||
pr_err("%s: Get Session Time return value =%lld\n",
|
||||
__func__, timestamp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
temp = (timestamp * 2 * runtime->channels);
|
||||
temp = temp * (runtime->rate/1000);
|
||||
temp = div_u64(temp, 1000);
|
||||
tstamp.sampling_rate = runtime->rate;
|
||||
tstamp.timestamp = timestamp;
|
||||
pr_debug("%s: bytes_consumed:"
|
||||
"timestamp = %lld,\n",__func__,
|
||||
tstamp.timestamp);
|
||||
if (copy_to_user((void *) arg, &tstamp,
|
||||
sizeof(struct snd_compr_tstamp)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_PCM_IOCTL1_RESET:
|
||||
prtd->cmd_ack = 0;
|
||||
rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
|
||||
if (rc < 0)
|
||||
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("Flush cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = msm_pcm_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n",
|
||||
__func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-lpa",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
spin_lock_init(&the_locks.event_lock);
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
740
sound/soc/msm/msm-pcm-q6.c
Normal file
740
sound/soc/msm/msm-pcm-q6.c
Normal file
@@ -0,0 +1,740 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
|
||||
#include "msm-pcm-q6.h"
|
||||
#include "msm-pcm-routing.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
#define PLAYBACK_NUM_PERIODS 8
|
||||
#define PLAYBACK_PERIOD_SIZE 2048
|
||||
#define CAPTURE_NUM_PERIODS 16
|
||||
#define CAPTURE_PERIOD_SIZE 320
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_capture = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 4,
|
||||
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_min = CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_max = CAPTURE_PERIOD_SIZE,
|
||||
.periods_min = CAPTURE_NUM_PERIODS,
|
||||
.periods_max = CAPTURE_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_min = PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_max = PLAYBACK_PERIOD_SIZE,
|
||||
.periods_min = PLAYBACK_NUM_PERIODS,
|
||||
.periods_max = PLAYBACK_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
uint32_t *ptrmem = (uint32_t *)payload;
|
||||
int i = 0;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start))
|
||||
break;
|
||||
if (!prtd->mmap_flag)
|
||||
break;
|
||||
if (q6asm_is_cpu_buf_avail_nolock(IN,
|
||||
prtd->audio_client,
|
||||
&size, &idx)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count, 0, 0, NO_TIMESTAMP);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_CMDRSP_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case ASM_DATA_EVENT_READ_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
|
||||
pr_debug("token = 0x%08x\n", token);
|
||||
for (i = 0; i < 8; i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
in_frame_info[token][0] = payload[2];
|
||||
in_frame_info[token][1] = payload[3];
|
||||
prtd->pcm_irq_pos += in_frame_info[token][0];
|
||||
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (atomic_read(&prtd->in_count) <= prtd->periods)
|
||||
atomic_inc(&prtd->in_count);
|
||||
wake_up(&the_locks.read_wait);
|
||||
if (prtd->mmap_flag
|
||||
&& q6asm_is_cpu_buf_avail_nolock(OUT,
|
||||
prtd->audio_client,
|
||||
&size, &idx))
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN:
|
||||
if (substream->stream
|
||||
!= SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
}
|
||||
if (prtd->mmap_flag) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
} else {
|
||||
while (atomic_read(&prtd->out_needed)) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
atomic_dec(&prtd->out_needed);
|
||||
wake_up(&the_locks.write_wait);
|
||||
};
|
||||
}
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
|
||||
runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_info("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
|
||||
pr_debug("Channel = %d\n", prtd->channel_mode);
|
||||
if (prtd->channel_mode > 2) {
|
||||
ret = q6asm_enc_cfg_blk_multi_ch_pcm(prtd->audio_client,
|
||||
prtd->samp_rate, prtd->channel_mode);
|
||||
} else {
|
||||
ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client,
|
||||
prtd->samp_rate, prtd->channel_mode);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
pr_debug("%s: cmd cfg pcm was block failed", __func__);
|
||||
|
||||
for (i = 0; i < runtime->periods; i++)
|
||||
q6asm_read(prtd->audio_client);
|
||||
prtd->periods = runtime->periods;
|
||||
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_info("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw = msm_pcm_hardware_playback;
|
||||
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_hardware_capture;
|
||||
}
|
||||
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
runtime->private_data = prtd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer = 0;
|
||||
char *bufptr = NULL;
|
||||
void *data = NULL;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
pr_debug("%s: prtd->out_count = %d\n",
|
||||
__func__, atomic_read(&prtd->out_count));
|
||||
ret = wait_event_timeout(the_locks.write_wait,
|
||||
(atomic_read(&prtd->out_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!atomic_read(&prtd->out_count)) {
|
||||
pr_err("%s: pcm stopped out_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
if (bufptr) {
|
||||
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
|
||||
__func__, fbytes, xfer, size);
|
||||
xfer = fbytes;
|
||||
if (copy_from_user(bufptr, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
buf += xfer;
|
||||
fbytes -= xfer;
|
||||
pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
|
||||
if (atomic_read(&prtd->start)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp\n",
|
||||
__func__, xfer);
|
||||
ret = q6asm_write(prtd->audio_client, xfer,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
if (ret < 0) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
atomic_inc(&prtd->out_needed);
|
||||
atomic_dec(&prtd->out_count);
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
ret = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD_EOS failed\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer;
|
||||
char *bufptr;
|
||||
void *data = NULL;
|
||||
static uint32_t idx;
|
||||
static uint32_t size;
|
||||
uint32_t offset = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
|
||||
pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
|
||||
pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
|
||||
pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
|
||||
|
||||
ret = wait_event_timeout(the_locks.read_wait,
|
||||
(atomic_read(&prtd->in_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (!atomic_read(&prtd->in_count)) {
|
||||
pr_debug("%s: pcm stopped in_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("Checking if valid buffer is available...%08x\n",
|
||||
(unsigned int) data);
|
||||
data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
pr_debug("Size = %d\n", size);
|
||||
pr_debug("fbytes = %d\n", fbytes);
|
||||
pr_debug("idx = %d\n", idx);
|
||||
if (bufptr) {
|
||||
xfer = fbytes;
|
||||
if (xfer > size)
|
||||
xfer = size;
|
||||
offset = in_frame_info[idx][1];
|
||||
pr_debug("Offset value = %d\n", offset);
|
||||
if (copy_to_user(buf, bufptr+offset, xfer)) {
|
||||
pr_err("Failed to copy buf to user\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
fbytes -= xfer;
|
||||
size -= xfer;
|
||||
in_frame_info[idx][1] += xfer;
|
||||
pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
|
||||
__func__, fbytes, size, xfer);
|
||||
pr_debug(" Sending next buffer to dsp\n");
|
||||
memset(&in_frame_info[idx], 0,
|
||||
sizeof(uint32_t) * 2);
|
||||
atomic_dec(&prtd->in_count);
|
||||
ret = q6asm_read(prtd->audio_client);
|
||||
if (ret < 0) {
|
||||
pr_err("q6asm read failed\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
pr_err("No valid buffer\n");
|
||||
|
||||
pr_debug("Returning from capture_copy... %d\n", ret);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = OUT;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
int format = FORMAT_LINEAR_PCM;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
dir = OUT;
|
||||
|
||||
/*capture path*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (params_channels(params) > 2)
|
||||
format = FORMAT_MULTI_CHANNEL_LINEAR_PCM;
|
||||
pr_debug("%s format = :0x%x\n", __func__, format);
|
||||
|
||||
ret = q6asm_open_read(prtd->audio_client, format);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: q6asm_open_read failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed \
|
||||
rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
if (buf == NULL || buf[0].data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
83
sound/soc/msm/msm-pcm-q6.h
Normal file
83
sound/soc/msm/msm-pcm-q6.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2009,2011 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
#include <sound/apr_audio.h>
|
||||
#include <sound/q6asm.h>
|
||||
|
||||
|
||||
/* Support unconventional sample rates 12000, 24000 as well */
|
||||
#define USE_RATE \
|
||||
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
|
||||
|
||||
extern int copy_count;
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used;
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct buffer_rec {
|
||||
void *data;
|
||||
unsigned int size;
|
||||
unsigned int read;
|
||||
unsigned int addr;
|
||||
};
|
||||
|
||||
struct audio_locks {
|
||||
spinlock_t event_lock;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t eos_wait;
|
||||
wait_queue_head_t enable_wait;
|
||||
};
|
||||
|
||||
struct msm_audio {
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
uint16_t source; /* Encoding source bit mask */
|
||||
|
||||
struct audio_client *audio_client;
|
||||
|
||||
uint16_t session_id;
|
||||
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t dsp_cnt;
|
||||
|
||||
int abort; /* set when error, like sample rate mismatch */
|
||||
|
||||
int enabled;
|
||||
int close_ack;
|
||||
int cmd_ack;
|
||||
atomic_t start;
|
||||
atomic_t out_count;
|
||||
atomic_t in_count;
|
||||
atomic_t out_needed;
|
||||
int out_head;
|
||||
int periods;
|
||||
int mmap_flag;
|
||||
atomic_t pending_buffer;
|
||||
};
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
2478
sound/soc/msm/msm-pcm-routing.c
Normal file
2478
sound/soc/msm/msm-pcm-routing.c
Normal file
File diff suppressed because it is too large
Load Diff
124
sound/soc/msm/msm-pcm-routing.h
Normal file
124
sound/soc/msm/msm-pcm-routing.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_ROUTING_H
|
||||
#define _MSM_PCM_ROUTING_H
|
||||
#include <sound/apr_audio.h>
|
||||
|
||||
#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX"
|
||||
#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX"
|
||||
#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
|
||||
#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
|
||||
#define LPASS_BE_HDMI "HDMI"
|
||||
#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
|
||||
#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
|
||||
#define LPASS_BE_INT_FM_RX "INT_FM_RX"
|
||||
#define LPASS_BE_INT_FM_TX "INT_FM_TX"
|
||||
#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX"
|
||||
#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX"
|
||||
#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX"
|
||||
#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX"
|
||||
#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
|
||||
#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
|
||||
#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
|
||||
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
|
||||
#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
|
||||
#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
|
||||
|
||||
#define LPASS_BE_MI2S_RX "MI2S_RX"
|
||||
#define LPASS_BE_MI2S_TX "MI2S_TX"
|
||||
#define LPASS_BE_STUB_RX "STUB_RX"
|
||||
#define LPASS_BE_STUB_TX "STUB_TX"
|
||||
#define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX"
|
||||
#define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX"
|
||||
#define LPASS_BE_STUB_1_TX "STUB_1_TX"
|
||||
#define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX"
|
||||
#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
|
||||
#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
|
||||
#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
|
||||
|
||||
/* For multimedia front-ends, asm session is allocated dynamically.
|
||||
* Hence, asm session/multimedia front-end mapping has to be maintained.
|
||||
* Due to this reason, additional multimedia front-end must be placed before
|
||||
* non-multimedia front-ends.
|
||||
*/
|
||||
|
||||
enum {
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA2,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA3,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA4,
|
||||
MSM_FRONTEND_DAI_CS_VOICE,
|
||||
MSM_FRONTEND_DAI_VOIP,
|
||||
MSM_FRONTEND_DAI_AFE_RX,
|
||||
MSM_FRONTEND_DAI_AFE_TX,
|
||||
MSM_FRONTEND_DAI_VOICE_STUB,
|
||||
MSM_FRONTEND_DAI_VOLTE,
|
||||
MSM_FRONTEND_DAI_MAX,
|
||||
};
|
||||
|
||||
#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA4 + 1)
|
||||
#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA4
|
||||
|
||||
enum {
|
||||
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
|
||||
MSM_BACKEND_DAI_PRI_I2S_TX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_0_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_0_TX,
|
||||
MSM_BACKEND_DAI_HDMI_RX,
|
||||
MSM_BACKEND_DAI_INT_BT_SCO_RX,
|
||||
MSM_BACKEND_DAI_INT_BT_SCO_TX,
|
||||
MSM_BACKEND_DAI_INT_FM_RX,
|
||||
MSM_BACKEND_DAI_INT_FM_TX,
|
||||
MSM_BACKEND_DAI_AFE_PCM_RX,
|
||||
MSM_BACKEND_DAI_AFE_PCM_TX,
|
||||
MSM_BACKEND_DAI_AUXPCM_RX,
|
||||
MSM_BACKEND_DAI_AUXPCM_TX,
|
||||
MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
|
||||
MSM_BACKEND_DAI_INCALL_RECORD_RX,
|
||||
MSM_BACKEND_DAI_INCALL_RECORD_TX,
|
||||
MSM_BACKEND_DAI_MI2S_RX,
|
||||
MSM_BACKEND_DAI_MI2S_TX,
|
||||
MSM_BACKEND_DAI_SEC_I2S_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_1_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_1_TX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_4_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_4_TX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_3_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_3_TX,
|
||||
MSM_BACKEND_DAI_EXTPROC_RX,
|
||||
MSM_BACKEND_DAI_EXTPROC_TX,
|
||||
MSM_BACKEND_DAI_EXTPROC_EC_TX,
|
||||
MSM_BACKEND_DAI_SEC_AUXPCM_RX,
|
||||
MSM_BACKEND_DAI_SEC_AUXPCM_TX,
|
||||
MSM_BACKEND_DAI_MAX,
|
||||
};
|
||||
|
||||
/* dai_id: front-end ID,
|
||||
* dspst_id: DSP audio stream ID
|
||||
* stream_type: playback or capture
|
||||
*/
|
||||
void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
|
||||
int stream_type);
|
||||
void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
|
||||
int stream_type);
|
||||
|
||||
void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
|
||||
|
||||
int lpa_set_volume(unsigned volume);
|
||||
|
||||
int msm_routing_check_backend_enabled(int fedai_id);
|
||||
|
||||
int multi_ch_pcm_set_volume(unsigned volume);
|
||||
|
||||
int compressed_set_volume(unsigned volume);
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
558
sound/soc/msm/msm-pcm-voice.c
Normal file
558
sound/soc/msm/msm-pcm-voice.c
Normal file
@@ -0,0 +1,558 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "msm-pcm-voice.h"
|
||||
#include "qdsp6/q6voice.h"
|
||||
|
||||
static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX];
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED|
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 16000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
|
||||
.buffer_bytes_max = 4096 * 2,
|
||||
.period_bytes_min = 4096,
|
||||
.period_bytes_max = 4096,
|
||||
.periods_min = 2,
|
||||
.periods_max = 2,
|
||||
|
||||
.fifo_size = 0,
|
||||
};
|
||||
static int is_volte(struct msm_voice *pvolte)
|
||||
{
|
||||
if (pvolte == &voice_info[VOLTE_SESSION_INDEX])
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!prtd->playback_start)
|
||||
prtd->playback_start = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!prtd->capture_start)
|
||||
prtd->capture_start = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *voice;
|
||||
|
||||
if (!strncmp("VoLTE", substream->pcm->id, 5)) {
|
||||
voice = &voice_info[VOLTE_SESSION_INDEX];
|
||||
pr_debug("%s: Open VoLTE Substream Id=%s\n",
|
||||
__func__, substream->pcm->id);
|
||||
} else {
|
||||
voice = &voice_info[VOICE_SESSION_INDEX];
|
||||
pr_debug("%s: Open VOICE Substream Id=%s\n",
|
||||
__func__, substream->pcm->id);
|
||||
}
|
||||
mutex_lock(&voice->lock);
|
||||
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
voice->playback_substream = substream;
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
voice->capture_substream = substream;
|
||||
|
||||
voice->instance++;
|
||||
pr_debug("%s: Instance = %d, Stream ID = %s\n",
|
||||
__func__ , voice->instance, substream->pcm->id);
|
||||
runtime->private_data = voice;
|
||||
|
||||
mutex_unlock(&voice->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (prtd->playback_start)
|
||||
prtd->playback_start = 0;
|
||||
|
||||
prtd->playback_substream = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (prtd->capture_start)
|
||||
prtd->capture_start = 0;
|
||||
prtd->capture_substream = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
uint16_t session_id = 0;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
|
||||
prtd->instance--;
|
||||
if (!prtd->playback_start && !prtd->capture_start) {
|
||||
pr_debug("end voice call\n");
|
||||
if (is_volte(prtd))
|
||||
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
|
||||
else
|
||||
session_id = voc_get_session_id(VOICE_SESSION_NAME);
|
||||
voc_end_voice_call(session_id);
|
||||
}
|
||||
mutex_unlock(&prtd->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
uint16_t session_id = 0;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
|
||||
if (prtd->playback_start && prtd->capture_start) {
|
||||
if (is_volte(prtd))
|
||||
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
|
||||
else
|
||||
session_id = voc_get_session_id(VOICE_SESSION_NAME);
|
||||
voc_start_voice_call(session_id);
|
||||
}
|
||||
mutex_unlock(&prtd->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
||||
pr_debug("%s: Voice\n", __func__);
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_voice *prtd = runtime->private_data;
|
||||
uint16_t session_id = 0;
|
||||
|
||||
pr_debug("%s: cmd = %d\n", __func__, cmd);
|
||||
if (is_volte(prtd))
|
||||
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
|
||||
else
|
||||
session_id = voc_get_session_id(VOICE_SESSION_NAME);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("Start & Stop Voice call not handled in Trigger.\n");
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: resume call session_id = %d\n", __func__,
|
||||
session_id);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
if (prtd->playback_start && prtd->capture_start)
|
||||
voc_resume_voice_call(session_id);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("%s: pause call session_id=%d\n",
|
||||
__func__, session_id);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (prtd->playback_start)
|
||||
prtd->playback_start = 0;
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (prtd->capture_start)
|
||||
prtd->capture_start = 0;
|
||||
}
|
||||
voc_standby_voice_call(session_id);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_voice_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int volume = ucontrol->value.integer.value[0];
|
||||
pr_debug("%s: volume: %d\n", __func__, volume);
|
||||
voc_set_rx_vol_index(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
RX_PATH, volume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int volume = ucontrol->value.integer.value[0];
|
||||
pr_debug("%s: volume: %d\n", __func__, volume);
|
||||
voc_set_rx_vol_index(voc_get_session_id(VOLTE_SESSION_NAME),
|
||||
RX_PATH, volume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int mute = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: mute=%d\n", __func__, mute);
|
||||
|
||||
voc_set_tx_mute(voc_get_session_id(VOICE_SESSION_NAME), TX_PATH, mute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int mute = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: mute=%d\n", __func__, mute);
|
||||
|
||||
voc_set_tx_mute(voc_get_session_id(VOLTE_SESSION_NAME), TX_PATH, mute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_rx_device_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_rx_device_mute(voc_get_session_id(VOICE_SESSION_NAME));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int mute = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: mute=%d\n", __func__, mute);
|
||||
|
||||
voc_set_rx_device_mute(voc_get_session_id(VOICE_SESSION_NAME), mute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_rx_device_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_rx_device_mute(voc_get_session_id(VOLTE_SESSION_NAME));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_volte_rx_device_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int mute = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: mute=%d\n", __func__, mute);
|
||||
|
||||
voc_set_rx_device_mute(voc_get_session_id(VOLTE_SESSION_NAME), mute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char const *tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
|
||||
static const struct soc_enum msm_tty_mode_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(4, tty_mode),
|
||||
};
|
||||
|
||||
static int msm_voice_tty_mode_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_tty_mode(voc_get_session_id(VOICE_SESSION_NAME));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_tty_mode_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int tty_mode = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: tty_mode=%d\n", __func__, tty_mode);
|
||||
|
||||
voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_voice_widevoice_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int wv_enable = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: wv enable=%d\n", __func__, wv_enable);
|
||||
|
||||
voc_set_widevoice_enable(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
wv_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_widevoice_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_widevoice_enable(voc_get_session_id(VOICE_SESSION_NAME));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int st_enable = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: st enable=%d\n", __func__, st_enable);
|
||||
|
||||
voc_set_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
MODULE_ID_VOICE_MODULE_ST, st_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_slowtalk_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
MODULE_ID_VOICE_MODULE_ST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_fens_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int fens_enable = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("%s: fens enable=%d\n", __func__, fens_enable);
|
||||
|
||||
voc_set_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
MODULE_ID_VOICE_MODULE_FENS, fens_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_voice_fens_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] =
|
||||
voc_get_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
|
||||
MODULE_ID_VOICE_MODULE_FENS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new msm_voice_controls[] = {
|
||||
SOC_SINGLE_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_voice_rx_device_mute_get,
|
||||
msm_voice_rx_device_mute_put),
|
||||
SOC_SINGLE_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_voice_mute_get, msm_voice_mute_put),
|
||||
SOC_SINGLE_EXT("Voice Rx Volume", SND_SOC_NOPM, 0, 5, 0,
|
||||
msm_voice_volume_get, msm_voice_volume_put),
|
||||
SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get,
|
||||
msm_voice_tty_mode_put),
|
||||
SOC_SINGLE_EXT("Widevoice Enable", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_voice_widevoice_get, msm_voice_widevoice_put),
|
||||
SOC_SINGLE_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_voice_slowtalk_get, msm_voice_slowtalk_put),
|
||||
SOC_SINGLE_EXT("FENS Enable", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_voice_fens_get, msm_voice_fens_put),
|
||||
SOC_SINGLE_EXT("VoLTE Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_volte_rx_device_mute_get,
|
||||
msm_volte_rx_device_mute_put),
|
||||
SOC_SINGLE_EXT("VoLTE Tx Mute", SND_SOC_NOPM, 0, 1, 0,
|
||||
msm_volte_mute_get, msm_volte_mute_put),
|
||||
SOC_SINGLE_EXT("VoLTE Rx Volume", SND_SOC_NOPM, 0, 5, 0,
|
||||
msm_volte_volume_get, msm_volte_volume_put),
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
};
|
||||
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_voice_probe(struct snd_soc_platform *platform)
|
||||
{
|
||||
snd_soc_add_platform_controls(platform, msm_voice_controls,
|
||||
ARRAY_SIZE(msm_voice_controls));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
.probe = msm_pcm_voice_probe,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-voice",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
memset(&voice_info, 0, sizeof(voice_info));
|
||||
mutex_init(&voice_info[VOICE_SESSION_INDEX].lock);
|
||||
mutex_init(&voice_info[VOLTE_SESSION_INDEX].lock);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Voice PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
37
sound/soc/msm/msm-pcm-voice.h
Normal file
37
sound/soc/msm/msm-pcm-voice.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_VOICE_H
|
||||
#define _MSM_PCM_VOICE_H
|
||||
#include <sound/apr_audio.h>
|
||||
|
||||
enum {
|
||||
VOICE_SESSION_INDEX,
|
||||
VOLTE_SESSION_INDEX,
|
||||
VOICE_SESSION_INDEX_MAX,
|
||||
};
|
||||
|
||||
struct msm_voice {
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
int instance;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
|
||||
int playback_start;
|
||||
int capture_start;
|
||||
};
|
||||
|
||||
#endif /*_MSM_PCM_VOICE_H*/
|
||||
1169
sound/soc/msm/msm-pcm-voip.c
Normal file
1169
sound/soc/msm/msm-pcm-voip.c
Normal file
File diff suppressed because it is too large
Load Diff
646
sound/soc/msm/msm-pcm.c
Normal file
646
sound/soc/msm/msm-pcm.c
Normal file
@@ -0,0 +1,646 @@
|
||||
/* sound/soc/msm/msm-pcm.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "msm-pcm.h"
|
||||
|
||||
#define MAX_DATA_SIZE 496
|
||||
#define AUDPP_ALSA_DECODER (-1)
|
||||
|
||||
#define DB_TABLE_INDEX (50)
|
||||
|
||||
#define audio_send_queue_recbs(prtd, cmd, len) \
|
||||
msm_adsp_write(prtd->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
|
||||
#define audio_send_queue_rec(prtd, cmd, len) \
|
||||
msm_adsp_write(prtd->audrec, QDSP_uPAudRecCmdQueue, cmd, len)
|
||||
|
||||
int intcnt;
|
||||
|
||||
struct audio_frame {
|
||||
uint16_t count_low;
|
||||
uint16_t count_high;
|
||||
uint16_t bytes;
|
||||
uint16_t unknown;
|
||||
unsigned char samples[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Table contains dB to raw value mapping */
|
||||
static const unsigned decoder_db_table[] = {
|
||||
|
||||
31 , /* -50 dB */
|
||||
35 , 39 , 44 , 50 , 56 ,
|
||||
63 , 70 , 79 , 89 , 99 ,
|
||||
112 , 125 , 141 , 158 , 177 ,
|
||||
199 , 223 , 251 , 281 , 316 ,
|
||||
354 , 398 , 446 , 501 , 562 ,
|
||||
630 , 707 , 794 , 891 , 999 ,
|
||||
1122 , 1258 , 1412 , 1584 , 1778 ,
|
||||
1995 , 2238 , 2511 , 2818 , 3162 ,
|
||||
3548 , 3981 , 4466 , 5011 , 5623 ,
|
||||
6309 , 7079 , 7943 , 8912 , 10000 ,
|
||||
11220 , 12589 , 14125 , 15848 , 17782 ,
|
||||
19952 , 22387 , 25118 , 28183 , 31622 ,
|
||||
35481 , 39810 , 44668 , 50118 , 56234 ,
|
||||
63095 , 70794 , 79432 , 89125 , 100000 ,
|
||||
112201 , 125892 , 141253 , 158489 , 177827 ,
|
||||
199526 , 223872 , 251188 , 281838 , 316227 ,
|
||||
354813 , 398107 , 446683 , 501187 , 562341 ,
|
||||
630957 , 707945 , 794328 , 891250 , 1000000 ,
|
||||
1122018 , 1258925 , 1412537 , 1584893 , 1778279 ,
|
||||
1995262 , 2238721 , 2511886 , 2818382 , 3162277 ,
|
||||
3548133 /* 51 dB */
|
||||
|
||||
};
|
||||
|
||||
static unsigned compute_db_raw(int db)
|
||||
{
|
||||
unsigned reg_val = 0; /* Computed result for correspondent db */
|
||||
/* Check if the given db is out of range */
|
||||
if (db <= MIN_DB)
|
||||
return 0;
|
||||
else if (db > MAX_DB)
|
||||
db = MAX_DB; /* If db is too high then set to max */
|
||||
reg_val = decoder_db_table[DB_TABLE_INDEX+db];
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
int msm_audio_volume_update(unsigned id,
|
||||
int volume, int pan)
|
||||
{
|
||||
unsigned vol_raw;
|
||||
|
||||
vol_raw = compute_db_raw(volume);
|
||||
printk(KERN_INFO "volume: %8x vol_raw: %8x \n", volume, vol_raw);
|
||||
return audpp_set_volume_and_pan(id, vol_raw, pan);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_audio_volume_update);
|
||||
|
||||
void alsa_dsp_event(void *data, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
struct buffer *frame;
|
||||
unsigned long flag;
|
||||
|
||||
switch (id) {
|
||||
case AUDPP_MSG_STATUS_MSG:
|
||||
break;
|
||||
case AUDPP_MSG_SPA_BANDS:
|
||||
break;
|
||||
case AUDPP_MSG_HOST_PCM_INTF_MSG:{
|
||||
unsigned id = msg[2];
|
||||
unsigned idx = msg[3] - 1;
|
||||
if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
|
||||
printk(KERN_ERR "bogus id\n");
|
||||
break;
|
||||
}
|
||||
if (idx > 1) {
|
||||
printk(KERN_ERR "bogus buffer idx\n");
|
||||
break;
|
||||
}
|
||||
/* Update with actual sent buffer size */
|
||||
if (prtd->out[idx].used != BUF_INVALID_LEN)
|
||||
prtd->pcm_irq_pos += prtd->out[idx].used;
|
||||
|
||||
if (prtd->pcm_irq_pos > prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = prtd->pcm_count;
|
||||
|
||||
if (prtd->ops->playback)
|
||||
prtd->ops->playback(prtd);
|
||||
|
||||
if (prtd->mmap_flag)
|
||||
break;
|
||||
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
if (prtd->running) {
|
||||
prtd->out[idx].used = 0;
|
||||
frame = prtd->out + prtd->out_tail;
|
||||
if (frame->used) {
|
||||
alsa_dsp_send_buffer(prtd,
|
||||
prtd->out_tail,
|
||||
frame->used);
|
||||
prtd->out_tail ^= 1;
|
||||
} else {
|
||||
prtd->out_needed++;
|
||||
}
|
||||
wake_up(&the_locks.write_wait);
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_PCMDMAMISSED:
|
||||
pr_info("alsa_dsp_event: PCMDMAMISSED %d\n", msg[0]);
|
||||
prtd->eos_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
prtd->out_needed = 0;
|
||||
prtd->running = 1;
|
||||
audio_dsp_out_enable(prtd, 1);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
prtd->running = 0;
|
||||
} else {
|
||||
printk(KERN_ERR "alsa_dsp_event:CFG_MSG=%d\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
case EVENT_MSG_ID:
|
||||
printk(KERN_INFO"alsa_dsp_event: arm9 event\n");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "alsa_dsp_event: UNKNOWN (%d)\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
void alsa_audpre_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
uint16_t msg[MAX_DATA_SIZE/2];
|
||||
|
||||
if (len > MAX_DATA_SIZE) {
|
||||
printk(KERN_ERR"audpre: event too large(%d bytes)\n", len);
|
||||
return;
|
||||
}
|
||||
getevent(msg, len);
|
||||
|
||||
switch (id) {
|
||||
case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
|
||||
break;
|
||||
case AUDPREPROC_MSG_ERROR_MSG_ID:
|
||||
printk(KERN_ERR "audpre: err_index %d\n", msg[0]);
|
||||
break;
|
||||
case EVENT_MSG_ID:
|
||||
printk(KERN_INFO"audpre: arm9 event\n");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "audpre: unknown event %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
void audrec_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
unsigned long flag;
|
||||
uint16_t msg[MAX_DATA_SIZE/2];
|
||||
|
||||
if (len > MAX_DATA_SIZE) {
|
||||
printk(KERN_ERR"audrec: event/msg too large(%d bytes)\n", len);
|
||||
return;
|
||||
}
|
||||
getevent(msg, len);
|
||||
|
||||
switch (id) {
|
||||
case AUDREC_MSG_CMD_CFG_DONE_MSG:
|
||||
if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
|
||||
if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA)
|
||||
audrec_encoder_config(prtd);
|
||||
else
|
||||
prtd->running = 0;
|
||||
}
|
||||
break;
|
||||
case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG:{
|
||||
prtd->running = 1;
|
||||
break;
|
||||
}
|
||||
case AUDREC_MSG_FATAL_ERR_MSG:
|
||||
printk(KERN_ERR "audrec: ERROR %x\n", msg[0]);
|
||||
break;
|
||||
case AUDREC_MSG_PACKET_READY_MSG:
|
||||
alsa_get_dsp_frames(prtd);
|
||||
++intcnt;
|
||||
if (prtd->channel_mode == 1) {
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
|
||||
if (prtd->ops->capture)
|
||||
prtd->ops->capture(prtd);
|
||||
} else if ((prtd->channel_mode == 0) && (intcnt % 2 == 0)) {
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
if (prtd->ops->capture)
|
||||
prtd->ops->capture(prtd);
|
||||
}
|
||||
break;
|
||||
case EVENT_MSG_ID:
|
||||
printk(KERN_INFO"audrec: arm9 event\n");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "audrec: unknown event %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
struct msm_adsp_ops aud_pre_adsp_ops = {
|
||||
.event = alsa_audpre_dsp_event,
|
||||
};
|
||||
|
||||
struct msm_adsp_ops aud_rec_adsp_ops = {
|
||||
.event = audrec_dsp_event,
|
||||
};
|
||||
|
||||
int alsa_adsp_configure(struct msm_audio *prtd)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prtd->data = prtd->playback_substream->dma_buffer.area;
|
||||
prtd->phys = prtd->playback_substream->dma_buffer.addr;
|
||||
}
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
prtd->data = prtd->capture_substream->dma_buffer.area;
|
||||
prtd->phys = prtd->capture_substream->dma_buffer.addr;
|
||||
}
|
||||
if (!prtd->data) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = audmgr_open(&prtd->audmgr);
|
||||
if (ret)
|
||||
goto err2;
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prtd->out_buffer_size = PLAYBACK_DMASZ;
|
||||
prtd->out_sample_rate = 44100;
|
||||
prtd->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
|
||||
prtd->out_weight = 100;
|
||||
|
||||
prtd->out[0].data = prtd->data + 0;
|
||||
prtd->out[0].addr = prtd->phys + 0;
|
||||
prtd->out[0].size = BUFSZ;
|
||||
prtd->out[1].data = prtd->data + BUFSZ;
|
||||
prtd->out[1].addr = prtd->phys + BUFSZ;
|
||||
prtd->out[1].size = BUFSZ;
|
||||
}
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
prtd->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_44100;
|
||||
prtd->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_44100;
|
||||
prtd->channel_mode = AUDREC_CMD_STEREO_MODE_STEREO;
|
||||
prtd->buffer_size = STEREO_DATA_SIZE;
|
||||
prtd->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
|
||||
prtd->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
|
||||
prtd->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
|
||||
prtd->iir_cfg.cmd_id =
|
||||
AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
|
||||
|
||||
ret = msm_adsp_get("AUDPREPROCTASK",
|
||||
&prtd->audpre, &aud_pre_adsp_ops, prtd);
|
||||
if (ret)
|
||||
goto err3;
|
||||
ret = msm_adsp_get("AUDRECTASK",
|
||||
&prtd->audrec, &aud_rec_adsp_ops, prtd);
|
||||
if (ret) {
|
||||
msm_adsp_put(prtd->audpre);
|
||||
goto err3;
|
||||
}
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->in_head = 0;
|
||||
prtd->in_tail = 0;
|
||||
prtd->in_count = 0;
|
||||
for (i = 0; i < FRAME_NUM; i++) {
|
||||
prtd->in[i].size = 0;
|
||||
prtd->in[i].read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
audmgr_close(&prtd->audmgr);
|
||||
|
||||
err2:
|
||||
prtd->data = NULL;
|
||||
err1:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_adsp_configure);
|
||||
|
||||
int alsa_audio_configure(struct msm_audio *prtd)
|
||||
{
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
/* refuse to start if we're not ready with first buffer */
|
||||
if (!prtd->out[0].used)
|
||||
return -EIO;
|
||||
|
||||
cfg.tx_rate = 0;
|
||||
cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
|
||||
cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_PCM;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
rc = audmgr_enable(&prtd->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (audpp_enable(AUDPP_ALSA_DECODER, alsa_dsp_event, prtd)) {
|
||||
printk(KERN_ERR "audio: audpp_enable() failed\n");
|
||||
audmgr_disable(&prtd->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
prtd->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audio_configure);
|
||||
|
||||
ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
unsigned long flag;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&the_locks.write_lock);
|
||||
while (count > 0) {
|
||||
frame = prtd->out + prtd->out_head;
|
||||
rc = wait_event_interruptible(the_locks.write_wait,
|
||||
(frame->used == 0)
|
||||
|| (prtd->stopped));
|
||||
if (rc < 0)
|
||||
break;
|
||||
if (prtd->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
xfer = count > frame->size ? frame->size : count;
|
||||
if (copy_from_user(frame->data, buf, xfer)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
frame->used = xfer;
|
||||
prtd->out_head ^= 1;
|
||||
count -= xfer;
|
||||
buf += xfer;
|
||||
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
frame = prtd->out + prtd->out_tail;
|
||||
if (frame->used && prtd->out_needed) {
|
||||
alsa_dsp_send_buffer(prtd, prtd->out_tail,
|
||||
frame->used);
|
||||
prtd->out_tail ^= 1;
|
||||
prtd->out_needed--;
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
}
|
||||
mutex_unlock(&the_locks.write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_send_buffer);
|
||||
|
||||
int alsa_audio_disable(struct msm_audio *prtd)
|
||||
{
|
||||
if (prtd->enabled) {
|
||||
mutex_lock(&the_locks.lock);
|
||||
prtd->enabled = 0;
|
||||
audio_dsp_out_enable(prtd, 0);
|
||||
wake_up(&the_locks.write_wait);
|
||||
audpp_disable(AUDPP_ALSA_DECODER, prtd);
|
||||
audmgr_disable(&prtd->audmgr);
|
||||
prtd->out_needed = 0;
|
||||
mutex_unlock(&the_locks.lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audio_disable);
|
||||
|
||||
int alsa_audrec_disable(struct msm_audio *prtd)
|
||||
{
|
||||
if (prtd->enabled) {
|
||||
mutex_lock(&the_locks.lock);
|
||||
prtd->enabled = 0;
|
||||
alsa_rec_dsp_enable(prtd, 0);
|
||||
wake_up(&the_locks.read_wait);
|
||||
msm_adsp_disable(prtd->audpre);
|
||||
msm_adsp_disable(prtd->audrec);
|
||||
audmgr_disable(&prtd->audmgr);
|
||||
prtd->out_needed = 0;
|
||||
prtd->opened = 0;
|
||||
mutex_unlock(&the_locks.lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audrec_disable);
|
||||
|
||||
static int audio_dsp_read_buffer(struct msm_audio *prtd, uint32_t read_cnt)
|
||||
{
|
||||
audrec_cmd_packet_ext_ptr cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
|
||||
/* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
|
||||
cmd.type = AUDREC_CMD_TYPE_0;
|
||||
cmd.curr_rec_count_msw = read_cnt >> 16;
|
||||
cmd.curr_rec_count_lsw = read_cnt;
|
||||
|
||||
return audio_send_queue_recbs(prtd, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int audrec_encoder_config(struct msm_audio *prtd)
|
||||
{
|
||||
audrec_cmd_arec0param_cfg cmd;
|
||||
uint16_t *data = (void *)prtd->data;
|
||||
unsigned n;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
|
||||
cmd.ptr_to_extpkt_buffer_msw = prtd->phys >> 16;
|
||||
cmd.ptr_to_extpkt_buffer_lsw = prtd->phys;
|
||||
cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
|
||||
cmd.samp_rate_index = prtd->samp_rate_index;
|
||||
/* 0 for mono, 1 for stereo */
|
||||
cmd.stereo_mode = prtd->channel_mode;
|
||||
cmd.rec_quality = 0x1C00;
|
||||
|
||||
/* prepare buffer pointers:
|
||||
* Mono: 1024 samples + 4 halfword header
|
||||
* Stereo: 2048 samples + 4 halfword header
|
||||
*/
|
||||
|
||||
for (n = 0; n < FRAME_NUM; n++) {
|
||||
prtd->in[n].data = data + 4;
|
||||
data += (4 + (prtd->channel_mode ? 2048 : 1024));
|
||||
}
|
||||
|
||||
return audio_send_queue_rec(prtd, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int audio_dsp_out_enable(struct msm_audio *prtd, int yes)
|
||||
{
|
||||
audpp_cmd_pcm_intf cmd;
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
|
||||
cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
|
||||
cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
|
||||
cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
|
||||
|
||||
if (yes) {
|
||||
cmd.write_buf1LSW = prtd->out[0].addr;
|
||||
cmd.write_buf1MSW = prtd->out[0].addr >> 16;
|
||||
cmd.write_buf1_len = 0;
|
||||
cmd.write_buf2LSW = prtd->out[1].addr;
|
||||
cmd.write_buf2MSW = prtd->out[1].addr >> 16;
|
||||
cmd.write_buf2_len = prtd->out[1].used;
|
||||
cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
|
||||
cmd.weight_decoder_to_rx = prtd->out_weight;
|
||||
cmd.weight_arm_to_rx = 1;
|
||||
cmd.partition_number_arm_to_dsp = 0;
|
||||
cmd.sample_rate = prtd->out_sample_rate;
|
||||
cmd.channel_mode = prtd->out_channel_mode;
|
||||
}
|
||||
return audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
unsigned long flag;
|
||||
void *data;
|
||||
uint32_t index;
|
||||
uint32_t size;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&the_locks.read_lock);
|
||||
while (count > 0) {
|
||||
rc = wait_event_interruptible(the_locks.read_wait,
|
||||
(prtd->in_count > 0)
|
||||
|| prtd->stopped);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
if (prtd->stopped) {
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
index = prtd->in_tail;
|
||||
data = (uint8_t *) prtd->in[index].data;
|
||||
size = prtd->in[index].size;
|
||||
if (count >= size) {
|
||||
if (copy_to_user(buf, data, size)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
if (index != prtd->in_tail) {
|
||||
/* overrun: data is invalid, we need to retry */
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock,
|
||||
flag);
|
||||
continue;
|
||||
}
|
||||
prtd->in[index].size = 0;
|
||||
prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
|
||||
prtd->in_count--;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
count -= size;
|
||||
buf += size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&the_locks.read_lock);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_buffer_read);
|
||||
|
||||
int alsa_dsp_send_buffer(struct msm_audio *prtd,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
audpp_cmd_pcm_intf_send_buffer cmd;
|
||||
cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
|
||||
cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
|
||||
cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
|
||||
cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
|
||||
cmd.dsp_to_arm_buf_id = 0;
|
||||
cmd.arm_to_dsp_buf_id = idx + 1;
|
||||
cmd.arm_to_dsp_buf_len = len;
|
||||
return audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int alsa_rec_dsp_enable(struct msm_audio *prtd, int enable)
|
||||
{
|
||||
audrec_cmd_cfg cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_CFG;
|
||||
cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
|
||||
cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | prtd->type);
|
||||
cmd.type_1 = 0;
|
||||
|
||||
return audio_send_queue_rec(prtd, &cmd, sizeof(cmd));
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_rec_dsp_enable);
|
||||
|
||||
void alsa_get_dsp_frames(struct msm_audio *prtd)
|
||||
{
|
||||
struct audio_frame *frame;
|
||||
uint32_t index = 0;
|
||||
unsigned long flag;
|
||||
|
||||
if (prtd->type == AUDREC_CMD_TYPE_0_INDEX_WAV) {
|
||||
index = prtd->in_head;
|
||||
|
||||
frame =
|
||||
(void *)(((char *)prtd->in[index].data) - sizeof(*frame));
|
||||
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
prtd->in[index].size = frame->bytes;
|
||||
|
||||
prtd->in_head = (prtd->in_head + 1) & (FRAME_NUM - 1);
|
||||
|
||||
/* If overflow, move the tail index foward. */
|
||||
if (prtd->in_head == prtd->in_tail)
|
||||
prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
|
||||
else
|
||||
prtd->in_count++;
|
||||
|
||||
audio_dsp_read_buffer(prtd, prtd->dsp_cnt++);
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
|
||||
wake_up(&the_locks.read_wait);
|
||||
} else {
|
||||
/* TODO AAC not supported yet. */
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_get_dsp_frames);
|
||||
204
sound/soc/msm/msm-pcm.h
Normal file
204
sound/soc/msm/msm-pcm.h
Normal file
@@ -0,0 +1,204 @@
|
||||
/* sound/soc/msm/msm-pcm.h
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
|
||||
|
||||
#include <mach/qdsp5/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audreccmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audrecmsg.h>
|
||||
#include <mach/qdsp5/qdsp5audpreproccmdi.h>
|
||||
#include <mach/qdsp5/qdsp5audpreprocmsg.h>
|
||||
|
||||
#include <../arch/arm/mach-msm/qdsp5/adsp.h>
|
||||
#include <../arch/arm/mach-msm/qdsp5/audmgr.h>
|
||||
|
||||
|
||||
#define FRAME_NUM (8)
|
||||
#define FRAME_SIZE (2052 * 2)
|
||||
#define MONO_DATA_SIZE (2048)
|
||||
#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
|
||||
#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
|
||||
|
||||
#define BUFSZ (960 * 5)
|
||||
#define PLAYBACK_DMASZ (BUFSZ * 2)
|
||||
|
||||
#define MSM_PLAYBACK_DEFAULT_VOLUME 0 /* 0dB */
|
||||
#define MSM_PLAYBACK_DEFAULT_PAN 0
|
||||
|
||||
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
#define USE_CHANNELS_MIN 1
|
||||
#define USE_CHANNELS_MAX 2
|
||||
/* Support unconventional sample rates 12000, 24000 as well */
|
||||
#define USE_RATE \
|
||||
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
|
||||
#define USE_RATE_MIN 8000
|
||||
#define USE_RATE_MAX 48000
|
||||
#define MAX_BUFFER_PLAYBACK_SIZE \
|
||||
(4800*4)
|
||||
/* 2048 frames (Mono), 1024 frames (Stereo) */
|
||||
#define CAPTURE_SIZE 4096
|
||||
#define MAX_BUFFER_CAPTURE_SIZE (4096*4)
|
||||
#define MAX_PERIOD_SIZE BUFSZ
|
||||
#define USE_PERIODS_MAX 1024
|
||||
#define USE_PERIODS_MIN 1
|
||||
|
||||
|
||||
#define MAX_DB (16)
|
||||
#define MIN_DB (-50)
|
||||
#define PCMPLAYBACK_DECODERID 5
|
||||
|
||||
/* 0xFFFFFFFF Indicates not to be used for audio data copy */
|
||||
#define BUF_INVALID_LEN 0xFFFFFFFF
|
||||
|
||||
extern int copy_count;
|
||||
extern int intcnt;
|
||||
|
||||
struct msm_volume {
|
||||
bool update;
|
||||
int volume; /* Volume parameter, in dB Scale */
|
||||
int pan;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used;
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct buffer_rec {
|
||||
void *data;
|
||||
unsigned int size;
|
||||
unsigned int read;
|
||||
unsigned int addr;
|
||||
};
|
||||
|
||||
struct audio_locks {
|
||||
struct mutex lock;
|
||||
struct mutex write_lock;
|
||||
struct mutex read_lock;
|
||||
spinlock_t read_dsp_lock;
|
||||
spinlock_t write_dsp_lock;
|
||||
spinlock_t mixer_lock;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t eos_wait;
|
||||
};
|
||||
|
||||
extern struct audio_locks the_locks;
|
||||
|
||||
struct msm_audio_event_callbacks {
|
||||
/* event is called from interrupt context when a message
|
||||
* arrives from the DSP.
|
||||
*/
|
||||
void (*playback)(void *);
|
||||
void (*capture)(void *);
|
||||
};
|
||||
|
||||
|
||||
struct msm_audio {
|
||||
struct buffer out[2];
|
||||
struct buffer_rec in[8];
|
||||
|
||||
uint8_t out_head;
|
||||
uint8_t out_tail;
|
||||
uint8_t out_needed; /* number of buffers the dsp is waiting for */
|
||||
atomic_t out_bytes;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t out_sample_rate;
|
||||
uint32_t out_channel_mode;
|
||||
uint32_t out_weight;
|
||||
uint32_t out_buffer_size;
|
||||
|
||||
struct audmgr audmgr;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_buf_pos; /* position in buffer */
|
||||
|
||||
struct msm_adsp_module *audpre;
|
||||
struct msm_adsp_module *audrec;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
|
||||
uint32_t type; /* 0 for PCM ,1 for AAC */
|
||||
uint32_t dsp_cnt;
|
||||
uint32_t in_head; /* next buffer dsp will write */
|
||||
uint32_t in_tail; /* next buffer read() will read */
|
||||
uint32_t in_count; /* number of buffers available to read() */
|
||||
|
||||
unsigned short samp_rate_index;
|
||||
|
||||
/* audpre settings */
|
||||
audpreproc_cmd_cfg_agc_params tx_agc_cfg;
|
||||
audpreproc_cmd_cfg_ns_params ns_cfg;
|
||||
/* For different sample rate, the coeff might be different. *
|
||||
* All the coeff should be passed from user space */
|
||||
audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg;
|
||||
|
||||
struct msm_audio_event_callbacks *ops;
|
||||
|
||||
int dir;
|
||||
int opened;
|
||||
int enabled;
|
||||
int running;
|
||||
int stopped; /* set when stopped, cleared on flush */
|
||||
int eos_ack;
|
||||
int mmap_flag;
|
||||
int period;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* platform data */
|
||||
extern int alsa_dsp_send_buffer(struct msm_audio *prtd,
|
||||
unsigned idx, unsigned len);
|
||||
extern int audio_dsp_out_enable(struct msm_audio *prtd, int yes);
|
||||
extern struct snd_soc_platform_driver msm_soc_platform;
|
||||
|
||||
int audrec_encoder_config(struct msm_audio *prtd);
|
||||
extern void alsa_get_dsp_frames(struct msm_audio *prtd);
|
||||
extern int alsa_rec_dsp_enable(struct msm_audio *prtd, int enable);
|
||||
extern int alsa_audrec_disable(struct msm_audio *prtd);
|
||||
extern int alsa_audio_configure(struct msm_audio *prtd);
|
||||
extern int alsa_audio_disable(struct msm_audio *prtd);
|
||||
extern int alsa_adsp_configure(struct msm_audio *prtd);
|
||||
extern int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
|
||||
size_t count, loff_t *pos);
|
||||
ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
|
||||
size_t count, loff_t *pos);
|
||||
int msm_audio_volume_update(unsigned id,
|
||||
int volume, int pan);
|
||||
extern struct audio_locks the_locks;
|
||||
extern struct msm_volume msm_vol_ctl;
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
610
sound/soc/msm/msm-voip.c
Normal file
610
sound/soc/msm/msm-voip.c
Normal file
@@ -0,0 +1,610 @@
|
||||
/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/q6asm.h>
|
||||
#include <sound/apr_audio.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
#include <mach/qdsp6v2/q6voice.h>
|
||||
#include <mach/qdsp6v2/audio_dev_ctl.h>
|
||||
#include "msm_audio_mvs.h"
|
||||
|
||||
|
||||
static struct audio_voip_info_type audio_voip_info;
|
||||
static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt,
|
||||
uint32_t pkt_len,
|
||||
void *private_data);
|
||||
static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt,
|
||||
uint32_t *pkt_len,
|
||||
void *private_data);
|
||||
|
||||
struct msm_audio_mvs_frame {
|
||||
uint32_t frame_type;
|
||||
uint32_t len;
|
||||
uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE];
|
||||
};
|
||||
|
||||
struct audio_mvs_buf_node {
|
||||
struct list_head list;
|
||||
struct msm_audio_mvs_frame frame;
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN,
|
||||
.period_bytes_min = MVS_MAX_VOC_PKT_SIZE,
|
||||
.period_bytes_max = MVS_MAX_VOC_PKT_SIZE,
|
||||
.periods_min = VOIP_MAX_Q_LEN,
|
||||
.periods_max = VOIP_MAX_Q_LEN,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
audio->playback_start = 1;
|
||||
else
|
||||
audio->capture_start = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
audio->playback_start = 0;
|
||||
else
|
||||
audio->capture_start = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
struct audio_mvs_release_msg release_msg;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
memset(&release_msg, 0, sizeof(release_msg));
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
audio->instance--;
|
||||
wake_up(&audio->out_wait);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
audio->playback_state = AUDIO_MVS_CLOSED;
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
audio->capture_state = AUDIO_MVS_CLOSED;
|
||||
if (!audio->instance) {
|
||||
/* Release MVS. */
|
||||
release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
|
||||
/* Derigstering the callbacks with voice driver */
|
||||
voice_register_mvs_cb(NULL, NULL, audio);
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
voice_register_mvs_cb(audio_mvs_process_ul_pkt,
|
||||
NULL, audio);
|
||||
} else {
|
||||
voice_register_mvs_cb(NULL, audio_mvs_process_dl_pkt,
|
||||
audio);
|
||||
}
|
||||
|
||||
mutex_unlock(&audio->lock);
|
||||
|
||||
wake_unlock(&audio->suspend_lock);
|
||||
pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE);
|
||||
/* Release the IO buffers. */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
audio->in_write = 0;
|
||||
audio->in_read = 0;
|
||||
memset(audio->in[0].voc_pkt, 0,
|
||||
MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
|
||||
audio->playback_substream = NULL;
|
||||
} else {
|
||||
audio->out_write = 0;
|
||||
audio->out_read = 0;
|
||||
memset(audio->out[0].voc_pkt, 0,
|
||||
MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
|
||||
audio->capture_substream = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
mutex_lock(&audio->lock);
|
||||
|
||||
if (audio->playback_substream == NULL ||
|
||||
audio->capture_substream == NULL) {
|
||||
if (substream->stream ==
|
||||
SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
audio->playback_substream = substream;
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
audio_voip_info.in_read = 0;
|
||||
audio_voip_info.in_write = 0;
|
||||
if (audio->playback_state < AUDIO_MVS_OPENED)
|
||||
audio->playback_state = AUDIO_MVS_OPENED;
|
||||
} else if (substream->stream ==
|
||||
SNDRV_PCM_STREAM_CAPTURE) {
|
||||
audio->capture_substream = substream;
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
audio_voip_info.out_read = 0;
|
||||
audio_voip_info.out_write = 0;
|
||||
if (audio->capture_state < AUDIO_MVS_OPENED)
|
||||
audio->capture_state = AUDIO_MVS_OPENED;
|
||||
}
|
||||
} else {
|
||||
ret = -EPERM;
|
||||
goto err;
|
||||
}
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s:snd_pcm_hw_constraint_integer failed\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
audio->instance++;
|
||||
|
||||
err:
|
||||
mutex_unlock(&audio->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 0;
|
||||
int count = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
uint32_t index;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
rc = wait_event_timeout(audio->in_wait,
|
||||
(audio->in_write - audio->in_read <= VOIP_MAX_Q_LEN-1),
|
||||
1 * HZ);
|
||||
if (rc < 0) {
|
||||
pr_debug("%s: write was interrupted\n", __func__);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
if (audio->playback_state == AUDIO_MVS_ENABLED) {
|
||||
index = audio->in_write % VOIP_MAX_Q_LEN;
|
||||
count = frames_to_bytes(runtime, frames);
|
||||
if (count == MVS_MAX_VOC_PKT_SIZE) {
|
||||
pr_debug("%s:write index = %d\n", __func__, index);
|
||||
rc = copy_from_user(audio->in[index].voc_pkt, buf,
|
||||
count);
|
||||
if (!rc) {
|
||||
audio->in[index].len = count;
|
||||
audio->in_write++;
|
||||
} else {
|
||||
pr_debug("%s:Copy from user returned %d\n",
|
||||
__func__, rc);
|
||||
rc = -EFAULT;
|
||||
}
|
||||
} else
|
||||
rc = -ENOMEM;
|
||||
|
||||
} else {
|
||||
pr_debug("%s:Write performed in invalid state %d\n",
|
||||
__func__, audio->playback_state);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff,
|
||||
void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 0;
|
||||
int count = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
uint32_t index = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
/* Ensure the driver has been enabled. */
|
||||
if (audio->capture_state != AUDIO_MVS_ENABLED) {
|
||||
pr_debug("%s:Read performed in invalid state %d\n",
|
||||
__func__, audio->capture_state);
|
||||
return -EPERM;
|
||||
}
|
||||
rc = wait_event_timeout(audio->out_wait,
|
||||
((audio->out_read < audio->out_write) ||
|
||||
(audio->capture_state == AUDIO_MVS_CLOSING) ||
|
||||
(audio->capture_state == AUDIO_MVS_CLOSED)),
|
||||
1 * HZ);
|
||||
|
||||
if (rc < 0) {
|
||||
pr_debug("%s: Read was interrupted\n", __func__);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
if (audio->capture_state == AUDIO_MVS_CLOSING
|
||||
|| audio->capture_state == AUDIO_MVS_CLOSED) {
|
||||
pr_debug("%s:EBUSY STATE\n", __func__);
|
||||
rc = -EBUSY;
|
||||
} else {
|
||||
count = frames_to_bytes(runtime, frames);
|
||||
index = audio->out_read % VOIP_MAX_Q_LEN;
|
||||
pr_debug("%s:index=%d\n", __func__, index);
|
||||
if (audio->out[index].len <= count) {
|
||||
rc = copy_to_user(buf,
|
||||
audio->out[index].voc_pkt,
|
||||
audio->out[index].len);
|
||||
if (rc) {
|
||||
pr_debug("%s:Copy to user %d\n",
|
||||
__func__, rc);
|
||||
rc = -EFAULT;
|
||||
} else
|
||||
audio->out_read++;
|
||||
} else {
|
||||
pr_debug("%s:returning ENOMEM\n", __func__);
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Capture path */
|
||||
static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt,
|
||||
uint32_t pkt_len,
|
||||
void *private_data)
|
||||
{
|
||||
struct audio_voip_info_type *audio = private_data;
|
||||
uint32_t index;
|
||||
static int i;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (audio->capture_substream == NULL)
|
||||
return;
|
||||
index = audio->out_write % VOIP_MAX_Q_LEN;
|
||||
memcpy(audio->out[index].voc_pkt, voc_pkt, pkt_len);
|
||||
audio->out[index].len = pkt_len;
|
||||
audio->out_write++;
|
||||
wake_up(&audio->out_wait);
|
||||
i++;
|
||||
if (audio->capture_start) {
|
||||
audio->pcm_capture_irq_pos += audio->pcm_count;
|
||||
if (!(i % 2))
|
||||
snd_pcm_period_elapsed(audio->capture_substream);
|
||||
}
|
||||
}
|
||||
|
||||
/* Playback path */
|
||||
static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt,
|
||||
uint32_t *pkt_len,
|
||||
void *private_data)
|
||||
{
|
||||
struct audio_voip_info_type *audio = private_data;
|
||||
uint32_t index;
|
||||
static int i;
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (audio->playback_substream == NULL)
|
||||
return;
|
||||
if ((audio->in_write - audio->in_read >= 0)
|
||||
&& (audio->playback_start)) {
|
||||
index = audio->in_read % VOIP_MAX_Q_LEN;
|
||||
*pkt_len = audio->pcm_count;
|
||||
memcpy(voc_pkt, audio->in[index].voc_pkt, *pkt_len);
|
||||
audio->in_read++;
|
||||
wake_up(&audio->in_wait);
|
||||
i++;
|
||||
audio->pcm_playback_irq_pos += audio->pcm_count;
|
||||
if (!(i%2))
|
||||
snd_pcm_period_elapsed(audio->playback_substream);
|
||||
pr_debug("%s:read_index=%d\n", __func__, index);
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct audio_voip_info_type *prtd = &audio_voip_info;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_playback_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
pr_debug("%s:prtd->pcm_playback_size:%d\n",
|
||||
__func__, prtd->pcm_playback_size);
|
||||
pr_debug("%s:prtd->pcm_count:%d\n", __func__, prtd->pcm_count);
|
||||
|
||||
mutex_lock(&prtd->prepare_lock);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (prtd->playback_state == AUDIO_MVS_ENABLED)
|
||||
goto enabled;
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (prtd->capture_state == AUDIO_MVS_ENABLED)
|
||||
goto enabled;
|
||||
}
|
||||
|
||||
pr_debug("%s:Register cbs with voice driver check audio_mvs_driver\n",
|
||||
__func__);
|
||||
if (prtd->instance == 2) {
|
||||
voice_register_mvs_cb(audio_mvs_process_ul_pkt,
|
||||
audio_mvs_process_dl_pkt,
|
||||
prtd);
|
||||
} else {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
voice_register_mvs_cb(NULL,
|
||||
audio_mvs_process_dl_pkt,
|
||||
prtd);
|
||||
} else {
|
||||
voice_register_mvs_cb(audio_mvs_process_ul_pkt,
|
||||
NULL,
|
||||
prtd);
|
||||
}
|
||||
}
|
||||
|
||||
enabled:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prtd->playback_state = AUDIO_MVS_ENABLED;
|
||||
prtd->pcm_playback_irq_pos = 0;
|
||||
prtd->pcm_playback_buf_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
prtd->capture_state = AUDIO_MVS_ENABLED;
|
||||
prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_capture_irq_pos = 0;
|
||||
prtd->pcm_capture_buf_pos = 0;
|
||||
}
|
||||
mutex_unlock(&prtd->prepare_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
|
||||
if (audio->pcm_playback_irq_pos >= audio->pcm_playback_size)
|
||||
audio->pcm_playback_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (audio->pcm_playback_irq_pos));
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_voip_info_type *audio = &audio_voip_info;
|
||||
|
||||
if (audio->pcm_capture_irq_pos >= audio->pcm_capture_size)
|
||||
audio->pcm_capture_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (audio->pcm_capture_irq_pos));
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_uframes_t ret = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_pointer(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_pointer(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_mvs_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
|
||||
};
|
||||
|
||||
static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int i, ret, offset = 0;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct snd_dma_buffer *dma_buffer = NULL;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_mvs_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_mvs_pcm_ops);
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
if (!substream)
|
||||
return -ENOMEM;
|
||||
|
||||
dma_buffer = &substream->dma_buffer;
|
||||
dma_buffer->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buffer->dev.dev = card->dev;
|
||||
dma_buffer->private_data = NULL;
|
||||
dma_buffer->area = dma_alloc_coherent(card->dev,
|
||||
(MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN),
|
||||
&dma_buffer->addr, GFP_KERNEL);
|
||||
if (!dma_buffer->area) {
|
||||
pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dma_buffer->bytes = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN;
|
||||
memset(dma_buffer->area, 0, MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
|
||||
audio_voip_info.in_read = 0;
|
||||
audio_voip_info.in_write = 0;
|
||||
audio_voip_info.out_read = 0;
|
||||
audio_voip_info.out_write = 0;
|
||||
for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
|
||||
audio_voip_info.in[i].voc_pkt =
|
||||
dma_buffer->area + offset;
|
||||
offset = offset + MVS_MAX_VOC_PKT_SIZE;
|
||||
}
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
if (!substream)
|
||||
return -ENOMEM;
|
||||
|
||||
dma_buffer = &substream->dma_buffer;
|
||||
dma_buffer->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buffer->dev.dev = card->dev;
|
||||
dma_buffer->private_data = NULL;
|
||||
dma_buffer->area = dma_alloc_coherent(card->dev,
|
||||
(MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN),
|
||||
&dma_buffer->addr, GFP_KERNEL);
|
||||
if (!dma_buffer->area) {
|
||||
pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(dma_buffer->area, 0, MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
|
||||
dma_buffer->bytes = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN;
|
||||
for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
|
||||
audio_voip_info.out[i].voc_pkt =
|
||||
dma_buffer->area + offset;
|
||||
offset = offset + MVS_MAX_VOC_PKT_SIZE;
|
||||
}
|
||||
audio_voip_info.playback_substream = NULL;
|
||||
audio_voip_info.capture_substream = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_pcm_free_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver msm_mvs_soc_platform = {
|
||||
.ops = &msm_mvs_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
.pcm_free = msm_pcm_free_buffers,
|
||||
};
|
||||
EXPORT_SYMBOL(msm_mvs_soc_platform);
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_mvs_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-mvs-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_mvs_soc_platform_init(void)
|
||||
{
|
||||
memset(&audio_voip_info, 0, sizeof(audio_voip_info));
|
||||
mutex_init(&audio_voip_info.lock);
|
||||
mutex_init(&audio_voip_info.prepare_lock);
|
||||
init_waitqueue_head(&audio_voip_info.out_wait);
|
||||
init_waitqueue_head(&audio_voip_info.in_wait);
|
||||
wake_lock_init(&audio_voip_info.suspend_lock, WAKE_LOCK_SUSPEND,
|
||||
"audio_mvs_suspend");
|
||||
pm_qos_add_request(&audio_voip_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_mvs_soc_platform_init);
|
||||
|
||||
static void __exit msm_mvs_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_mvs_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MVS PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
424
sound/soc/msm/msm7201.c
Normal file
424
sound/soc/msm/msm7201.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/* linux/sound/soc/msm/msm7201.c
|
||||
*
|
||||
* Copyright (c) 2008-2009, 2011, 2012 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "msm-pcm.h"
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
|
||||
static struct msm_rpc_endpoint *snd_ep;
|
||||
static uint32_t snd_mute_ear_mute;
|
||||
static uint32_t snd_mute_mic_mute;
|
||||
|
||||
struct msm_snd_rpc_ids {
|
||||
unsigned long prog;
|
||||
unsigned long vers;
|
||||
unsigned long vers2;
|
||||
unsigned long rpc_set_snd_device;
|
||||
unsigned long rpc_set_device_vol;
|
||||
int device;
|
||||
};
|
||||
|
||||
static struct msm_snd_rpc_ids snd_rpc_ids;
|
||||
|
||||
static struct platform_device *msm_audio_snd_device;
|
||||
|
||||
static int snd_msm_volume_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1; /* Volume Param, in dB */
|
||||
uinfo->value.integer.min = MIN_DB;
|
||||
uinfo->value.integer.max = MAX_DB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msm_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
spin_lock_irq(&the_locks.mixer_lock);
|
||||
ucontrol->value.integer.value[0] = msm_vol_ctl.volume;
|
||||
spin_unlock_irq(&the_locks.mixer_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int change;
|
||||
int volume;
|
||||
|
||||
volume = ucontrol->value.integer.value[0];
|
||||
spin_lock_irq(&the_locks.mixer_lock);
|
||||
change = (msm_vol_ctl.volume != volume);
|
||||
if (change) {
|
||||
msm_vol_ctl.volume = volume;
|
||||
msm_audio_volume_update(PCMPLAYBACK_DECODERID,
|
||||
msm_vol_ctl.volume, msm_vol_ctl.pan);
|
||||
}
|
||||
spin_unlock_irq(&the_locks.mixer_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msm_device_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 3; /* Device */
|
||||
|
||||
/*
|
||||
* The number of devices supported is 26 (0 to 25)
|
||||
*/
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 36;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msm_device_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = (uint32_t)snd_rpc_ids.device;
|
||||
ucontrol->value.integer.value[1] = snd_mute_ear_mute;
|
||||
ucontrol->value.integer.value[2] = snd_mute_mic_mute;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_snd_init_rpc_ids(void)
|
||||
{
|
||||
snd_rpc_ids.prog = 0x30000002;
|
||||
snd_rpc_ids.vers = 0x00020001;
|
||||
snd_rpc_ids.vers2 = 0x00030001;
|
||||
/*
|
||||
* The magic number 2 corresponds to the rpc call
|
||||
* index for snd_set_device
|
||||
*/
|
||||
snd_rpc_ids.rpc_set_snd_device = 2;
|
||||
snd_rpc_ids.rpc_set_device_vol = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_snd_rpc_connect(void)
|
||||
{
|
||||
if (snd_ep) {
|
||||
printk(KERN_INFO "%s: snd_ep already connected\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize rpc ids */
|
||||
if (msm_snd_init_rpc_ids()) {
|
||||
printk(KERN_ERR "%s: snd rpc ids initialization failed\n"
|
||||
, __func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog,
|
||||
snd_rpc_ids.vers, 0);
|
||||
if (IS_ERR(snd_ep)) {
|
||||
printk(KERN_DEBUG "%s failed (compatible VERS = %ld) \
|
||||
trying again with another API\n",
|
||||
__func__, snd_rpc_ids.vers);
|
||||
snd_ep =
|
||||
msm_rpc_connect_compatible(snd_rpc_ids.prog,
|
||||
snd_rpc_ids.vers2, 0);
|
||||
}
|
||||
if (IS_ERR(snd_ep)) {
|
||||
printk(KERN_ERR "%s: failed (compatible VERS = %ld)\n",
|
||||
__func__, snd_rpc_ids.vers2);
|
||||
snd_ep = NULL;
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_snd_rpc_close(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (IS_ERR(snd_ep)) {
|
||||
printk(KERN_ERR "%s: snd handle unavailable, rc = %ld\n",
|
||||
__func__, PTR_ERR(snd_ep));
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
rc = msm_rpc_close(snd_ep);
|
||||
snd_ep = NULL;
|
||||
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
|
||||
__func__, rc);
|
||||
return -EAGAIN;
|
||||
} else
|
||||
printk(KERN_INFO "rpc close success\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int snd_msm_device_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_start_req {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t rpc_snd_device;
|
||||
uint32_t snd_mute_ear_mute;
|
||||
uint32_t snd_mute_mic_mute;
|
||||
uint32_t callback_ptr;
|
||||
uint32_t client_data;
|
||||
} req;
|
||||
|
||||
snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
|
||||
|
||||
if (ucontrol->value.integer.value[1] > 1)
|
||||
ucontrol->value.integer.value[1] = 1;
|
||||
if (ucontrol->value.integer.value[2] > 1)
|
||||
ucontrol->value.integer.value[2] = 1;
|
||||
|
||||
req.hdr.type = 0;
|
||||
req.hdr.rpc_vers = 2;
|
||||
|
||||
req.rpc_snd_device = cpu_to_be32(snd_rpc_ids.device);
|
||||
req.snd_mute_ear_mute =
|
||||
cpu_to_be32((int)ucontrol->value.integer.value[1]);
|
||||
req.snd_mute_mic_mute =
|
||||
cpu_to_be32((int)ucontrol->value.integer.value[2]);
|
||||
req.callback_ptr = -1;
|
||||
req.client_data = cpu_to_be32(0);
|
||||
|
||||
req.hdr.prog = snd_rpc_ids.prog;
|
||||
req.hdr.vers = snd_rpc_ids.vers;
|
||||
|
||||
rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_snd_device ,
|
||||
&req, sizeof(req), 5 * HZ);
|
||||
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
|
||||
__func__, rc);
|
||||
} else {
|
||||
printk(KERN_INFO "snd device connected\n");
|
||||
snd_mute_ear_mute = ucontrol->value.integer.value[1];
|
||||
snd_mute_mic_mute = ucontrol->value.integer.value[2];
|
||||
printk(KERN_ERR "%s: snd_mute_ear_mute =%d, snd_mute_mic_mute = %d\n",
|
||||
__func__, snd_mute_ear_mute, snd_mute_mic_mute);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int snd_msm_device_vol_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 2; /* Device/Volume */
|
||||
|
||||
/*
|
||||
* The number of devices supported is 37 (0 to 36)
|
||||
*/
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 36;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msm_device_vol_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_vol_req {
|
||||
struct rpc_request_hdr hdr;
|
||||
uint32_t device;
|
||||
uint32_t method;
|
||||
uint32_t volume;
|
||||
uint32_t cb_func;
|
||||
uint32_t client_data;
|
||||
} req;
|
||||
|
||||
snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
|
||||
|
||||
if ((ucontrol->value.integer.value[1] < 0) ||
|
||||
(ucontrol->value.integer.value[1] > 6)) {
|
||||
pr_err("Device volume should be in range of 1 to 6\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ucontrol->value.integer.value[0] > 36) ||
|
||||
(ucontrol->value.integer.value[0] < 0)) {
|
||||
pr_err("Device range supported is 0 to 36\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req.device = cpu_to_be32((int)ucontrol->value.integer.value[0]);
|
||||
req.method = cpu_to_be32(0);
|
||||
req.volume = cpu_to_be32((int)ucontrol->value.integer.value[1]);
|
||||
req.cb_func = -1;
|
||||
req.client_data = cpu_to_be32(0);
|
||||
|
||||
rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_device_vol ,
|
||||
&req, sizeof(req), 5 * HZ);
|
||||
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
|
||||
__func__, rc);
|
||||
} else {
|
||||
printk(KERN_ERR "%s: device [%d] volume set to [%d]\n",
|
||||
__func__, (int)ucontrol->value.integer.value[0],
|
||||
(int)ucontrol->value.integer.value[1]);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Supported range -50dB to 18dB */
|
||||
static const DECLARE_TLV_DB_LINEAR(db_scale_linear, -5000, 1800);
|
||||
|
||||
#define MSM_EXT(xname, xindex, fp_info, fp_get, fp_put, addr) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
||||
.name = xname, .index = xindex, \
|
||||
.info = fp_info,\
|
||||
.get = fp_get, .put = fp_put, \
|
||||
.private_value = addr, \
|
||||
}
|
||||
|
||||
#define MSM_EXT_TLV(xname, xindex, fp_info, fp_get, fp_put, addr, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
SNDRV_CTL_ELEM_ACCESS_READWRITE), \
|
||||
.name = xname, .index = xindex, \
|
||||
.info = fp_info,\
|
||||
.get = fp_get, .put = fp_put, .tlv.p = tlv_array, \
|
||||
.private_value = addr, \
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_msm_controls[] = {
|
||||
MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
|
||||
snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
|
||||
MSM_EXT("device", 0, snd_msm_device_info, snd_msm_device_get, \
|
||||
snd_msm_device_put, 0),
|
||||
MSM_EXT("Device Volume", 0, snd_msm_device_vol_info, NULL, \
|
||||
snd_msm_device_vol_put, 0),
|
||||
};
|
||||
|
||||
static int msm_new_mixer(struct snd_soc_codec *codec)
|
||||
{
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
pr_err("msm_soc: ALSA MSM Mixer Setting\n");
|
||||
strcpy(codec->card->snd_card->mixername, "MSM Mixer");
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
|
||||
err = snd_ctl_add(codec->card->snd_card,
|
||||
snd_ctl_new1(&snd_msm_controls[idx], NULL));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_soc_dai_init(
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
||||
mutex_init(&the_locks.lock);
|
||||
mutex_init(&the_locks.write_lock);
|
||||
mutex_init(&the_locks.read_lock);
|
||||
spin_lock_init(&the_locks.read_dsp_lock);
|
||||
spin_lock_init(&the_locks.write_dsp_lock);
|
||||
spin_lock_init(&the_locks.mixer_lock);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
msm_vol_ctl.volume = MSM_PLAYBACK_DEFAULT_VOLUME;
|
||||
msm_vol_ctl.pan = MSM_PLAYBACK_DEFAULT_PAN;
|
||||
|
||||
ret = msm_new_mixer(codec);
|
||||
if (ret < 0) {
|
||||
pr_err("msm_soc: ALSA MSM Mixer Fail\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link msm_dai[] = {
|
||||
{
|
||||
.name = "MSM Primary I2S",
|
||||
.stream_name = "DSP 1",
|
||||
.cpu_dai_name = "msm-cpu-dai.0",
|
||||
.platform_name = "msm-dsp-audio.0",
|
||||
.codec_name = "msm-codec-dai.0",
|
||||
.codec_dai_name = "msm-codec-dai",
|
||||
.init = &msm_soc_dai_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_card_msm = {
|
||||
.name = "msm-audio",
|
||||
.dai_link = msm_dai,
|
||||
.num_links = ARRAY_SIZE(msm_dai),
|
||||
};
|
||||
|
||||
static int __init msm_audio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!msm_audio_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
|
||||
ret = platform_device_add(msm_audio_snd_device);
|
||||
if (ret) {
|
||||
platform_device_put(msm_audio_snd_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = msm_snd_rpc_connect();
|
||||
snd_mute_ear_mute = 0;
|
||||
snd_mute_mic_mute = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit msm_audio_exit(void)
|
||||
{
|
||||
msm_snd_rpc_close();
|
||||
platform_device_unregister(msm_audio_snd_device);
|
||||
}
|
||||
|
||||
module_init(msm_audio_init);
|
||||
module_exit(msm_audio_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
699
sound/soc/msm/msm7k-pcm.c
Normal file
699
sound/soc/msm/msm7k-pcm.c
Normal file
@@ -0,0 +1,699 @@
|
||||
/* linux/sound/soc/msm/msm7k-pcm.c
|
||||
*
|
||||
* Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "msm-pcm.h"
|
||||
|
||||
#define SND_DRIVER "snd_msm"
|
||||
#define MAX_PCM_DEVICES SNDRV_CARDS
|
||||
#define MAX_PCM_SUBSTREAMS 1
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
int copy_count;
|
||||
|
||||
struct audio_locks the_locks;
|
||||
EXPORT_SYMBOL(the_locks);
|
||||
struct msm_volume msm_vol_ctl;
|
||||
EXPORT_SYMBOL(msm_vol_ctl);
|
||||
|
||||
|
||||
static unsigned convert_dsp_samp_index(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 48000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_48000;
|
||||
case 44100:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_44100;
|
||||
case 32000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_32000;
|
||||
case 24000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_24000;
|
||||
case 22050:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_22050;
|
||||
case 16000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_16000;
|
||||
case 12000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_12000;
|
||||
case 11025:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_11025;
|
||||
case 8000:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_8000;
|
||||
default:
|
||||
return AUDREC_CMD_SAMP_RATE_INDX_44100;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned convert_samp_rate(unsigned hz)
|
||||
{
|
||||
switch (hz) {
|
||||
case 48000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_48000;
|
||||
case 44100:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_44100;
|
||||
case 32000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_32000;
|
||||
case 24000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_24000;
|
||||
case 22050:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_22050;
|
||||
case 16000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_16000;
|
||||
case 12000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_12000;
|
||||
case 11025:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_11025;
|
||||
case 8000:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_8000;
|
||||
default:
|
||||
return RPC_AUD_DEF_SAMPLE_RATE_44100;
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_playback_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.buffer_bytes_max = 4800 * 2,
|
||||
.period_bytes_min = 4800,
|
||||
.period_bytes_max = 4800,
|
||||
.periods_min = 2,
|
||||
.periods_max = 2,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_capture_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.buffer_bytes_max = MAX_BUFFER_CAPTURE_SIZE,
|
||||
.period_bytes_min = CAPTURE_SIZE,
|
||||
.period_bytes_max = CAPTURE_SIZE,
|
||||
.periods_min = USE_PERIODS_MIN,
|
||||
.periods_max = USE_PERIODS_MAX,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void msm_pcm_enqueue_data(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
unsigned int period_size;
|
||||
|
||||
pr_debug("prtd->out_tail =%d mmap_flag=%d\n",
|
||||
prtd->out_tail, prtd->mmap_flag);
|
||||
period_size = snd_pcm_lib_period_bytes(substream);
|
||||
alsa_dsp_send_buffer(prtd, prtd->out_tail, period_size);
|
||||
prtd->out_tail ^= 1;
|
||||
++copy_count;
|
||||
prtd->period++;
|
||||
if (unlikely(prtd->period >= runtime->periods))
|
||||
prtd->period = 0;
|
||||
|
||||
}
|
||||
|
||||
static void playback_event_handler(void *data)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
snd_pcm_period_elapsed(prtd->playback_substream);
|
||||
if (prtd->mmap_flag) {
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return;
|
||||
if (!prtd->stopped)
|
||||
msm_pcm_enqueue_data(prtd->playback_substream);
|
||||
else
|
||||
prtd->out_needed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_event_handler(void *data)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
snd_pcm_period_elapsed(prtd->capture_substream);
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
prtd->pcm_buf_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->out_sample_rate = runtime->rate;
|
||||
prtd->out_channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled | !(prtd->mmap_flag))
|
||||
return 0;
|
||||
|
||||
prtd->data = substream->dma_buffer.area;
|
||||
prtd->phys = substream->dma_buffer.addr;
|
||||
prtd->out[0].data = prtd->data + 0;
|
||||
prtd->out[0].addr = prtd->phys + 0;
|
||||
prtd->out[0].size = BUFSZ;
|
||||
prtd->out[1].data = prtd->data + BUFSZ;
|
||||
prtd->out[1].addr = prtd->phys + BUFSZ;
|
||||
prtd->out[1].size = BUFSZ;
|
||||
|
||||
prtd->out[0].used = prtd->pcm_count;
|
||||
prtd->out[1].used = prtd->pcm_count;
|
||||
|
||||
mutex_lock(&the_locks.lock);
|
||||
alsa_audio_configure(prtd);
|
||||
mutex_unlock(&the_locks.lock);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct audmgr_config cfg;
|
||||
int rc;
|
||||
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
prtd->pcm_buf_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = convert_samp_rate(runtime->rate);
|
||||
prtd->samp_rate_index = convert_dsp_samp_index(runtime->rate);
|
||||
prtd->channel_mode = (runtime->channels - 1);
|
||||
prtd->buffer_size = prtd->channel_mode ? STEREO_DATA_SIZE : \
|
||||
MONO_DATA_SIZE;
|
||||
|
||||
if (prtd->enabled == 1)
|
||||
return 0;
|
||||
|
||||
prtd->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
|
||||
|
||||
cfg.tx_rate = convert_samp_rate(runtime->rate);
|
||||
cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
|
||||
cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
|
||||
cfg.codec = RPC_AUD_DEF_CODEC_PCM;
|
||||
cfg.snd_method = RPC_SND_METHOD_MIDI;
|
||||
|
||||
rc = audmgr_enable(&prtd->audmgr, &cfg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (msm_adsp_enable(prtd->audpre)) {
|
||||
audmgr_disable(&prtd->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (msm_adsp_enable(prtd->audrec)) {
|
||||
msm_adsp_disable(prtd->audpre);
|
||||
audmgr_disable(&prtd->audmgr);
|
||||
return -ENODEV;
|
||||
}
|
||||
prtd->enabled = 1;
|
||||
alsa_rec_dsp_enable(prtd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
unsigned long flag = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
|| !prtd->mmap_flag)
|
||||
break;
|
||||
if (!prtd->out_needed) {
|
||||
prtd->stopped = 0;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
if (prtd->running == 1) {
|
||||
if (prtd->stopped == 1) {
|
||||
prtd->stopped = 0;
|
||||
prtd->period = 0;
|
||||
if (prtd->pcm_irq_pos == 0) {
|
||||
prtd->out_tail = 0;
|
||||
msm_pcm_enqueue_data(
|
||||
prtd->playback_substream);
|
||||
prtd->out_needed--;
|
||||
} else {
|
||||
prtd->out_tail = 1;
|
||||
msm_pcm_enqueue_data(
|
||||
prtd->playback_substream);
|
||||
prtd->out_needed--;
|
||||
}
|
||||
if (prtd->out_needed) {
|
||||
prtd->out_tail ^= 1;
|
||||
msm_pcm_enqueue_data(
|
||||
prtd->playback_substream);
|
||||
prtd->out_needed--;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
|| !prtd->mmap_flag)
|
||||
break;
|
||||
prtd->stopped = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos == prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 0, rc1 = 0, rc2 = 0;
|
||||
int fbytes = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
int monofbytes = 0;
|
||||
char *bufferp = NULL;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
monofbytes = fbytes / 2;
|
||||
if (runtime->channels == 2) {
|
||||
rc = alsa_buffer_read(prtd, buf, fbytes, NULL);
|
||||
} else {
|
||||
bufferp = buf;
|
||||
rc1 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
|
||||
bufferp = buf + monofbytes ;
|
||||
rc2 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
|
||||
rc = rc1 + rc2;
|
||||
}
|
||||
prtd->pcm_buf_pos += fbytes;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
alsa_audrec_disable(prtd);
|
||||
audmgr_close(&prtd->audmgr);
|
||||
msm_adsp_put(prtd->audrec);
|
||||
msm_adsp_put(prtd->audpre);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msm_audio_event_callbacks snd_msm_audio_ops = {
|
||||
.playback = playback_event_handler,
|
||||
.capture = capture_event_handler,
|
||||
};
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw = msm_pcm_playback_hardware;
|
||||
prtd->dir = SNDRV_PCM_STREAM_PLAYBACK;
|
||||
prtd->playback_substream = substream;
|
||||
prtd->eos_ack = 0;
|
||||
ret = msm_audio_volume_update(PCMPLAYBACK_DECODERID,
|
||||
msm_vol_ctl.volume, msm_vol_ctl.pan);
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_capture_hardware;
|
||||
prtd->dir = SNDRV_PCM_STREAM_CAPTURE;
|
||||
prtd->capture_substream = substream;
|
||||
}
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prtd->ops = &snd_msm_audio_ops;
|
||||
prtd->out[0].used = BUF_INVALID_LEN;
|
||||
prtd->out_head = 1; /* point to second buffer on startup */
|
||||
runtime->private_data = prtd;
|
||||
|
||||
ret = alsa_adsp_configure(prtd);
|
||||
if (ret)
|
||||
goto out;
|
||||
copy_count = 0;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
kfree(prtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int rc = 1;
|
||||
int fbytes = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
rc = alsa_send_buffer(prtd, buf, fbytes, NULL);
|
||||
++copy_count;
|
||||
if (copy_count == 1) {
|
||||
mutex_lock(&the_locks.lock);
|
||||
alsa_audio_configure(prtd);
|
||||
mutex_unlock(&the_locks.lock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
/* pcm dmamiss message is sent continously
|
||||
* when decoder is starved so no race
|
||||
* condition concern
|
||||
*/
|
||||
if (prtd->enabled)
|
||||
rc = wait_event_interruptible(the_locks.eos_wait,
|
||||
prtd->eos_ack);
|
||||
|
||||
alsa_audio_disable(prtd);
|
||||
audmgr_close(&prtd->audmgr);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_uframes_t ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_pointer(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_pointer(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
if (substream->pcm->device & 1) {
|
||||
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
|
||||
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
|
||||
}
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
prtd->out_head = 0; /* point to First buffer on startup */
|
||||
prtd->mmap_flag = 1;
|
||||
runtime->dma_bytes = snd_pcm_lib_period_bytes(substream)*2;
|
||||
dma_mmap_coherent(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size;
|
||||
if (!stream)
|
||||
size = PLAYBACK_DMASZ;
|
||||
else
|
||||
size = CAPTURE_DMASZ;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
|
||||
|
||||
ret = pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
msm_pcm_free_dma_buffers(pcm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
.pcm_free = msm_pcm_free_dma_buffers,
|
||||
};
|
||||
EXPORT_SYMBOL(msm_soc_platform);
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-dsp-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
149
sound/soc/msm/msm7kv2-dai.c
Normal file
149
sound/soc/msm/msm7kv2-dai.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/* sound/soc/msm/msm-dai.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Derived from msm-pcm.c and msm7201.c.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/slab.h>
|
||||
#include "msm7kv2-pcm.h"
|
||||
|
||||
static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
|
||||
{
|
||||
.name = "msm-codec-dai",
|
||||
.playback = {
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
|
||||
{
|
||||
.name = "msm-cpu-dai",
|
||||
.playback = {
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_msm = {
|
||||
.compress_type = SND_SOC_FLAT_COMPRESSION,
|
||||
};
|
||||
|
||||
static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
|
||||
msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
|
||||
}
|
||||
|
||||
static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __devinit int asoc_msm_cpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
|
||||
}
|
||||
|
||||
static int __devexit asoc_msm_cpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver asoc_msm_codec_driver = {
|
||||
.probe = asoc_msm_codec_probe,
|
||||
.remove = __devexit_p(asoc_msm_codec_remove),
|
||||
.driver = {
|
||||
.name = "msm-codec-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver asoc_msm_cpu_driver = {
|
||||
.probe = asoc_msm_cpu_probe,
|
||||
.remove = __devexit_p(asoc_msm_cpu_remove),
|
||||
.driver = {
|
||||
.name = "msm-cpu-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_codec_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_msm_codec_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_codec_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_msm_codec_driver);
|
||||
}
|
||||
|
||||
static int __init msm_cpu_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_msm_cpu_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_cpu_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_msm_cpu_driver);
|
||||
}
|
||||
|
||||
module_init(msm_codec_dai_init);
|
||||
module_exit(msm_codec_dai_exit);
|
||||
module_init(msm_cpu_dai_init);
|
||||
module_exit(msm_cpu_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
633
sound/soc/msm/msm7kv2-dsp.c
Normal file
633
sound/soc/msm/msm7kv2-dsp.c
Normal file
@@ -0,0 +1,633 @@
|
||||
/* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <mach/qdsp5v2/audio_dev_ctl.h>
|
||||
#include <mach/debug_mm.h>
|
||||
|
||||
#include "msm7kv2-pcm.h"
|
||||
|
||||
/* Audrec Queue command sent macro's */
|
||||
#define audrec_send_bitstreamqueue(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audrec, ((audio->queue_id & 0xFFFF0000) >> 16),\
|
||||
cmd, len)
|
||||
|
||||
#define audrec_send_audrecqueue(audio, cmd, len) \
|
||||
msm_adsp_write(audio->audrec, (audio->queue_id & 0x0000FFFF),\
|
||||
cmd, len)
|
||||
|
||||
static int alsa_dsp_read_buffer(struct msm_audio *audio,
|
||||
uint32_t read_cnt);
|
||||
static void alsa_get_dsp_frames(struct msm_audio *prtd);
|
||||
static int alsa_in_param_config(struct msm_audio *audio);
|
||||
|
||||
static int alsa_in_mem_config(struct msm_audio *audio);
|
||||
static int alsa_in_enc_config(struct msm_audio *audio, int enable);
|
||||
|
||||
int intcnt;
|
||||
struct audio_frame {
|
||||
uint16_t count_low;
|
||||
uint16_t count_high;
|
||||
uint16_t bytes;
|
||||
uint16_t unknown;
|
||||
unsigned char samples[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
void alsa_dsp_event(void *data, unsigned id, uint16_t *msg)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
struct buffer *frame;
|
||||
unsigned long flag = 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
switch (id) {
|
||||
case AUDPP_MSG_HOST_PCM_INTF_MSG: {
|
||||
unsigned id = msg[3];
|
||||
unsigned idx = msg[4] - 1;
|
||||
|
||||
MM_DBG("HOST_PCM id %d idx %d\n", id, idx);
|
||||
if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
|
||||
MM_ERR("bogus id\n");
|
||||
break;
|
||||
}
|
||||
if (idx > 1) {
|
||||
MM_ERR("bogus buffer idx\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update with actual sent buffer size */
|
||||
if (prtd->out[idx].used != BUF_INVALID_LEN)
|
||||
prtd->pcm_irq_pos += prtd->out[idx].used;
|
||||
|
||||
if (prtd->pcm_irq_pos > prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = prtd->pcm_count;
|
||||
|
||||
if (prtd->ops->playback)
|
||||
prtd->ops->playback(prtd);
|
||||
|
||||
if (prtd->mmap_flag)
|
||||
break;
|
||||
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
if (prtd->running) {
|
||||
prtd->out[idx].used = 0;
|
||||
frame = prtd->out + prtd->out_tail;
|
||||
if (frame->used) {
|
||||
alsa_dsp_send_buffer(
|
||||
prtd, prtd->out_tail, frame->used);
|
||||
/* Reset eos_ack flag to avoid stale
|
||||
* PCMDMAMISS been considered
|
||||
*/
|
||||
prtd->eos_ack = 0;
|
||||
prtd->out_tail ^= 1;
|
||||
} else {
|
||||
prtd->out_needed++;
|
||||
}
|
||||
wake_up(&the_locks.write_wait);
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
break;
|
||||
}
|
||||
case AUDPP_MSG_PCMDMAMISSED:
|
||||
MM_INFO("PCMDMAMISSED %d\n", msg[0]);
|
||||
prtd->eos_ack++;
|
||||
MM_DBG("PCMDMAMISSED Count per Buffer %d\n", prtd->eos_ack);
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case AUDPP_MSG_CFG_MSG:
|
||||
if (msg[0] == AUDPP_MSG_ENA_ENA) {
|
||||
MM_DBG("CFG_MSG ENABLE\n");
|
||||
prtd->out_needed = 0;
|
||||
prtd->running = 1;
|
||||
audpp_dsp_set_vol_pan(prtd->session_id, &prtd->vol_pan,
|
||||
POPP);
|
||||
audpp_route_stream(prtd->session_id,
|
||||
msm_snddev_route_dec(prtd->session_id));
|
||||
audio_dsp_out_enable(prtd, 1);
|
||||
} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
|
||||
MM_DBG("CFG_MSG DISABLE\n");
|
||||
prtd->running = 0;
|
||||
} else {
|
||||
MM_DBG("CFG_MSG %d?\n", msg[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MM_DBG("UNKNOWN (%d)\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
|
||||
switch (id) {
|
||||
case AUDPREPROC_ERROR_MSG: {
|
||||
struct audpreproc_err_msg *err_msg = msg;
|
||||
|
||||
MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
|
||||
err_msg->stream_id, err_msg->aud_preproc_err_idx);
|
||||
/* Error case */
|
||||
break;
|
||||
}
|
||||
case AUDPREPROC_CMD_CFG_DONE_MSG: {
|
||||
MM_DBG("CMD_CFG_DONE_MSG\n");
|
||||
break;
|
||||
}
|
||||
case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
|
||||
struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
|
||||
|
||||
MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
|
||||
0x%8x\n", enc_cfg_msg->stream_id,
|
||||
enc_cfg_msg->rec_enc_type);
|
||||
/* Encoder enable success */
|
||||
if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
|
||||
alsa_in_param_config(prtd);
|
||||
else { /* Encoder disable success */
|
||||
prtd->running = 0;
|
||||
alsa_in_record_config(prtd, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
|
||||
MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
|
||||
alsa_in_mem_config(prtd);
|
||||
break;
|
||||
}
|
||||
case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
|
||||
MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG\n");
|
||||
wake_up(&the_locks.enable_wait);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MM_DBG("Unknown Event id %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
static void audrec_dsp_event(void *data, unsigned id, size_t len,
|
||||
void (*getevent) (void *ptr, size_t len))
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
unsigned long flag = 0;
|
||||
|
||||
switch (id) {
|
||||
case AUDREC_CMD_MEM_CFG_DONE_MSG: {
|
||||
MM_DBG("AUDREC_CMD_MEM_CFG_DONE_MSG\n");
|
||||
prtd->running = 1;
|
||||
alsa_in_record_config(prtd, 1);
|
||||
break;
|
||||
}
|
||||
case AUDREC_FATAL_ERR_MSG: {
|
||||
struct audrec_fatal_err_msg fatal_err_msg;
|
||||
|
||||
getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
|
||||
MM_ERR("FATAL_ERR_MSG: err id %d\n",
|
||||
fatal_err_msg.audrec_err_id);
|
||||
/* Error stop the encoder */
|
||||
prtd->stopped = 1;
|
||||
wake_up(&the_locks.read_wait);
|
||||
break;
|
||||
}
|
||||
case AUDREC_UP_PACKET_READY_MSG: {
|
||||
struct audrec_up_pkt_ready_msg pkt_ready_msg;
|
||||
MM_DBG("AUDREC_UP_PACKET_READY_MSG\n");
|
||||
|
||||
getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
|
||||
MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
|
||||
write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
|
||||
pkt_ready_msg.audrec_packet_write_cnt_lsw, \
|
||||
pkt_ready_msg.audrec_packet_write_cnt_msw, \
|
||||
pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
|
||||
pkt_ready_msg.audrec_up_prev_read_cnt_msw);
|
||||
|
||||
alsa_get_dsp_frames(prtd);
|
||||
++intcnt;
|
||||
if (prtd->channel_mode == 1) {
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
|
||||
if (prtd->ops->capture)
|
||||
prtd->ops->capture(prtd);
|
||||
} else if ((prtd->channel_mode == 0) && (intcnt % 2 == 0)) {
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
if (prtd->ops->capture)
|
||||
prtd->ops->capture(prtd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MM_DBG("Unknown Event id %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
struct msm_adsp_ops alsa_audrec_adsp_ops = {
|
||||
.event = audrec_dsp_event,
|
||||
};
|
||||
|
||||
int alsa_audio_configure(struct msm_audio *prtd)
|
||||
{
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prtd->out_weight = 100;
|
||||
if (audpp_enable(-1, alsa_dsp_event, prtd)) {
|
||||
MM_ERR("audpp_enable() failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (audpreproc_enable(prtd->session_id,
|
||||
&audpreproc_dsp_event, prtd)) {
|
||||
MM_ERR("audpreproc_enable failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (msm_adsp_enable(prtd->audrec)) {
|
||||
MM_ERR("msm_adsp_enable(audrec) enable failed\n");
|
||||
audpreproc_disable(prtd->session_id, prtd);
|
||||
return -ENODEV;
|
||||
}
|
||||
alsa_in_enc_config(prtd, 1);
|
||||
|
||||
}
|
||||
prtd->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audio_configure);
|
||||
|
||||
ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
unsigned long flag = 0;
|
||||
const char __user *start = buf;
|
||||
struct buffer *frame;
|
||||
size_t xfer;
|
||||
int ret = 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
mutex_lock(&the_locks.write_lock);
|
||||
while (count > 0) {
|
||||
frame = prtd->out + prtd->out_head;
|
||||
ret = wait_event_interruptible(the_locks.write_wait,
|
||||
(frame->used == 0)
|
||||
|| (prtd->stopped));
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (prtd->stopped) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
xfer = count > frame->size ? frame->size : count;
|
||||
if (copy_from_user(frame->data, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
frame->used = xfer;
|
||||
prtd->out_head ^= 1;
|
||||
count -= xfer;
|
||||
buf += xfer;
|
||||
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
frame = prtd->out + prtd->out_tail;
|
||||
if (frame->used && prtd->out_needed) {
|
||||
alsa_dsp_send_buffer(prtd, prtd->out_tail,
|
||||
frame->used);
|
||||
/* Reset eos_ack flag to avoid stale
|
||||
* PCMDMAMISS been considered
|
||||
*/
|
||||
prtd->eos_ack = 0;
|
||||
prtd->out_tail ^= 1;
|
||||
prtd->out_needed--;
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
}
|
||||
mutex_unlock(&the_locks.write_lock);
|
||||
if (buf > start)
|
||||
return buf - start;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_send_buffer);
|
||||
|
||||
int alsa_audio_disable(struct msm_audio *prtd)
|
||||
{
|
||||
if (prtd->enabled) {
|
||||
MM_DBG("\n");
|
||||
mutex_lock(&the_locks.lock);
|
||||
prtd->enabled = 0;
|
||||
audio_dsp_out_enable(prtd, 0);
|
||||
wake_up(&the_locks.write_wait);
|
||||
audpp_disable(-1, prtd);
|
||||
prtd->out_needed = 0;
|
||||
mutex_unlock(&the_locks.lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audio_disable);
|
||||
|
||||
int alsa_audrec_disable(struct msm_audio *prtd)
|
||||
{
|
||||
if (prtd->enabled) {
|
||||
prtd->enabled = 0;
|
||||
alsa_in_enc_config(prtd, 0);
|
||||
wake_up(&the_locks.read_wait);
|
||||
msm_adsp_disable(prtd->audrec);
|
||||
prtd->out_needed = 0;
|
||||
audpreproc_disable(prtd->session_id, prtd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_audrec_disable);
|
||||
|
||||
static int alsa_in_enc_config(struct msm_audio *prtd, int enable)
|
||||
{
|
||||
struct audpreproc_audrec_cmd_enc_cfg cmd;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG;
|
||||
cmd.stream_id = prtd->session_id;
|
||||
|
||||
if (enable)
|
||||
cmd.audrec_enc_type = prtd->type | ENCODE_ENABLE;
|
||||
else
|
||||
cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
|
||||
return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int alsa_in_param_config(struct msm_audio *prtd)
|
||||
{
|
||||
struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
|
||||
cmd.common.stream_id = prtd->session_id;
|
||||
|
||||
cmd.aud_rec_samplerate_idx = prtd->samp_rate;
|
||||
cmd.aud_rec_stereo_mode = prtd->channel_mode;
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
|
||||
return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int alsa_in_record_config(struct msm_audio *prtd, int enable)
|
||||
{
|
||||
struct audpreproc_afe_cmd_audio_record_cfg cmd;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
|
||||
cmd.stream_id = prtd->session_id;
|
||||
if (enable)
|
||||
cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
|
||||
else
|
||||
cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
|
||||
cmd.source_mix_mask = prtd->source;
|
||||
if (prtd->session_id == 2) {
|
||||
if ((cmd.source_mix_mask &
|
||||
INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
|
||||
(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
|
||||
(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
|
||||
(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
|
||||
cmd.pipe_id = SOURCE_PIPE_1;
|
||||
}
|
||||
if (cmd.source_mix_mask &
|
||||
AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
|
||||
cmd.pipe_id |= SOURCE_PIPE_0;
|
||||
}
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int alsa_in_mem_config(struct msm_audio *prtd)
|
||||
{
|
||||
struct audrec_cmd_arecmem_cfg cmd;
|
||||
uint16_t *data = (void *) prtd->data;
|
||||
int n;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
|
||||
cmd.audrec_up_pkt_intm_count = 1;
|
||||
cmd.audrec_ext_pkt_start_addr_msw = prtd->phys >> 16;
|
||||
cmd.audrec_ext_pkt_start_addr_lsw = prtd->phys;
|
||||
cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
|
||||
|
||||
/* prepare buffer pointers:
|
||||
* Mono: 1024 samples + 4 halfword header
|
||||
* Stereo: 2048 samples + 4 halfword header
|
||||
*/
|
||||
for (n = 0; n < FRAME_NUM; n++) {
|
||||
prtd->in[n].data = data + 4;
|
||||
data += (4 + (prtd->channel_mode ? 2048 : 1024));
|
||||
MM_DBG("0x%8x\n", (int)(prtd->in[n].data - 8));
|
||||
}
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
|
||||
return audrec_send_audrecqueue(prtd, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int audio_dsp_out_enable(struct msm_audio *prtd, int yes)
|
||||
{
|
||||
struct audpp_cmd_pcm_intf cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = AUDPP_CMD_PCM_INTF;
|
||||
cmd.stream = AUDPP_CMD_POPP_STREAM;
|
||||
cmd.stream_id = prtd->session_id;
|
||||
cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
|
||||
cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
|
||||
|
||||
if (yes) {
|
||||
cmd.write_buf1LSW = prtd->out[0].addr;
|
||||
cmd.write_buf1MSW = prtd->out[0].addr >> 16;
|
||||
cmd.write_buf1_len = prtd->out[0].size;
|
||||
cmd.write_buf2LSW = prtd->out[1].addr;
|
||||
cmd.write_buf2MSW = prtd->out[1].addr >> 16;
|
||||
if (prtd->out[1].used)
|
||||
cmd.write_buf2_len = prtd->out[1].used;
|
||||
else
|
||||
cmd.write_buf2_len = prtd->out[1].size;
|
||||
cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
|
||||
cmd.weight_decoder_to_rx = prtd->out_weight;
|
||||
cmd.weight_arm_to_rx = 1;
|
||||
cmd.partition_number_arm_to_dsp = 0;
|
||||
cmd.sample_rate = prtd->out_sample_rate;
|
||||
cmd.channel_mode = prtd->out_channel_mode;
|
||||
}
|
||||
|
||||
return audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
unsigned long flag;
|
||||
void *data;
|
||||
uint32_t index;
|
||||
uint32_t size;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&the_locks.read_lock);
|
||||
while (count > 0) {
|
||||
ret = wait_event_interruptible(the_locks.read_wait,
|
||||
(prtd->in_count > 0)
|
||||
|| prtd->stopped ||
|
||||
prtd->abort);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (prtd->stopped) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prtd->abort) {
|
||||
MM_DBG(" prtd->abort !\n");
|
||||
ret = -EPERM; /* Not permitted due to abort */
|
||||
break;
|
||||
}
|
||||
|
||||
index = prtd->in_tail;
|
||||
data = (uint8_t *) prtd->in[index].data;
|
||||
size = prtd->in[index].size;
|
||||
if (count >= size) {
|
||||
if (copy_to_user(buf, data, size)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
if (index != prtd->in_tail) {
|
||||
/* overrun: data is invalid, we need to retry */
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock,
|
||||
flag);
|
||||
continue;
|
||||
}
|
||||
prtd->in[index].size = 0;
|
||||
prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
|
||||
prtd->in_count--;
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
count -= size;
|
||||
buf += size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&the_locks.read_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(alsa_buffer_read);
|
||||
|
||||
int alsa_dsp_send_buffer(struct msm_audio *prtd,
|
||||
unsigned idx, unsigned len)
|
||||
{
|
||||
struct audpp_cmd_pcm_intf_send_buffer cmd;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
cmd.cmd_id = AUDPP_CMD_PCM_INTF;
|
||||
cmd.stream = AUDPP_CMD_POPP_STREAM;
|
||||
cmd.stream_id = prtd->session_id;
|
||||
cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
|
||||
cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
|
||||
cmd.dsp_to_arm_buf_id = 0;
|
||||
cmd.arm_to_dsp_buf_id = idx + 1;
|
||||
cmd.arm_to_dsp_buf_len = len;
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
|
||||
return audpp_send_queue2(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int alsa_dsp_read_buffer(struct msm_audio *audio, uint32_t read_cnt)
|
||||
{
|
||||
struct up_audrec_packet_ext_ptr cmd;
|
||||
int i;
|
||||
unsigned short *ptrmem = (unsigned short *)&cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
|
||||
cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
|
||||
cmd.audrec_up_curr_read_count_lsw = read_cnt;
|
||||
for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
|
||||
MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
|
||||
|
||||
return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void alsa_get_dsp_frames(struct msm_audio *prtd)
|
||||
{
|
||||
struct audio_frame *frame;
|
||||
uint32_t index = 0;
|
||||
unsigned long flag;
|
||||
|
||||
if (prtd->type == ENC_TYPE_WAV) {
|
||||
index = prtd->in_head;
|
||||
|
||||
frame =
|
||||
(void *)(((char *)prtd->in[index].data) - sizeof(*frame));
|
||||
|
||||
spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
|
||||
prtd->in[index].size = frame->bytes;
|
||||
MM_DBG("frame = %08x\n", (unsigned int) frame);
|
||||
MM_DBG("prtd->in[index].size = %08x\n",
|
||||
(unsigned int) prtd->in[index].size);
|
||||
|
||||
prtd->in_head = (prtd->in_head + 1) & (FRAME_NUM - 1);
|
||||
|
||||
/* If overflow, move the tail index foward. */
|
||||
if (prtd->in_head == prtd->in_tail)
|
||||
prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
|
||||
else
|
||||
prtd->in_count++;
|
||||
|
||||
prtd->pcm_irq_pos += frame->bytes;
|
||||
alsa_dsp_read_buffer(prtd, prtd->dsp_cnt++);
|
||||
spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
|
||||
|
||||
wake_up(&the_locks.read_wait);
|
||||
}
|
||||
}
|
||||
774
sound/soc/msm/msm7kv2-pcm.c
Normal file
774
sound/soc/msm/msm7kv2-pcm.c
Normal file
@@ -0,0 +1,774 @@
|
||||
/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <linux/slab.h>
|
||||
#include "msm7kv2-pcm.h"
|
||||
#include <mach/qdsp5v2/audio_dev_ctl.h>
|
||||
#include <mach/debug_mm.h>
|
||||
|
||||
#define HOSTPCM_STREAM_ID 5
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
int copy_count;
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_playback_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MAX_BUFFER_PLAYBACK_SIZE,
|
||||
.period_bytes_min = BUFSZ,
|
||||
.period_bytes_max = BUFSZ,
|
||||
.periods_min = 2,
|
||||
.periods_max = 2,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_capture_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MAX_BUFFER_CAPTURE_SIZE,
|
||||
.period_bytes_min = 4096,
|
||||
.period_bytes_max = 4096,
|
||||
.periods_min = 4,
|
||||
.periods_max = 4,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
static void alsa_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
|
||||
void *private_data)
|
||||
{
|
||||
struct msm_audio *prtd = (struct msm_audio *) private_data;
|
||||
MM_DBG("evt_id = 0x%8x\n", evt_id);
|
||||
switch (evt_id) {
|
||||
case AUDDEV_EVT_DEV_RDY:
|
||||
MM_DBG("AUDDEV_EVT_DEV_RDY\n");
|
||||
prtd->source |= (0x1 << evt_payload->routing_id);
|
||||
if (prtd->running == 1 && prtd->enabled == 1)
|
||||
audpp_route_stream(prtd->session_id, prtd->source);
|
||||
break;
|
||||
case AUDDEV_EVT_DEV_RLS:
|
||||
MM_DBG("AUDDEV_EVT_DEV_RLS\n");
|
||||
prtd->source &= ~(0x1 << evt_payload->routing_id);
|
||||
if (prtd->running == 1 && prtd->enabled == 1)
|
||||
audpp_route_stream(prtd->session_id, prtd->source);
|
||||
break;
|
||||
case AUDDEV_EVT_STREAM_VOL_CHG:
|
||||
prtd->vol_pan.volume = evt_payload->session_vol;
|
||||
MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
|
||||
prtd->vol_pan.volume);
|
||||
if (prtd->running)
|
||||
audpp_set_volume_and_pan(prtd->session_id,
|
||||
prtd->vol_pan.volume,
|
||||
0, POPP);
|
||||
break;
|
||||
default:
|
||||
MM_DBG("Unknown Event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void alsa_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
|
||||
void *private_data)
|
||||
{
|
||||
struct msm_audio *prtd = (struct msm_audio *) private_data;
|
||||
MM_DBG("evt_id = 0x%8x\n", evt_id);
|
||||
|
||||
switch (evt_id) {
|
||||
case AUDDEV_EVT_DEV_RDY: {
|
||||
MM_DBG("AUDDEV_EVT_DEV_RDY\n");
|
||||
prtd->source |= (0x1 << evt_payload->routing_id);
|
||||
|
||||
if ((prtd->running == 1) && (prtd->enabled == 1))
|
||||
alsa_in_record_config(prtd, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
case AUDDEV_EVT_DEV_RLS: {
|
||||
MM_DBG("AUDDEV_EVT_DEV_RLS\n");
|
||||
prtd->source &= ~(0x1 << evt_payload->routing_id);
|
||||
|
||||
if (!prtd->running || !prtd->enabled)
|
||||
break;
|
||||
|
||||
/* Turn off as per source */
|
||||
if (prtd->source)
|
||||
alsa_in_record_config(prtd, 1);
|
||||
else
|
||||
/* Turn off all */
|
||||
alsa_in_record_config(prtd, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
case AUDDEV_EVT_FREQ_CHG: {
|
||||
MM_DBG("Encoder Driver got sample rate change event\n");
|
||||
MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
|
||||
MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
|
||||
MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
|
||||
if (prtd->running == 1) {
|
||||
/* Stop Recording sample rate does not match
|
||||
with device sample rate */
|
||||
if (evt_payload->freq_info.sample_rate !=
|
||||
prtd->samp_rate) {
|
||||
alsa_in_record_config(prtd, 0);
|
||||
prtd->abort = 1;
|
||||
wake_up(&the_locks.read_wait);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MM_DBG("Unknown Event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_pcm_enqueue_data(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
unsigned int period_size;
|
||||
|
||||
MM_DBG("prtd->out_tail =%d mmap_flag=%d\n",
|
||||
prtd->out_tail, prtd->mmap_flag);
|
||||
period_size = snd_pcm_lib_period_bytes(substream);
|
||||
alsa_dsp_send_buffer(prtd, prtd->out_tail, period_size);
|
||||
prtd->out_tail ^= 1;
|
||||
++copy_count;
|
||||
prtd->period++;
|
||||
if (unlikely(prtd->period >= runtime->periods))
|
||||
prtd->period = 0;
|
||||
|
||||
}
|
||||
|
||||
static void event_handler(void *data)
|
||||
{
|
||||
struct msm_audio *prtd = data;
|
||||
MM_DBG("\n");
|
||||
snd_pcm_period_elapsed(prtd->substream);
|
||||
if (prtd->mmap_flag) {
|
||||
if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return;
|
||||
if (!prtd->stopped)
|
||||
msm_pcm_enqueue_data(prtd->substream);
|
||||
else
|
||||
prtd->out_needed++;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
MM_DBG("\n");
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
prtd->pcm_buf_pos = 0;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->out_sample_rate = runtime->rate;
|
||||
prtd->out_channel_mode = runtime->channels;
|
||||
prtd->data = prtd->substream->dma_buffer.area;
|
||||
prtd->phys = prtd->substream->dma_buffer.addr;
|
||||
prtd->out[0].data = prtd->data + 0;
|
||||
prtd->out[0].addr = prtd->phys + 0;
|
||||
prtd->out[0].size = BUFSZ;
|
||||
prtd->out[1].data = prtd->data + BUFSZ;
|
||||
prtd->out[1].addr = prtd->phys + BUFSZ;
|
||||
prtd->out[1].size = BUFSZ;
|
||||
|
||||
if (prtd->enabled | !(prtd->mmap_flag))
|
||||
return 0;
|
||||
|
||||
prtd->out[0].used = prtd->pcm_count;
|
||||
prtd->out[1].used = prtd->pcm_count;
|
||||
|
||||
mutex_lock(&the_locks.lock);
|
||||
alsa_audio_configure(prtd);
|
||||
mutex_unlock(&the_locks.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
uint32_t freq;
|
||||
MM_DBG("\n");
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
prtd->pcm_buf_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->type = ENC_TYPE_WAV;
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = (runtime->channels - 1);
|
||||
prtd->buffer_size = prtd->channel_mode ? STEREO_DATA_SIZE : \
|
||||
MONO_DATA_SIZE;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
freq = prtd->samp_rate;
|
||||
|
||||
prtd->data = prtd->substream->dma_buffer.area;
|
||||
prtd->phys = prtd->substream->dma_buffer.addr;
|
||||
MM_DBG("prtd->data =%08x\n", (unsigned int)prtd->data);
|
||||
MM_DBG("prtd->phys =%08x\n", (unsigned int)prtd->phys);
|
||||
|
||||
mutex_lock(&the_locks.lock);
|
||||
ret = alsa_audio_configure(prtd);
|
||||
mutex_unlock(&the_locks.lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = wait_event_interruptible(the_locks.enable_wait,
|
||||
prtd->running != 0);
|
||||
MM_DBG("state prtd->running = %d ret = %d\n", prtd->running, ret);
|
||||
|
||||
if (prtd->running == 0)
|
||||
ret = -ENODEV;
|
||||
else
|
||||
ret = 0;
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
unsigned long flag = 0;
|
||||
int ret = 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
|| !prtd->mmap_flag)
|
||||
break;
|
||||
if (!prtd->out_needed) {
|
||||
prtd->stopped = 0;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
|
||||
if (prtd->running == 1) {
|
||||
if (prtd->stopped == 1) {
|
||||
prtd->stopped = 0;
|
||||
prtd->period = 0;
|
||||
if (prtd->pcm_irq_pos == 0) {
|
||||
prtd->out_tail = 0;
|
||||
msm_pcm_enqueue_data(prtd->substream);
|
||||
prtd->out_needed--;
|
||||
} else {
|
||||
prtd->out_tail = 1;
|
||||
msm_pcm_enqueue_data(prtd->substream);
|
||||
prtd->out_needed--;
|
||||
}
|
||||
if (prtd->out_needed) {
|
||||
prtd->out_tail ^= 1;
|
||||
msm_pcm_enqueue_data(prtd->substream);
|
||||
prtd->out_needed--;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
|| !prtd->mmap_flag)
|
||||
break;
|
||||
prtd->stopped = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct msm_audio_event_callbacks snd_msm_audio_ops = {
|
||||
.playback = event_handler,
|
||||
.capture = event_handler,
|
||||
};
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
int session_attrb, sessionid;
|
||||
|
||||
MM_DBG("\n");
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (prtd->opened) {
|
||||
kfree(prtd);
|
||||
return -EBUSY;
|
||||
}
|
||||
runtime->hw = msm_pcm_playback_hardware;
|
||||
prtd->dir = SNDRV_PCM_STREAM_PLAYBACK;
|
||||
prtd->eos_ack = 0;
|
||||
prtd->session_id = HOSTPCM_STREAM_ID;
|
||||
prtd->device_events = AUDDEV_EVT_DEV_RDY |
|
||||
AUDDEV_EVT_STREAM_VOL_CHG |
|
||||
AUDDEV_EVT_DEV_RLS;
|
||||
prtd->source = msm_snddev_route_dec(prtd->session_id);
|
||||
MM_ERR("Register device event listener\n");
|
||||
ret = auddev_register_evt_listner(prtd->device_events,
|
||||
AUDDEV_CLNT_DEC, prtd->session_id,
|
||||
alsa_out_listener, (void *) prtd);
|
||||
if (ret) {
|
||||
MM_ERR("failed to register device event listener\n");
|
||||
goto evt_error;
|
||||
}
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_capture_hardware;
|
||||
prtd->dir = SNDRV_PCM_STREAM_CAPTURE;
|
||||
session_attrb = ENC_TYPE_WAV;
|
||||
sessionid = audpreproc_aenc_alloc(session_attrb,
|
||||
&prtd->module_name, &prtd->queue_id);
|
||||
if (sessionid < 0) {
|
||||
MM_ERR("AUDREC not available\n");
|
||||
kfree(prtd);
|
||||
return -ENODEV;
|
||||
}
|
||||
prtd->session_id = sessionid;
|
||||
MM_DBG("%s\n", prtd->module_name);
|
||||
ret = msm_adsp_get(prtd->module_name, &prtd->audrec,
|
||||
&alsa_audrec_adsp_ops, prtd);
|
||||
if (ret < 0) {
|
||||
audpreproc_aenc_free(prtd->session_id);
|
||||
kfree(prtd);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
prtd->abort = 0;
|
||||
prtd->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
|
||||
AUDDEV_EVT_FREQ_CHG;
|
||||
prtd->source = msm_snddev_route_enc(prtd->session_id);
|
||||
MM_ERR("Register device event listener\n");
|
||||
ret = auddev_register_evt_listner(prtd->device_events,
|
||||
AUDDEV_CLNT_ENC, prtd->session_id,
|
||||
alsa_in_listener, (void *) prtd);
|
||||
if (ret) {
|
||||
MM_ERR("failed to register device event listener\n");
|
||||
audpreproc_aenc_free(prtd->session_id);
|
||||
msm_adsp_put(prtd->audrec);
|
||||
goto evt_error;
|
||||
}
|
||||
}
|
||||
prtd->substream = substream;
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
MM_ERR("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
MM_ERR("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->ops = &snd_msm_audio_ops;
|
||||
prtd->out[0].used = BUF_INVALID_LEN;
|
||||
prtd->out[1].used = 0;
|
||||
prtd->out_head = 1; /* point to second buffer on startup */
|
||||
prtd->out_tail = 0;
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->in_head = 0;
|
||||
prtd->in_tail = 0;
|
||||
prtd->in_count = 0;
|
||||
prtd->out_needed = 0;
|
||||
for (i = 0; i < FRAME_NUM; i++) {
|
||||
prtd->in[i].size = 0;
|
||||
prtd->in[i].read = 0;
|
||||
}
|
||||
prtd->vol_pan.volume = 0x2000;
|
||||
prtd->vol_pan.pan = 0x0;
|
||||
prtd->opened = 1;
|
||||
runtime->private_data = prtd;
|
||||
|
||||
copy_count = 0;
|
||||
return 0;
|
||||
evt_error:
|
||||
kfree(prtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
MM_DBG("%d\n", fbytes);
|
||||
ret = alsa_send_buffer(prtd, buf, fbytes, NULL);
|
||||
++copy_count;
|
||||
prtd->pcm_buf_pos += fbytes;
|
||||
if (copy_count == 1) {
|
||||
mutex_lock(&the_locks.lock);
|
||||
ret = alsa_audio_configure(prtd);
|
||||
mutex_unlock(&the_locks.lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
if ((!prtd->mmap_flag) && prtd->enabled) {
|
||||
ret = wait_event_interruptible(the_locks.eos_wait,
|
||||
(!(prtd->out[0].used) && !(prtd->out[1].used)));
|
||||
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* PCM DMAMISS message is sent only once in
|
||||
* hpcm interface. So, wait for buffer complete
|
||||
* and teos flag.
|
||||
*/
|
||||
if (prtd->enabled)
|
||||
ret = wait_event_interruptible(the_locks.eos_wait,
|
||||
prtd->eos_ack);
|
||||
|
||||
done:
|
||||
alsa_audio_disable(prtd);
|
||||
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, prtd->session_id);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0, rc1 = 0, rc2 = 0;
|
||||
int fbytes = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
int monofbytes = 0;
|
||||
char *bufferp = NULL;
|
||||
|
||||
if (prtd->abort)
|
||||
return -EPERM;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
MM_DBG("%d\n", fbytes);
|
||||
monofbytes = fbytes / 2;
|
||||
if (runtime->channels == 2) {
|
||||
ret = alsa_buffer_read(prtd, buf, fbytes, NULL);
|
||||
} else {
|
||||
bufferp = buf;
|
||||
rc1 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
|
||||
bufferp = buf + monofbytes ;
|
||||
rc2 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
|
||||
ret = rc1 + rc2;
|
||||
}
|
||||
prtd->pcm_buf_pos += fbytes;
|
||||
MM_DBG("prtd->pcm_buf_pos =%d, prtd->mmap_flag =%d\n",
|
||||
prtd->pcm_buf_pos, prtd->mmap_flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
|
||||
MM_DBG("\n");
|
||||
ret = msm_snddev_withdraw_freq(prtd->session_id,
|
||||
SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
|
||||
MM_DBG("msm_snddev_withdraw_freq\n");
|
||||
auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, prtd->session_id);
|
||||
prtd->abort = 0;
|
||||
wake_up(&the_locks.enable_wait);
|
||||
alsa_audrec_disable(prtd);
|
||||
audpreproc_aenc_free(prtd->session_id);
|
||||
msm_adsp_put(prtd->audrec);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
MM_DBG("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
if (prtd->pcm_irq_pos == prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
prtd->out_head = 0; /* point to First buffer on startup */
|
||||
prtd->mmap_flag = 1;
|
||||
runtime->dma_bytes = snd_pcm_lib_period_bytes(substream)*2;
|
||||
dma_mmap_coherent(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int pcm_preallocate_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size;
|
||||
if (!stream)
|
||||
size = PLAYBACK_DMASZ;
|
||||
else
|
||||
size = CAPTURE_DMASZ;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_pcm_free_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
ret = pcm_preallocate_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = pcm_preallocate_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
msm_pcm_free_buffers(pcm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
.pcm_free = msm_pcm_free_buffers,
|
||||
};
|
||||
EXPORT_SYMBOL(msm_soc_platform);
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-dsp-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
207
sound/soc/msm/msm7kv2-pcm.h
Normal file
207
sound/soc/msm/msm7kv2-pcm.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
|
||||
|
||||
#include <mach/qdsp5v2/qdsp5audppcmdi.h>
|
||||
#include <mach/qdsp5v2/qdsp5audppmsg.h>
|
||||
#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
|
||||
#include <mach/qdsp5v2/qdsp5audplaymsg.h>
|
||||
#include <mach/qdsp5v2/audpp.h>
|
||||
#include <mach/msm_adsp.h>
|
||||
#include <mach/qdsp5v2/qdsp5audreccmdi.h>
|
||||
#include <mach/qdsp5v2/qdsp5audrecmsg.h>
|
||||
#include <mach/qdsp5v2/audpreproc.h>
|
||||
|
||||
|
||||
#define FRAME_NUM (8)
|
||||
#define FRAME_SIZE (2052 * 2)
|
||||
#define MONO_DATA_SIZE (2048)
|
||||
#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
|
||||
#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
|
||||
|
||||
#define BUFSZ (960 * 5)
|
||||
#define PLAYBACK_DMASZ (BUFSZ * 2)
|
||||
|
||||
#define MSM_PLAYBACK_DEFAULT_VOLUME 0 /* 0dB */
|
||||
#define MSM_PLAYBACK_DEFAULT_PAN 0
|
||||
|
||||
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
#define USE_CHANNELS_MIN 1
|
||||
#define USE_CHANNELS_MAX 2
|
||||
/* Support unconventional sample rates 12000, 24000 as well */
|
||||
#define USE_RATE \
|
||||
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
|
||||
#define USE_RATE_MIN 8000
|
||||
#define USE_RATE_MAX 48000
|
||||
#define MAX_BUFFER_PLAYBACK_SIZE \
|
||||
PLAYBACK_DMASZ
|
||||
/* 2048 frames (Mono), 1024 frames (Stereo) */
|
||||
#define CAPTURE_SIZE 4096
|
||||
#define MAX_BUFFER_CAPTURE_SIZE (4096*4)
|
||||
#define MAX_PERIOD_SIZE BUFSZ
|
||||
#define USE_PERIODS_MAX 1024
|
||||
#define USE_PERIODS_MIN 1
|
||||
|
||||
|
||||
#define MAX_DB (16)
|
||||
#define MIN_DB (-50)
|
||||
#define PCMPLAYBACK_DECODERID 5
|
||||
|
||||
/* 0xFFFFFFFF Indicates not to be used for audio data copy */
|
||||
#define BUF_INVALID_LEN 0xFFFFFFFF
|
||||
#define EVENT_MSG_ID ((uint16_t)~0)
|
||||
|
||||
#define AUDDEC_DEC_PCM 0
|
||||
/* Decoder status received from AUDPPTASK */
|
||||
#define AUDPP_DEC_STATUS_SLEEP 0
|
||||
#define AUDPP_DEC_STATUS_INIT 1
|
||||
#define AUDPP_DEC_STATUS_CFG 2
|
||||
#define AUDPP_DEC_STATUS_PLAY 3
|
||||
|
||||
extern int copy_count;
|
||||
extern int intcnt;
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used;
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct buffer_rec {
|
||||
void *data;
|
||||
unsigned int size;
|
||||
unsigned int read;
|
||||
unsigned int addr;
|
||||
};
|
||||
|
||||
struct audio_locks {
|
||||
struct mutex lock;
|
||||
struct mutex write_lock;
|
||||
struct mutex read_lock;
|
||||
spinlock_t read_dsp_lock;
|
||||
spinlock_t write_dsp_lock;
|
||||
spinlock_t mixer_lock;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t wait;
|
||||
wait_queue_head_t eos_wait;
|
||||
wait_queue_head_t enable_wait;
|
||||
};
|
||||
|
||||
extern struct audio_locks the_locks;
|
||||
|
||||
struct msm_audio_event_callbacks {
|
||||
/* event is called from interrupt context when a message
|
||||
* arrives from the DSP.
|
||||
*/
|
||||
void (*playback)(void *);
|
||||
void (*capture)(void *);
|
||||
};
|
||||
|
||||
|
||||
struct msm_audio {
|
||||
struct buffer out[2];
|
||||
struct buffer_rec in[8];
|
||||
|
||||
uint8_t out_head;
|
||||
uint8_t out_tail;
|
||||
uint8_t out_needed; /* number of buffers the dsp is waiting for */
|
||||
atomic_t out_bytes;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t out_sample_rate;
|
||||
uint32_t out_channel_mode;
|
||||
uint32_t out_weight;
|
||||
uint32_t out_buffer_size;
|
||||
|
||||
struct snd_pcm_substream *substream;
|
||||
|
||||
/* data allocated for various buffers */
|
||||
char *data;
|
||||
dma_addr_t phys;
|
||||
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_buf_pos; /* position in buffer */
|
||||
uint16_t source; /* Encoding source bit mask */
|
||||
|
||||
struct msm_adsp_module *audpre;
|
||||
struct msm_adsp_module *audrec;
|
||||
struct msm_adsp_module *audplay;
|
||||
enum msm_aud_decoder_state dec_state; /* Represents decoder state */
|
||||
|
||||
uint16_t session_id;
|
||||
uint32_t out_bits; /* bits per sample */
|
||||
const char *module_name;
|
||||
unsigned queue_id;
|
||||
|
||||
/* configuration to use on next enable */
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
|
||||
uint32_t type; /* 0 for PCM ,1 for AAC */
|
||||
uint32_t dsp_cnt;
|
||||
uint32_t in_head; /* next buffer dsp will write */
|
||||
uint32_t in_tail; /* next buffer read() will read */
|
||||
uint32_t in_count; /* number of buffers available to read() */
|
||||
|
||||
unsigned short samp_rate_index;
|
||||
uint32_t device_events; /* device events interested in */
|
||||
int abort; /* set when error, like sample rate mismatch */
|
||||
|
||||
/* audpre settings */
|
||||
/* For different sample rate, the coeff might be different. *
|
||||
* All the coeff should be passed from user space */
|
||||
|
||||
struct msm_audio_event_callbacks *ops;
|
||||
|
||||
int dir;
|
||||
int opened;
|
||||
int enabled;
|
||||
int running;
|
||||
int stopped; /* set when stopped, cleared on flush */
|
||||
int eos_ack;
|
||||
int mmap_flag;
|
||||
int period;
|
||||
struct audpp_cmd_cfg_object_params_volume vol_pan;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* platform data */
|
||||
extern int alsa_dsp_send_buffer(struct msm_audio *prtd,
|
||||
unsigned idx, unsigned len);
|
||||
extern int audio_dsp_out_enable(struct msm_audio *prtd, int yes);
|
||||
extern struct snd_soc_platform_driver msm_soc_platform;
|
||||
|
||||
extern int audrec_encoder_config(struct msm_audio *prtd);
|
||||
extern int alsa_audrec_disable(struct msm_audio *prtd);
|
||||
extern int alsa_audio_configure(struct msm_audio *prtd);
|
||||
extern int alsa_audio_disable(struct msm_audio *prtd);
|
||||
extern int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
|
||||
size_t count, loff_t *pos);
|
||||
ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
|
||||
size_t count, loff_t *pos);
|
||||
extern struct msm_adsp_ops alsa_audrec_adsp_ops;
|
||||
extern int alsa_in_record_config(struct msm_audio *prtd, int enable);
|
||||
#endif /*_MSM_PCM_H*/
|
||||
1004
sound/soc/msm/msm7x30.c
Normal file
1004
sound/soc/msm/msm7x30.c
Normal file
File diff suppressed because it is too large
Load Diff
725
sound/soc/msm/msm8660-apq-wm8903.c
Normal file
725
sound/soc/msm/msm8660-apq-wm8903.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/pmic8058.h>
|
||||
#include <linux/mfd/pmic8901.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <mach/mpp.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/soc-dsp.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include "msm-pcm-routing.h"
|
||||
#include "../codecs/wm8903.h"
|
||||
|
||||
#define MSM_GPIO_CLASS_D0_EN 80
|
||||
#define MSM_GPIO_CLASS_D1_EN 81
|
||||
|
||||
#define MSM_CDC_MIC_I2S_MCLK 108
|
||||
|
||||
static int msm8660_spk_func;
|
||||
static int msm8660_headset_func;
|
||||
static int msm8660_headphone_func;
|
||||
|
||||
static struct clk *mic_bit_clk;
|
||||
static struct clk *spkr_osr_clk;
|
||||
static struct clk *spkr_bit_clk;
|
||||
static struct clk *wm8903_mclk;
|
||||
|
||||
static int rx_hw_param_status;
|
||||
static int tx_hw_param_status;
|
||||
/* Platform specific logic */
|
||||
|
||||
enum {
|
||||
GET_ERR,
|
||||
SET_ERR,
|
||||
ENABLE_ERR,
|
||||
NONE
|
||||
};
|
||||
|
||||
enum {
|
||||
FUNC_OFF,
|
||||
FUNC_ON,
|
||||
};
|
||||
|
||||
static struct wm8903_vdd {
|
||||
struct regulator *reg_id;
|
||||
const char *name;
|
||||
u32 voltage;
|
||||
} wm8903_vdds[] = {
|
||||
{ NULL, "8058_l16", 1800000 },
|
||||
{ NULL, "8058_l0", 1200000 },
|
||||
{ NULL, "8058_s3", 1800000 },
|
||||
};
|
||||
|
||||
static void classd_amp_pwr(int enable)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pr_debug("%s, enable = %d\n", __func__, enable);
|
||||
if (enable) {
|
||||
/* currently external PA isn't used for LINEOUTL */
|
||||
rc = gpio_request(MSM_GPIO_CLASS_D0_EN, "CLASSD0_EN");
|
||||
if (rc) {
|
||||
pr_err("%s: spkr PA gpio %d request failed\n",
|
||||
__func__, MSM_GPIO_CLASS_D0_EN);
|
||||
return;
|
||||
}
|
||||
gpio_direction_output(MSM_GPIO_CLASS_D0_EN, 1);
|
||||
gpio_set_value_cansleep(MSM_GPIO_CLASS_D0_EN, 1);
|
||||
rc = gpio_request(MSM_GPIO_CLASS_D1_EN, "CLASSD1_EN");
|
||||
if (rc) {
|
||||
pr_err("%s: spkr PA gpio %d request failed\n",
|
||||
__func__, MSM_GPIO_CLASS_D1_EN);
|
||||
return;
|
||||
}
|
||||
gpio_direction_output(MSM_GPIO_CLASS_D1_EN, 1);
|
||||
gpio_set_value_cansleep(MSM_GPIO_CLASS_D1_EN, 1);
|
||||
} else {
|
||||
gpio_set_value_cansleep(MSM_GPIO_CLASS_D0_EN, 0);
|
||||
gpio_free(MSM_GPIO_CLASS_D0_EN);
|
||||
|
||||
gpio_set_value_cansleep(MSM_GPIO_CLASS_D1_EN, 0);
|
||||
gpio_free(MSM_GPIO_CLASS_D1_EN);
|
||||
}
|
||||
}
|
||||
|
||||
static void extern_poweramp_on(void)
|
||||
{
|
||||
pr_debug("%s: enable stereo spkr amp\n", __func__);
|
||||
classd_amp_pwr(1);
|
||||
}
|
||||
|
||||
static void extern_poweramp_off(void)
|
||||
{
|
||||
pr_debug("%s: disable stereo spkr amp\n", __func__);
|
||||
classd_amp_pwr(0);
|
||||
}
|
||||
|
||||
static int msm8660_wm8903_powerup(void)
|
||||
{
|
||||
int rc = 0, index, stage = NONE;
|
||||
struct wm8903_vdd *vdd = NULL;
|
||||
|
||||
for (index = 0; index < ARRAY_SIZE(wm8903_vdds); index++) {
|
||||
vdd = &wm8903_vdds[index];
|
||||
vdd->reg_id = regulator_get(NULL, vdd->name);
|
||||
if (IS_ERR(vdd->reg_id)) {
|
||||
pr_err("%s: Unable to get %s\n", __func__, vdd->name);
|
||||
stage = GET_ERR;
|
||||
rc = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = regulator_set_voltage(vdd->reg_id,
|
||||
vdd->voltage, vdd->voltage);
|
||||
if (rc) {
|
||||
pr_err("%s: unable to set %s voltage to %dV\n",
|
||||
__func__, vdd->name, vdd->voltage);
|
||||
stage = SET_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = regulator_enable(vdd->reg_id);
|
||||
if (rc) {
|
||||
pr_err("%s:failed to enable %s\n", __func__, vdd->name);
|
||||
stage = ENABLE_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != ARRAY_SIZE(wm8903_vdds)) {
|
||||
if (stage != GET_ERR) {
|
||||
vdd = &wm8903_vdds[index];
|
||||
regulator_put(vdd->reg_id);
|
||||
vdd->reg_id = NULL;
|
||||
}
|
||||
|
||||
while (index--) {
|
||||
vdd = &wm8903_vdds[index];
|
||||
regulator_disable(vdd->reg_id);
|
||||
regulator_put(vdd->reg_id);
|
||||
vdd->reg_id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void msm8660_wm8903_powerdown(void)
|
||||
{
|
||||
int index = ARRAY_SIZE(wm8903_vdds);
|
||||
struct wm8903_vdd *vdd = NULL;
|
||||
|
||||
while (index--) {
|
||||
vdd = &wm8903_vdds[index];
|
||||
if (vdd->reg_id) {
|
||||
regulator_disable(vdd->reg_id);
|
||||
regulator_put(vdd->reg_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int msm8660_wm8903_enable_mclk(int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
ret = gpio_request(MSM_CDC_MIC_I2S_MCLK, "I2S_Clock");
|
||||
if (ret != 0) {
|
||||
pr_err("%s: failed to request GPIO\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
wm8903_mclk = clk_get_sys(NULL, "i2s_mic_osr_clk");
|
||||
if (IS_ERR(wm8903_mclk)) {
|
||||
pr_err("Failed to get i2s_mic_osr_clk\n");
|
||||
gpio_free(MSM_CDC_MIC_I2S_MCLK);
|
||||
return IS_ERR(wm8903_mclk);
|
||||
}
|
||||
/* Master clock OSR 256 */
|
||||
clk_set_rate(wm8903_mclk, 48000 * 256);
|
||||
ret = clk_prepare_enable(wm8903_mclk);
|
||||
if (ret != 0) {
|
||||
pr_err("Unable to enable i2s_mic_osr_clk\n");
|
||||
gpio_free(MSM_CDC_MIC_I2S_MCLK);
|
||||
clk_put(wm8903_mclk);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (wm8903_mclk) {
|
||||
clk_disable_unprepare(wm8903_mclk);
|
||||
clk_put(wm8903_mclk);
|
||||
gpio_free(MSM_CDC_MIC_I2S_MCLK);
|
||||
wm8903_mclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm8660_wm8903_prepare(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = msm8660_wm8903_powerup();
|
||||
if (ret) {
|
||||
pr_err("Unable to powerup wm8903\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = msm8660_wm8903_enable_mclk(1);
|
||||
if (ret) {
|
||||
pr_err("Unable to enable mclk to wm8903\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm8660_wm8903_unprepare(void)
|
||||
{
|
||||
msm8660_wm8903_powerdown();
|
||||
msm8660_wm8903_enable_mclk(0);
|
||||
}
|
||||
|
||||
static int msm8660_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
int rate = params_rate(params), ret = 0;
|
||||
|
||||
pr_debug("Enter %s rate = %d\n", __func__, rate);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (rx_hw_param_status)
|
||||
return 0;
|
||||
/* wm8903 run @ LRC*256 */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256,
|
||||
SND_SOC_CLOCK_IN);
|
||||
snd_soc_dai_digital_mute(codec_dai, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("can't set rx codec clk configuration\n");
|
||||
return ret;
|
||||
}
|
||||
clk_set_rate(wm8903_mclk, rate * 256);
|
||||
/* set as slave mode CPU */
|
||||
clk_set_rate(spkr_bit_clk, 0);
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
|
||||
rx_hw_param_status++;
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (tx_hw_param_status)
|
||||
return 0;
|
||||
clk_set_rate(wm8903_mclk, rate * 256);
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
pr_err("can't set tx codec clk configuration\n");
|
||||
return ret;
|
||||
}
|
||||
clk_set_rate(mic_bit_clk, 0);
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
|
||||
tx_hw_param_status++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm8660_i2s_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
pr_debug("Enter %s\n", __func__);
|
||||
/* ON Dragonboard, I2S between wm8903 and CPU is shared by
|
||||
* CODEC_SPEAKER and CODEC_MIC therefore CPU only can operate
|
||||
* as input SLAVE mode.
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* config WM8903 in Mater mode */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBM_CFM |
|
||||
SND_SOC_DAIFMT_I2S);
|
||||
if (ret != 0) {
|
||||
pr_err("codec_dai set_fmt error\n");
|
||||
return ret;
|
||||
}
|
||||
/* config CPU in SLAVE mode */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret != 0) {
|
||||
pr_err("cpu_dai set_fmt error\n");
|
||||
return ret;
|
||||
}
|
||||
spkr_osr_clk = clk_get_sys(NULL, "i2s_spkr_osr_clk");
|
||||
if (IS_ERR(spkr_osr_clk)) {
|
||||
pr_err("Failed to get i2s_spkr_osr_clk\n");
|
||||
return PTR_ERR(spkr_osr_clk);
|
||||
}
|
||||
clk_set_rate(spkr_osr_clk, 48000 * 256);
|
||||
ret = clk_prepare_enable(spkr_osr_clk);
|
||||
if (ret != 0) {
|
||||
pr_err("Unable to enable i2s_spkr_osr_clk\n");
|
||||
clk_put(spkr_osr_clk);
|
||||
return ret;
|
||||
}
|
||||
spkr_bit_clk = clk_get_sys(NULL, "i2s_spkr_bit_clk");
|
||||
if (IS_ERR(spkr_bit_clk)) {
|
||||
pr_err("Failed to get i2s_spkr_bit_clk\n");
|
||||
clk_disable_unprepare(spkr_osr_clk);
|
||||
clk_put(spkr_osr_clk);
|
||||
return PTR_ERR(spkr_bit_clk);
|
||||
}
|
||||
clk_set_rate(spkr_bit_clk, 0);
|
||||
ret = clk_prepare_enable(spkr_bit_clk);
|
||||
if (ret != 0) {
|
||||
pr_err("Unable to enable i2s_spkr_bit_clk\n");
|
||||
clk_disable_unprepare(spkr_osr_clk);
|
||||
clk_put(spkr_osr_clk);
|
||||
clk_put(spkr_bit_clk);
|
||||
return ret;
|
||||
}
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
/* config WM8903 in Mater mode */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBM_CFM |
|
||||
SND_SOC_DAIFMT_I2S);
|
||||
if (ret != 0) {
|
||||
pr_err("codec_dai set_fmt error\n");
|
||||
return ret;
|
||||
}
|
||||
/* config CPU in SLAVE mode */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret != 0) {
|
||||
pr_err("codec_dai set_fmt error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mic_bit_clk = clk_get_sys(NULL, "i2s_mic_bit_clk");
|
||||
if (IS_ERR(mic_bit_clk)) {
|
||||
pr_err("Failed to get i2s_mic_bit_clk\n");
|
||||
return PTR_ERR(mic_bit_clk);
|
||||
}
|
||||
clk_set_rate(mic_bit_clk, 0);
|
||||
ret = clk_prepare_enable(mic_bit_clk);
|
||||
if (ret != 0) {
|
||||
pr_err("Unable to enable i2s_mic_bit_clk\n");
|
||||
clk_put(mic_bit_clk);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm8660_i2s_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
pr_debug("Enter %s\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
|
||||
substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
tx_hw_param_status = 0;
|
||||
rx_hw_param_status = 0;
|
||||
if (spkr_bit_clk) {
|
||||
clk_disable_unprepare(spkr_bit_clk);
|
||||
clk_put(spkr_bit_clk);
|
||||
spkr_bit_clk = NULL;
|
||||
}
|
||||
if (spkr_osr_clk) {
|
||||
clk_disable_unprepare(spkr_osr_clk);
|
||||
clk_put(spkr_osr_clk);
|
||||
spkr_osr_clk = NULL;
|
||||
}
|
||||
if (mic_bit_clk) {
|
||||
clk_disable_unprepare(mic_bit_clk);
|
||||
clk_put(mic_bit_clk);
|
||||
mic_bit_clk = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void msm8660_ext_control(struct snd_soc_codec *codec)
|
||||
{
|
||||
/* set the enpoints to their new connetion states */
|
||||
if (msm8660_spk_func == FUNC_ON)
|
||||
snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk");
|
||||
else
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
|
||||
|
||||
/* set the enpoints to their new connetion states */
|
||||
if (msm8660_headset_func == FUNC_ON)
|
||||
snd_soc_dapm_enable_pin(&codec->dapm, "Headset Jack");
|
||||
else
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Headset Jack");
|
||||
|
||||
/* set the enpoints to their new connetion states */
|
||||
if (msm8660_headphone_func == FUNC_ON)
|
||||
snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
|
||||
else
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack");
|
||||
|
||||
/* signal a DAPM event */
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
|
||||
static int msm8660_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = msm8660_spk_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm8660_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
if (msm8660_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
msm8660_spk_func = ucontrol->value.integer.value[0];
|
||||
msm8660_ext_control(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msm8660_get_hs(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = msm8660_headset_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm8660_set_hs(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
if (msm8660_headset_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
msm8660_headset_func = ucontrol->value.integer.value[0];
|
||||
msm8660_ext_control(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msm8660_get_hph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = msm8660_headphone_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm8660_set_hph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
if (msm8660_headphone_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
msm8660_headphone_func = ucontrol->value.integer.value[0];
|
||||
msm8660_ext_control(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msm8660_spkramp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
extern_poweramp_on();
|
||||
else
|
||||
extern_poweramp_off();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops machine_ops = {
|
||||
.startup = msm8660_i2s_startup,
|
||||
.shutdown = msm8660_i2s_shutdown,
|
||||
.hw_params = msm8660_i2s_hw_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget msm8660_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Ext Spk", msm8660_spkramp_event),
|
||||
SND_SOC_DAPM_MIC("Headset Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headphone Jack", NULL),
|
||||
/* to fix a bug in wm8903.c, where audio doesn't function
|
||||
* after suspend/resume
|
||||
*/
|
||||
SND_SOC_DAPM_SUPPLY("CLK_SYS_ENA", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* Match with wm8903 codec line out pin */
|
||||
{"Ext Spk", NULL, "LINEOUTL"},
|
||||
{"Ext Spk", NULL, "LINEOUTR"},
|
||||
/* Headset connects to IN3L with Bias */
|
||||
{"IN3L", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Headset Jack"},
|
||||
/* Headphone connects to IN3R with Bias */
|
||||
{"IN3R", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Headphone Jack"},
|
||||
{"ADCL", NULL, "CLK_SYS_ENA"},
|
||||
{"ADCR", NULL, "CLK_SYS_ENA"},
|
||||
{"DACL", NULL, "CLK_SYS_ENA"},
|
||||
{"DACR", NULL, "CLK_SYS_ENA"},
|
||||
};
|
||||
|
||||
static const char *cmn_status[] = {"Off", "On"};
|
||||
static const struct soc_enum msm8660_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, cmn_status),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8903_msm8660_controls[] = {
|
||||
SOC_ENUM_EXT("Speaker Function", msm8660_enum[0], msm8660_get_spk,
|
||||
msm8660_set_spk),
|
||||
SOC_ENUM_EXT("Headset Function", msm8660_enum[0], msm8660_get_hs,
|
||||
msm8660_set_hs),
|
||||
SOC_ENUM_EXT("Headphone Function", msm8660_enum[0], msm8660_get_hph,
|
||||
msm8660_set_hph),
|
||||
};
|
||||
|
||||
static int msm8660_audrx_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
int err;
|
||||
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
|
||||
snd_soc_dapm_enable_pin(&codec->dapm, "CLK_SYS_ENA");
|
||||
|
||||
err = snd_soc_add_controls(codec, wm8903_msm8660_controls,
|
||||
ARRAY_SIZE(wm8903_msm8660_controls));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_soc_dapm_new_controls(&codec->dapm, msm8660_dapm_widgets,
|
||||
ARRAY_SIZE(msm8660_dapm_widgets));
|
||||
|
||||
snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map));
|
||||
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pri_i2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
|
||||
rate->min = rate->max = 48000;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* LPA Needs only RX BE DAI links.
|
||||
* Hence define seperate BE list for lpa
|
||||
*/
|
||||
static const char *lpa_mm_be[] = {
|
||||
LPASS_BE_PRI_I2S_RX,
|
||||
};
|
||||
|
||||
static struct snd_soc_dsp_link lpa_fe_media = {
|
||||
.supported_be = lpa_mm_be,
|
||||
.num_be = ARRAY_SIZE(lpa_mm_be),
|
||||
.fe_playback_channels = 2,
|
||||
.fe_capture_channels = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DSP_TRIGGER_POST,
|
||||
SND_SOC_DSP_TRIGGER_POST
|
||||
},
|
||||
};
|
||||
|
||||
static const char *mm1_be[] = {
|
||||
LPASS_BE_PRI_I2S_RX,
|
||||
LPASS_BE_PRI_I2S_TX,
|
||||
LPASS_BE_HDMI,
|
||||
};
|
||||
|
||||
static struct snd_soc_dsp_link fe_media = {
|
||||
.supported_be = mm1_be,
|
||||
.num_be = ARRAY_SIZE(mm1_be),
|
||||
.fe_playback_channels = 2,
|
||||
.fe_capture_channels = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DSP_TRIGGER_POST, SND_SOC_DSP_TRIGGER_POST},
|
||||
};
|
||||
|
||||
/* Digital audio interface glue - connects codec <---> CPU */
|
||||
static struct snd_soc_dai_link msm8660_dai[] = {
|
||||
/* FrontEnd DAI Links */
|
||||
{
|
||||
.name = "MSM8660 Media",
|
||||
.stream_name = "MultiMedia",
|
||||
.cpu_dai_name = "MultiMedia1",
|
||||
.platform_name = "msm-pcm-dsp",
|
||||
.dynamic = 1,
|
||||
.dsp_link = &fe_media,
|
||||
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
|
||||
},
|
||||
{
|
||||
.name = "MSM8660 Media2",
|
||||
.stream_name = "MultiMedia2",
|
||||
.cpu_dai_name = "MultiMedia2",
|
||||
.platform_name = "msm-pcm-dsp",
|
||||
.dynamic = 1,
|
||||
.dsp_link = &fe_media,
|
||||
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
|
||||
},
|
||||
/* Backend DAI Links */
|
||||
{
|
||||
.name = LPASS_BE_PRI_I2S_RX,
|
||||
.stream_name = "Primary I2S Playback",
|
||||
.cpu_dai_name = "msm-dai-q6.0",
|
||||
.platform_name = "msm-pcm-routing",
|
||||
.codec_name = "wm8903-codec.3-001a",
|
||||
.codec_dai_name = "wm8903-hifi",
|
||||
.no_pcm = 1,
|
||||
.be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
|
||||
.ops = &machine_ops,
|
||||
.init = &msm8660_audrx_init,
|
||||
.be_id = MSM_BACKEND_DAI_PRI_I2S_RX
|
||||
},
|
||||
{
|
||||
.name = LPASS_BE_PRI_I2S_TX,
|
||||
.stream_name = "Primary I2S Capture",
|
||||
.cpu_dai_name = "msm-dai-q6.1",
|
||||
.platform_name = "msm-pcm-routing",
|
||||
.codec_name = "wm8903-codec.3-001a",
|
||||
.codec_dai_name = "wm8903-hifi",
|
||||
.no_pcm = 1,
|
||||
.ops = &machine_ops,
|
||||
.be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
|
||||
.be_id = MSM_BACKEND_DAI_PRI_I2S_TX
|
||||
},
|
||||
/* LPA frontend DAI link*/
|
||||
{
|
||||
.name = "MSM8660 LPA",
|
||||
.stream_name = "LPA",
|
||||
.cpu_dai_name = "MultiMedia3",
|
||||
.platform_name = "msm-pcm-lpa",
|
||||
.dynamic = 1,
|
||||
.dsp_link = &lpa_fe_media,
|
||||
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
|
||||
},
|
||||
/* HDMI backend DAI link */
|
||||
{
|
||||
.name = LPASS_BE_HDMI,
|
||||
.stream_name = "HDMI Playback",
|
||||
.cpu_dai_name = "msm-dai-q6.8",
|
||||
.platform_name = "msm-pcm-routing",
|
||||
.codec_name = "msm-stub-codec.1",
|
||||
.codec_dai_name = "msm-stub-rx",
|
||||
.no_codec = 1,
|
||||
.no_pcm = 1,
|
||||
.be_hw_params_fixup = pri_i2s_be_hw_params_fixup,
|
||||
.be_id = MSM_BACKEND_DAI_HDMI_RX
|
||||
},
|
||||
};
|
||||
|
||||
struct snd_soc_card snd_soc_card_msm8660 = {
|
||||
.name = "msm8660-snd-card",
|
||||
.dai_link = msm8660_dai,
|
||||
.num_links = ARRAY_SIZE(msm8660_dai),
|
||||
};
|
||||
|
||||
static struct platform_device *msm_snd_device;
|
||||
|
||||
static int __init msm_audio_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (machine_is_msm8x60_dragon()) {
|
||||
/* wm8903 audio codec needs to power up and mclk existing
|
||||
before it's probed */
|
||||
ret = msm8660_wm8903_prepare();
|
||||
if (ret) {
|
||||
pr_err("failed to prepare wm8903 audio codec\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msm_snd_device = platform_device_alloc("soc-audio", 0);
|
||||
if (!msm_snd_device) {
|
||||
pr_err("Platform device allocation failed\n");
|
||||
msm8660_wm8903_unprepare();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(msm_snd_device, &snd_soc_card_msm8660);
|
||||
ret = platform_device_add(msm_snd_device);
|
||||
if (ret) {
|
||||
platform_device_put(msm_snd_device);
|
||||
msm8660_wm8903_unprepare();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
module_init(msm_audio_init);
|
||||
|
||||
static void __exit msm_audio_exit(void)
|
||||
{
|
||||
msm8660_wm8903_unprepare();
|
||||
platform_device_unregister(msm_snd_device);
|
||||
}
|
||||
module_exit(msm_audio_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ALSA SoC MSM8660");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
342
sound/soc/msm/msm8660.c
Normal file
342
sound/soc/msm/msm8660.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/pmic8058.h>
|
||||
#include <linux/mfd/pmic8901.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <mach/board.h>
|
||||
#include <mach/mpp.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/dai.h>
|
||||
#include "msm8660-pcm.h"
|
||||
#include "../codecs/timpani.h"
|
||||
|
||||
#define PM8058_GPIO_BASE NR_MSM_GPIOS
|
||||
#define PM8901_GPIO_BASE (PM8058_GPIO_BASE + \
|
||||
PM8058_GPIOS + PM8058_MPPS)
|
||||
#define PM8901_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + PM8901_GPIO_BASE)
|
||||
#define GPIO_EXPANDER_GPIO_BASE \
|
||||
(PM8901_GPIO_BASE + PM8901_MPPS)
|
||||
|
||||
static struct clk *rx_osr_clk;
|
||||
static struct clk *rx_bit_clk;
|
||||
static struct clk *tx_osr_clk;
|
||||
static struct clk *tx_bit_clk;
|
||||
|
||||
static int rx_hw_param_status;
|
||||
static int tx_hw_param_status;
|
||||
/* Platform specific logic */
|
||||
|
||||
static int timpani_rx_route_enable(void)
|
||||
{
|
||||
int ret = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
ret = gpio_request(109, "I2S_Clock");
|
||||
if (ret != 0) {
|
||||
pr_err("%s: I2s clk gpio 109 request"
|
||||
"failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int timpani_rx_route_disable(void)
|
||||
{
|
||||
int ret = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
gpio_free(109);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define GPIO_CLASS_D1_EN (GPIO_EXPANDER_GPIO_BASE + 0)
|
||||
#define PM8901_MPP_3 (2) /* PM8901 MPP starts from 0 */
|
||||
static void config_class_d1_gpio(int enable)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (enable) {
|
||||
rc = gpio_request(GPIO_CLASS_D1_EN, "CLASSD1_EN");
|
||||
if (rc) {
|
||||
pr_err("%s: spkr pamp gpio %d request"
|
||||
"failed\n", __func__, GPIO_CLASS_D1_EN);
|
||||
return;
|
||||
}
|
||||
gpio_direction_output(GPIO_CLASS_D1_EN, 1);
|
||||
gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 1);
|
||||
} else {
|
||||
gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 0);
|
||||
gpio_free(GPIO_CLASS_D1_EN);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_class_d0_gpio(int enable)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (enable) {
|
||||
rc = pm8901_mpp_config_digital_out(PM8901_MPP_3,
|
||||
PM8901_MPP_DIG_LEVEL_MSMIO, 1);
|
||||
|
||||
if (rc) {
|
||||
pr_err("%s: CLASS_D0_EN failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = gpio_request(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3),
|
||||
"CLASSD0_EN");
|
||||
|
||||
if (rc) {
|
||||
pr_err("%s: spkr pamp gpio pm8901 mpp3 request"
|
||||
"failed\n", __func__);
|
||||
pm8901_mpp_config_digital_out(PM8901_MPP_3,
|
||||
PM8901_MPP_DIG_LEVEL_MSMIO, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_direction_output(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
|
||||
gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
|
||||
|
||||
} else {
|
||||
pm8901_mpp_config_digital_out(PM8901_MPP_3,
|
||||
PM8901_MPP_DIG_LEVEL_MSMIO, 0);
|
||||
gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 0);
|
||||
gpio_free(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3));
|
||||
}
|
||||
}
|
||||
|
||||
static void timpani_poweramp_on(void)
|
||||
{
|
||||
|
||||
pr_debug("%s: enable stereo spkr amp\n", __func__);
|
||||
timpani_rx_route_enable();
|
||||
config_class_d0_gpio(1);
|
||||
config_class_d1_gpio(1);
|
||||
}
|
||||
|
||||
static void timpani_poweramp_off(void)
|
||||
{
|
||||
|
||||
pr_debug("%s: disable stereo spkr amp\n", __func__);
|
||||
timpani_rx_route_disable();
|
||||
config_class_d0_gpio(0);
|
||||
config_class_d1_gpio(0);
|
||||
}
|
||||
|
||||
static int msm8660_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int rate = params_rate(params);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (rx_hw_param_status)
|
||||
return 0;
|
||||
clk_set_rate(rx_osr_clk, rate * 256);
|
||||
rx_hw_param_status++;
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
if (tx_hw_param_status)
|
||||
return 0;
|
||||
clk_set_rate(tx_osr_clk, rate * 256);
|
||||
tx_hw_param_status++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm8660_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
rx_osr_clk = clk_get(NULL, "i2s_spkr_osr_clk");
|
||||
if (IS_ERR(rx_osr_clk)) {
|
||||
pr_debug("Failed to get i2s_spkr_osr_clk\n");
|
||||
return PTR_ERR(rx_osr_clk);
|
||||
}
|
||||
/* Master clock OSR 256 */
|
||||
/* Initially set to Lowest sample rate Needed */
|
||||
clk_set_rate(rx_osr_clk, 8000 * 256);
|
||||
ret = clk_prepare_enable(rx_osr_clk);
|
||||
if (ret != 0) {
|
||||
pr_debug("Unable to enable i2s_spkr_osr_clk\n");
|
||||
clk_put(rx_osr_clk);
|
||||
return ret;
|
||||
}
|
||||
rx_bit_clk = clk_get(NULL, "i2s_spkr_bit_clk");
|
||||
if (IS_ERR(rx_bit_clk)) {
|
||||
pr_debug("Failed to get i2s_spkr_bit_clk\n");
|
||||
clk_disable_unprepare(rx_osr_clk);
|
||||
clk_put(rx_osr_clk);
|
||||
return PTR_ERR(rx_bit_clk);
|
||||
}
|
||||
clk_set_rate(rx_bit_clk, 8);
|
||||
ret = clk_prepare_enable(rx_bit_clk);
|
||||
if (ret != 0) {
|
||||
pr_debug("Unable to enable i2s_spkr_bit_clk\n");
|
||||
clk_put(rx_bit_clk);
|
||||
clk_disable_unprepare(rx_osr_clk);
|
||||
clk_put(rx_osr_clk);
|
||||
return ret;
|
||||
}
|
||||
timpani_poweramp_on();
|
||||
msleep(30);
|
||||
/* End of platform specific logic */
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
tx_osr_clk = clk_get(NULL, "i2s_mic_osr_clk");
|
||||
if (IS_ERR(tx_osr_clk)) {
|
||||
pr_debug("Failed to get i2s_mic_osr_clk\n");
|
||||
return PTR_ERR(tx_osr_clk);
|
||||
}
|
||||
/* Master clock OSR 256 */
|
||||
clk_set_rate(tx_osr_clk, 8000 * 256);
|
||||
ret = clk_prepare_enable(tx_osr_clk);
|
||||
if (ret != 0) {
|
||||
pr_debug("Unable to enable i2s_mic_osr_clk\n");
|
||||
clk_put(tx_osr_clk);
|
||||
return ret;
|
||||
}
|
||||
tx_bit_clk = clk_get(NULL, "i2s_mic_bit_clk");
|
||||
if (IS_ERR(tx_bit_clk)) {
|
||||
pr_debug("Failed to get i2s_mic_bit_clk\n");
|
||||
clk_disable_unprepare(tx_osr_clk);
|
||||
clk_put(tx_osr_clk);
|
||||
return PTR_ERR(tx_bit_clk);
|
||||
}
|
||||
clk_set_rate(tx_bit_clk, 8);
|
||||
ret = clk_prepare_enable(tx_bit_clk);
|
||||
if (ret != 0) {
|
||||
pr_debug("Unable to enable i2s_mic_bit_clk\n");
|
||||
clk_put(tx_bit_clk);
|
||||
clk_disable_unprepare(tx_osr_clk);
|
||||
clk_put(tx_osr_clk);
|
||||
return ret;
|
||||
}
|
||||
msm_snddev_enable_dmic_power();
|
||||
msleep(30);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: rx/tx_hw_param_status should be a counter in the below code
|
||||
* when driver starts supporting mutisession else setting it to 0
|
||||
* will stop audio in all sessions.
|
||||
*/
|
||||
static void msm8660_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
rx_hw_param_status = 0;
|
||||
timpani_poweramp_off();
|
||||
msleep(30);
|
||||
if (rx_bit_clk) {
|
||||
clk_disable_unprepare(rx_bit_clk);
|
||||
clk_put(rx_bit_clk);
|
||||
rx_bit_clk = NULL;
|
||||
}
|
||||
if (rx_osr_clk) {
|
||||
clk_disable_unprepare(rx_osr_clk);
|
||||
clk_put(rx_osr_clk);
|
||||
rx_osr_clk = NULL;
|
||||
}
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
tx_hw_param_status = 0;
|
||||
msm_snddev_disable_dmic_power();
|
||||
msleep(30);
|
||||
if (tx_bit_clk) {
|
||||
clk_disable_unprepare(tx_bit_clk);
|
||||
clk_put(tx_bit_clk);
|
||||
tx_bit_clk = NULL;
|
||||
}
|
||||
if (tx_osr_clk) {
|
||||
clk_disable_unprepare(tx_osr_clk);
|
||||
clk_put(tx_osr_clk);
|
||||
tx_osr_clk = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_soc_ops machine_ops = {
|
||||
.startup = msm8660_startup,
|
||||
.shutdown = msm8660_shutdown,
|
||||
.hw_params = msm8660_hw_params,
|
||||
};
|
||||
|
||||
/* Digital audio interface glue - connects codec <---> CPU */
|
||||
static struct snd_soc_dai_link msm8660_dai[] = {
|
||||
{
|
||||
.name = "Audio Rx",
|
||||
.stream_name = "Audio Rx",
|
||||
.cpu_dai = &msm_cpu_dai[0],
|
||||
.codec_dai = &timpani_codec_dai[0],
|
||||
.ops = &machine_ops,
|
||||
},
|
||||
{
|
||||
.name = "Audio Tx",
|
||||
.stream_name = "Audio Tx",
|
||||
.cpu_dai = &msm_cpu_dai[5],
|
||||
.codec_dai = &timpani_codec_dai[1],
|
||||
.ops = &machine_ops,
|
||||
}
|
||||
};
|
||||
|
||||
struct snd_soc_card snd_soc_card_msm8660 = {
|
||||
.name = "msm8660-pcm-audio",
|
||||
.dai_link = msm8660_dai,
|
||||
.num_links = ARRAY_SIZE(msm8660_dai),
|
||||
.platform = &msm8660_soc_platform,
|
||||
};
|
||||
|
||||
/* msm_audio audio subsystem */
|
||||
static struct snd_soc_device msm_snd_devdata = {
|
||||
.card = &snd_soc_card_msm8660,
|
||||
.codec_dev = &soc_codec_dev_timpani,
|
||||
};
|
||||
|
||||
static struct platform_device *msm_snd_device;
|
||||
|
||||
|
||||
static int __init msm_audio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
msm_snd_device = platform_device_alloc("soc-audio", 0);
|
||||
if (!msm_snd_device) {
|
||||
pr_err("Platform device allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(msm_snd_device, &msm_snd_devdata);
|
||||
|
||||
msm_snd_devdata.dev = &msm_snd_device->dev;
|
||||
ret = platform_device_add(msm_snd_device);
|
||||
if (ret) {
|
||||
platform_device_put(msm_snd_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(msm_audio_init);
|
||||
|
||||
static void __exit msm_audio_exit(void)
|
||||
{
|
||||
platform_device_unregister(msm_snd_device);
|
||||
}
|
||||
module_exit(msm_audio_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ALSA SoC MSM8660");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1274
sound/soc/msm/msm8930.c
Normal file
1274
sound/soc/msm/msm8930.c
Normal file
File diff suppressed because it is too large
Load Diff
1740
sound/soc/msm/msm8960.c
Normal file
1740
sound/soc/msm/msm8960.c
Normal file
File diff suppressed because it is too large
Load Diff
752
sound/soc/msm/msm8974.c
Normal file
752
sound/soc/msm/msm8974.c
Normal file
@@ -0,0 +1,752 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/pm8xxx/pm8921.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/pm8xxx/pm8921.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/soc-dsp.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/jack.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/socinfo.h>
|
||||
#include <qdsp6v2/msm-pcm-routing-v2.h>
|
||||
#include "../codecs/wcd9310.h"
|
||||
|
||||
/* 8974 machine driver */
|
||||
|
||||
#define PM8921_GPIO_BASE NR_GPIO_IRQS
|
||||
#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
|
||||
|
||||
#define MSM8974_SPK_ON 1
|
||||
#define MSM8974_SPK_OFF 0
|
||||
|
||||
#define MSM_SLIM_0_RX_MAX_CHANNELS 2
|
||||
#define MSM_SLIM_0_TX_MAX_CHANNELS 4
|
||||
|
||||
#define BTSCO_RATE_8KHZ 8000
|
||||
#define BTSCO_RATE_16KHZ 16000
|
||||
|
||||
#define BOTTOM_SPK_AMP_POS 0x1
|
||||
#define BOTTOM_SPK_AMP_NEG 0x2
|
||||
#define TOP_SPK_AMP_POS 0x4
|
||||
#define TOP_SPK_AMP_NEG 0x8
|
||||
|
||||
#define GPIO_AUX_PCM_DOUT 43
|
||||
#define GPIO_AUX_PCM_DIN 44
|
||||
#define GPIO_AUX_PCM_SYNC 45
|
||||
#define GPIO_AUX_PCM_CLK 46
|
||||
|
||||
#define TABLA_EXT_CLK_RATE 12288000
|
||||
|
||||
#define TABLA_MBHC_DEF_BUTTONS 8
|
||||
#define TABLA_MBHC_DEF_RLOADS 5
|
||||
|
||||
/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
|
||||
enum {
|
||||
SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
|
||||
SLIM_1_TX_1 = 146, /* BT-SCO and USB RX */
|
||||
SLIM_2_RX_1 = 147, /* HDMI RX */
|
||||
SLIM_3_RX_1 = 148, /* In-call recording RX */
|
||||
SLIM_3_RX_2 = 149, /* In-call recording RX */
|
||||
SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
|
||||
};
|
||||
|
||||
static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
|
||||
static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
|
||||
static int msm_spk_control;
|
||||
static int msm_ext_bottom_spk_pamp;
|
||||
static int msm_ext_top_spk_pamp;
|
||||
static int msm_slim_0_rx_ch = 1;
|
||||
static int msm_slim_0_tx_ch = 1;
|
||||
|
||||
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
|
||||
static int msm_headset_gpios_configured;
|
||||
|
||||
static struct snd_soc_jack hs_jack;
|
||||
static struct snd_soc_jack button_jack;
|
||||
|
||||
static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
|
||||
bool dapm);
|
||||
|
||||
static struct tabla_mbhc_config mbhc_cfg = {
|
||||
.headset_jack = &hs_jack,
|
||||
.button_jack = &button_jack,
|
||||
.read_fw_bin = false,
|
||||
.calibration = NULL,
|
||||
.micbias = TABLA_MICBIAS2,
|
||||
.mclk_cb_fn = msm_enable_codec_ext_clk,
|
||||
.mclk_rate = TABLA_EXT_CLK_RATE,
|
||||
.gpio = 0, /* MBHC GPIO is not configured */
|
||||
.gpio_irq = 0,
|
||||
.gpio_level_insert = 1,
|
||||
};
|
||||
|
||||
static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct pm_gpio param = {
|
||||
.direction = PM_GPIO_DIR_OUT,
|
||||
.output_buffer = PM_GPIO_OUT_BUF_CMOS,
|
||||
.output_value = 1,
|
||||
.pull = PM_GPIO_PULL_NO,
|
||||
.vin_sel = PM_GPIO_VIN_S4,
|
||||
.out_strength = PM_GPIO_STRENGTH_MED,
|
||||
.
|
||||
function = PM_GPIO_FUNC_NORMAL,
|
||||
};
|
||||
|
||||
if (spk_amp_gpio == bottom_spk_pamp_gpio) {
|
||||
|
||||
ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
|
||||
if (ret) {
|
||||
pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
|
||||
__func__, bottom_spk_pamp_gpio);
|
||||
return;
|
||||
}
|
||||
ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
|
||||
if (ret)
|
||||
pr_err("%s: Failed to configure Bottom Spk Ampl"
|
||||
" gpio %u\n", __func__, bottom_spk_pamp_gpio);
|
||||
else {
|
||||
pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
|
||||
gpio_direction_output(bottom_spk_pamp_gpio, 1);
|
||||
}
|
||||
|
||||
} else if (spk_amp_gpio == top_spk_pamp_gpio) {
|
||||
|
||||
ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
|
||||
if (ret) {
|
||||
pr_err("%s: Error requesting GPIO %d\n", __func__,
|
||||
top_spk_pamp_gpio);
|
||||
return;
|
||||
}
|
||||
ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
|
||||
if (ret)
|
||||
pr_err("%s: Failed to configure Top Spk Ampl"
|
||||
" gpio %u\n", __func__, top_spk_pamp_gpio);
|
||||
else {
|
||||
pr_debug("%s: enable Top spkr amp gpio\n", __func__);
|
||||
gpio_direction_output(top_spk_pamp_gpio, 1);
|
||||
}
|
||||
} else {
|
||||
pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
|
||||
" gpio = %u\n", __func__, spk_amp_gpio);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_ext_spk_power_amp_on(u32 spk)
|
||||
{
|
||||
if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
|
||||
|
||||
if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
|
||||
(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
|
||||
|
||||
pr_debug("%s() External Bottom Speaker Ampl already "
|
||||
"turned on. spk = 0x%08x\n", __func__, spk);
|
||||
return;
|
||||
}
|
||||
|
||||
msm_ext_bottom_spk_pamp |= spk;
|
||||
|
||||
if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
|
||||
(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
|
||||
|
||||
msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
|
||||
pr_debug("%s: slepping 4 ms after turning on external "
|
||||
" Bottom Speaker Ampl\n", __func__);
|
||||
usleep_range(4000, 4000);
|
||||
}
|
||||
|
||||
} else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
|
||||
|
||||
if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
|
||||
(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
|
||||
|
||||
pr_debug("%s() External Top Speaker Ampl already"
|
||||
"turned on. spk = 0x%08x\n", __func__, spk);
|
||||
return;
|
||||
}
|
||||
|
||||
msm_ext_top_spk_pamp |= spk;
|
||||
|
||||
if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
|
||||
(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
|
||||
|
||||
msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
|
||||
pr_debug("%s: sleeping 4 ms after turning on "
|
||||
" external Top Speaker Ampl\n", __func__);
|
||||
usleep_range(4000, 4000);
|
||||
}
|
||||
} else {
|
||||
|
||||
pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
|
||||
__func__, spk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_ext_spk_power_amp_off(u32 spk)
|
||||
{
|
||||
if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
|
||||
|
||||
if (!msm_ext_bottom_spk_pamp)
|
||||
return;
|
||||
|
||||
gpio_direction_output(bottom_spk_pamp_gpio, 0);
|
||||
gpio_free(bottom_spk_pamp_gpio);
|
||||
msm_ext_bottom_spk_pamp = 0;
|
||||
|
||||
pr_debug("%s: sleeping 4 ms after turning off external Bottom"
|
||||
" Speaker Ampl\n", __func__);
|
||||
|
||||
usleep_range(4000, 4000);
|
||||
|
||||
} else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
|
||||
|
||||
if (!msm_ext_top_spk_pamp)
|
||||
return;
|
||||
|
||||
gpio_direction_output(top_spk_pamp_gpio, 0);
|
||||
gpio_free(top_spk_pamp_gpio);
|
||||
msm_ext_top_spk_pamp = 0;
|
||||
|
||||
pr_debug("%s: sleeping 4 ms after turning off external Top"
|
||||
" Spkaker Ampl\n", __func__);
|
||||
|
||||
usleep_range(4000, 4000);
|
||||
} else {
|
||||
|
||||
pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
|
||||
__func__, spk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_ext_control(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
|
||||
if (msm_spk_control == MSM8974_SPK_ON) {
|
||||
snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
|
||||
snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
|
||||
snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
|
||||
snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
|
||||
} else {
|
||||
snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
|
||||
snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
|
||||
snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
|
||||
snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
|
||||
}
|
||||
|
||||
snd_soc_dapm_sync(dapm);
|
||||
}
|
||||
|
||||
static int msm_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
|
||||
ucontrol->value.integer.value[0] = msm_spk_control;
|
||||
return 0;
|
||||
}
|
||||
static int msm_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
if (msm_spk_control == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
msm_spk_control = ucontrol->value.integer.value[0];
|
||||
msm_ext_control(codec);
|
||||
return 1;
|
||||
}
|
||||
static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
|
||||
msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
|
||||
else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
|
||||
msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
|
||||
else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
|
||||
msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
|
||||
else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
|
||||
msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
|
||||
else {
|
||||
pr_err("%s() Invalid Speaker Widget = %s\n",
|
||||
__func__, w->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
|
||||
msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
|
||||
else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
|
||||
msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
|
||||
else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
|
||||
msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
|
||||
else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
|
||||
msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
|
||||
else {
|
||||
pr_err("%s() Invalid Speaker Widget = %s\n",
|
||||
__func__, w->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
|
||||
bool dapm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
|
||||
msm_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
|
||||
SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
|
||||
|
||||
SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
|
||||
SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
|
||||
|
||||
SND_SOC_DAPM_MIC("Handset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic1", NULL),
|
||||
SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
|
||||
|
||||
SND_SOC_DAPM_MIC("Digital Mic1", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic2", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic3", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic4", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic5", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic6", NULL),
|
||||
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route common_audio_map[] = {
|
||||
|
||||
{"RX_BIAS", NULL, "MCLK"},
|
||||
{"LDO_H", NULL, "MCLK"},
|
||||
|
||||
/* Speaker path */
|
||||
{"Ext Spk Bottom Pos", NULL, "LINEOUT1"},
|
||||
{"Ext Spk Bottom Neg", NULL, "LINEOUT3"},
|
||||
|
||||
{"Ext Spk Top Pos", NULL, "LINEOUT2"},
|
||||
{"Ext Spk Top Neg", NULL, "LINEOUT4"},
|
||||
|
||||
/* Microphone path */
|
||||
{"AMIC1", NULL, "MIC BIAS1 Internal1"},
|
||||
{"MIC BIAS1 Internal1", NULL, "Handset Mic"},
|
||||
|
||||
{"AMIC2", NULL, "MIC BIAS2 External"},
|
||||
{"MIC BIAS2 External", NULL, "Headset Mic"},
|
||||
|
||||
/**
|
||||
* AMIC3 and AMIC4 inputs are connected to ANC microphones
|
||||
* These mics are biased differently on CDP and FLUID
|
||||
* routing entries below are based on bias arrangement
|
||||
* on FLUID.
|
||||
*/
|
||||
{"AMIC3", NULL, "MIC BIAS3 Internal1"},
|
||||
{"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
|
||||
|
||||
{"AMIC4", NULL, "MIC BIAS1 Internal2"},
|
||||
{"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
|
||||
|
||||
{"HEADPHONE", NULL, "LDO_H"},
|
||||
|
||||
/**
|
||||
* The digital Mic routes are setup considering
|
||||
* fluid as default device.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
|
||||
* Digital Mic GM5 on CDP mainboard.
|
||||
* Conncted to DMIC2 Input on Tabla codec.
|
||||
*/
|
||||
{"DMIC2", NULL, "MIC BIAS1 External"},
|
||||
{"MIC BIAS1 External", NULL, "Digital Mic1"},
|
||||
|
||||
/**
|
||||
* Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
|
||||
* Digital Mic GM6 on CDP mainboard.
|
||||
* Conncted to DMIC1 Input on Tabla codec.
|
||||
*/
|
||||
{"DMIC1", NULL, "MIC BIAS1 External"},
|
||||
{"MIC BIAS1 External", NULL, "Digital Mic2"},
|
||||
|
||||
/**
|
||||
* Digital Mic3. Back Bottom Digital Mic on Fluid.
|
||||
* Digital Mic GM1 on CDP mainboard.
|
||||
* Conncted to DMIC4 Input on Tabla codec.
|
||||
*/
|
||||
{"DMIC4", NULL, "MIC BIAS3 External"},
|
||||
{"MIC BIAS3 External", NULL, "Digital Mic3"},
|
||||
|
||||
/**
|
||||
* Digital Mic4. Back top Digital Mic on Fluid.
|
||||
* Digital Mic GM2 on CDP mainboard.
|
||||
* Conncted to DMIC3 Input on Tabla codec.
|
||||
*/
|
||||
{"DMIC3", NULL, "MIC BIAS3 External"},
|
||||
{"MIC BIAS3 External", NULL, "Digital Mic4"},
|
||||
|
||||
/**
|
||||
* Digital Mic5. Front top Digital Mic on Fluid.
|
||||
* Digital Mic GM3 on CDP mainboard.
|
||||
* Conncted to DMIC5 Input on Tabla codec.
|
||||
*/
|
||||
{"DMIC5", NULL, "MIC BIAS4 External"},
|
||||
{"MIC BIAS4 External", NULL, "Digital Mic5"},
|
||||
|
||||
/* Tabla digital Mic6 - back bottom digital Mic on Liquid and
|
||||
* bottom mic on CDP. FLUID/MTP do not have dmic6 installed.
|
||||
*/
|
||||
{"DMIC6", NULL, "MIC BIAS4 External"},
|
||||
{"MIC BIAS4 External", NULL, "Digital Mic6"},
|
||||
};
|
||||
|
||||
static const char *spk_function[] = {"Off", "On"};
|
||||
static const char *slim0_rx_ch_text[] = {"One", "Two"};
|
||||
static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
|
||||
|
||||
static const struct soc_enum msm_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
|
||||
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
|
||||
};
|
||||
|
||||
static const char *btsco_rate_text[] = {"8000", "16000"};
|
||||
static const struct soc_enum msm_btsco_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
|
||||
};
|
||||
|
||||
static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
|
||||
msm_slim_0_rx_ch);
|
||||
ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
|
||||
|
||||
pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
|
||||
msm_slim_0_rx_ch);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
|
||||
msm_slim_0_tx_ch);
|
||||
ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
|
||||
|
||||
pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
|
||||
msm_slim_0_tx_ch);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("%s: msm_btsco_rate = %d", __func__,
|
||||
msm_btsco_rate);
|
||||
ucontrol->value.integer.value[0] = msm_btsco_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
switch (ucontrol->value.integer.value[0]) {
|
||||
case 0:
|
||||
msm_btsco_rate = BTSCO_RATE_8KHZ;
|
||||
break;
|
||||
case 1:
|
||||
msm_btsco_rate = BTSCO_RATE_16KHZ;
|
||||
break;
|
||||
default:
|
||||
msm_btsco_rate = BTSCO_RATE_8KHZ;
|
||||
break;
|
||||
}
|
||||
pr_debug("%s: msm_btsco_rate = %d\n", __func__,
|
||||
msm_btsco_rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new tabla_msm_controls[] = {
|
||||
SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
|
||||
msm_set_spk),
|
||||
SOC_ENUM_EXT("SLIM_0_RX Channels", msm_enum[1],
|
||||
msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
|
||||
SOC_ENUM_EXT("SLIM_0_TX Channels", msm_enum[2],
|
||||
msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
|
||||
SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
|
||||
msm_btsco_rate_get, msm_btsco_rate_put),
|
||||
};
|
||||
|
||||
static struct snd_soc_dsp_link lpa_fe_media = {
|
||||
.playback = true,
|
||||
.trigger = {
|
||||
SND_SOC_DSP_TRIGGER_POST,
|
||||
SND_SOC_DSP_TRIGGER_POST
|
||||
},
|
||||
};
|
||||
static struct snd_soc_dsp_link fe_media = {
|
||||
.playback = true,
|
||||
.capture = true,
|
||||
.trigger = {
|
||||
SND_SOC_DSP_TRIGGER_POST,
|
||||
SND_SOC_DSP_TRIGGER_POST
|
||||
},
|
||||
};
|
||||
static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
|
||||
/* PCM only supports mono output with 8khz sample rate */
|
||||
rate->min = rate->max = 8000;
|
||||
channels->min = channels->max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int msm_aux_pcm_get_gpios(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
|
||||
__func__, GPIO_AUX_PCM_DOUT);
|
||||
goto fail_dout;
|
||||
}
|
||||
|
||||
ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
|
||||
__func__, GPIO_AUX_PCM_DIN);
|
||||
goto fail_din;
|
||||
}
|
||||
|
||||
ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
|
||||
__func__, GPIO_AUX_PCM_SYNC);
|
||||
goto fail_sync;
|
||||
}
|
||||
ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
|
||||
__func__, GPIO_AUX_PCM_CLK);
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_clk:
|
||||
gpio_free(GPIO_AUX_PCM_SYNC);
|
||||
fail_sync:
|
||||
gpio_free(GPIO_AUX_PCM_DIN);
|
||||
fail_din:
|
||||
gpio_free(GPIO_AUX_PCM_DOUT);
|
||||
fail_dout:
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int msm_aux_pcm_free_gpios(void)
|
||||
{
|
||||
gpio_free(GPIO_AUX_PCM_DIN);
|
||||
gpio_free(GPIO_AUX_PCM_DOUT);
|
||||
gpio_free(GPIO_AUX_PCM_SYNC);
|
||||
gpio_free(GPIO_AUX_PCM_CLK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s(): substream = %s\n", __func__, substream->name);
|
||||
ret = msm_aux_pcm_get_gpios();
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Aux PCM GPIO request failed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
pr_debug("%s(): substream = %s\n", __func__, substream->name);
|
||||
msm_aux_pcm_free_gpios();
|
||||
}
|
||||
static struct snd_soc_ops msm_auxpcm_be_ops = {
|
||||
.startup = msm_auxpcm_startup,
|
||||
.shutdown = msm_auxpcm_shutdown,
|
||||
};
|
||||
/* Digital audio interface glue - connects codec <---> CPU */
|
||||
static struct snd_soc_dai_link msm_dai[] = {
|
||||
/* FrontEnd DAI Links */
|
||||
{
|
||||
.name = "MSM8974 Media1",
|
||||
.stream_name = "MultiMedia1",
|
||||
.cpu_dai_name = "MultiMedia1",
|
||||
.platform_name = "msm-pcm-dsp",
|
||||
.dynamic = 1,
|
||||
.dsp_link = &fe_media,
|
||||
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
|
||||
},
|
||||
{
|
||||
.name = "MSM8974 LPA",
|
||||
.stream_name = "LPA",
|
||||
.cpu_dai_name = "MultiMedia3",
|
||||
.platform_name = "msm-pcm-lpa",
|
||||
.dynamic = 1,
|
||||
.dsp_link = &lpa_fe_media,
|
||||
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
|
||||
},
|
||||
|
||||
/* AUX PCM Backend DAI Links */
|
||||
{
|
||||
.name = LPASS_BE_AUXPCM_RX,
|
||||
.stream_name = "AUX PCM Playback",
|
||||
.cpu_dai_name = "msm-dai-q6.4106",
|
||||
.platform_name = "msm-pcm-routing",
|
||||
.codec_name = "msm-stub-codec.1",
|
||||
.codec_dai_name = "msm-stub-rx",
|
||||
.no_pcm = 1,
|
||||
.be_id = MSM_BACKEND_DAI_AUXPCM_RX,
|
||||
.be_hw_params_fixup = msm_auxpcm_be_params_fixup,
|
||||
.ops = &msm_auxpcm_be_ops,
|
||||
},
|
||||
{
|
||||
.name = LPASS_BE_AUXPCM_TX,
|
||||
.stream_name = "AUX PCM Capture",
|
||||
.cpu_dai_name = "msm-dai-q6.4107",
|
||||
.platform_name = "msm-pcm-routing",
|
||||
.codec_name = "msm-stub-codec.1",
|
||||
.codec_dai_name = "msm-stub-tx",
|
||||
.no_pcm = 1,
|
||||
.be_id = MSM_BACKEND_DAI_AUXPCM_TX,
|
||||
.be_hw_params_fixup = msm_auxpcm_be_params_fixup,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
struct snd_soc_card snd_soc_card_msm = {
|
||||
.name = "msm8974-taiko-snd-card",
|
||||
.dai_link = msm_dai,
|
||||
.num_links = ARRAY_SIZE(msm_dai),
|
||||
};
|
||||
|
||||
static struct platform_device *msm_snd_device;
|
||||
|
||||
static void msm_free_headset_mic_gpios(void)
|
||||
{
|
||||
if (msm_headset_gpios_configured) {
|
||||
gpio_free(PM8921_GPIO_PM_TO_SYS(23));
|
||||
gpio_free(PM8921_GPIO_PM_TO_SYS(35));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init msm_audio_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!machine_is_copper_sim()) {
|
||||
pr_err("%s: Not the right machine type\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
msm_snd_device = platform_device_alloc("soc-audio", 0);
|
||||
if (!msm_snd_device) {
|
||||
pr_err("Platform device allocation failed\n");
|
||||
kfree(mbhc_cfg.calibration);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(msm_snd_device, &snd_soc_card_msm);
|
||||
ret = platform_device_add(msm_snd_device);
|
||||
if (ret) {
|
||||
platform_device_put(msm_snd_device);
|
||||
kfree(mbhc_cfg.calibration);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
module_init(msm_audio_init);
|
||||
|
||||
static void __exit msm_audio_exit(void)
|
||||
{
|
||||
if (!machine_is_copper_sim()) {
|
||||
pr_err("%s: Not the right machine type\n", __func__);
|
||||
return ;
|
||||
}
|
||||
msm_free_headset_mic_gpios();
|
||||
platform_device_unregister(msm_snd_device);
|
||||
kfree(mbhc_cfg.calibration);
|
||||
}
|
||||
module_exit(msm_audio_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ALSA SoC msm");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
148
sound/soc/msm/msm8x60-dai.c
Normal file
148
sound/soc/msm/msm8x60-dai.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* sound/soc/msm/msm-dai.c
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Derived from msm-pcm.c and msm7201.c.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include "msm8x60-pcm.h"
|
||||
|
||||
static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
|
||||
{
|
||||
.name = "msm-codec-dai",
|
||||
.playback = {
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
|
||||
{
|
||||
.name = "msm-cpu-dai",
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_msm = {
|
||||
.compress_type = SND_SOC_FLAT_COMPRESSION,
|
||||
};
|
||||
|
||||
static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
|
||||
msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
|
||||
}
|
||||
|
||||
static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __devinit int asoc_msm_cpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
|
||||
}
|
||||
|
||||
static int __devexit asoc_msm_cpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver asoc_msm_codec_driver = {
|
||||
.probe = asoc_msm_codec_probe,
|
||||
.remove = __devexit_p(asoc_msm_codec_remove),
|
||||
.driver = {
|
||||
.name = "msm-codec-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver asoc_msm_cpu_driver = {
|
||||
.probe = asoc_msm_cpu_probe,
|
||||
.remove = __devexit_p(asoc_msm_cpu_remove),
|
||||
.driver = {
|
||||
.name = "msm-cpu-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_codec_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_msm_codec_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_codec_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_msm_codec_driver);
|
||||
}
|
||||
|
||||
static int __init msm_cpu_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_msm_cpu_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_cpu_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_msm_cpu_driver);
|
||||
}
|
||||
|
||||
module_init(msm_codec_dai_init);
|
||||
module_exit(msm_codec_dai_exit);
|
||||
module_init(msm_cpu_dai_init);
|
||||
module_exit(msm_cpu_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
806
sound/soc/msm/msm8x60-pcm.c
Normal file
806
sound/soc/msm/msm8x60-pcm.c
Normal file
@@ -0,0 +1,806 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <mach/qdsp6v2/audio_dev_ctl.h>
|
||||
|
||||
#include "msm8x60-pcm.h"
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 960 * 10,
|
||||
.period_bytes_min = 960 * 5,
|
||||
.period_bytes_max = 960 * 5,
|
||||
.periods_min = 2,
|
||||
.periods_max = 2,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
uint32_t in_frame_info[8][2];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void alsa_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
|
||||
void *private_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_audio *prtd = (struct msm_audio *) private_data;
|
||||
int dev_rate = 48000;
|
||||
pr_debug("evt_id = 0x%8x\n", evt_id);
|
||||
switch (evt_id) {
|
||||
case AUDDEV_EVT_DEV_RDY:
|
||||
pr_debug("AUDDEV_EVT_DEV_RDY\n");
|
||||
prtd->copp_id = evt_payload->routing_id;
|
||||
pr_debug("prtd->session_id = %d, copp_id= %d",
|
||||
prtd->session_id, prtd->copp_id);
|
||||
if (prtd->copp_id == PCM_RX)
|
||||
dev_rate = 8000;
|
||||
|
||||
ret = msm_snddev_set_dec(prtd->session_id, prtd->copp_id, 1,
|
||||
dev_rate, 1);
|
||||
break;
|
||||
case AUDDEV_EVT_DEV_RLS:
|
||||
pr_debug("AUDDEV_EVT_DEV_RLS\n");
|
||||
prtd->copp_id = evt_payload->routing_id;
|
||||
pr_debug("prtd->session_id = %d, copp_id= %d",
|
||||
prtd->session_id, prtd->copp_id);
|
||||
if (prtd->copp_id == PCM_RX)
|
||||
dev_rate = 8000;
|
||||
|
||||
ret = msm_snddev_set_dec(prtd->session_id, prtd->copp_id, 0,
|
||||
dev_rate, 1);
|
||||
break;
|
||||
case AUDDEV_EVT_STREAM_VOL_CHG:
|
||||
pr_debug("AUDDEV_EVT_STREAM_VOL_CHG\n");
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unknown Event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void alsa_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
|
||||
void *private_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_audio *prtd = (struct msm_audio *) private_data;
|
||||
int dev_rate = 48000;
|
||||
pr_debug("evt_id = 0x%8x\n", evt_id);
|
||||
|
||||
switch (evt_id) {
|
||||
case AUDDEV_EVT_DEV_RDY:
|
||||
prtd->copp_id = evt_payload->routing_id;
|
||||
if (prtd->copp_id == PCM_TX)
|
||||
dev_rate = 8000;
|
||||
|
||||
ret = msm_snddev_set_enc(prtd->session_id, prtd->copp_id, 1,
|
||||
dev_rate, 1);
|
||||
break;
|
||||
case AUDDEV_EVT_DEV_RLS:
|
||||
prtd->copp_id = evt_payload->routing_id;
|
||||
if (prtd->copp_id == PCM_TX)
|
||||
dev_rate = 8000;
|
||||
|
||||
ret = msm_snddev_set_enc(prtd->session_id, prtd->copp_id, 0,
|
||||
dev_rate, 1);
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unknown Event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
uint32_t *ptrmem = (uint32_t *)payload;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start))
|
||||
break;
|
||||
if (!prtd->mmap_flag)
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count, 0, 0, NO_TIMESTAMP);
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_CMDRSP_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case ASM_DATA_EVENT_READ_DONE: {
|
||||
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
|
||||
pr_debug("token = 0x%08x\n", token);
|
||||
for (i = 0; i < 8; i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
in_frame_info[token][0] = payload[2];
|
||||
in_frame_info[token][1] = payload[3];
|
||||
prtd->pcm_irq_pos += in_frame_info[token][0];
|
||||
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (atomic_read(&prtd->in_count) <= prtd->periods)
|
||||
atomic_inc(&prtd->in_count);
|
||||
wake_up(&the_locks.read_wait);
|
||||
if (prtd->mmap_flag)
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
if (!prtd->mmap_flag
|
||||
&& !atomic_read(&prtd->out_needed))
|
||||
break;
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN:
|
||||
if (substream->stream
|
||||
!= SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
if (prtd->mmap_flag) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
} else {
|
||||
while (atomic_read(&prtd->out_needed)) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
atomic_dec(&prtd->out_needed);
|
||||
wake_up(&the_locks.write_wait);
|
||||
};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
int dev_rate = 48000;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
|
||||
runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
atomic_set(&prtd->in_count, 0);
|
||||
for (i = 0; i < MAX_COPP; i++) {
|
||||
pr_debug("prtd->session_id = %d, copp_id= %d",
|
||||
prtd->session_id, i);
|
||||
if (session_route.playback_session[substream->number][i]
|
||||
!= DEVICE_IGNORE) {
|
||||
pr_err("Device active\n");
|
||||
if (i == PCM_RX)
|
||||
dev_rate = 8000;
|
||||
msm_snddev_set_dec(prtd->session_id,
|
||||
i, 1, dev_rate, runtime->channels);
|
||||
}
|
||||
}
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
int dev_rate = 48000;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
|
||||
pr_debug("Channel = %d\n", prtd->channel_mode);
|
||||
ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
|
||||
prtd->channel_mode);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: cmd cfg pcm was block failed", __func__);
|
||||
|
||||
for (i = 0; i < runtime->periods; i++)
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
prtd->periods = runtime->periods;
|
||||
for (i = 0; i < MAX_COPP; i++) {
|
||||
pr_debug("prtd->session_id = %d, copp_id= %d",
|
||||
prtd->session_id,
|
||||
session_route.capture_session[prtd->session_id][i]);
|
||||
if (session_route.capture_session[prtd->session_id][i]
|
||||
!= DEVICE_IGNORE) {
|
||||
if (i == PCM_RX)
|
||||
dev_rate = 8000;
|
||||
msm_snddev_set_enc(prtd->session_id, i, 1, dev_rate, 1);
|
||||
}
|
||||
}
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_START\n");
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_debug("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm in open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* The session id returned by q6asm_open_read above is random and
|
||||
* hence we cannot use the session id to route from user space.
|
||||
* This results in need of a hardcoded session id for both playback
|
||||
* and capture sessions. we can use the subdevice id to identify
|
||||
* the session and use that for routing. Hence using
|
||||
* substream->number as the session id for routing purpose. However
|
||||
* DSP understands the session based on the allocated session id,
|
||||
* hence using the variable prtd->session_id for all dsp commands.
|
||||
*/
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
prtd->cmd_ack = 1;
|
||||
prtd->device_events = AUDDEV_EVT_DEV_RDY |
|
||||
AUDDEV_EVT_STREAM_VOL_CHG |
|
||||
AUDDEV_EVT_DEV_RLS;
|
||||
prtd->source = msm_snddev_route_dec(prtd->session_id);
|
||||
pr_debug("Register device event listener for"
|
||||
"SNDRV_PCM_STREAM_PLAYBACK session %d\n",
|
||||
substream->number);
|
||||
ret = auddev_register_evt_listner(prtd->device_events,
|
||||
AUDDEV_CLNT_DEC, substream->number,
|
||||
alsa_out_listener, (void *) prtd);
|
||||
if (ret)
|
||||
pr_debug("failed to register device event listener\n");
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
prtd->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
|
||||
AUDDEV_EVT_FREQ_CHG;
|
||||
prtd->source = msm_snddev_route_enc(prtd->session_id);
|
||||
pr_debug("Register device event listener for"
|
||||
"SNDRV_PCM_STREAM_CAPTURE session %d\n",
|
||||
substream->number);
|
||||
ret = auddev_register_evt_listner(prtd->device_events,
|
||||
AUDDEV_CLNT_ENC, substream->number,
|
||||
alsa_in_listener, (void *) prtd);
|
||||
if (ret)
|
||||
pr_debug("failed to register device event listener\n");
|
||||
}
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
runtime->private_data = prtd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer = 0;
|
||||
char *bufptr = NULL;
|
||||
void *data = NULL;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
pr_debug("%s: prtd->out_count = %d\n",
|
||||
__func__, atomic_read(&prtd->out_count));
|
||||
ret = wait_event_timeout(the_locks.write_wait,
|
||||
(atomic_read(&prtd->out_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!atomic_read(&prtd->out_count)) {
|
||||
pr_debug("%s: pcm stopped out_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
if (bufptr) {
|
||||
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
|
||||
__func__, fbytes, xfer, size);
|
||||
xfer = fbytes;
|
||||
if (copy_from_user(bufptr, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
buf += xfer;
|
||||
fbytes -= xfer;
|
||||
pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
|
||||
if (atomic_read(&prtd->start)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp\n",
|
||||
__func__, xfer);
|
||||
ret = q6asm_write_nolock(prtd->audio_client, xfer,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
if (ret < 0) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
atomic_inc(&prtd->out_needed);
|
||||
atomic_dec(&prtd->out_count);
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
ret = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD_EOS failed\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC,
|
||||
substream->number);
|
||||
pr_debug("%s\n", __func__);
|
||||
msm_clear_session_id(prtd->session_id);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer;
|
||||
char *bufptr;
|
||||
void *data = NULL;
|
||||
static uint32_t idx;
|
||||
static uint32_t size;
|
||||
uint32_t offset = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
|
||||
pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
|
||||
pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
|
||||
pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
|
||||
|
||||
ret = wait_event_timeout(the_locks.read_wait,
|
||||
(atomic_read(&prtd->in_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (!atomic_read(&prtd->in_count)) {
|
||||
pr_debug("%s: pcm stopped in_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("Checking if valid buffer is available...%08x\n",
|
||||
(unsigned int) data);
|
||||
data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
pr_debug("Size = %d\n", size);
|
||||
pr_debug("fbytes = %d\n", fbytes);
|
||||
pr_debug("idx = %d\n", idx);
|
||||
if (bufptr) {
|
||||
xfer = fbytes;
|
||||
if (xfer > size)
|
||||
xfer = size;
|
||||
offset = in_frame_info[idx][1];
|
||||
pr_debug("Offset value = %d\n", offset);
|
||||
if (copy_to_user(buf, bufptr+offset, xfer)) {
|
||||
pr_err("Failed to copy buf to user\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
fbytes -= xfer;
|
||||
size -= xfer;
|
||||
in_frame_info[idx][1] += xfer;
|
||||
pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
|
||||
__func__, fbytes, size, xfer);
|
||||
pr_debug(" Sending next buffer to dsp\n");
|
||||
memset(&in_frame_info[idx], 0,
|
||||
sizeof(uint32_t) * 2);
|
||||
atomic_dec(&prtd->in_count);
|
||||
ret = q6asm_read_nolock(prtd->audio_client);
|
||||
if (ret < 0) {
|
||||
pr_err("q6asm read failed\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
pr_err("No valid buffer\n");
|
||||
|
||||
pr_debug("Returning from capture_copy... %d\n", ret);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = OUT;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
auddev_unregister_evt_listner(AUDDEV_CLNT_ENC,
|
||||
substream->number);
|
||||
msm_clear_session_id(prtd->session_id);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
dma_mmap_coherent(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
dir = OUT;
|
||||
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed \
|
||||
rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_pcm_new,
|
||||
};
|
||||
EXPORT_SYMBOL(msm_soc_platform);
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
.driver = {
|
||||
.name = "msm-dsp-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
92
sound/soc/msm/msm8x60-pcm.h
Normal file
92
sound/soc/msm/msm8x60-pcm.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
#include <sound/apr_audio.h>
|
||||
#include <sound/q6asm.h>
|
||||
|
||||
#define MAX_PLAYBACK_SESSIONS 2
|
||||
#define MAX_CAPTURE_SESSIONS 1
|
||||
#define MAX_COPP 12
|
||||
|
||||
extern int copy_count;
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used;
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct buffer_rec {
|
||||
void *data;
|
||||
unsigned int size;
|
||||
unsigned int read;
|
||||
unsigned int addr;
|
||||
};
|
||||
|
||||
struct audio_locks {
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t eos_wait;
|
||||
wait_queue_head_t enable_wait;
|
||||
};
|
||||
|
||||
extern struct audio_locks the_locks;
|
||||
|
||||
struct msm_audio {
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
uint16_t source; /* Encoding source bit mask */
|
||||
|
||||
struct audio_client *audio_client;
|
||||
|
||||
uint16_t session_id;
|
||||
int copp_id;
|
||||
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t dsp_cnt;
|
||||
|
||||
uint32_t device_events; /* device events interested in */
|
||||
int abort; /* set when error, like sample rate mismatch */
|
||||
|
||||
int enabled;
|
||||
int close_ack;
|
||||
int cmd_ack;
|
||||
atomic_t start;
|
||||
atomic_t out_count;
|
||||
atomic_t in_count;
|
||||
atomic_t out_needed;
|
||||
int periods;
|
||||
int mmap_flag;
|
||||
};
|
||||
|
||||
struct pcm_session {
|
||||
unsigned short playback_session[MAX_PLAYBACK_SESSIONS][MAX_COPP];
|
||||
unsigned short capture_session[MAX_CAPTURE_SESSIONS][MAX_COPP];
|
||||
};
|
||||
|
||||
/* platform data */
|
||||
extern struct snd_soc_platform_driver msm_soc_platform;
|
||||
extern struct pcm_session session_route;
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
1231
sound/soc/msm/msm8x60.c
Normal file
1231
sound/soc/msm/msm8x60.c
Normal file
File diff suppressed because it is too large
Load Diff
369
sound/soc/msm/msm_audio_mvs.h
Normal file
369
sound/soc/msm/msm_audio_mvs.h
Normal file
@@ -0,0 +1,369 @@
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __MSM_AUDIO_MVS_H
|
||||
#define __MSM_AUDIO_MVS_H
|
||||
#include <linux/msm_audio.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
#include <mach/debug_mm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
||||
#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \
|
||||
(AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned)
|
||||
#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
|
||||
(AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned)
|
||||
#define AUDIO_SET_SCR_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
|
||||
(AUDIO_MAX_COMMON_IOCTL_NUM + 2), unsigned)
|
||||
#define AUDIO_SET_DTX_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
|
||||
(AUDIO_MAX_COMMON_IOCTL_NUM + 3), unsigned)
|
||||
/* MVS modes */
|
||||
#define MVS_MODE_LINEAR_PCM 9
|
||||
|
||||
#define MVS_PROG 0x30000014
|
||||
#define MVS_VERS 0x00030001
|
||||
|
||||
#define MVS_CLIENT_ID_VOIP 0x00000003 /* MVS_CLIENT_VOIP */
|
||||
|
||||
#define MVS_ACQUIRE_PROC 4
|
||||
#define MVS_ENABLE_PROC 5
|
||||
#define MVS_RELEASE_PROC 6
|
||||
#define MVS_SET_PCM_MODE_PROC 9
|
||||
|
||||
#define MVS_EVENT_CB_TYPE_PROC 1
|
||||
#define MVS_PACKET_UL_FN_TYPE_PROC 2
|
||||
#define MVS_PACKET_DL_FN_TYPE_PROC 3
|
||||
|
||||
#define MVS_CB_FUNC_ID 0xAAAABBBB
|
||||
#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC
|
||||
#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD
|
||||
|
||||
/* MVS frame modes */
|
||||
|
||||
#define MVS_FRAME_MODE_PCM_UL 13
|
||||
#define MVS_FRAME_MODE_PCM_DL 14
|
||||
|
||||
/* MVS context */
|
||||
#define MVS_PKT_CONTEXT_ISR 0x00000001
|
||||
|
||||
/* Max voc packet size */
|
||||
#define MVS_MAX_VOC_PKT_SIZE 320
|
||||
|
||||
#define VOIP_MAX_Q_LEN 20
|
||||
#define MVS_MAX_Q_LEN 8
|
||||
#define RPC_TYPE_REQUEST 0
|
||||
#define RPC_TYPE_REPLY 1
|
||||
|
||||
#define RPC_STATUS_FAILURE 0
|
||||
#define RPC_STATUS_SUCCESS 1
|
||||
#define RPC_STATUS_REJECT 1
|
||||
|
||||
|
||||
#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
|
||||
#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
|
||||
#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
|
||||
|
||||
|
||||
enum audio_mvs_state_type { AUDIO_MVS_CLOSED, AUDIO_MVS_OPENED,
|
||||
AUDIO_MVS_PREPARING, AUDIO_MVS_ACQUIRE, AUDIO_MVS_ENABLED,
|
||||
AUDIO_MVS_CLOSING
|
||||
};
|
||||
|
||||
enum audio_mvs_event_type { AUDIO_MVS_COMMAND, AUDIO_MVS_MODE,
|
||||
AUDIO_MVS_NOTIFY
|
||||
};
|
||||
|
||||
enum audio_mvs_cmd_status_type { AUDIO_MVS_CMD_FAILURE, AUDIO_MVS_CMD_BUSY,
|
||||
AUDIO_MVS_CMD_SUCCESS
|
||||
};
|
||||
|
||||
enum audio_mvs_mode_status_type { AUDIO_MVS_MODE_NOT_AVAIL,
|
||||
AUDIO_MVS_MODE_INIT, AUDIO_MVS_MODE_READY
|
||||
};
|
||||
|
||||
enum audio_mvs_pkt_status_type { AUDIO_MVS_PKT_NORMAL, AUDIO_MVS_PKT_FAST,
|
||||
AUDIO_MVS_PKT_SLOW
|
||||
};
|
||||
|
||||
struct rpc_audio_mvs_acquire_args {
|
||||
uint32_t client_id;
|
||||
uint32_t cb_func_id;
|
||||
};
|
||||
|
||||
struct audio_mvs_acquire_msg {
|
||||
struct rpc_request_hdr rpc_hdr;
|
||||
struct rpc_audio_mvs_acquire_args acquire_args;
|
||||
};
|
||||
|
||||
struct rpc_audio_mvs_enable_args {
|
||||
uint32_t client_id;
|
||||
uint32_t mode;
|
||||
uint32_t ul_cb_func_id;
|
||||
uint32_t dl_cb_func_id;
|
||||
uint32_t context;
|
||||
};
|
||||
|
||||
struct audio_mvs_enable_msg {
|
||||
struct rpc_request_hdr rpc_hdr;
|
||||
struct rpc_audio_mvs_enable_args enable_args;
|
||||
};
|
||||
|
||||
struct audio_mvs_release_msg {
|
||||
struct rpc_request_hdr rpc_hdr;
|
||||
uint32_t client_id;
|
||||
};
|
||||
|
||||
struct audio_mvs_set_pcm_mode_msg {
|
||||
struct rpc_request_hdr rpc_hdr;
|
||||
uint32_t pcm_mode;
|
||||
};
|
||||
|
||||
struct audio_mvs_set_pcmwb_mode_msg {
|
||||
struct rpc_request_hdr rpc_hdr;
|
||||
uint32_t pcmwb_mode;
|
||||
};
|
||||
|
||||
struct audio_mvs_buffer {
|
||||
uint8_t *voc_pkt;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
union audio_mvs_event_data {
|
||||
struct mvs_ev_command_type {
|
||||
uint32_t event;
|
||||
uint32_t client_id;
|
||||
uint32_t cmd_status;
|
||||
} mvs_ev_command_type;
|
||||
|
||||
struct mvs_ev_mode_type {
|
||||
uint32_t event;
|
||||
uint32_t client_id;
|
||||
uint32_t mode_status;
|
||||
uint32_t mode;
|
||||
} mvs_ev_mode_type;
|
||||
|
||||
struct mvs_ev_notify_type {
|
||||
uint32_t event;
|
||||
uint32_t client_id;
|
||||
uint32_t buf_dir;
|
||||
uint32_t max_frames;
|
||||
} mvs_ev_notify_type;
|
||||
};
|
||||
|
||||
struct audio_mvs_cb_func_args {
|
||||
uint32_t cb_func_id;
|
||||
uint32_t valid_ptr;
|
||||
uint32_t event;
|
||||
union audio_mvs_event_data event_data;
|
||||
};
|
||||
|
||||
struct audio_mvs_frame_info_hdr {
|
||||
uint32_t frame_mode;
|
||||
uint32_t mvs_mode;
|
||||
uint32_t buf_free_cnt;
|
||||
};
|
||||
|
||||
struct audio_mvs_ul_cb_func_args {
|
||||
uint32_t cb_func_id;
|
||||
uint32_t pkt_len;
|
||||
uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE / 4];
|
||||
|
||||
uint32_t valid_ptr;
|
||||
|
||||
uint32_t frame_mode;
|
||||
uint32_t frame_mode_ignore;
|
||||
|
||||
struct audio_mvs_frame_info_hdr frame_info_hdr;
|
||||
|
||||
uint32_t pcm_frame;
|
||||
uint32_t pcm_mode;
|
||||
|
||||
uint32_t pkt_len_ignore;
|
||||
};
|
||||
|
||||
struct audio_mvs_ul_reply {
|
||||
struct rpc_reply_hdr reply_hdr;
|
||||
uint32_t valid_pkt_status_ptr;
|
||||
uint32_t pkt_status;
|
||||
};
|
||||
|
||||
struct audio_mvs_dl_cb_func_args {
|
||||
uint32_t cb_func_id;
|
||||
uint32_t valid_ptr;
|
||||
|
||||
uint32_t frame_mode;
|
||||
uint32_t frame_mode_ignore;
|
||||
|
||||
struct audio_mvs_frame_info_hdr frame_info_hdr;
|
||||
|
||||
uint32_t pcm_frame;
|
||||
uint32_t pcm_mode;
|
||||
|
||||
};
|
||||
|
||||
struct audio_mvs_dl_reply {
|
||||
struct rpc_reply_hdr reply_hdr;
|
||||
uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE / 4];
|
||||
uint32_t valid_frame_info_ptr;
|
||||
|
||||
uint32_t frame_mode;
|
||||
uint32_t frame_mode_again;
|
||||
|
||||
struct audio_mvs_frame_info_hdr frame_info_hdr;
|
||||
|
||||
uint32_t pcm_frame;
|
||||
uint32_t pcm_mode;
|
||||
|
||||
uint32_t valid_pkt_status_ptr;
|
||||
uint32_t pkt_status;
|
||||
};
|
||||
|
||||
struct audio_mvs_info_type {
|
||||
enum audio_mvs_state_type state;
|
||||
uint32_t frame_mode;
|
||||
uint32_t mvs_mode;
|
||||
uint32_t buf_free_cnt;
|
||||
uint32_t pcm_frame;
|
||||
uint32_t pcm_mode;
|
||||
uint32_t out_sample_rate;
|
||||
uint32_t out_channel_mode;
|
||||
uint32_t out_weight;
|
||||
uint32_t out_buffer_size;
|
||||
int dl_play;
|
||||
struct msm_rpc_endpoint *rpc_endpt;
|
||||
uint32_t rpc_prog;
|
||||
uint32_t rpc_ver;
|
||||
uint32_t rpc_status;
|
||||
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_playback_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_playback_buf_pos; /* position in buffer */
|
||||
|
||||
unsigned int pcm_capture_size;
|
||||
unsigned int pcm_capture_count;
|
||||
unsigned int pcm_capture_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_capture_buf_pos; /* position in buffer */
|
||||
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
|
||||
uint8_t *mem_chunk;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
struct audio_mvs_buffer in[MVS_MAX_Q_LEN];
|
||||
uint32_t in_read;
|
||||
uint32_t in_write;
|
||||
|
||||
struct audio_mvs_buffer out[MVS_MAX_Q_LEN];
|
||||
uint32_t out_read;
|
||||
uint32_t out_write;
|
||||
|
||||
struct task_struct *task;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
wait_queue_head_t prepare_wait;
|
||||
wait_queue_head_t out_wait;
|
||||
wait_queue_head_t in_wait;
|
||||
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex prepare_lock;
|
||||
struct mutex in_lock;
|
||||
struct mutex out_lock;
|
||||
|
||||
struct wake_lock suspend_lock;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
struct timer_list timer;
|
||||
unsigned long expiry;
|
||||
int ack_dl_count;
|
||||
int ack_ul_count;
|
||||
int prepare_ack;
|
||||
int playback_start;
|
||||
int capture_start;
|
||||
unsigned long expiry_delta;
|
||||
int mvs_enable;
|
||||
int playback_enable;
|
||||
int capture_enable;
|
||||
int instance;
|
||||
|
||||
};
|
||||
|
||||
struct audio_voip_info_type {
|
||||
enum audio_mvs_state_type state;
|
||||
enum audio_mvs_state_type playback_state;
|
||||
enum audio_mvs_state_type capture_state;
|
||||
|
||||
unsigned int pcm_playback_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_playback_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_playback_buf_pos; /* position in buffer */
|
||||
|
||||
unsigned int pcm_capture_size;
|
||||
unsigned int pcm_capture_count;
|
||||
unsigned int pcm_capture_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_capture_buf_pos; /* position in buffer */
|
||||
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
struct audio_mvs_buffer in[VOIP_MAX_Q_LEN];
|
||||
uint32_t in_read;
|
||||
uint32_t in_write;
|
||||
|
||||
struct audio_mvs_buffer out[VOIP_MAX_Q_LEN];
|
||||
uint32_t out_read;
|
||||
uint32_t out_write;
|
||||
|
||||
wait_queue_head_t out_wait;
|
||||
wait_queue_head_t in_wait;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex prepare_lock;
|
||||
|
||||
struct wake_lock suspend_lock;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
int playback_start;
|
||||
int capture_start;
|
||||
int instance;
|
||||
};
|
||||
|
||||
enum msm_audio_pcm_frame_type {
|
||||
MVS_AMR_SPEECH_GOOD, /* Good speech frame */
|
||||
MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */
|
||||
MVS_AMR_ONSET, /* onset */
|
||||
MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */
|
||||
MVS_AMR_SID_FIRST, /* First silence descriptor */
|
||||
MVS_AMR_SID_UPDATE, /* Comfort noise frame */
|
||||
MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */
|
||||
MVS_AMR_NO_DATA, /* Nothing to transmit */
|
||||
MVS_AMR_SPEECH_LOST, /* downlink speech lost */
|
||||
};
|
||||
|
||||
enum msm_audio_dtx_mode_type { MVS_DTX_OFF, MVS_DTX_ON
|
||||
};
|
||||
|
||||
struct msm_audio_mvs_config {
|
||||
uint32_t mvs_mode;
|
||||
uint32_t bit_rate;
|
||||
};
|
||||
|
||||
extern struct snd_soc_dai_driver msm_mvs_dais[2];
|
||||
extern struct snd_soc_codec_device soc_codec_dev_msm_mvs;
|
||||
extern struct snd_soc_platform_driver msm_mvs_soc_platform;
|
||||
extern struct snd_soc_platform_driver msm_voip_soc_platform;
|
||||
#endif /* __MSM_AUDIO_MVS_H */
|
||||
141
sound/soc/msm/mvs-dai.c
Normal file
141
sound/soc/msm/mvs-dai.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include "msm_audio_mvs.h"
|
||||
|
||||
static struct snd_soc_dai_driver msm_mvs_codec_dais[] = {
|
||||
{
|
||||
.name = "mvs-codec-dai",
|
||||
.playback = {
|
||||
.channels_max = 2,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_max = 2,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
static struct snd_soc_dai_driver msm_mvs_cpu_dais[] = {
|
||||
{
|
||||
.name = "mvs-cpu-dai",
|
||||
.playback = {
|
||||
.channels_max = 2,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_max = 2,
|
||||
.rates = (SNDRV_PCM_RATE_8000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 8000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_msm = {
|
||||
.compress_type = SND_SOC_FLAT_COMPRESSION,
|
||||
};
|
||||
|
||||
static __devinit int asoc_mvs_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
|
||||
msm_mvs_codec_dais, ARRAY_SIZE(msm_mvs_codec_dais));
|
||||
}
|
||||
|
||||
static int __devexit asoc_mvs_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __devinit int asoc_mvs_cpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_dai(&pdev->dev, msm_mvs_cpu_dais);
|
||||
}
|
||||
|
||||
static int __devexit asoc_mvs_cpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver asoc_mvs_codec_driver = {
|
||||
.probe = asoc_mvs_codec_probe,
|
||||
.remove = __devexit_p(asoc_mvs_codec_remove),
|
||||
.driver = {
|
||||
.name = "mvs-codec-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver asoc_mvs_cpu_driver = {
|
||||
.probe = asoc_mvs_cpu_probe,
|
||||
.remove = __devexit_p(asoc_mvs_cpu_remove),
|
||||
.driver = {
|
||||
.name = "mvs-cpu-dai",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mvs_codec_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_mvs_codec_driver);
|
||||
}
|
||||
|
||||
static void __exit mvs_codec_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_mvs_codec_driver);
|
||||
}
|
||||
|
||||
static int __init mvs_cpu_dai_init(void)
|
||||
{
|
||||
return platform_driver_register(&asoc_mvs_cpu_driver);
|
||||
}
|
||||
|
||||
static void __exit mvs_cpu_dai_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&asoc_mvs_cpu_driver);
|
||||
}
|
||||
|
||||
module_init(mvs_codec_dai_init);
|
||||
module_exit(mvs_codec_dai_exit);
|
||||
module_init(mvs_cpu_dai_init);
|
||||
module_exit(mvs_cpu_dai_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
2
sound/soc/msm/qdsp6/Makefile
Normal file
2
sound/soc/msm/qdsp6/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-y := q6asm.o q6adm.o q6afe.o
|
||||
obj-$(CONFIG_SND_SOC_VOICE) += q6voice.o
|
||||
1088
sound/soc/msm/qdsp6/q6adm.c
Normal file
1088
sound/soc/msm/qdsp6/q6adm.c
Normal file
File diff suppressed because it is too large
Load Diff
1738
sound/soc/msm/qdsp6/q6afe.c
Normal file
1738
sound/soc/msm/qdsp6/q6afe.c
Normal file
File diff suppressed because it is too large
Load Diff
3533
sound/soc/msm/qdsp6/q6asm.c
Normal file
3533
sound/soc/msm/qdsp6/q6asm.c
Normal file
File diff suppressed because it is too large
Load Diff
4013
sound/soc/msm/qdsp6/q6voice.c
Normal file
4013
sound/soc/msm/qdsp6/q6voice.c
Normal file
File diff suppressed because it is too large
Load Diff
997
sound/soc/msm/qdsp6/q6voice.h
Normal file
997
sound/soc/msm/qdsp6/q6voice.h
Normal file
@@ -0,0 +1,997 @@
|
||||
/* Copyright (c) 2011-2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 __QDSP6VOICE_H__
|
||||
#define __QDSP6VOICE_H__
|
||||
|
||||
#include <mach/qdsp6v2/apr.h>
|
||||
#include <linux/ion.h>
|
||||
|
||||
#define MAX_VOC_PKT_SIZE 642
|
||||
#define SESSION_NAME_LEN 20
|
||||
|
||||
#define VOC_REC_UPLINK 0x00
|
||||
#define VOC_REC_DOWNLINK 0x01
|
||||
#define VOC_REC_BOTH 0x02
|
||||
|
||||
struct voice_header {
|
||||
uint32_t id;
|
||||
uint32_t data_len;
|
||||
};
|
||||
|
||||
struct voice_init {
|
||||
struct voice_header hdr;
|
||||
void *cb_handle;
|
||||
};
|
||||
|
||||
/* Device information payload structure */
|
||||
|
||||
struct device_data {
|
||||
uint32_t volume; /* in index */
|
||||
uint32_t mute;
|
||||
uint32_t sample;
|
||||
uint32_t enabled;
|
||||
uint32_t dev_id;
|
||||
uint32_t port_id;
|
||||
};
|
||||
|
||||
struct voice_dev_route_state {
|
||||
u16 rx_route_flag;
|
||||
u16 tx_route_flag;
|
||||
};
|
||||
|
||||
struct voice_rec_route_state {
|
||||
u16 ul_flag;
|
||||
u16 dl_flag;
|
||||
};
|
||||
|
||||
enum {
|
||||
VOC_INIT = 0,
|
||||
VOC_RUN,
|
||||
VOC_CHANGE,
|
||||
VOC_RELEASE,
|
||||
VOC_STANDBY,
|
||||
};
|
||||
|
||||
/* Common */
|
||||
#define VSS_ICOMMON_CMD_SET_UI_PROPERTY 0x00011103
|
||||
/* Set a UI property */
|
||||
#define VSS_ICOMMON_CMD_MAP_MEMORY 0x00011025
|
||||
#define VSS_ICOMMON_CMD_UNMAP_MEMORY 0x00011026
|
||||
/* General shared memory; byte-accessible, 4 kB-aligned. */
|
||||
#define VSS_ICOMMON_MAP_MEMORY_SHMEM8_4K_POOL 3
|
||||
|
||||
struct vss_icommon_cmd_map_memory_t {
|
||||
uint32_t phys_addr;
|
||||
/* Physical address of a memory region; must be at least
|
||||
* 4 kB aligned.
|
||||
*/
|
||||
|
||||
uint32_t mem_size;
|
||||
/* Number of bytes in the region; should be a multiple of 32. */
|
||||
|
||||
uint16_t mem_pool_id;
|
||||
/* Type of memory being provided. The memory ID implicitly defines
|
||||
* the characteristics of the memory. The characteristics might include
|
||||
* alignment type, permissions, etc.
|
||||
* Memory pool ID. Possible values:
|
||||
* 3 -- VSS_ICOMMON_MEM_TYPE_SHMEM8_4K_POOL.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_icommon_cmd_unmap_memory_t {
|
||||
uint32_t phys_addr;
|
||||
/* Physical address of a memory region; must be at least
|
||||
* 4 kB aligned.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_map_memory_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_icommon_cmd_map_memory_t vss_map_mem;
|
||||
} __packed;
|
||||
|
||||
struct vss_unmap_memory_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_icommon_cmd_unmap_memory_t vss_unmap_mem;
|
||||
} __packed;
|
||||
|
||||
/* TO MVM commands */
|
||||
#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF
|
||||
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL 0x00011327
|
||||
/*
|
||||
* VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL
|
||||
* Description: This command is required to let MVM know
|
||||
* who is in control of session.
|
||||
* Payload: Defined by vss_imvm_cmd_set_policy_dual_control_t.
|
||||
* Result: Wait for APRV2_IBASIC_RSP_RESULT response.
|
||||
*/
|
||||
|
||||
#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE
|
||||
/* Create a new full control MVM session. */
|
||||
|
||||
#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
|
||||
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C
|
||||
/* Attach a stream to the MVM. */
|
||||
|
||||
#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D
|
||||
/* Detach a stream from the MVM. */
|
||||
|
||||
#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E
|
||||
/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc
|
||||
* to all the streams currently attached to it.
|
||||
*/
|
||||
|
||||
#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F
|
||||
/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this
|
||||
* vocproc from all the streams to which it is currently attached.
|
||||
*/
|
||||
|
||||
#define VSS_IMVM_CMD_START_VOICE 0x00011190
|
||||
/*
|
||||
* Start Voice call command.
|
||||
* Wait for APRV2_IBASIC_RSP_RESULT response.
|
||||
* No pay load.
|
||||
*/
|
||||
|
||||
#define VSS_IMVM_CMD_STANDBY_VOICE 0x00011191
|
||||
/* No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_IMVM_CMD_STOP_VOICE 0x00011192
|
||||
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8
|
||||
/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9
|
||||
/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
|
||||
#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196
|
||||
/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C
|
||||
/* Set the network type. */
|
||||
|
||||
#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0
|
||||
/* Set the voice timing parameters. */
|
||||
|
||||
#define VSS_IWIDEVOICE_CMD_SET_WIDEVOICE 0x00011243
|
||||
/* Enable/disable WideVoice */
|
||||
|
||||
enum msm_audio_voc_rate {
|
||||
VOC_0_RATE, /* Blank frame */
|
||||
VOC_8_RATE, /* 1/8 rate */
|
||||
VOC_4_RATE, /* 1/4 rate */
|
||||
VOC_2_RATE, /* 1/2 rate */
|
||||
VOC_1_RATE /* Full rate */
|
||||
};
|
||||
|
||||
struct vss_istream_cmd_set_tty_mode_t {
|
||||
uint32_t mode;
|
||||
/**<
|
||||
* TTY mode.
|
||||
*
|
||||
* 0 : TTY disabled
|
||||
* 1 : HCO
|
||||
* 2 : VCO
|
||||
* 3 : FULL
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_attach_vocproc_t {
|
||||
uint16_t handle;
|
||||
/**< Handle of vocproc being attached. */
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_detach_vocproc_t {
|
||||
uint16_t handle;
|
||||
/**< Handle of vocproc being detached. */
|
||||
} __packed;
|
||||
|
||||
struct vss_imvm_cmd_attach_stream_t {
|
||||
uint16_t handle;
|
||||
/* The stream handle to attach. */
|
||||
} __packed;
|
||||
|
||||
struct vss_imvm_cmd_detach_stream_t {
|
||||
uint16_t handle;
|
||||
/* The stream handle to detach. */
|
||||
} __packed;
|
||||
|
||||
struct vss_icommon_cmd_set_network_t {
|
||||
uint32_t network_id;
|
||||
/* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
|
||||
} __packed;
|
||||
|
||||
struct vss_icommon_cmd_set_voice_timing_t {
|
||||
uint16_t mode;
|
||||
/*
|
||||
* The vocoder frame synchronization mode.
|
||||
*
|
||||
* 0 : No frame sync.
|
||||
* 1 : Hard VFR (20ms Vocoder Frame Reference interrupt).
|
||||
*/
|
||||
uint16_t enc_offset;
|
||||
/*
|
||||
* The offset in microseconds from the VFR to deliver a Tx vocoder
|
||||
* packet. The offset should be less than 20000us.
|
||||
*/
|
||||
uint16_t dec_req_offset;
|
||||
/*
|
||||
* The offset in microseconds from the VFR to request for an Rx vocoder
|
||||
* packet. The offset should be less than 20000us.
|
||||
*/
|
||||
uint16_t dec_offset;
|
||||
/*
|
||||
* The offset in microseconds from the VFR to indicate the deadline to
|
||||
* receive an Rx vocoder packet. The offset should be less than 20000us.
|
||||
* Rx vocoder packets received after this deadline are not guaranteed to
|
||||
* be processed.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_imvm_cmd_create_control_session_t {
|
||||
char name[SESSION_NAME_LEN];
|
||||
/*
|
||||
* A variable-sized stream name.
|
||||
*
|
||||
* The stream name size is the payload size minus the size of the other
|
||||
* fields.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
|
||||
struct vss_imvm_cmd_set_policy_dual_control_t {
|
||||
bool enable_flag;
|
||||
/* Set to TRUE to enable modem state machine control */
|
||||
} __packed;
|
||||
|
||||
struct vss_iwidevoice_cmd_set_widevoice_t {
|
||||
uint32_t enable;
|
||||
/* WideVoice enable/disable; possible values:
|
||||
* - 0 -- WideVoice disabled
|
||||
* - 1 -- WideVoice enabled
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct mvm_attach_vocproc_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
|
||||
} __packed;
|
||||
|
||||
struct mvm_detach_vocproc_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle;
|
||||
} __packed;
|
||||
|
||||
struct mvm_create_ctl_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_imvm_cmd_create_control_session_t mvm_session;
|
||||
} __packed;
|
||||
|
||||
struct mvm_modem_dual_control_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_imvm_cmd_set_policy_dual_control_t voice_ctl;
|
||||
} __packed;
|
||||
|
||||
struct mvm_set_tty_mode_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_set_tty_mode_t tty_mode;
|
||||
} __packed;
|
||||
|
||||
struct mvm_attach_stream_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_imvm_cmd_attach_stream_t attach_stream;
|
||||
} __packed;
|
||||
|
||||
struct mvm_detach_stream_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_imvm_cmd_detach_stream_t detach_stream;
|
||||
} __packed;
|
||||
|
||||
struct mvm_set_network_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_icommon_cmd_set_network_t network;
|
||||
} __packed;
|
||||
|
||||
struct mvm_set_voice_timing_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_icommon_cmd_set_voice_timing_t timing;
|
||||
} __packed;
|
||||
|
||||
struct mvm_set_widevoice_enable_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_iwidevoice_cmd_set_widevoice_t vss_set_wv;
|
||||
} __packed;
|
||||
|
||||
/* TO CVS commands */
|
||||
#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140
|
||||
/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7
|
||||
/* Create a new full control stream session. */
|
||||
|
||||
#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
|
||||
|
||||
#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022
|
||||
|
||||
#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA 0x00011279
|
||||
|
||||
#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA 0x0001127A
|
||||
|
||||
#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186
|
||||
/* Set media type on the stream. */
|
||||
|
||||
#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015
|
||||
/* Event sent by the stream to its client to provide an encoded packet. */
|
||||
|
||||
#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017
|
||||
/* Event sent by the stream to its client requesting for a decoder packet.
|
||||
* The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event.
|
||||
*/
|
||||
|
||||
#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016
|
||||
/* Event sent by the client to the stream in response to a
|
||||
* VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet.
|
||||
*/
|
||||
|
||||
#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E
|
||||
/* Set AMR encoder rate. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F
|
||||
/* Set AMR-WB encoder rate. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019
|
||||
/* Set encoder minimum and maximum rate. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D
|
||||
/* Set encoder DTX mode. */
|
||||
|
||||
#define MODULE_ID_VOICE_MODULE_FENS 0x00010EEB
|
||||
#define MODULE_ID_VOICE_MODULE_ST 0x00010EE3
|
||||
#define VOICE_PARAM_MOD_ENABLE 0x00010E00
|
||||
#define MOD_ENABLE_PARAM_LEN 4
|
||||
|
||||
#define VSS_ISTREAM_CMD_START_PLAYBACK 0x00011238
|
||||
/* Start in-call music delivery on the Tx voice path. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_STOP_PLAYBACK 0x00011239
|
||||
/* Stop the in-call music delivery on the Tx voice path. */
|
||||
|
||||
#define VSS_ISTREAM_CMD_START_RECORD 0x00011236
|
||||
/* Start in-call conversation recording. */
|
||||
#define VSS_ISTREAM_CMD_STOP_RECORD 0x00011237
|
||||
/* Stop in-call conversation recording. */
|
||||
|
||||
#define VSS_TAP_POINT_NONE 0x00010F78
|
||||
/* Indicates no tapping for specified path. */
|
||||
|
||||
#define VSS_TAP_POINT_STREAM_END 0x00010F79
|
||||
/* Indicates that specified path should be tapped at the end of the stream. */
|
||||
|
||||
struct vss_istream_cmd_start_record_t {
|
||||
uint32_t rx_tap_point;
|
||||
/* Tap point to use on the Rx path. Supported values are:
|
||||
* VSS_TAP_POINT_NONE : Do not record Rx path.
|
||||
* VSS_TAP_POINT_STREAM_END : Rx tap point is at the end of the stream.
|
||||
*/
|
||||
uint32_t tx_tap_point;
|
||||
/* Tap point to use on the Tx path. Supported values are:
|
||||
* VSS_TAP_POINT_NONE : Do not record tx path.
|
||||
* VSS_TAP_POINT_STREAM_END : Tx tap point is at the end of the stream.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_create_passive_control_session_t {
|
||||
char name[SESSION_NAME_LEN];
|
||||
/**<
|
||||
* A variable-sized stream name.
|
||||
*
|
||||
* The stream name size is the payload size minus the size of the other
|
||||
* fields.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_set_mute_t {
|
||||
uint16_t direction;
|
||||
/**<
|
||||
* 0 : TX only
|
||||
* 1 : RX only
|
||||
* 2 : TX and Rx
|
||||
*/
|
||||
uint16_t mute_flag;
|
||||
/**<
|
||||
* Mute, un-mute.
|
||||
*
|
||||
* 0 : Silence disable
|
||||
* 1 : Silence enable
|
||||
* 2 : CNG enable. Applicable to TX only. If set on RX behavior
|
||||
* will be the same as 1
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_create_full_control_session_t {
|
||||
uint16_t direction;
|
||||
/*
|
||||
* Stream direction.
|
||||
*
|
||||
* 0 : TX only
|
||||
* 1 : RX only
|
||||
* 2 : TX and RX
|
||||
* 3 : TX and RX loopback
|
||||
*/
|
||||
uint32_t enc_media_type;
|
||||
/* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
|
||||
uint32_t dec_media_type;
|
||||
/* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
|
||||
uint32_t network_id;
|
||||
/* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
|
||||
char name[SESSION_NAME_LEN];
|
||||
/*
|
||||
* A variable-sized stream name.
|
||||
*
|
||||
* The stream name size is the payload size minus the size of the other
|
||||
* fields.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_set_media_type_t {
|
||||
uint32_t rx_media_id;
|
||||
/* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
|
||||
uint32_t tx_media_id;
|
||||
/* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_evt_send_enc_buffer_t {
|
||||
uint32_t media_id;
|
||||
/* Media ID of the packet. */
|
||||
uint8_t packet_data[MAX_VOC_PKT_SIZE];
|
||||
/* Packet data buffer. */
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_evt_send_dec_buffer_t {
|
||||
uint32_t media_id;
|
||||
/* Media ID of the packet. */
|
||||
uint8_t packet_data[MAX_VOC_PKT_SIZE];
|
||||
/* Packet data. */
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_voc_amr_set_enc_rate_t {
|
||||
uint32_t mode;
|
||||
/* Set the AMR encoder rate.
|
||||
*
|
||||
* 0x00000000 : 4.75 kbps
|
||||
* 0x00000001 : 5.15 kbps
|
||||
* 0x00000002 : 5.90 kbps
|
||||
* 0x00000003 : 6.70 kbps
|
||||
* 0x00000004 : 7.40 kbps
|
||||
* 0x00000005 : 7.95 kbps
|
||||
* 0x00000006 : 10.2 kbps
|
||||
* 0x00000007 : 12.2 kbps
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_voc_amrwb_set_enc_rate_t {
|
||||
uint32_t mode;
|
||||
/* Set the AMR-WB encoder rate.
|
||||
*
|
||||
* 0x00000000 : 6.60 kbps
|
||||
* 0x00000001 : 8.85 kbps
|
||||
* 0x00000002 : 12.65 kbps
|
||||
* 0x00000003 : 14.25 kbps
|
||||
* 0x00000004 : 15.85 kbps
|
||||
* 0x00000005 : 18.25 kbps
|
||||
* 0x00000006 : 19.85 kbps
|
||||
* 0x00000007 : 23.05 kbps
|
||||
* 0x00000008 : 23.85 kbps
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_cdma_set_enc_minmax_rate_t {
|
||||
uint16_t min_rate;
|
||||
/* Set the lower bound encoder rate.
|
||||
*
|
||||
* 0x0000 : Blank frame
|
||||
* 0x0001 : Eighth rate
|
||||
* 0x0002 : Quarter rate
|
||||
* 0x0003 : Half rate
|
||||
* 0x0004 : Full rate
|
||||
*/
|
||||
uint16_t max_rate;
|
||||
/* Set the upper bound encoder rate.
|
||||
*
|
||||
* 0x0000 : Blank frame
|
||||
* 0x0001 : Eighth rate
|
||||
* 0x0002 : Quarter rate
|
||||
* 0x0003 : Half rate
|
||||
* 0x0004 : Full rate
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_set_enc_dtx_mode_t {
|
||||
uint32_t enable;
|
||||
/* Toggle DTX on or off.
|
||||
*
|
||||
* 0 : Disables DTX
|
||||
* 1 : Enables DTX
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_istream_cmd_register_calibration_data_t {
|
||||
uint32_t phys_addr;
|
||||
/* Phsical address to be registered with stream. The calibration data
|
||||
* is stored at this address.
|
||||
*/
|
||||
uint32_t mem_size;
|
||||
/* Size of the calibration data in bytes. */
|
||||
};
|
||||
|
||||
struct vss_icommon_cmd_set_ui_property_enable_t {
|
||||
uint32_t module_id;
|
||||
/* Unique ID of the module. */
|
||||
uint32_t param_id;
|
||||
/* Unique ID of the parameter. */
|
||||
uint16_t param_size;
|
||||
/* Size of the parameter in bytes: MOD_ENABLE_PARAM_LEN */
|
||||
uint16_t reserved;
|
||||
/* Reserved; set to 0. */
|
||||
uint16_t enable;
|
||||
uint16_t reserved_field;
|
||||
/* Reserved, set to 0. */
|
||||
};
|
||||
|
||||
struct cvs_create_passive_ctl_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_create_passive_control_session_t cvs_session;
|
||||
} __packed;
|
||||
|
||||
struct cvs_create_full_ctl_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_create_full_control_session_t cvs_session;
|
||||
} __packed;
|
||||
|
||||
struct cvs_destroy_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_mute_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_set_mute_t cvs_set_mute;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_media_type_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_set_media_type_t media_type;
|
||||
} __packed;
|
||||
|
||||
struct cvs_send_dec_buf_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_evt_send_dec_buffer_t dec_buf;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_amr_enc_rate_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_amrwb_enc_rate_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_cdma_enc_minmax_rate_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_enc_dtx_mode_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode;
|
||||
} __packed;
|
||||
|
||||
struct cvs_register_cal_data_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_register_calibration_data_t cvs_cal_data;
|
||||
} __packed;
|
||||
|
||||
struct cvs_deregister_cal_data_cmd {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvs_set_pp_enable_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_icommon_cmd_set_ui_property_enable_t vss_set_pp;
|
||||
} __packed;
|
||||
struct cvs_start_record_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_istream_cmd_start_record_t rec_mode;
|
||||
} __packed;
|
||||
|
||||
/* TO CVP commands */
|
||||
|
||||
#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3
|
||||
/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
|
||||
|
||||
#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4
|
||||
|
||||
#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB
|
||||
|
||||
#define VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX 0x000110EE
|
||||
|
||||
#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6
|
||||
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1
|
||||
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
|
||||
|
||||
#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA 0x00011275
|
||||
#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276
|
||||
|
||||
#define VSS_IVOCPROC_CMD_REGISTER_VOLUME_CAL_TABLE 0x00011277
|
||||
#define VSS_IVOCPROC_CMD_DEREGISTER_VOLUME_CAL_TABLE 0x00011278
|
||||
|
||||
#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70
|
||||
#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71
|
||||
#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72
|
||||
|
||||
#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77
|
||||
|
||||
/* Newtwork IDs */
|
||||
#define VSS_NETWORK_ID_DEFAULT 0x00010037
|
||||
#define VSS_NETWORK_ID_VOIP_NB 0x00011240
|
||||
#define VSS_NETWORK_ID_VOIP_WB 0x00011241
|
||||
#define VSS_NETWORK_ID_VOIP_WV 0x00011242
|
||||
|
||||
/* Media types */
|
||||
#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2
|
||||
/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */
|
||||
#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6
|
||||
/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */
|
||||
#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7
|
||||
/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */
|
||||
#define VSS_MEDIA_ID_PCM_NB 0x00010FCB
|
||||
#define VSS_MEDIA_ID_PCM_WB 0x00010FCC
|
||||
/* Linear PCM (16-bit, little-endian). */
|
||||
#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD
|
||||
/* G.711 a-law (contains two 10ms vocoder frames). */
|
||||
#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE
|
||||
/* G.711 mu-law (contains two 10ms vocoder frames). */
|
||||
#define VSS_MEDIA_ID_G729 0x00010FD0
|
||||
/* G.729AB (contains two 10ms vocoder frames. */
|
||||
#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3
|
||||
/*CDMA EVRC-B vocoder modem format */
|
||||
#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4
|
||||
/*CDMA EVRC-WB vocoder modem format */
|
||||
|
||||
#define VSS_IVOCPROC_CMD_SET_MUTE 0x000110EF
|
||||
|
||||
#define VOICE_CMD_SET_PARAM 0x00011006
|
||||
#define VOICE_CMD_GET_PARAM 0x00011007
|
||||
#define VOICE_EVT_GET_PARAM_ACK 0x00011008
|
||||
|
||||
struct vss_ivocproc_cmd_create_full_control_session_t {
|
||||
uint16_t direction;
|
||||
/*
|
||||
* stream direction.
|
||||
* 0 : TX only
|
||||
* 1 : RX only
|
||||
* 2 : TX and RX
|
||||
*/
|
||||
uint32_t tx_port_id;
|
||||
/*
|
||||
* TX device port ID which vocproc will connect to. If not supplying a
|
||||
* port ID set to VSS_IVOCPROC_PORT_ID_NONE.
|
||||
*/
|
||||
uint32_t tx_topology_id;
|
||||
/*
|
||||
* Tx leg topology ID. If not supplying a topology ID set to
|
||||
* VSS_IVOCPROC_TOPOLOGY_ID_NONE.
|
||||
*/
|
||||
uint32_t rx_port_id;
|
||||
/*
|
||||
* RX device port ID which vocproc will connect to. If not supplying a
|
||||
* port ID set to VSS_IVOCPROC_PORT_ID_NONE.
|
||||
*/
|
||||
uint32_t rx_topology_id;
|
||||
/*
|
||||
* Rx leg topology ID. If not supplying a topology ID set to
|
||||
* VSS_IVOCPROC_TOPOLOGY_ID_NONE.
|
||||
*/
|
||||
int32_t network_id;
|
||||
/*
|
||||
* Network ID. (Refer to VSS_NETWORK_ID_XXX). If not supplying a network
|
||||
* ID set to VSS_NETWORK_ID_DEFAULT.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_ivocproc_cmd_set_volume_index_t {
|
||||
uint16_t vol_index;
|
||||
/**<
|
||||
* Volume index utilized by the vocproc to index into the volume table
|
||||
* provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set
|
||||
* volume on the VDSP.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_ivocproc_cmd_set_device_t {
|
||||
uint32_t tx_port_id;
|
||||
/**<
|
||||
* TX device port ID which vocproc will connect to.
|
||||
* VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
|
||||
*/
|
||||
uint32_t tx_topology_id;
|
||||
/**<
|
||||
* TX leg topology ID.
|
||||
* VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
|
||||
* pre/post-processing blocks and is pass-through.
|
||||
*/
|
||||
int32_t rx_port_id;
|
||||
/**<
|
||||
* RX device port ID which vocproc will connect to.
|
||||
* VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
|
||||
*/
|
||||
uint32_t rx_topology_id;
|
||||
/**<
|
||||
* RX leg topology ID.
|
||||
* VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
|
||||
* pre/post-processing blocks and is pass-through.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct vss_ivocproc_cmd_register_calibration_data_t {
|
||||
uint32_t phys_addr;
|
||||
/* Phsical address to be registered with vocproc. Calibration data
|
||||
* is stored at this address.
|
||||
*/
|
||||
uint32_t mem_size;
|
||||
/* Size of the calibration data in bytes. */
|
||||
} __packed;
|
||||
|
||||
struct vss_ivocproc_cmd_register_volume_cal_table_t {
|
||||
uint32_t phys_addr;
|
||||
/* Phsical address to be registered with the vocproc. The volume
|
||||
* calibration table is stored at this location.
|
||||
*/
|
||||
|
||||
uint32_t mem_size;
|
||||
/* Size of the volume calibration table in bytes. */
|
||||
} __packed;
|
||||
|
||||
struct vss_ivocproc_cmd_set_mute_t {
|
||||
uint16_t direction;
|
||||
/*
|
||||
* 0 : TX only.
|
||||
* 1 : RX only.
|
||||
* 2 : TX and Rx.
|
||||
*/
|
||||
uint16_t mute_flag;
|
||||
/*
|
||||
* Mute, un-mute.
|
||||
*
|
||||
* 0 : Disable.
|
||||
* 1 : Enable.
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct cvp_create_full_ctl_session_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_create_full_control_session_t cvp_session;
|
||||
} __packed;
|
||||
|
||||
struct cvp_command {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvp_set_device_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_set_device_t cvp_set_device;
|
||||
} __packed;
|
||||
|
||||
struct cvp_set_vp3_data_cmd {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvp_set_rx_volume_index_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
|
||||
} __packed;
|
||||
|
||||
struct cvp_register_cal_data_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_register_calibration_data_t cvp_cal_data;
|
||||
} __packed;
|
||||
|
||||
struct cvp_deregister_cal_data_cmd {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvp_register_vol_cal_table_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_register_volume_cal_table_t cvp_vol_cal_tbl;
|
||||
} __packed;
|
||||
|
||||
struct cvp_deregister_vol_cal_table_cmd {
|
||||
struct apr_hdr hdr;
|
||||
} __packed;
|
||||
|
||||
struct cvp_set_mute_cmd {
|
||||
struct apr_hdr hdr;
|
||||
struct vss_ivocproc_cmd_set_mute_t cvp_set_mute;
|
||||
} __packed;
|
||||
|
||||
/* CB for up-link packets. */
|
||||
typedef void (*ul_cb_fn)(uint8_t *voc_pkt,
|
||||
uint32_t pkt_len,
|
||||
void *private_data);
|
||||
|
||||
/* CB for down-link packets. */
|
||||
typedef void (*dl_cb_fn)(uint8_t *voc_pkt,
|
||||
uint32_t *pkt_len,
|
||||
void *private_data);
|
||||
|
||||
|
||||
struct mvs_driver_info {
|
||||
uint32_t media_type;
|
||||
uint32_t rate;
|
||||
uint32_t network_type;
|
||||
uint32_t dtx_mode;
|
||||
ul_cb_fn ul_cb;
|
||||
dl_cb_fn dl_cb;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct incall_rec_info {
|
||||
uint32_t rec_enable;
|
||||
uint32_t rec_mode;
|
||||
uint32_t recording;
|
||||
};
|
||||
|
||||
struct incall_music_info {
|
||||
uint32_t play_enable;
|
||||
uint32_t playing;
|
||||
int count;
|
||||
int force;
|
||||
};
|
||||
|
||||
struct voice_data {
|
||||
int voc_state;/*INIT, CHANGE, RELEASE, RUN */
|
||||
|
||||
wait_queue_head_t mvm_wait;
|
||||
wait_queue_head_t cvs_wait;
|
||||
wait_queue_head_t cvp_wait;
|
||||
|
||||
/* cache the values related to Rx and Tx */
|
||||
struct device_data dev_rx;
|
||||
struct device_data dev_tx;
|
||||
|
||||
u32 mvm_state;
|
||||
u32 cvs_state;
|
||||
u32 cvp_state;
|
||||
|
||||
/* Handle to MVM in the Q6 */
|
||||
u16 mvm_handle;
|
||||
/* Handle to CVS in the Q6 */
|
||||
u16 cvs_handle;
|
||||
/* Handle to CVP in the Q6 */
|
||||
u16 cvp_handle;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
uint16_t sidetone_gain;
|
||||
uint8_t tty_mode;
|
||||
/* widevoice enable value */
|
||||
uint8_t wv_enable;
|
||||
/* slowtalk enable value */
|
||||
uint32_t st_enable;
|
||||
/* FENC enable value */
|
||||
uint32_t fens_enable;
|
||||
|
||||
struct voice_dev_route_state voc_route_state;
|
||||
|
||||
u16 session_id;
|
||||
|
||||
struct incall_rec_info rec_info;
|
||||
|
||||
struct incall_music_info music_info;
|
||||
|
||||
struct voice_rec_route_state rec_route_state;
|
||||
};
|
||||
|
||||
struct cal_mem {
|
||||
struct ion_handle *handle;
|
||||
uint32_t phy;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
#define MAX_VOC_SESSIONS 3
|
||||
#define SESSION_ID_BASE 0xFFF0
|
||||
|
||||
struct common_data {
|
||||
/* these default values are for all devices */
|
||||
uint32_t default_mute_val;
|
||||
uint32_t default_vol_val;
|
||||
uint32_t default_sample_val;
|
||||
|
||||
/* APR to MVM in the Q6 */
|
||||
void *apr_q6_mvm;
|
||||
/* APR to CVS in the Q6 */
|
||||
void *apr_q6_cvs;
|
||||
/* APR to CVP in the Q6 */
|
||||
void *apr_q6_cvp;
|
||||
|
||||
struct ion_client *client;
|
||||
struct cal_mem cvp_cal;
|
||||
struct cal_mem cvs_cal;
|
||||
|
||||
struct mutex common_lock;
|
||||
|
||||
struct mvs_driver_info mvs_info;
|
||||
|
||||
struct voice_data voice[MAX_VOC_SESSIONS];
|
||||
};
|
||||
|
||||
void voc_register_mvs_cb(ul_cb_fn ul_cb,
|
||||
dl_cb_fn dl_cb,
|
||||
void *private_data);
|
||||
|
||||
void voc_config_vocoder(uint32_t media_type,
|
||||
uint32_t rate,
|
||||
uint32_t network_type,
|
||||
uint32_t dtx_mode);
|
||||
|
||||
enum {
|
||||
DEV_RX = 0,
|
||||
DEV_TX,
|
||||
};
|
||||
|
||||
enum {
|
||||
RX_PATH = 0,
|
||||
TX_PATH,
|
||||
};
|
||||
|
||||
/* called by alsa driver */
|
||||
int voc_set_pp_enable(uint16_t session_id, uint32_t module_id, uint32_t enable);
|
||||
int voc_get_pp_enable(uint16_t session_id, uint32_t module_id);
|
||||
int voc_set_widevoice_enable(uint16_t session_id, uint32_t wv_enable);
|
||||
uint32_t voc_get_widevoice_enable(uint16_t session_id);
|
||||
uint8_t voc_get_tty_mode(uint16_t session_id);
|
||||
int voc_set_tty_mode(uint16_t session_id, uint8_t tty_mode);
|
||||
int voc_start_voice_call(uint16_t session_id);
|
||||
int voc_standby_voice_call(uint16_t session_id);
|
||||
int voc_resume_voice_call(uint16_t session_id);
|
||||
int voc_end_voice_call(uint16_t session_id);
|
||||
int voc_set_rxtx_port(uint16_t session_id,
|
||||
uint32_t dev_port_id,
|
||||
uint32_t dev_type);
|
||||
int voc_set_rx_vol_index(uint16_t session_id, uint32_t dir, uint32_t voc_idx);
|
||||
int voc_set_tx_mute(uint16_t session_id, uint32_t dir, uint32_t mute);
|
||||
int voc_set_rx_device_mute(uint16_t session_id, uint32_t mute);
|
||||
int voc_get_rx_device_mute(uint16_t session_id);
|
||||
int voc_disable_cvp(uint16_t session_id);
|
||||
int voc_enable_cvp(uint16_t session_id);
|
||||
int voc_set_route_flag(uint16_t session_id, uint8_t path_dir, uint8_t set);
|
||||
uint8_t voc_get_route_flag(uint16_t session_id, uint8_t path_dir);
|
||||
|
||||
#define VOICE_SESSION_NAME "Voice session"
|
||||
#define VOIP_SESSION_NAME "VoIP session"
|
||||
#define VOLTE_SESSION_NAME "VoLTE session"
|
||||
uint16_t voc_get_session_id(char *name);
|
||||
|
||||
int voc_start_playback(uint32_t set);
|
||||
int voc_start_record(uint32_t port_id, uint32_t set);
|
||||
#endif
|
||||
4
sound/soc/msm/qdsp6v2/Makefile
Normal file
4
sound/soc/msm/qdsp6v2/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
|
||||
snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o
|
||||
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
|
||||
obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o
|
||||
666
sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
Normal file
666
sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
Normal file
@@ -0,0 +1,666 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
|
||||
#include "msm-compr-q6-v2.h"
|
||||
#include "msm-pcm-routing-v2.h"
|
||||
|
||||
struct snd_msm {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm compressed_audio = {NULL, 0x2000} ;
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
static struct snd_pcm_hardware msm_compr_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 1200 * 1024 * 2,
|
||||
.period_bytes_min = 4800,
|
||||
.period_bytes_max = 1200 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 512,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void compr_event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct compr_audio *compr = priv;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_aio_write_param param;
|
||||
struct audio_buffer *buf = NULL;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s opcode =%08x\n", __func__, opcode);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE_V2: {
|
||||
uint32_t *ptrmem = (uint32_t *)¶m;
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start)) {
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
break;
|
||||
} else
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
|
||||
if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
|
||||
break;
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
|
||||
__func__, prtd->pcm_count, prtd->out_head);
|
||||
pr_debug("%s:writing buffer[%d] from 0x%08x\n",
|
||||
__func__, prtd->out_head,
|
||||
((unsigned int)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count)));
|
||||
|
||||
param.paddr = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
|
||||
i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1) & (runtime->periods - 1);
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_EVENT_RENDERED_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN_V2: {
|
||||
if (!atomic_read(&prtd->pending_buffer))
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer[%d] to dsp\n",
|
||||
__func__, prtd->pcm_count, prtd->out_head);
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
pr_debug("%s:writing buffer[%d] from 0x%08x\n",
|
||||
__func__, prtd->out_head,
|
||||
((unsigned int)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count)));
|
||||
param.paddr = (unsigned long)buf[prtd->out_head].phys;
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[prtd->out_head].phys;
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1)
|
||||
& (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
}
|
||||
break;
|
||||
case ASM_STREAM_CMD_FLUSH:
|
||||
pr_debug("ASM_STREAM_CMD_FLUSH\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct asm_aac_cfg aac_cfg;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
prtd->out_head = 0;
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
switch (compr->info.codec_param.codec.id) {
|
||||
case SND_AUDIOCODEC_MP3:
|
||||
/* No media format block for mp3 */
|
||||
break;
|
||||
case SND_AUDIOCODEC_AAC:
|
||||
pr_debug("SND_AUDIOCODEC_AAC\n");
|
||||
memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
|
||||
aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
|
||||
aac_cfg.format = 0x03;
|
||||
aac_cfg.ch_cfg = runtime->channels;
|
||||
aac_cfg.sample_rate = runtime->rate;
|
||||
ret = q6asm_media_format_block_aac(prtd->audio_client,
|
||||
&aac_cfg);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD Format block failed\n", __func__);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->pcm_irq_pos = 0;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void populate_codec_list(struct compr_audio *compr,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
/* MP3 Block */
|
||||
compr->info.compr_cap.num_codecs = 1;
|
||||
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
|
||||
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
|
||||
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
|
||||
compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
|
||||
compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
|
||||
compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
|
||||
/* Add new codecs here */
|
||||
}
|
||||
|
||||
static int msm_compr_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct compr_audio *compr;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
|
||||
if (compr == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd = &compr->prtd;
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)compr_event_handler, compr);
|
||||
if (!prtd->audio_client) {
|
||||
pr_info("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
runtime->hw = msm_compr_hardware_playback;
|
||||
|
||||
pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
compr->codec = FORMAT_MP3;
|
||||
populate_codec_list(compr, runtime);
|
||||
runtime->private_data = compr;
|
||||
compressed_audio.prtd = &compr->prtd;
|
||||
ret = compressed_set_volume(compressed_audio.volume);
|
||||
if (ret < 0)
|
||||
pr_err("%s : Set Volume failed : %d", __func__, ret);
|
||||
|
||||
ret = q6asm_set_softpause(compressed_audio.prtd->audio_client,
|
||||
&softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(compressed_audio.prtd->audio_client,
|
||||
&softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compressed_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
|
||||
rc = q6asm_set_volume(compressed_audio.prtd->audio_client,
|
||||
volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
compressed_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_compr_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
int dir = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
compressed_audio.prtd = NULL;
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_compr_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
static int msm_compr_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_compr_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_compr_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_compr_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = q6asm_open_write(prtd->audio_client, compr->codec);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Session out open failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Set IO mode failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed "
|
||||
"rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_compr_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct compr_audio *compr = runtime->private_data;
|
||||
struct msm_audio *prtd = &compr->prtd;
|
||||
uint64_t timestamp;
|
||||
uint64_t temp;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_COMPRESS_TSTAMP: {
|
||||
struct snd_compr_tstamp tstamp;
|
||||
pr_debug("SNDRV_COMPRESS_TSTAMP\n");
|
||||
|
||||
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
|
||||
timestamp = q6asm_get_session_time(prtd->audio_client);
|
||||
if (timestamp < 0) {
|
||||
pr_err("%s: Get Session Time return value =%lld\n",
|
||||
__func__, timestamp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
temp = (timestamp * 2 * runtime->channels);
|
||||
temp = temp * (runtime->rate/1000);
|
||||
temp = div_u64(temp, 1000);
|
||||
tstamp.sampling_rate = runtime->rate;
|
||||
tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
|
||||
tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
|
||||
tstamp.timestamp = timestamp;
|
||||
pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
|
||||
"timestamp = %lld,\n",
|
||||
__func__, tstamp.rendered, tstamp.decoded,
|
||||
tstamp.timestamp);
|
||||
if (copy_to_user((void *) arg, &tstamp,
|
||||
sizeof(struct snd_compr_tstamp)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_COMPRESS_GET_CAPS:
|
||||
pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
|
||||
if (copy_to_user((void *) arg, &compr->info.compr_cap,
|
||||
sizeof(struct snd_compr_caps))) {
|
||||
rc = -EFAULT;
|
||||
pr_err("%s: ERROR: copy to user\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
case SNDRV_COMPRESS_SET_PARAMS:
|
||||
pr_debug("SNDRV_COMPRESS_SET_PARAMS: ");
|
||||
if (copy_from_user(&compr->info.codec_param, (void *) arg,
|
||||
sizeof(struct snd_compr_params))) {
|
||||
rc = -EFAULT;
|
||||
pr_err("%s: ERROR: copy from user\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
switch (compr->info.codec_param.codec.id) {
|
||||
case SND_AUDIOCODEC_MP3:
|
||||
/* For MP3 we dont need any other parameter */
|
||||
pr_debug("SND_AUDIOCODEC_MP3\n");
|
||||
compr->codec = FORMAT_MP3;
|
||||
break;
|
||||
case SND_AUDIOCODEC_AAC:
|
||||
pr_debug("SND_AUDIOCODEC_AAC\n");
|
||||
compr->codec = FORMAT_MPEG4_AAC;
|
||||
break;
|
||||
default:
|
||||
pr_debug("FORMAT_LINEAR_PCM\n");
|
||||
compr->codec = FORMAT_LINEAR_PCM;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case SNDRV_PCM_IOCTL1_RESET:
|
||||
prtd->cmd_ack = 0;
|
||||
rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
|
||||
if (rc < 0)
|
||||
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("Flush cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_compr_ops = {
|
||||
.open = msm_compr_open,
|
||||
.hw_params = msm_compr_hw_params,
|
||||
.close = msm_compr_close,
|
||||
.ioctl = msm_compr_ioctl,
|
||||
.prepare = msm_compr_prepare,
|
||||
.trigger = msm_compr_trigger,
|
||||
.pointer = msm_compr_pointer,
|
||||
.mmap = msm_compr_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_compr_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_compr_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_compr_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_compr_driver = {
|
||||
.driver = {
|
||||
.name = "msm-compr-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_compr_probe,
|
||||
.remove = __devexit_p(msm_compr_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_compr_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_compr_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
36
sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
Normal file
36
sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_COMPR_H
|
||||
#define _MSM_COMPR_H
|
||||
#include <sound/apr_audio-v2.h>
|
||||
#include <sound/q6asm-v2.h>
|
||||
#include <sound/snd_compress_params.h>
|
||||
#include <sound/compress_offload.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "msm-pcm-q6-v2.h"
|
||||
|
||||
struct compr_info {
|
||||
struct snd_compr_caps compr_cap;
|
||||
struct snd_compr_codec_caps codec_caps;
|
||||
struct snd_compr_params codec_param;
|
||||
};
|
||||
|
||||
struct compr_audio {
|
||||
struct msm_audio prtd;
|
||||
struct compr_info info;
|
||||
uint32_t codec;
|
||||
};
|
||||
|
||||
#endif /*_MSM_COMPR_H*/
|
||||
1229
sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
Normal file
1229
sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
Normal file
File diff suppressed because it is too large
Load Diff
777
sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
Normal file
777
sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
Normal file
@@ -0,0 +1,777 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <asm/dma.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "msm-pcm-q6-v2.h"
|
||||
#include "msm-pcm-routing-v2.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
struct snd_msm_volume {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm_volume multi_ch_pcm_audio = {NULL, 0x2000};
|
||||
|
||||
#define PLAYBACK_NUM_PERIODS 8
|
||||
#define PLAYBACK_PERIOD_SIZE 4032
|
||||
#define CAPTURE_NUM_PERIODS 16
|
||||
#define CAPTURE_PERIOD_SIZE 320
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_capture = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_min = CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_max = CAPTURE_PERIOD_SIZE,
|
||||
.periods_min = CAPTURE_NUM_PERIODS,
|
||||
.periods_max = CAPTURE_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 6,
|
||||
.buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_min = PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_max = PLAYBACK_PERIOD_SIZE,
|
||||
.periods_min = PLAYBACK_NUM_PERIODS,
|
||||
.periods_max = PLAYBACK_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
uint32_t *ptrmem = (uint32_t *)payload;
|
||||
int i = 0;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE_V2: {
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start))
|
||||
break;
|
||||
if (!prtd->mmap_flag)
|
||||
break;
|
||||
if (q6asm_is_cpu_buf_avail_nolock(IN,
|
||||
prtd->audio_client,
|
||||
&size, &idx)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count, 0, 0, NO_TIMESTAMP);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_EVENT_RENDERED_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case ASM_DATA_EVENT_READ_DONE_V2: {
|
||||
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
|
||||
pr_debug("token = 0x%08x\n", token);
|
||||
for (i = 0; i < 8; i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
in_frame_info[token][0] = payload[2];
|
||||
in_frame_info[token][1] = payload[3];
|
||||
prtd->pcm_irq_pos += in_frame_info[token][0];
|
||||
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (atomic_read(&prtd->in_count) <= prtd->periods)
|
||||
atomic_inc(&prtd->in_count);
|
||||
wake_up(&the_locks.read_wait);
|
||||
if (prtd->mmap_flag
|
||||
&& q6asm_is_cpu_buf_avail_nolock(OUT,
|
||||
prtd->audio_client,
|
||||
&size, &idx))
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN_V2:
|
||||
if (substream->stream
|
||||
!= SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
}
|
||||
if (prtd->mmap_flag) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
} else {
|
||||
while (atomic_read(&prtd->out_needed)) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
atomic_dec(&prtd->out_needed);
|
||||
wake_up(&the_locks.write_wait);
|
||||
};
|
||||
}
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client,
|
||||
runtime->rate, runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_info("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
|
||||
pr_debug("Channel = %d\n", prtd->channel_mode);
|
||||
ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
|
||||
prtd->channel_mode);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: cmd cfg pcm was block failed", __func__);
|
||||
|
||||
for (i = 0; i < runtime->periods; i++)
|
||||
q6asm_read(prtd->audio_client);
|
||||
prtd->periods = runtime->periods;
|
||||
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_err("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw = msm_pcm_hardware_playback;
|
||||
ret = q6asm_open_write(prtd->audio_client,
|
||||
FORMAT_MULTI_CHANNEL_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_hardware_capture;
|
||||
ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm in open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
runtime->private_data = prtd;
|
||||
pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
|
||||
pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
|
||||
multi_ch_pcm_audio.prtd = prtd;
|
||||
ret = multi_ch_pcm_set_volume(multi_ch_pcm_audio.volume);
|
||||
if (ret < 0)
|
||||
pr_err("%s : Set Volume failed : %d", __func__, ret);
|
||||
|
||||
ret = q6asm_set_softpause(multi_ch_pcm_audio.prtd->audio_client,
|
||||
&softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(multi_ch_pcm_audio.prtd->audio_client,
|
||||
&softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multi_ch_pcm_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
pr_err("multi_ch_pcm_set_volume\n");
|
||||
|
||||
if (multi_ch_pcm_audio.prtd && multi_ch_pcm_audio.prtd->audio_client) {
|
||||
pr_err("%s q6asm_set_volume\n", __func__);
|
||||
rc = q6asm_set_volume(multi_ch_pcm_audio.prtd->audio_client,
|
||||
volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
multi_ch_pcm_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer = 0;
|
||||
char *bufptr = NULL;
|
||||
void *data = NULL;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
pr_debug("%s: prtd->out_count = %d\n",
|
||||
__func__, atomic_read(&prtd->out_count));
|
||||
ret = wait_event_timeout(the_locks.write_wait,
|
||||
(atomic_read(&prtd->out_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!atomic_read(&prtd->out_count)) {
|
||||
pr_err("%s: pcm stopped out_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
if (bufptr) {
|
||||
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
|
||||
__func__, fbytes, xfer, size);
|
||||
xfer = fbytes;
|
||||
if (copy_from_user(bufptr, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
buf += xfer;
|
||||
fbytes -= xfer;
|
||||
pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
|
||||
if (atomic_read(&prtd->start)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp\n",
|
||||
__func__, xfer);
|
||||
ret = q6asm_write(prtd->audio_client, xfer,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
if (ret < 0) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
atomic_inc(&prtd->out_needed);
|
||||
atomic_dec(&prtd->out_count);
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
ret = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD_EOS failed\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
multi_ch_pcm_audio.prtd = NULL;
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer;
|
||||
char *bufptr;
|
||||
void *data = NULL;
|
||||
static uint32_t idx;
|
||||
static uint32_t size;
|
||||
uint32_t offset = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
|
||||
pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
|
||||
pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
|
||||
pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
|
||||
|
||||
ret = wait_event_timeout(the_locks.read_wait,
|
||||
(atomic_read(&prtd->in_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (!atomic_read(&prtd->in_count)) {
|
||||
pr_debug("%s: pcm stopped in_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("Checking if valid buffer is available...%08x\n",
|
||||
(unsigned int) data);
|
||||
data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
pr_debug("Size = %d\n", size);
|
||||
pr_debug("fbytes = %d\n", fbytes);
|
||||
pr_debug("idx = %d\n", idx);
|
||||
if (bufptr) {
|
||||
xfer = fbytes;
|
||||
if (xfer > size)
|
||||
xfer = size;
|
||||
offset = in_frame_info[idx][1];
|
||||
pr_debug("Offset value = %d\n", offset);
|
||||
if (copy_to_user(buf, bufptr+offset, xfer)) {
|
||||
pr_err("Failed to copy buf to user\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
fbytes -= xfer;
|
||||
size -= xfer;
|
||||
in_frame_info[idx][1] += xfer;
|
||||
pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
|
||||
__func__, fbytes, size, xfer);
|
||||
pr_debug(" Sending next buffer to dsp\n");
|
||||
memset(&in_frame_info[idx], 0,
|
||||
sizeof(uint32_t) * 2);
|
||||
atomic_dec(&prtd->in_count);
|
||||
ret = q6asm_read(prtd->audio_client);
|
||||
if (ret < 0) {
|
||||
pr_err("q6asm read failed\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
pr_err("No valid buffer\n");
|
||||
|
||||
pr_debug("Returning from capture_copy... %d\n", ret);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = OUT;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
dir = OUT;
|
||||
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-multi-ch-pcm-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Multi channel PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
581
sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
Normal file
581
sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
Normal file
@@ -0,0 +1,581 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/q6adm-v2.h>
|
||||
#include <asm/dma.h>
|
||||
#include "msm-pcm-afe-v2.h"
|
||||
|
||||
#define MIN_PERIOD_SIZE (128 * 2)
|
||||
#define MAX_PERIOD_SIZE (128 * 2 * 2 * 6)
|
||||
static struct snd_pcm_hardware msm_afe_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MAX_PERIOD_SIZE * 32,
|
||||
.period_bytes_min = MIN_PERIOD_SIZE,
|
||||
.period_bytes_max = MAX_PERIOD_SIZE,
|
||||
.periods_min = 32,
|
||||
.periods_max = 384,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
|
||||
static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
|
||||
|
||||
static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct pcm_afe_info *prtd =
|
||||
container_of(hrt, struct pcm_afe_info, hrt);
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
u32 mem_map_handle = 0;
|
||||
if (prtd->start) {
|
||||
pr_debug("sending frame to DSP: poll_time: %d\n",
|
||||
prtd->poll_time);
|
||||
if (prtd->dsp_cnt == runtime->periods)
|
||||
prtd->dsp_cnt = 0;
|
||||
pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
|
||||
afe_rt_proxy_port_write(
|
||||
(prtd->dma_addr +
|
||||
(prtd->dsp_cnt *
|
||||
snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
|
||||
snd_pcm_lib_period_bytes(prtd->substream));
|
||||
prtd->dsp_cnt++;
|
||||
hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
|
||||
* 1000));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
} else
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct pcm_afe_info *prtd =
|
||||
container_of(hrt, struct pcm_afe_info, hrt);
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
u32 mem_map_handle = 0;
|
||||
if (prtd->start) {
|
||||
if (prtd->dsp_cnt == runtime->periods)
|
||||
prtd->dsp_cnt = 0;
|
||||
pr_err("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
|
||||
afe_rt_proxy_port_read(
|
||||
(prtd->dma_addr + (prtd->dsp_cnt
|
||||
* snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
|
||||
snd_pcm_lib_period_bytes(prtd->substream));
|
||||
prtd->dsp_cnt++;
|
||||
pr_debug("sending frame rec to DSP: poll_time: %d\n",
|
||||
prtd->poll_time);
|
||||
hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
|
||||
* 1000));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
} else
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
static void pcm_afe_process_tx_pkt(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload,
|
||||
void *priv)
|
||||
{
|
||||
struct pcm_afe_info *prtd = priv;
|
||||
unsigned long dsp_flags;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
uint16_t event;
|
||||
|
||||
if (prtd == NULL)
|
||||
return;
|
||||
substream = prtd->substream;
|
||||
runtime = substream->runtime;
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
|
||||
switch (opcode) {
|
||||
case AFE_EVENT_RT_PROXY_PORT_STATUS: {
|
||||
event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
|
||||
switch (event) {
|
||||
case AFE_EVENT_RTPORT_START: {
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->poll_time = ((unsigned long)((
|
||||
snd_pcm_lib_period_bytes
|
||||
(prtd->substream) *
|
||||
1000 * 1000)/
|
||||
(runtime->rate *
|
||||
runtime->channels * 2)));
|
||||
pr_debug("prtd->poll_time: %d",
|
||||
prtd->poll_time);
|
||||
hrtimer_start(&prtd->hrt,
|
||||
ns_to_ktime(0),
|
||||
HRTIMER_MODE_REL);
|
||||
break;
|
||||
}
|
||||
case AFE_EVENT_RTPORT_STOP:
|
||||
pr_debug("%s: event!=0\n", __func__);
|
||||
prtd->start = 0;
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_LOW_WM:
|
||||
pr_debug("%s: Underrun\n", __func__);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_HI_WM:
|
||||
pr_debug("%s: Overrun\n", __func__);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
|
||||
pr_debug("write done\n");
|
||||
prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
|
||||
(prtd->substream);
|
||||
snd_pcm_period_elapsed(prtd->substream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
|
||||
}
|
||||
|
||||
static void pcm_afe_process_rx_pkt(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload,
|
||||
void *priv)
|
||||
{
|
||||
struct pcm_afe_info *prtd = priv;
|
||||
unsigned long dsp_flags;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
uint16_t event;
|
||||
|
||||
if (prtd == NULL)
|
||||
return;
|
||||
substream = prtd->substream;
|
||||
runtime = substream->runtime;
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
|
||||
switch (opcode) {
|
||||
case AFE_EVENT_RT_PROXY_PORT_STATUS: {
|
||||
event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
|
||||
switch (event) {
|
||||
case AFE_EVENT_RTPORT_START: {
|
||||
prtd->dsp_cnt = 0;
|
||||
prtd->poll_time = ((unsigned long)((
|
||||
snd_pcm_lib_period_bytes(prtd->substream)
|
||||
* 1000 * 1000)/(runtime->rate
|
||||
* runtime->channels * 2)));
|
||||
hrtimer_start(&prtd->hrt,
|
||||
ns_to_ktime(0),
|
||||
HRTIMER_MODE_REL);
|
||||
pr_debug("prtd->poll_time : %d", prtd->poll_time);
|
||||
break;
|
||||
}
|
||||
case AFE_EVENT_RTPORT_STOP:
|
||||
pr_debug("%s: event!=0\n", __func__);
|
||||
prtd->start = 0;
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_LOW_WM:
|
||||
pr_debug("%s: Underrun\n", __func__);
|
||||
break;
|
||||
case AFE_EVENT_RTPORT_HI_WM:
|
||||
pr_debug("%s: Overrun\n", __func__);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
|
||||
pr_debug("Read done\n");
|
||||
prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
|
||||
(prtd->substream);
|
||||
snd_pcm_period_elapsed(prtd->substream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
|
||||
}
|
||||
|
||||
static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
|
||||
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
ret = afe_register_get_events(dai->id,
|
||||
pcm_afe_process_tx_pkt, prtd);
|
||||
if (ret < 0) {
|
||||
pr_err("afe-pcm:register for events failed\n");
|
||||
return ret;
|
||||
}
|
||||
pr_debug("%s:success\n", __func__);
|
||||
prtd->prepared++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
ret = afe_register_get_events(dai->id,
|
||||
pcm_afe_process_rx_pkt, prtd);
|
||||
if (ret < 0) {
|
||||
pr_err("afe-pcm:register for events failed\n");
|
||||
return ret;
|
||||
}
|
||||
pr_debug("%s:success\n", __func__);
|
||||
prtd->prepared++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 16000, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int msm_afe_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = NULL;
|
||||
int ret = 0;
|
||||
|
||||
prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
} else
|
||||
pr_debug("prtd %x\n", (unsigned int)prtd);
|
||||
|
||||
mutex_init(&prtd->lock);
|
||||
spin_lock_init(&prtd->dsp_lock);
|
||||
prtd->dsp_cnt = 0;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
runtime->hw = msm_afe_hardware;
|
||||
prtd->substream = substream;
|
||||
runtime->private_data = prtd;
|
||||
mutex_unlock(&prtd->lock);
|
||||
hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->hrt.function = afe_hrtimer_callback;
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
prtd->hrt.function = afe_hrtimer_rec_callback;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_err("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_afe_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_dma_buffer *dma_buf;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct pcm_afe_info *prtd;
|
||||
struct snd_soc_pcm_runtime *rtd = NULL;
|
||||
struct snd_soc_dai *dai = NULL;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (substream == NULL) {
|
||||
pr_err("substream is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rtd = substream->private_data;
|
||||
dai = rtd->cpu_dai;
|
||||
runtime = substream->runtime;
|
||||
prtd = runtime->private_data;
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = afe_unregister_get_events(dai->id);
|
||||
if (ret < 0)
|
||||
pr_err("AFE unregister for events failed\n");
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
ret = afe_unregister_get_events(dai->id);
|
||||
if (ret < 0)
|
||||
pr_err("AFE unregister for events failed\n");
|
||||
}
|
||||
hrtimer_cancel(&prtd->hrt);
|
||||
|
||||
rc = afe_cmd_memory_unmap(runtime->dma_addr);
|
||||
if (rc < 0)
|
||||
pr_err("AFE memory unmap failed\n");
|
||||
|
||||
pr_debug("release all buffer\n");
|
||||
dma_buf = &substream->dma_buffer;
|
||||
if (dma_buf == NULL) {
|
||||
pr_debug("dma_buf is NULL\n");
|
||||
goto done;
|
||||
}
|
||||
if (dma_buf->area != NULL) {
|
||||
dma_free_coherent(substream->pcm->card->dev,
|
||||
runtime->hw.buffer_bytes_max, dma_buf->area,
|
||||
dma_buf->addr);
|
||||
dma_buf->area = NULL;
|
||||
}
|
||||
done:
|
||||
pr_debug("%s: dai->id =%x\n", __func__, dai->id);
|
||||
mutex_unlock(&prtd->lock);
|
||||
prtd->prepared--;
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
static int msm_afe_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
prtd->pcm_irq_pos = 0;
|
||||
if (prtd->prepared)
|
||||
return 0;
|
||||
mutex_lock(&prtd->lock);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_afe_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_afe_capture_prepare(substream);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return ret;
|
||||
}
|
||||
static int msm_afe_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
dma_mmap_coherent(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
return 0;
|
||||
}
|
||||
static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
|
||||
prtd->start = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
|
||||
prtd->start = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int msm_afe_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
int rc;
|
||||
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
mutex_lock(&prtd->lock);
|
||||
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = dma_alloc_coherent(dma_buf->dev.dev,
|
||||
runtime->hw.buffer_bytes_max,
|
||||
&dma_buf->addr, GFP_KERNEL);
|
||||
|
||||
pr_debug("%s: dma_buf->area: 0x%p, dma_buf->addr: 0x%x", __func__,
|
||||
(unsigned int *) dma_buf->area, dma_buf->addr);
|
||||
if (!dma_buf->area) {
|
||||
pr_err("%s:MSM AFE memory allocation failed\n", __func__);
|
||||
mutex_unlock(&prtd->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
|
||||
prtd->dma_addr = (u32) dma_buf->addr;
|
||||
|
||||
mutex_unlock(&prtd->lock);
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
|
||||
rc = afe_cmd_memory_map(dma_buf->addr, dma_buf->bytes);
|
||||
if (rc < 0)
|
||||
pr_err("fail to map memory to DSP\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_afe_info *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_afe_ops = {
|
||||
.open = msm_afe_open,
|
||||
.hw_params = msm_afe_hw_params,
|
||||
.trigger = msm_afe_trigger,
|
||||
.close = msm_afe_close,
|
||||
.prepare = msm_afe_prepare,
|
||||
.mmap = msm_afe_mmap,
|
||||
.pointer = msm_afe_pointer,
|
||||
};
|
||||
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_afe_afe_probe(struct snd_soc_platform *platform)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_afe_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
.probe = msm_afe_afe_probe,
|
||||
};
|
||||
|
||||
static __devinit int msm_afe_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_afe_remove(struct platform_device *pdev)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_afe_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-afe",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_afe_probe,
|
||||
.remove = __devexit_p(msm_afe_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
return platform_driver_register(&msm_afe_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
platform_driver_unregister(&msm_afe_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AFE PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
45
sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
Normal file
45
sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_AFE_H
|
||||
#define _MSM_PCM_AFE_H
|
||||
#include <sound/apr_audio-v2.h>
|
||||
#include <sound/q6afe-v2.h>
|
||||
|
||||
|
||||
struct pcm_afe_info {
|
||||
unsigned long dma_addr;
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
struct mutex lock;
|
||||
spinlock_t dsp_lock;
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint8_t start;
|
||||
uint32_t dsp_cnt;
|
||||
uint32_t buf_phys;
|
||||
int32_t mmap_flag;
|
||||
int prepared;
|
||||
struct hrtimer hrt;
|
||||
int poll_time;
|
||||
};
|
||||
|
||||
|
||||
#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
|
||||
{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
||||
.name = xname, \
|
||||
.info = fp_info,\
|
||||
.get = fp_get, .put = fp_put, \
|
||||
.private_value = addr, \
|
||||
}
|
||||
|
||||
#endif /*_MSM_PCM_AFE_H*/
|
||||
609
sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
Normal file
609
sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
Normal file
@@ -0,0 +1,609 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
#include <sound/snd_compress_params.h>
|
||||
#include <sound/compress_offload.h>
|
||||
#include <sound/compress_driver.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
#include "msm-pcm-q6-v2.h"
|
||||
#include "msm-pcm-routing-v2.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct msm_audio *prtd;
|
||||
unsigned volume;
|
||||
};
|
||||
static struct snd_msm lpa_audio;
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 2 * 1024 * 1024,
|
||||
.period_bytes_min = 128 * 1024,
|
||||
.period_bytes_max = 512 * 1024,
|
||||
.periods_min = 4,
|
||||
.periods_max = 16,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_aio_write_param param;
|
||||
struct audio_buffer *buf = NULL;
|
||||
unsigned long flag = 0;
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
spin_lock_irqsave(&the_locks.event_lock, flag);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE_V2: {
|
||||
uint32_t *ptrmem = (uint32_t *)¶m;
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
else
|
||||
if (substream->timer_running)
|
||||
snd_timer_interrupt(substream->timer, 1);
|
||||
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start)) {
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
break;
|
||||
} else
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
param.paddr = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[0].phys
|
||||
+ (prtd->out_head * prtd->pcm_count);
|
||||
for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
|
||||
i++, ++ptrmem)
|
||||
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1) & (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_EVENT_RENDERED_EOS:
|
||||
pr_debug("ASM_DATA_CMDRSP_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN_V2: {
|
||||
if (!atomic_read(&prtd->pending_buffer))
|
||||
break;
|
||||
if (runtime->status->hw_ptr >=
|
||||
runtime->control->appl_ptr)
|
||||
break;
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__, prtd->pcm_count);
|
||||
buf = prtd->audio_client->port[IN].buf;
|
||||
param.paddr = (unsigned long)buf[prtd->out_head].phys;
|
||||
param.len = prtd->pcm_count;
|
||||
param.msw_ts = 0;
|
||||
param.lsw_ts = 0;
|
||||
param.flags = NO_TIMESTAMP;
|
||||
param.uid = (unsigned long)buf[prtd->out_head].phys;
|
||||
if (q6asm_async_write(prtd->audio_client,
|
||||
¶m) < 0)
|
||||
pr_err("%s:q6asm_async_write failed\n",
|
||||
__func__);
|
||||
else
|
||||
prtd->out_head =
|
||||
(prtd->out_head + 1)
|
||||
& (runtime->periods - 1);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
}
|
||||
break;
|
||||
case ASM_STREAM_CMD_FLUSH:
|
||||
pr_debug("ASM_STREAM_CMD_FLUSH\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&the_locks.event_lock, flag);
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
prtd->out_head = 0;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
|
||||
runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
pr_debug("%s\n", __func__);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->pcm_irq_pos = 0;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_START\n");
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
struct asm_softpause_params softpause = {
|
||||
.enable = SOFT_PAUSE_ENABLE,
|
||||
.period = SOFT_PAUSE_PERIOD,
|
||||
.step = SOFT_PAUSE_STEP,
|
||||
.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
|
||||
};
|
||||
struct asm_softvolume_params softvol = {
|
||||
.period = SOFT_VOLUME_PERIOD,
|
||||
.step = SOFT_VOLUME_STEP,
|
||||
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
runtime->hw = msm_pcm_hardware;
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_debug("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Set IO mode failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return -EPERM;
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_debug("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
atomic_set(&prtd->pending_buffer, 1);
|
||||
runtime->private_data = prtd;
|
||||
lpa_audio.prtd = prtd;
|
||||
lpa_set_volume(lpa_audio.volume);
|
||||
ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftPause Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lpa_set_volume(unsigned volume)
|
||||
{
|
||||
int rc = 0;
|
||||
if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
|
||||
rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Send Volume command failed"
|
||||
" rc=%d\n", __func__, rc);
|
||||
}
|
||||
}
|
||||
lpa_audio.volume = volume;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
If routing is still enabled, we need to issue EOS to
|
||||
the DSP
|
||||
To issue EOS to dsp, we need to be run state otherwise
|
||||
EOS is not honored.
|
||||
*/
|
||||
if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
|
||||
rc = q6asm_run(prtd->audio_client, 0, 0, 0);
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
pr_debug("%s\n", __func__);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("EOS cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
}
|
||||
|
||||
dir = IN;
|
||||
atomic_set(&prtd->pending_buffer, 0);
|
||||
lpa_audio.prtd = NULL;
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
return -EPERM;
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed "
|
||||
"rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
|
||||
if (buf == NULL || buf[0].data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
uint64_t timestamp;
|
||||
uint64_t temp;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_COMPRESS_TSTAMP: {
|
||||
struct snd_compr_tstamp tstamp;
|
||||
pr_debug("SNDRV_COMPRESS_TSTAMP\n");
|
||||
|
||||
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
|
||||
timestamp = q6asm_get_session_time(prtd->audio_client);
|
||||
if (timestamp < 0) {
|
||||
pr_err("%s: Get Session Time return value =%lld\n",
|
||||
__func__, timestamp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
temp = (timestamp * 2 * runtime->channels);
|
||||
temp = temp * (runtime->rate/1000);
|
||||
temp = div_u64(temp, 1000);
|
||||
tstamp.sampling_rate = runtime->rate;
|
||||
tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
|
||||
tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
|
||||
tstamp.timestamp = timestamp;
|
||||
pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
|
||||
"timestamp = %lld,\n",
|
||||
__func__, tstamp.rendered, tstamp.decoded,
|
||||
tstamp.timestamp);
|
||||
if (copy_to_user((void *) arg, &tstamp,
|
||||
sizeof(struct snd_compr_tstamp)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_PCM_IOCTL1_RESET:
|
||||
prtd->cmd_ack = 0;
|
||||
rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
|
||||
if (rc < 0)
|
||||
pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
|
||||
rc = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (rc < 0)
|
||||
pr_err("Flush cmd timeout\n");
|
||||
prtd->pcm_irq_pos = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = msm_pcm_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s: dev name %s\n",
|
||||
__func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-lpa",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
spin_lock_init(&the_locks.event_lock);
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
725
sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
Normal file
725
sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
|
||||
#include "msm-pcm-q6-v2.h"
|
||||
#include "msm-pcm-routing-v2.h"
|
||||
|
||||
static struct audio_locks the_locks;
|
||||
|
||||
struct snd_msm {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
#define PLAYBACK_NUM_PERIODS 8
|
||||
#define PLAYBACK_PERIOD_SIZE 2048
|
||||
#define CAPTURE_NUM_PERIODS 16
|
||||
#define CAPTURE_PERIOD_SIZE 512
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_capture = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_min = CAPTURE_PERIOD_SIZE,
|
||||
.period_bytes_max = CAPTURE_PERIOD_SIZE,
|
||||
.periods_min = CAPTURE_NUM_PERIODS,
|
||||
.periods_max = CAPTURE_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware msm_pcm_hardware_playback = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_min = PLAYBACK_PERIOD_SIZE,
|
||||
.period_bytes_max = PLAYBACK_PERIOD_SIZE,
|
||||
.periods_min = PLAYBACK_NUM_PERIODS,
|
||||
.periods_max = PLAYBACK_NUM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Conventional and unconventional sample rate supported */
|
||||
static unsigned int supported_sample_rates[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
};
|
||||
|
||||
static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
|
||||
.count = ARRAY_SIZE(supported_sample_rates),
|
||||
.list = supported_sample_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static void event_handler(uint32_t opcode,
|
||||
uint32_t token, uint32_t *payload, void *priv)
|
||||
{
|
||||
struct msm_audio *prtd = priv;
|
||||
struct snd_pcm_substream *substream = prtd->substream;
|
||||
uint32_t *ptrmem = (uint32_t *)payload;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
pr_err("%s\n", __func__);
|
||||
switch (opcode) {
|
||||
case ASM_DATA_EVENT_WRITE_DONE_V2: {
|
||||
pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
|
||||
pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
|
||||
prtd->pcm_irq_pos += prtd->pcm_count;
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
atomic_inc(&prtd->out_count);
|
||||
wake_up(&the_locks.write_wait);
|
||||
if (!atomic_read(&prtd->start))
|
||||
break;
|
||||
if (!prtd->mmap_flag)
|
||||
break;
|
||||
if (q6asm_is_cpu_buf_avail_nolock(IN,
|
||||
prtd->audio_client,
|
||||
&size, &idx)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
|
||||
__func__, prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count, 0, 0, NO_TIMESTAMP);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASM_DATA_EVENT_RENDERED_EOS:
|
||||
pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n");
|
||||
prtd->cmd_ack = 1;
|
||||
wake_up(&the_locks.eos_wait);
|
||||
break;
|
||||
case ASM_DATA_EVENT_READ_DONE_V2: {
|
||||
pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n");
|
||||
pr_debug("token = 0x%08x\n", token);
|
||||
in_frame_info[token][0] = payload[4];
|
||||
in_frame_info[token][1] = payload[5];
|
||||
prtd->pcm_irq_pos += in_frame_info[token][0];
|
||||
pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
|
||||
if (atomic_read(&prtd->start))
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (atomic_read(&prtd->in_count) <= prtd->periods)
|
||||
atomic_inc(&prtd->in_count);
|
||||
wake_up(&the_locks.read_wait);
|
||||
if (prtd->mmap_flag
|
||||
&& q6asm_is_cpu_buf_avail_nolock(OUT,
|
||||
prtd->audio_client,
|
||||
&size, &idx))
|
||||
q6asm_read_nolock(prtd->audio_client);
|
||||
break;
|
||||
}
|
||||
case APR_BASIC_RSP_RESULT: {
|
||||
switch (payload[0]) {
|
||||
case ASM_SESSION_CMD_RUN_V2:
|
||||
if (substream->stream
|
||||
!= SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
}
|
||||
if (prtd->mmap_flag) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
} else {
|
||||
while (atomic_read(&prtd->out_needed)) {
|
||||
pr_debug("%s:writing %d bytes"
|
||||
" of buffer to dsp\n",
|
||||
__func__,
|
||||
prtd->pcm_count);
|
||||
q6asm_write_nolock(prtd->audio_client,
|
||||
prtd->pcm_count,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
atomic_dec(&prtd->out_needed);
|
||||
wake_up(&the_locks.write_wait);
|
||||
};
|
||||
}
|
||||
atomic_set(&prtd->start, 1);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
|
||||
__func__, payload[0], payload[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
|
||||
runtime->channels);
|
||||
if (ret < 0)
|
||||
pr_info("%s: CMD Format block failed\n", __func__);
|
||||
|
||||
atomic_set(&prtd->out_count, runtime->periods);
|
||||
|
||||
prtd->enabled = 1;
|
||||
prtd->cmd_ack = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
/* rate and channels are sent to audio driver */
|
||||
prtd->samp_rate = runtime->rate;
|
||||
prtd->channel_mode = runtime->channels;
|
||||
|
||||
if (prtd->enabled)
|
||||
return 0;
|
||||
|
||||
pr_debug("Samp_rate = %d\n", prtd->samp_rate);
|
||||
pr_debug("Channel = %d\n", prtd->channel_mode);
|
||||
ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
|
||||
prtd->channel_mode);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: cmd cfg pcm was block failed", __func__);
|
||||
|
||||
for (i = 0; i < runtime->periods; i++)
|
||||
q6asm_read(prtd->audio_client);
|
||||
prtd->periods = runtime->periods;
|
||||
|
||||
prtd->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pr_debug("%s: Trigger start\n", __func__);
|
||||
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
|
||||
atomic_set(&prtd->start, 0);
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
break;
|
||||
prtd->cmd_ack = 0;
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
|
||||
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
|
||||
atomic_set(&prtd->start, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
pr_err("Failed to allocate memory for msm_audio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prtd->substream = substream;
|
||||
prtd->audio_client = q6asm_audio_client_alloc(
|
||||
(app_cb)event_handler, prtd);
|
||||
if (!prtd->audio_client) {
|
||||
pr_info("%s: Could not allocate memory\n", __func__);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw = msm_pcm_hardware_playback;
|
||||
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm out open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
/* Capture path */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw = msm_pcm_hardware_capture;
|
||||
ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pcm in open failed\n", __func__);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
|
||||
|
||||
prtd->session_id = prtd->audio_client->session;
|
||||
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
prtd->session_id, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prtd->cmd_ack = 1;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_sample_rates);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_list failed\n");
|
||||
/* Ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
pr_info("snd_pcm_hw_constraint_integer failed\n");
|
||||
|
||||
prtd->dsp_cnt = 0;
|
||||
runtime->private_data = prtd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer = 0;
|
||||
char *bufptr = NULL;
|
||||
void *data = NULL;
|
||||
uint32_t idx = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
pr_debug("%s: prtd->out_count = %d\n",
|
||||
__func__, atomic_read(&prtd->out_count));
|
||||
ret = wait_event_timeout(the_locks.write_wait,
|
||||
(atomic_read(&prtd->out_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!atomic_read(&prtd->out_count)) {
|
||||
pr_err("%s: pcm stopped out_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
if (bufptr) {
|
||||
pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
|
||||
__func__, fbytes, xfer, size);
|
||||
xfer = fbytes;
|
||||
if (copy_from_user(bufptr, buf, xfer)) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
buf += xfer;
|
||||
fbytes -= xfer;
|
||||
pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
|
||||
if (atomic_read(&prtd->start)) {
|
||||
pr_debug("%s:writing %d bytes of buffer to dsp\n",
|
||||
__func__, xfer);
|
||||
ret = q6asm_write(prtd->audio_client, xfer,
|
||||
0, 0, NO_TIMESTAMP);
|
||||
if (ret < 0) {
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
atomic_inc(&prtd->out_needed);
|
||||
atomic_dec(&prtd->out_count);
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = 0;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
dir = IN;
|
||||
ret = wait_event_timeout(the_locks.eos_wait,
|
||||
prtd->cmd_ack, 5 * HZ);
|
||||
if (ret < 0)
|
||||
pr_err("%s: CMD_EOS failed\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t hwoff, void __user *buf,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
int fbytes = 0;
|
||||
int xfer;
|
||||
char *bufptr;
|
||||
void *data = NULL;
|
||||
static uint32_t idx;
|
||||
static uint32_t size;
|
||||
uint32_t offset = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = substream->runtime->private_data;
|
||||
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
fbytes = frames_to_bytes(runtime, frames);
|
||||
|
||||
pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
|
||||
pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
|
||||
pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
|
||||
|
||||
ret = wait_event_timeout(the_locks.read_wait,
|
||||
(atomic_read(&prtd->in_count)), 5 * HZ);
|
||||
if (ret < 0) {
|
||||
pr_debug("%s: wait_event_timeout failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (!atomic_read(&prtd->in_count)) {
|
||||
pr_debug("%s: pcm stopped in_count 0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("Checking if valid buffer is available...%08x\n",
|
||||
(unsigned int) data);
|
||||
data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
|
||||
bufptr = data;
|
||||
pr_debug("Size = %d\n", size);
|
||||
pr_debug("fbytes = %d\n", fbytes);
|
||||
pr_debug("idx = %d\n", idx);
|
||||
if (bufptr) {
|
||||
xfer = fbytes;
|
||||
if (xfer > size)
|
||||
xfer = size;
|
||||
offset = in_frame_info[idx][1];
|
||||
pr_debug("Offset value = %d\n", offset);
|
||||
if (copy_to_user(buf, bufptr+offset, xfer)) {
|
||||
pr_err("Failed to copy buf to user\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
fbytes -= xfer;
|
||||
size -= xfer;
|
||||
in_frame_info[idx][1] += xfer;
|
||||
pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
|
||||
__func__, fbytes, size, xfer);
|
||||
pr_debug(" Sending next buffer to dsp\n");
|
||||
memset(&in_frame_info[idx], 0,
|
||||
sizeof(uint32_t) * 2);
|
||||
atomic_dec(&prtd->in_count);
|
||||
ret = q6asm_read(prtd->audio_client);
|
||||
if (ret < 0) {
|
||||
pr_err("q6asm read failed\n");
|
||||
ret = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
pr_err("No valid buffer\n");
|
||||
|
||||
pr_debug("Returning from capture_copy... %d\n", ret);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
int dir = OUT;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
|
||||
q6asm_audio_client_buf_free_contiguous(dir,
|
||||
prtd->audio_client);
|
||||
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
q6asm_audio_client_free(prtd->audio_client);
|
||||
kfree(prtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
|
||||
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_close(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_close(substream);
|
||||
return ret;
|
||||
}
|
||||
static int msm_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = msm_pcm_playback_prepare(substream);
|
||||
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ret = msm_pcm_capture_prepare(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->pcm_irq_pos >= prtd->pcm_size)
|
||||
prtd->pcm_irq_pos = 0;
|
||||
|
||||
pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
|
||||
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
|
||||
}
|
||||
|
||||
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int result = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
prtd->mmap_flag = 1;
|
||||
|
||||
if (runtime->dma_addr && runtime->dma_bytes) {
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
result = remap_pfn_range(vma, vma->vm_start,
|
||||
runtime->dma_addr >> PAGE_SHIFT,
|
||||
runtime->dma_bytes,
|
||||
vma->vm_page_prot);
|
||||
} else {
|
||||
pr_err("Physical address or size of buf is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct msm_audio *prtd = runtime->private_data;
|
||||
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
|
||||
struct audio_buffer *buf;
|
||||
int dir, ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = IN;
|
||||
else
|
||||
dir = OUT;
|
||||
pr_err("%s: before buf alloc\n", __func__);
|
||||
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
|
||||
prtd->audio_client,
|
||||
runtime->hw.period_bytes_min,
|
||||
runtime->hw.periods_max);
|
||||
if (ret < 0) {
|
||||
pr_err("Audio Start: Buffer Allocation failed "
|
||||
"rc = %d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pr_err("%s: after buf alloc\n", __func__);
|
||||
buf = prtd->audio_client->port[dir].buf;
|
||||
if (buf == NULL || buf[0].data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("%s:buf = %p\n", __func__, buf);
|
||||
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dma_buf->dev.dev = substream->pcm->card->dev;
|
||||
dma_buf->private_data = NULL;
|
||||
dma_buf->area = buf[0].data;
|
||||
dma_buf->addr = buf[0].phys;
|
||||
dma_buf->bytes = runtime->hw.buffer_bytes_max;
|
||||
if (!dma_buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops msm_pcm_ops = {
|
||||
.open = msm_pcm_open,
|
||||
.copy = msm_pcm_copy,
|
||||
.hw_params = msm_pcm_hw_params,
|
||||
.close = msm_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.prepare = msm_pcm_prepare,
|
||||
.trigger = msm_pcm_trigger,
|
||||
.pointer = msm_pcm_pointer,
|
||||
.mmap = msm_pcm_mmap,
|
||||
};
|
||||
|
||||
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver msm_soc_platform = {
|
||||
.ops = &msm_pcm_ops,
|
||||
.pcm_new = msm_asoc_pcm_new,
|
||||
};
|
||||
|
||||
static __devinit int msm_pcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
|
||||
return snd_soc_register_platform(&pdev->dev,
|
||||
&msm_soc_platform);
|
||||
}
|
||||
|
||||
static int msm_pcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msm_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "msm-pcm-dsp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = msm_pcm_probe,
|
||||
.remove = __devexit_p(msm_pcm_remove),
|
||||
};
|
||||
|
||||
static int __init msm_soc_platform_init(void)
|
||||
{
|
||||
init_waitqueue_head(&the_locks.enable_wait);
|
||||
init_waitqueue_head(&the_locks.eos_wait);
|
||||
init_waitqueue_head(&the_locks.write_wait);
|
||||
init_waitqueue_head(&the_locks.read_wait);
|
||||
|
||||
return platform_driver_register(&msm_pcm_driver);
|
||||
}
|
||||
module_init(msm_soc_platform_init);
|
||||
|
||||
static void __exit msm_soc_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msm_pcm_driver);
|
||||
}
|
||||
module_exit(msm_soc_platform_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCM module platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
84
sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
Normal file
84
sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2008 HTC Corporation
|
||||
* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org.
|
||||
*/
|
||||
|
||||
#ifndef _MSM_PCM_H
|
||||
#define _MSM_PCM_H
|
||||
#include <sound/apr_audio-v2.h>
|
||||
#include <sound/q6asm-v2.h>
|
||||
|
||||
|
||||
|
||||
/* Support unconventional sample rates 12000, 24000 as well */
|
||||
#define USE_RATE \
|
||||
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
|
||||
|
||||
extern int copy_count;
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
unsigned size;
|
||||
unsigned used;
|
||||
unsigned addr;
|
||||
};
|
||||
|
||||
struct buffer_rec {
|
||||
void *data;
|
||||
unsigned int size;
|
||||
unsigned int read;
|
||||
unsigned int addr;
|
||||
};
|
||||
|
||||
struct audio_locks {
|
||||
spinlock_t event_lock;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t eos_wait;
|
||||
wait_queue_head_t enable_wait;
|
||||
};
|
||||
|
||||
struct msm_audio {
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
uint16_t source; /* Encoding source bit mask */
|
||||
|
||||
struct audio_client *audio_client;
|
||||
|
||||
uint16_t session_id;
|
||||
|
||||
uint32_t samp_rate;
|
||||
uint32_t channel_mode;
|
||||
uint32_t dsp_cnt;
|
||||
|
||||
int abort; /* set when error, like sample rate mismatch */
|
||||
|
||||
int enabled;
|
||||
int close_ack;
|
||||
int cmd_ack;
|
||||
atomic_t start;
|
||||
atomic_t out_count;
|
||||
atomic_t in_count;
|
||||
atomic_t out_needed;
|
||||
int out_head;
|
||||
int periods;
|
||||
int mmap_flag;
|
||||
atomic_t pending_buffer;
|
||||
};
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
1834
sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
Normal file
1834
sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
Normal file
File diff suppressed because it is too large
Load Diff
103
sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
Normal file
103
sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _MSM_PCM_ROUTING_H
|
||||
#define _MSM_PCM_ROUTING_H
|
||||
#include <sound/apr_audio-v2.h>
|
||||
|
||||
#define LPASS_BE_PRI_I2S_RX "(Backend) PRIMARY_I2S_RX"
|
||||
#define LPASS_BE_PRI_I2S_TX "(Backend) PRIMARY_I2S_TX"
|
||||
#define LPASS_BE_SLIMBUS_0_RX "(Backend) SLIMBUS_0_RX"
|
||||
#define LPASS_BE_SLIMBUS_0_TX "(Backend) SLIMBUS_0_TX"
|
||||
#define LPASS_BE_HDMI "(Backend) HDMI"
|
||||
#define LPASS_BE_INT_BT_SCO_RX "(Backend) INT_BT_SCO_RX"
|
||||
#define LPASS_BE_INT_BT_SCO_TX "(Backend) INT_BT_SCO_TX"
|
||||
#define LPASS_BE_INT_FM_RX "(Backend) INT_FM_RX"
|
||||
#define LPASS_BE_INT_FM_TX "(Backend) INT_FM_TX"
|
||||
#define LPASS_BE_AFE_PCM_RX "(Backend) RT_PROXY_DAI_001_RX"
|
||||
#define LPASS_BE_AFE_PCM_TX "(Backend) RT_PROXY_DAI_002_TX"
|
||||
#define LPASS_BE_AUXPCM_RX "(Backend) AUX_PCM_RX"
|
||||
#define LPASS_BE_AUXPCM_TX "(Backend) AUX_PCM_TX"
|
||||
#define LPASS_BE_VOICE_PLAYBACK_TX "(Backend) VOICE_PLAYBACK_TX"
|
||||
#define LPASS_BE_INCALL_RECORD_RX "(Backend) INCALL_RECORD_TX"
|
||||
#define LPASS_BE_INCALL_RECORD_TX "(Backend) INCALL_RECORD_RX"
|
||||
#define LPASS_BE_SEC_I2S_RX "(Backend) SECONDARY_I2S_RX"
|
||||
|
||||
#define LPASS_BE_MI2S_RX "(Backend) MI2S_RX"
|
||||
#define LPASS_BE_STUB_RX "(Backend) STUB_RX"
|
||||
#define LPASS_BE_STUB_TX "(Backend) STUB_TX"
|
||||
#define LPASS_BE_SLIMBUS_1_RX "(Backend) SLIMBUS_1_RX"
|
||||
#define LPASS_BE_SLIMBUS_1_TX "(Backend) SLIMBUS_1_TX"
|
||||
|
||||
/* For multimedia front-ends, asm session is allocated dynamically.
|
||||
* Hence, asm session/multimedia front-end mapping has to be maintained.
|
||||
* Due to this reason, additional multimedia front-end must be placed before
|
||||
* non-multimedia front-ends.
|
||||
*/
|
||||
|
||||
enum {
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA2,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA3,
|
||||
MSM_FRONTEND_DAI_MULTIMEDIA4,
|
||||
MSM_FRONTEND_DAI_CS_VOICE,
|
||||
MSM_FRONTEND_DAI_VOIP,
|
||||
MSM_FRONTEND_DAI_AFE_RX,
|
||||
MSM_FRONTEND_DAI_AFE_TX,
|
||||
MSM_FRONTEND_DAI_VOICE_STUB,
|
||||
MSM_FRONTEND_DAI_MAX,
|
||||
};
|
||||
|
||||
#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA4 + 1)
|
||||
#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA4
|
||||
|
||||
enum {
|
||||
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
|
||||
MSM_BACKEND_DAI_PRI_I2S_TX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_0_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_0_TX,
|
||||
MSM_BACKEND_DAI_HDMI_RX,
|
||||
MSM_BACKEND_DAI_INT_BT_SCO_RX,
|
||||
MSM_BACKEND_DAI_INT_BT_SCO_TX,
|
||||
MSM_BACKEND_DAI_INT_FM_RX,
|
||||
MSM_BACKEND_DAI_INT_FM_TX,
|
||||
MSM_BACKEND_DAI_AFE_PCM_RX,
|
||||
MSM_BACKEND_DAI_AFE_PCM_TX,
|
||||
MSM_BACKEND_DAI_AUXPCM_RX,
|
||||
MSM_BACKEND_DAI_AUXPCM_TX,
|
||||
MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
|
||||
MSM_BACKEND_DAI_INCALL_RECORD_RX,
|
||||
MSM_BACKEND_DAI_INCALL_RECORD_TX,
|
||||
MSM_BACKEND_DAI_MI2S_RX,
|
||||
MSM_BACKEND_DAI_SEC_I2S_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_1_RX,
|
||||
MSM_BACKEND_DAI_SLIMBUS_1_TX,
|
||||
MSM_BACKEND_DAI_INVALID,
|
||||
MSM_BACKEND_DAI_MAX,
|
||||
};
|
||||
|
||||
/* dai_id: front-end ID,
|
||||
* dspst_id: DSP audio stream ID
|
||||
* stream_type: playback or capture
|
||||
*/
|
||||
void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
|
||||
int stream_type);
|
||||
void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
|
||||
|
||||
int lpa_set_volume(unsigned volume);
|
||||
|
||||
int msm_routing_check_backend_enabled(int fedai_id);
|
||||
|
||||
int multi_ch_pcm_set_volume(unsigned volume);
|
||||
|
||||
int compressed_set_volume(unsigned volume);
|
||||
|
||||
#endif /*_MSM_PCM_H*/
|
||||
621
sound/soc/msm/qdsp6v2/q6adm.c
Normal file
621
sound/soc/msm/qdsp6v2/q6adm.c
Normal file
@@ -0,0 +1,621 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
|
||||
#include <mach/qdsp6v2/audio_acdb.h>
|
||||
#include <mach/qdsp6v2/rtac.h>
|
||||
|
||||
#include <sound/apr_audio-v2.h>
|
||||
#include <mach/qdsp6v2/apr.h>
|
||||
#include <sound/q6adm-v2.h>
|
||||
#include <sound/q6audio-v2.h>
|
||||
|
||||
|
||||
#define TIMEOUT_MS 1000
|
||||
|
||||
#define RESET_COPP_ID 99
|
||||
#define INVALID_COPP_ID 0xFF
|
||||
|
||||
struct adm_ctl {
|
||||
void *apr;
|
||||
atomic_t copp_id[Q6_AFE_MAX_PORTS];
|
||||
atomic_t copp_cnt[Q6_AFE_MAX_PORTS];
|
||||
atomic_t copp_stat[Q6_AFE_MAX_PORTS];
|
||||
u32 mem_map_handle[Q6_AFE_MAX_PORTS];
|
||||
wait_queue_head_t wait[Q6_AFE_MAX_PORTS];
|
||||
};
|
||||
|
||||
static struct adm_ctl this_adm;
|
||||
|
||||
static int32_t adm_callback(struct apr_client_data *data, void *priv)
|
||||
{
|
||||
uint32_t *payload;
|
||||
int i, index;
|
||||
payload = data->payload;
|
||||
|
||||
if (data->opcode == RESET_EVENTS) {
|
||||
pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n",
|
||||
data->reset_event, data->reset_proc,
|
||||
this_adm.apr);
|
||||
if (this_adm.apr) {
|
||||
apr_reset(this_adm.apr);
|
||||
for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
|
||||
atomic_set(&this_adm.copp_id[i],
|
||||
RESET_COPP_ID);
|
||||
atomic_set(&this_adm.copp_cnt[i], 0);
|
||||
atomic_set(&this_adm.copp_stat[i], 0);
|
||||
}
|
||||
this_adm.apr = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
|
||||
data->opcode, payload[0], payload[1],
|
||||
data->payload_size);
|
||||
|
||||
if (data->payload_size) {
|
||||
index = q6audio_get_port_index(data->token);
|
||||
if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
|
||||
pr_err("%s: invalid port idx %d token %d\n",
|
||||
__func__, index, data->token);
|
||||
return 0;
|
||||
}
|
||||
if (data->opcode == APR_BASIC_RSP_RESULT) {
|
||||
pr_debug("APR_BASIC_RSP_RESULT\n");
|
||||
switch (payload[0]) {
|
||||
case ADM_CMD_SET_PP_PARAMS_V5:
|
||||
if (rtac_make_adm_callback(
|
||||
payload, data->payload_size))
|
||||
pr_debug("%s: payload[0]: 0x%x\n",
|
||||
__func__, payload[0]);
|
||||
break;
|
||||
case ADM_CMD_DEVICE_CLOSE_V5:
|
||||
case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
|
||||
case ADM_CMD_SHARED_MEM_MAP_REGIONS:
|
||||
case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
|
||||
pr_debug("ADM_CMD_MATRIX_MAP_ROUTINGS\n");
|
||||
atomic_set(&this_adm.copp_stat[index], 1);
|
||||
wake_up(&this_adm.wait[index]);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
|
||||
payload[0]);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (data->opcode) {
|
||||
case ADM_CMDRSP_DEVICE_OPEN_V5: {
|
||||
struct adm_cmd_rsp_device_open_v5 *open =
|
||||
(struct adm_cmd_rsp_device_open_v5 *)data->payload;
|
||||
if (open->copp_id == INVALID_COPP_ID) {
|
||||
pr_err("%s: invalid coppid rxed %d\n",
|
||||
__func__, open->copp_id);
|
||||
atomic_set(&this_adm.copp_stat[index], 1);
|
||||
wake_up(&this_adm.wait[index]);
|
||||
break;
|
||||
}
|
||||
atomic_set(&this_adm.copp_id[index], open->copp_id);
|
||||
atomic_set(&this_adm.copp_stat[index], 1);
|
||||
pr_debug("%s: coppid rxed=%d\n", __func__,
|
||||
open->copp_id);
|
||||
wake_up(&this_adm.wait[index]);
|
||||
}
|
||||
break;
|
||||
case ADM_CMD_GET_PP_PARAMS_V5:
|
||||
pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n", __func__);
|
||||
rtac_make_adm_callback(payload,
|
||||
data->payload_size);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unknown cmd:0x%x\n", __func__,
|
||||
data->opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: send_adm_cal_block function to be defined
|
||||
when calibration available for 8974 */
|
||||
static void send_adm_cal(int port_id, int path)
|
||||
{
|
||||
/* function to be defined when calibration available for 8974 */
|
||||
pr_debug("%s\n", __func__);
|
||||
}
|
||||
|
||||
int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
|
||||
{
|
||||
struct adm_cmd_device_open_v5 open;
|
||||
int ret = 0;
|
||||
int index;
|
||||
int tmp_port = q6audio_get_port_id(port_id);
|
||||
|
||||
pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
|
||||
port_id, path, rate, channel_mode);
|
||||
|
||||
port_id = q6audio_convert_virtual_to_portid(port_id);
|
||||
|
||||
if (q6audio_validate_port(port_id) < 0) {
|
||||
pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
index = q6audio_get_port_index(port_id);
|
||||
pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
|
||||
|
||||
if (this_adm.apr == NULL) {
|
||||
this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
|
||||
0xFFFFFFFF, &this_adm);
|
||||
if (this_adm.apr == NULL) {
|
||||
pr_err("%s: Unable to register ADM\n", __func__);
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
rtac_set_adm_handle(this_adm.apr);
|
||||
}
|
||||
|
||||
|
||||
/* Create a COPP if port id are not enabled */
|
||||
if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
|
||||
|
||||
open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
|
||||
open.hdr.pkt_size = sizeof(open);
|
||||
open.hdr.src_svc = APR_SVC_ADM;
|
||||
open.hdr.src_domain = APR_DOMAIN_APPS;
|
||||
open.hdr.src_port = tmp_port;
|
||||
open.hdr.dest_svc = APR_SVC_ADM;
|
||||
open.hdr.dest_domain = APR_DOMAIN_ADSP;
|
||||
open.hdr.dest_port = tmp_port;
|
||||
open.hdr.token = port_id;
|
||||
open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
|
||||
|
||||
open.mode_of_operation = path;
|
||||
/* Reserved for future use, need to set this to 0 */
|
||||
open.flags = 0x00;
|
||||
open.endpoint_id_1 = tmp_port;
|
||||
open.endpoint_id_2 = 0xFFFF;
|
||||
|
||||
/* convert path to acdb path */
|
||||
if (path == ADM_PATH_PLAYBACK)
|
||||
open.topology_id = get_adm_rx_topology();
|
||||
else {
|
||||
open.topology_id = get_adm_tx_topology();
|
||||
if ((open.topology_id ==
|
||||
VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
|
||||
(open.topology_id ==
|
||||
VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
|
||||
rate = 16000;
|
||||
}
|
||||
|
||||
if (open.topology_id == 0)
|
||||
open.topology_id = topology;
|
||||
|
||||
open.dev_num_channel = channel_mode & 0x00FF;
|
||||
open.bit_width = 16;
|
||||
open.sample_rate = rate;
|
||||
memset(open.dev_channel_mapping, 0, 8);
|
||||
|
||||
if (channel_mode == 1) {
|
||||
open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
|
||||
} else if (channel_mode == 2) {
|
||||
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
|
||||
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
|
||||
} else if (channel_mode == 6) {
|
||||
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
|
||||
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
|
||||
open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
|
||||
open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
|
||||
open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
|
||||
open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
|
||||
} else {
|
||||
pr_err("%s invalid num_chan %d\n", __func__,
|
||||
channel_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("%s: port_id=%d rate=%d"
|
||||
"topology_id=0x%X\n", __func__, open.endpoint_id_1, \
|
||||
open.sample_rate, open.topology_id);
|
||||
|
||||
atomic_set(&this_adm.copp_stat[index], 0);
|
||||
|
||||
ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
|
||||
if (ret < 0) {
|
||||
pr_err("%s:ADM enable for port %d for[%d] failed\n",
|
||||
__func__, tmp_port, port_id);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
/* Wait for the callback with copp id */
|
||||
ret = wait_event_timeout(this_adm.wait[index],
|
||||
atomic_read(&this_adm.copp_stat[index]),
|
||||
msecs_to_jiffies(TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
pr_err("%s ADM open failed for port %d"
|
||||
"for [%d]\n", __func__, tmp_port, port_id);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
}
|
||||
atomic_inc(&this_adm.copp_cnt[index]);
|
||||
return 0;
|
||||
|
||||
fail_cmd:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
|
||||
int topology)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = adm_open(port_id, path, rate, channel_mode, topology);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adm_matrix_map(int session_id, int path, int num_copps,
|
||||
unsigned int *port_id, int copp_id)
|
||||
{
|
||||
struct adm_cmd_matrix_map_routings_v5 *route;
|
||||
struct adm_session_map_node_v5 *node;
|
||||
uint32_t *copps_list;
|
||||
int cmd_size = 0;
|
||||
int ret = 0, i = 0;
|
||||
void *payload = NULL;
|
||||
void *matrix_map = NULL;
|
||||
|
||||
/* Assumes port_ids have already been validated during adm_open */
|
||||
int index = q6audio_get_port_index(copp_id);
|
||||
if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
|
||||
pr_err("%s: invalid port idx %d token %d\n",
|
||||
__func__, index, copp_id);
|
||||
return 0;
|
||||
}
|
||||
cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) +
|
||||
sizeof(struct adm_session_map_node_v5) +
|
||||
(sizeof(uint32_t) * num_copps));
|
||||
matrix_map = kzalloc(cmd_size, GFP_KERNEL);
|
||||
if (matrix_map == NULL) {
|
||||
pr_err("%s: Mem alloc failed\n", __func__);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
|
||||
|
||||
pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0] :%d coppid[%d]\n",
|
||||
__func__, session_id, path, num_copps, port_id[0], copp_id);
|
||||
|
||||
route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
|
||||
route->hdr.pkt_size = cmd_size;
|
||||
route->hdr.src_svc = 0;
|
||||
route->hdr.src_domain = APR_DOMAIN_APPS;
|
||||
route->hdr.src_port = copp_id;
|
||||
route->hdr.dest_svc = APR_SVC_ADM;
|
||||
route->hdr.dest_domain = APR_DOMAIN_ADSP;
|
||||
route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
|
||||
route->hdr.token = copp_id;
|
||||
route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
|
||||
route->num_sessions = 1;
|
||||
|
||||
switch (path) {
|
||||
case 0x1:
|
||||
route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
|
||||
break;
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Wrong path set[%d]\n", __func__, path);
|
||||
break;
|
||||
}
|
||||
payload = ((u8 *)matrix_map +
|
||||
sizeof(struct adm_cmd_matrix_map_routings_v5));
|
||||
node = (struct adm_session_map_node_v5 *)payload;
|
||||
|
||||
node->session_id = session_id;
|
||||
node->num_copps = num_copps;
|
||||
payload = (u8 *)node + sizeof(struct adm_session_map_node_v5);
|
||||
copps_list = (uint32_t *)payload;
|
||||
for (i = 0; i < num_copps; i++) {
|
||||
int tmp;
|
||||
port_id[i] = q6audio_convert_virtual_to_portid(port_id[i]);
|
||||
|
||||
tmp = q6audio_get_port_index(port_id[i]);
|
||||
|
||||
|
||||
if (tmp >= 0 && tmp < Q6_AFE_MAX_PORTS)
|
||||
copps_list[i] =
|
||||
atomic_read(&this_adm.copp_id[tmp]);
|
||||
pr_debug("%s: port_id[%d]: %d, index: %d act coppid[0x%x]\n",
|
||||
__func__, i, port_id[i], tmp,
|
||||
atomic_read(&this_adm.copp_id[tmp]));
|
||||
}
|
||||
atomic_set(&this_adm.copp_stat[index], 0);
|
||||
|
||||
ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: ADM routing for port %d failed\n",
|
||||
__func__, port_id[0]);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
ret = wait_event_timeout(this_adm.wait[index],
|
||||
atomic_read(&this_adm.copp_stat[index]),
|
||||
msecs_to_jiffies(TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
pr_err("%s: ADM cmd Route failed for port %d\n",
|
||||
__func__, port_id[0]);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
for (i = 0; i < num_copps; i++)
|
||||
send_adm_cal(port_id[i], path);
|
||||
|
||||
fail_cmd:
|
||||
kfree(matrix_map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adm_memory_map_regions(int port_id,
|
||||
uint32_t *buf_add, uint32_t mempool_id,
|
||||
uint32_t *bufsz, uint32_t bufcnt)
|
||||
{
|
||||
struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
|
||||
struct avs_shared_map_region_payload *mregions = NULL;
|
||||
void *mmap_region_cmd = NULL;
|
||||
void *payload = NULL;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
int cmd_size = 0;
|
||||
int index = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (this_adm.apr == NULL) {
|
||||
this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
|
||||
0xFFFFFFFF, &this_adm);
|
||||
if (this_adm.apr == NULL) {
|
||||
pr_err("%s: Unable to register ADM\n", __func__);
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
rtac_set_adm_handle(this_adm.apr);
|
||||
}
|
||||
|
||||
port_id = q6audio_convert_virtual_to_portid(port_id);
|
||||
|
||||
if (q6audio_validate_port(port_id) < 0) {
|
||||
pr_err("%s port id[%d] is invalid\n", __func__, port_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
index = q6audio_get_port_index(port_id);
|
||||
|
||||
cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
|
||||
+ sizeof(struct avs_shared_map_region_payload)
|
||||
* bufcnt;
|
||||
|
||||
mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
|
||||
if (!mmap_region_cmd) {
|
||||
pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
|
||||
mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE),
|
||||
APR_PKT_VER);
|
||||
mmap_regions->hdr.pkt_size = cmd_size;
|
||||
mmap_regions->hdr.src_port = 0;
|
||||
mmap_regions->hdr.dest_port = 0;
|
||||
mmap_regions->hdr.token = 0;
|
||||
mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
|
||||
mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_EBI_POOL & 0x00ff;
|
||||
mmap_regions->num_regions = bufcnt & 0x00ff;
|
||||
mmap_regions->property_flag = 0x00;
|
||||
|
||||
pr_debug("%s: map_regions->num_regions = %d\n", __func__,
|
||||
mmap_regions->num_regions);
|
||||
payload = ((u8 *) mmap_region_cmd +
|
||||
sizeof(struct avs_cmd_shared_mem_map_regions));
|
||||
mregions = (struct avs_shared_map_region_payload *)payload;
|
||||
|
||||
for (i = 0; i < bufcnt; i++) {
|
||||
mregions->shm_addr_lsw = buf_add[i];
|
||||
mregions->shm_addr_msw = 0x00;
|
||||
mregions->mem_size_bytes = bufsz[i];
|
||||
++mregions;
|
||||
}
|
||||
|
||||
atomic_set(&this_adm.copp_stat[0], 0);
|
||||
ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
|
||||
mmap_regions->hdr.opcode, ret);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
ret = wait_event_timeout(this_adm.wait[index],
|
||||
atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
|
||||
if (!ret) {
|
||||
pr_err("%s: timeout. waited for memory_map\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
fail_cmd:
|
||||
kfree(mmap_region_cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adm_memory_unmap_regions(int32_t port_id, uint32_t *buf_add,
|
||||
uint32_t *bufsz, uint32_t bufcnt)
|
||||
{
|
||||
struct avs_cmd_shared_mem_unmap_regions unmap_regions;
|
||||
int ret = 0;
|
||||
int cmd_size = 0;
|
||||
int index = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (this_adm.apr == NULL) {
|
||||
pr_err("%s APR handle NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
port_id = q6audio_convert_virtual_to_portid(port_id);
|
||||
|
||||
if (q6audio_validate_port(port_id) < 0) {
|
||||
pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
index = q6audio_get_port_index(port_id);
|
||||
|
||||
unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE),
|
||||
APR_PKT_VER);
|
||||
unmap_regions.hdr.pkt_size = cmd_size;
|
||||
unmap_regions.hdr.src_port = 0;
|
||||
unmap_regions.hdr.dest_port = 0;
|
||||
unmap_regions.hdr.token = 0;
|
||||
unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
|
||||
unmap_regions.mem_map_handle = this_adm.mem_map_handle[index];
|
||||
atomic_set(&this_adm.copp_stat[0], 0);
|
||||
ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
|
||||
unmap_regions.hdr.opcode, ret);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
ret = wait_event_timeout(this_adm.wait[index],
|
||||
atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
|
||||
if (!ret) {
|
||||
pr_err("%s: timeout. waited for memory_unmap\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
fail_cmd:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adm_get_copp_id(int port_index)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (port_index < 0) {
|
||||
pr_err("%s: invalid port_id = %d\n", __func__, port_index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return atomic_read(&this_adm.copp_id[port_index]);
|
||||
}
|
||||
|
||||
int adm_close(int port_id)
|
||||
{
|
||||
struct apr_hdr close;
|
||||
|
||||
int ret = 0;
|
||||
int index = 0;
|
||||
|
||||
port_id = q6audio_convert_virtual_to_portid(port_id);
|
||||
|
||||
index = q6audio_get_port_index(port_id);
|
||||
if (q6audio_validate_port(port_id) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
|
||||
|
||||
if (!(atomic_read(&this_adm.copp_cnt[index]))) {
|
||||
pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
|
||||
|
||||
goto fail_cmd;
|
||||
}
|
||||
atomic_dec(&this_adm.copp_cnt[index]);
|
||||
if (!(atomic_read(&this_adm.copp_cnt[index]))) {
|
||||
|
||||
close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
|
||||
close.pkt_size = sizeof(close);
|
||||
close.src_svc = APR_SVC_ADM;
|
||||
close.src_domain = APR_DOMAIN_APPS;
|
||||
close.src_port = port_id;
|
||||
close.dest_svc = APR_SVC_ADM;
|
||||
close.dest_domain = APR_DOMAIN_ADSP;
|
||||
close.dest_port = atomic_read(&this_adm.copp_id[index]);
|
||||
close.token = port_id;
|
||||
close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
|
||||
|
||||
atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
|
||||
atomic_set(&this_adm.copp_stat[index], 0);
|
||||
|
||||
|
||||
pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
|
||||
__func__,
|
||||
atomic_read(&this_adm.copp_id[index]),
|
||||
port_id, index,
|
||||
atomic_read(&this_adm.copp_cnt[index]));
|
||||
|
||||
ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
|
||||
if (ret < 0) {
|
||||
pr_err("%s ADM close failed\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
ret = wait_event_timeout(this_adm.wait[index],
|
||||
atomic_read(&this_adm.copp_stat[index]),
|
||||
msecs_to_jiffies(TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
pr_err("%s: ADM cmd Route failed for port %d\n",
|
||||
__func__, port_id);
|
||||
ret = -EINVAL;
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
rtac_remove_adm_device(port_id);
|
||||
}
|
||||
|
||||
fail_cmd:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init adm_init(void)
|
||||
{
|
||||
int i = 0;
|
||||
this_adm.apr = NULL;
|
||||
|
||||
for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
|
||||
atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
|
||||
atomic_set(&this_adm.copp_cnt[i], 0);
|
||||
atomic_set(&this_adm.copp_stat[i], 0);
|
||||
init_waitqueue_head(&this_adm.wait[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(adm_init);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user