【Qualcomm高通音频】MSM8953安卓7如何配置QUIN MI2S为从模式?
1. pinctrl配置高通平台配置一组I2S一般对应四个或者六个GPIO,QUIN MI2S对应GPIO88 GPIO91 GPIO92 GPIO93对应代码文件为:kernel/msm-3.18/arch/arm/boot/dts/qcom/msm8953-pinctrl.dtsipri-tlmm-lines {pri_tlmm_lines_act: pri_tl...
1. pinctrl配置
高通平台配置一组I2S一般对应四个或者六个GPIO,
QUIN MI2S对应GPIO88 GPIO91 GPIO92 GPIO93
对应代码文件为:kernel/msm-3.18/arch/arm/boot/dts/qcom/msm8953-pinctrl.dtsi
pri-tlmm-lines {
pri_tlmm_lines_act: pri_tlmm_lines_act {
mux {
pins = "gpio91", "gpio88", "gpio93";
function = "pri_mi2s";
};
config {
pins = "gpio91", "gpio88", "gpio93";
drive-strength = <8>;
};
};
pri_tlmm_lines_sus: pri_tlmm_lines_sus {
mux {
pins = "gpio91", "gpio88", "gpio93";
function = "pri_mi2s";
};
config {
pins = "gpio91", "gpio88", "gpio93";
drive-strength = <2>;
bias-pull-down;
};
};
};
配置pinctrl还需要挂载到声卡节点下。
修改文件:kernel/msm-3.18/arch/arm/boot/dts/qcom/msm8953-audio.dtsi
qcom,msm-gpios =
"pri_i2s",
"quin_i2s";
qcom,pinctrl-names =
"all_off",
"pri_i2s_act",
"quin_act",
"quin_pri_i2s_act";
pinctrl-names =
"all_off",
"pri_i2s_act",
"quin_act",
"quin_pri_i2s_act";
pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &pri_tlmm_lines_sus &pri_tlmm_ws_sus>;
pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &pri_tlmm_lines_sus &pri_tlmm_ws_sus>;
pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &pri_tlmm_lines_act &pri_tlmm_ws_act>;
pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &pri_tlmm_lines_act &pri_tlmm_ws_act>;
2. 寄存器配置
从高通文档MSM8953 External MI2S Interface Overview上了解到配置QUIN MI2S需要配置以下寄存器。
那么如何在代码中配置这些寄存器呢?这就需要修改到以下几个文件。
修改文件:
kernel/msm-3.18/sound/soc/msm/msm8952.c
kernel/msm-3.18/sound/soc/codecs/msm8x16-wcd.h
kernel/msm-3.18/arch/arm/boot/dts/qcom/msm-audio.dtsi
其中kernel/msm-3.18/arch/arm/boot/dts/qcom/msm-audio.dtsi配置如下:
int_codec: sound {
compatible = "qcom,msm8952-audio-codec";
//added by kuangjincheng @20171221
reg = <0xc051000 0x4>,//LPASS_CSR_GP_IO_MUX_MIC_CTL
<0xc051004 0x4>,//LPASS_CSR_GP_IO_MUX_SPKR_CTL
<0xc055000 0x4>,//LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL
<0xc052000 0x4>,//LPASS_ CSR_GP_IO_MUX_QUI_CTL
<0xc054000 0x4>,//LPASS_CSR_GP_IO_MUX_MIC_EXT_CLK_CTL
<0xc056000 0x4>,// LPASS_CSR_GP_LPAIF_QUI_PCM_SEC_MODE_MUXSEL
<0xc054008 0x4>;//LPASS_CSR_GP_IO_MUX_QUI_EXT_CLK_CTL
reg-names = "csr_gp_io_mux_mic_ctl",
"csr_gp_io_mux_spkr_ctl",
"csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel",
"csr_gp_io_mux_quin_ctl",
"csr_gp_io_mux_mic_ext_clk_ctl",
"csr_gp_io_lpaif_qui_pcm_sec_mode_muxsel",
"csr_gp_io_mux_quin_ext_clk_ctl";
};
在头文件kernel/msm-3.18/sound/soc/codecs/msm8x16-wcd.h中的设备结构体中增加指针元素,存储寄存器地址。
struct msm8916_asoc_mach_data {
int codec_type;
int ext_pa;
int us_euro_gpio;
int spk_ext_pa_gpio;
int mclk_freq;
int lb_mode;
int afe_clk_ver;
u8 micbias1_cap_mode;
u8 micbias2_cap_mode;
atomic_t mclk_rsc_ref;
atomic_t mclk_enabled;
atomic_t wsa_mclk_rsc_ref;
struct mutex cdc_mclk_mutex;
struct mutex wsa_mclk_mutex;
struct delayed_work disable_mclk_work;
struct afe_digital_clk_cfg digital_cdc_clk;
struct afe_clk_set digital_cdc_core_clk;
void __iomem *vaddr_gpio_mux_spkr_ctl;
void __iomem *vaddr_gpio_mux_mic_ctl;
void __iomem *vaddr_gpio_mux_quin_ctl;
void __iomem *vaddr_gpio_mux_pcm_ctl;
//added by kuangjincheng @20171221 QUAT_MI2S_SLAVE_MODE config
void __iomem *vaddr_gpio_mux_mic_ext_clk_ctl;
//added by kuangjincheng @20180521 QUIN_MI2S_SLAVE_MODE config
void __iomem *vaddr_gpio_quin_sec_muxsel;
void __iomem *vaddr_gpio_mux_quin_ext_clk_ctl;
struct on_demand_supply wsa_switch_supply;
};
在kernel/msm-3.18/sound/soc/msm/msm8952.c的msm8952_asoc_machine_probe函数中解析对应的寄存器地址,并保存到设备结构体对应的元素中去。
//added by kuangjincheng @20180521 QUIN_MI2S_SLAVE_MODE config
muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_lpaif_qui_pcm_sec_mode_muxsel");
if (!muxsel) {
dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n");
goto parse_mclk_freq;
}
pdata->vaddr_gpio_quin_sec_muxsel = ioremap(muxsel->start, resource_size(muxsel));
if (pdata->vaddr_gpio_quin_sec_muxsel == NULL) {
pr_err("%s ==4ioremap failure for muxsel virt addr\n", __func__);
ret = -ENOMEM;
goto err;
}
muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_mux_quin_ext_clk_ctl");
if (!muxsel) {
dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n");
goto parse_mclk_freq;
}
pdata->vaddr_gpio_mux_quin_ext_clk_ctl = ioremap(muxsel->start, resource_size(muxsel));
if (pdata->vaddr_gpio_mux_quin_ext_clk_ctl == NULL) {
pr_err("%s ==5ioremap failure for muxsel virt addr\n", __func__);
ret = -ENOMEM;
goto err;
}
寄存器配置还有最后一步就是在I2S对应msm_quin_mi2s_snd_startup中进行寄存器配置。
static int msm_quin_mi2s_snd_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct msm8916_asoc_mach_data *pdata =
snd_soc_card_get_drvdata(card);
int ret = 0, val = 0;
pr_err("honeywell_msm8952>>>%s: substream = %s stream = %d\n", __func__, substream->name, substream->stream);
#ifdef QUIN_MI2S_USE_SLAVE_MODE
//0xc051000 LPASS_CSR_GP_IO_MUX_MIC_CTL
if(pdata->vaddr_gpio_mux_mic_ctl){
val = ioread32(pdata->vaddr_gpio_mux_mic_ctl);
val = 0x00000000;
iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl);
}
pr_err("msm8952>>>%s: iowrite32 vaddr_gpio_mux_mic_ctl\n", __func__);
//0xc056000 LPASS_CSR_GP_LPAIF_QUI_PCM_SEC_MODE_MUXSEL
if(pdata->vaddr_gpio_quin_sec_muxsel){
val = ioread32(pdata->vaddr_gpio_quin_sec_muxsel);
val = 0x00000000;
iowrite32(val, pdata->vaddr_gpio_quin_sec_muxsel);
}
pr_err("msm8952>>>%s: iowrite32 vaddr_gpio_quin_sec_muxsel\n", __func__);
//0xc052000 LPASS_ CSR_GP_IO_MUX_QUI_CTL
if(pdata->vaddr_gpio_mux_quin_ctl){
val = ioread32(pdata->vaddr_gpio_mux_quin_ctl);
val = 0x00000001;
iowrite32(val, pdata->vaddr_gpio_mux_quin_ctl);
}
pr_err("msm8952>>>%s: iowrite32 vaddr_gpio_mux_quin_ctl\n", __func__);
//0xc054008 LPASS_CSR_GP_IO_MUX_QUI_EXT_CLK_CTL
if(pdata->vaddr_gpio_mux_quin_ext_clk_ctl){
val = ioread32(pdata->vaddr_gpio_mux_quin_ext_clk_ctl);
val = 0x00000001;
iowrite32(val, pdata->vaddr_gpio_mux_quin_ext_clk_ctl);
}
pr_err("msm8952>>>%s: iowrite32 vaddr_gpio_mux_quin_ext_clk_ctl\n", __func__);
#else
if (pdata->vaddr_gpio_mux_quin_ctl) {
val = ioread32(pdata->vaddr_gpio_mux_quin_ctl);
val = val | 0x00000001;
iowrite32(val, pdata->vaddr_gpio_mux_quin_ctl);
} else {
return -EINVAL;
}
#endif
pr_err("honeywell_msm8952>>>%s: ---001\n", __func__);
#ifdef QUIN_MI2S_USE_SLAVE_MODE
ret = msm_quin_i2s_sclk_ctl(substream, true);
#else
ret = msm_mi2s_sclk_ctl(substream, true);
#endif
if (ret < 0) {
pr_err("honeywell_msm8952>>>%s: failed to enable sclk\n", __func__);
return ret;
}
pr_err("honeywell_msm8952>>>%s: ---002\n", __func__);
ret = msm_gpioset_activate(CLIENT_WCD_INT, "quin_i2s");
if (ret < 0) {
pr_err("honeywell_msm8952>>>%s: failed to enable codec gpios\n", __func__);
goto err;
}
pr_err("honeywell_msm8952>>>%s: ---003\n", __func__);
if (atomic_inc_return(&quin_mi2s_clk_ref) == 1) {
#ifdef QUIN_MI2S_USE_SLAVE_MODE
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
#else
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
#endif
if (ret < 0)
pr_err("honeywell_msm8952>>>%s: set fmt cpu dai failed\n", __func__);
}
pr_err("honeywell_msm8952>>>%s: ---004\n", __func__);
return ret;
err:
#ifdef QUIN_MI2S_USE_SLAVE_MODE
ret = msm_quin_i2s_sclk_ctl(substream, false);
#else
ret = msm_mi2s_sclk_ctl(substream, false);
#endif
if (ret < 0)
pr_err("failed to disable sclk\n");
return ret;
}
static void msm_quin_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
{
int ret;
pr_err("honeywell_msm8952>>>%s: substream = %s stream = %d\n", __func__, substream->name, substream->stream);
#ifdef QUIN_MI2S_USE_SLAVE_MODE
ret = msm_quin_i2s_sclk_ctl(substream, false);
#else
ret = msm_mi2s_sclk_ctl(substream, false);
#endif
if (ret < 0)
pr_err("honeywell_msm8952>>>%s: clock disable failed\n", __func__);
if (atomic_read(&quin_mi2s_clk_ref) > 0)
atomic_dec(&quin_mi2s_clk_ref);
ret = msm_gpioset_suspend(CLIENT_WCD_INT, "quin_i2s");
if (ret < 0) {
pr_err("honeywell_msm8952>>>%s: gpio set cannot be de-activated %sd", __func__, "quin_i2s");
return;
}
}
3. 时钟配置
(1)谁提供时钟?
时钟配置中的时钟是由MSM8953提供还是由外接晶振的第三方CODEC芯片提供,这个直接关系到I2S的主从模式设定。
I2S为主模式时,时钟有MSM8953内部晶振提供;反之,由第三方codec提供。
这里配置的代码也是在msm_quin_mi2s_snd_startup函数中设置的。
对应代码段单独截取出来就是:
#ifdef QUIN_MI2S_USE_SLAVE_MODE
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
#else
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
#endif
(2)时钟频率如何设定?
这里仿照msm_mi2s_sclk_ctl写了个函数msm_quin_i2s_sclk_ctl用来进行从模式时钟配置。
//added by kuangjincheng @20180521 QUIN_MI2S_SLAVE_MODE config
#ifdef QUIN_MI2S_USE_SLAVE_MODE
static struct afe_clk_set quin_i2s_rx_clk = {
AFE_API_VERSION_I2S_CONFIG,
Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT,
Q6AFE_LPASS_OSR_CLK_1_P536_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
static struct afe_clk_set quin_i2s_tx_clk = {
AFE_API_VERSION_I2S_CONFIG,
Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT,
Q6AFE_LPASS_OSR_CLK_3_P072_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
#endif
//added by kuangjincheng @20180521 QUIN_MI2S_SLAVE_MODE config
#ifdef QUIN_MI2S_USE_SLAVE_MODE
static int msm_quin_i2s_sclk_ctl(struct snd_pcm_substream *substream, bool enable)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int port_id = 0;
pr_err("msm8952>>>%s: enter\n", __func__);
port_id = msm8952_get_port_id(rtd->dai_link->be_id);
if (port_id < 0) {
pr_err("msm8952>>>%s: Invalid port_id\n", __func__);
return -EINVAL;
}
if (enable) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
quin_i2s_rx_clk.enable = enable;
quin_i2s_rx_clk.clk_id = msm8952_get_clk_id(port_id);
quin_i2s_rx_clk.clk_freq_in_hz = get_mi2s_rx_clk_val(port_id);
ret = afe_set_lpass_clock_v2(port_id, &quin_i2s_rx_clk);
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
quin_i2s_tx_clk.enable = enable;
quin_i2s_tx_clk.clk_id = msm8952_get_clk_id(port_id);
quin_i2s_tx_clk.clk_freq_in_hz = Q6AFE_LPASS_OSR_CLK_1_P536_MHZ;
ret = afe_set_lpass_clock_v2(port_id, &quin_i2s_tx_clk);
} else {
pr_err("msm8952>>>%s: Not valid substream.\n", __func__);
}
if (ret < 0)
pr_err("msm8952>>>%s: enable: %d, afe_set_lpass_clock_v2 failed\n", __func__, enable);
} else {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
quin_i2s_rx_clk.enable = enable;
quin_i2s_rx_clk.clk_id = msm8952_get_clk_id(port_id);
ret = afe_set_lpass_clock_v2(port_id, &quin_i2s_rx_clk);
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
quin_i2s_tx_clk.enable = enable;
quin_i2s_tx_clk.clk_id = msm8952_get_clk_id(port_id);
ret = afe_set_lpass_clock_v2(port_id, &quin_i2s_tx_clk);
} else {
pr_err("msm8952>>>%s: Not valid substream.\n", __func__);
}
if (ret < 0)
pr_err("msm8952>>>%s: enable: %d, afe_set_lpass_clock_v2 failed\n", __func__, enable);
}
return ret;
}
#endif
4. 综述以及延伸
通过以上代码修改基本就完成了I2S总线从模式配置,可以通过宏定义QUIN_MI2S_USE_SLAVE_MODE来切换主副麦克风。
延伸内容:
kernel/msm-3.18/include/uapi/sound/asound.h
kernel/msm-3.18/include/sound/apr_audio-v2.h
PCM格式在安卓7代码中可以设置为:
SNDRV_PCM_FORMAT_S16_LE
SNDRV_PCM_FORMAT_S24_LE
SNDRV_PCM_FORMAT_S24_3LE
位时钟可选择设置为:
/* Supported OSR clock values */
#define Q6AFE_LPASS_OSR_CLK_12_P288_MHZ 0xBB8000
#define Q6AFE_LPASS_OSR_CLK_9_P600_MHZ 0x927C00
#define Q6AFE_LPASS_OSR_CLK_8_P192_MHZ 0x7D0000
#define Q6AFE_LPASS_OSR_CLK_6_P144_MHZ 0x5DC000
#define Q6AFE_LPASS_OSR_CLK_4_P096_MHZ 0x3E8000
#define Q6AFE_LPASS_OSR_CLK_3_P072_MHZ 0x2EE000
#define Q6AFE_LPASS_OSR_CLK_2_P048_MHZ 0x1F4000
#define Q6AFE_LPASS_OSR_CLK_1_P536_MHZ 0x177000
#define Q6AFE_LPASS_OSR_CLK_1_P024_MHZ 0xFA000
#define Q6AFE_LPASS_OSR_CLK_768_kHZ 0xBB800
#define Q6AFE_LPASS_OSR_CLK_512_kHZ 0x7D000
#define Q6AFE_LPASS_OSR_CLK_DISABLE 0x0
/* Supported Bit clock values */
#define Q6AFE_LPASS_IBIT_CLK_12_P288_MHZ 0xBB8000
#define Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ 0xAC4400
#define Q6AFE_LPASS_IBIT_CLK_8_P192_MHZ 0x7D0000
#define Q6AFE_LPASS_IBIT_CLK_6_P144_MHZ 0x5DC000
#define Q6AFE_LPASS_IBIT_CLK_4_P096_MHZ 0x3E8000
#define Q6AFE_LPASS_IBIT_CLK_3_P072_MHZ 0x2EE000
#define Q6AFE_LPASS_IBIT_CLK_2_P8224_MHZ 0x2b1100
#define Q6AFE_LPASS_IBIT_CLK_2_P048_MHZ 0x1F4000
#define Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ 0x177000
#define Q6AFE_LPASS_IBIT_CLK_1_P4112_MHZ 0x158880
#define Q6AFE_LPASS_IBIT_CLK_1_P024_MHZ 0xFA000
#define Q6AFE_LPASS_IBIT_CLK_768_KHZ 0xBB800
#define Q6AFE_LPASS_IBIT_CLK_512_KHZ 0x7D000
#define Q6AFE_LPASS_IBIT_CLK_256_KHZ 0x3E800
#define Q6AFE_LPASS_IBIT_CLK_DISABLE 0x0
位深度可选择设置为:16、24
采样率可选择设置为:
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
#define SAMPLING_RATE_48KHZ 48000
#define SAMPLING_RATE_96KHZ 96000
#define SAMPLING_RATE_192KHZ 192000
这部分在高通文档上的介绍如下:
更多推荐
所有评论(0)