您好、TI 团队、
我们正在我们的平台上使用 TLV320AIC3262编解码器。
我们正在尝试测试挂起以恢复状态、我们面临输入/输出错误。
我已经将我们的平台使用的编解码器驱动程序连接起来、请告知我们附加的驱动程序是否支持暂停和恢复状态。
/*
* linux/sound/soc/codecs/tlv320aic326x.c
*
* Copyright (C) 2011 Texas Instruments Inc.,
*
* This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio
* codec with digital microphone inputs and programmable outputs.
*
* History:
*
* Rev 0.1 ASoC driver support TI 20-01-2011
*
* The AIC325x ASoC driver is ported for the codec AIC3262.
* Rev 0.2 ASoC driver support TI 21-03-2011
* The AIC326x ASoC driver is updated for linux 2.6.32 Kernel.
* Rev 0.3 ASoC driver support TI 20-04-2011
* The AIC326x ASoC driver is ported to 2.6.35 omap4 kernel
*/
/*
*****************************************************************************
* INCLUDES
*****************************************************************************
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <sound/jack.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/input.h>
#include <sound/tlv.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include "tlv320aic3262-registers.h"
#include "tlv320aic3xxx-core.h"
#include "aic3xxx/aic3xxx_cfw_ops.h"
#include "tlv320aic326x.h"
/*****************************************************************************
Macros
******************************************************************************
******************************************************************************
Function Prototype
******************************************************************************/
static int aic3262_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
static int aic3262_mute(struct snd_soc_dai *dai, int mute, int stream);
static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int Fin, unsigned int Fout);
static int aic3262_set_bias_level(struct snd_soc_component *codec,
enum snd_soc_bias_level level);
static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
static int aic3262_get_runstate(struct snd_soc_component *codec);
static int aic3262_dsp_pwrdwn_status(struct snd_soc_component *codec);
static int aic3262_dsp_pwrup(struct snd_soc_component *codec, int state);
static int aic3262_restart_dsps_sync(struct snd_soc_component *codec, int rs);
static inline unsigned int dsp_non_sync_mode(unsigned int state)
{ return (!((state & 0x03) && (state & 0x30))); }
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1200, 50, 0);
static const DECLARE_TLV_DB_SCALE(spk_gain_tlv, 600, 600, 0);
static const DECLARE_TLV_DB_SCALE(output_gain_tlv, -600, 100, 1);
static const DECLARE_TLV_DB_SCALE(micpga_gain_tlv, 0, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_fine_gain_tlv, -40, 10, 0);
static const DECLARE_TLV_DB_SCALE(beep_gen_volume_tlv, -6300, 100, 0);
/* Chip-level Input and Output CM Mode Controls */
static const char * const input_common_mode_text[] = {
"0.9v", "0.75v"
};
static const char * const output_common_mode_text[] = {
"Input CM", "1.25v", "1.5v", "1.65v"
};
static const struct soc_enum input_cm_mode =
SOC_ENUM_SINGLE(AIC3262_CM_REG, 2, 2, input_common_mode_text);
static const struct soc_enum output_cm_mode =
SOC_ENUM_SINGLE(AIC3262_CM_REG, 0, 4, output_common_mode_text);
/*
*****************************************************************************
* Structure Initialization
*****************************************************************************
*/
static const struct snd_kcontrol_new aic3262_snd_controls[] = {
/* Output */
#ifndef DAC_INDEPENDENT_VOL
/* sound new kcontrol for PCM Playback volume control */
SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
AIC3262_DAC_LVOL, AIC3262_DAC_RVOL, 0,
0x81, 0xaf, dac_vol_tlv),
#endif
/*HP Driver Gain Control */
SOC_DOUBLE_R_SX_TLV("HeadPhone Driver Amplifier Volume",
AIC3262_HPL_VOL, AIC3262_HPR_VOL, 0, 0x39,
0x15, output_gain_tlv),
/*LO Driver Gain Control */
SOC_DOUBLE_TLV("Speaker Amplifier Volume", AIC3262_SPK_AMP_CNTL_R4, 4,
0, 5, 0, spk_gain_tlv),
SOC_DOUBLE_R_SX_TLV("Receiver Amplifier Volume",
AIC3262_REC_AMP_CNTL_R5, AIC3262_RAMPR_VOL, 0,
0x39, 0x24, output_gain_tlv),
SOC_DOUBLE_R_SX_TLV("PCM Capture Volume", AIC3262_LADC_VOL,
AIC3262_RADC_VOL, 0, 0x68, 0x41,
adc_vol_tlv),
SOC_DOUBLE_R_TLV("MicPGA Volume Control", AIC3262_MICL_PGA,
AIC3262_MICR_PGA, 0, 0x5F, 0, micpga_gain_tlv),
SOC_DOUBLE_TLV("PCM Capture Fine Gain Volume", AIC3262_ADC_FINE_GAIN,
4, 0, 5, 1, adc_fine_gain_tlv),
SOC_DOUBLE("ADC channel mute", AIC3262_ADC_FINE_GAIN, 7, 3, 1, 0),
SOC_DOUBLE("DAC MUTE", AIC3262_DAC_MVOL_CONF, 2, 3, 1, 1),
SOC_SINGLE("RESET", AIC3262_RESET_REG, 0, 1, 0),
SOC_SINGLE("DAC VOL SOFT STEPPING", AIC3262_DAC_MVOL_CONF, 0, 2, 0),
SOC_SINGLE("DAC AUTO MUTE CONTROL", AIC3262_DAC_MVOL_CONF, 4, 7, 0),
SOC_SINGLE("RIGHT MODULATOR SETUP", AIC3262_DAC_MVOL_CONF, 7, 1, 0),
SOC_SINGLE("ADC Volume soft stepping", AIC3262_ADC_CHANNEL_POW,
0, 3, 0),
SOC_SINGLE("Mic Bias ext independent enable", AIC3262_MIC_BIAS_CNTL,
7, 1, 0),
SOC_SINGLE("MICBIAS EXT Power Level", AIC3262_MIC_BIAS_CNTL, 4, 3, 0),
SOC_SINGLE("MICBIAS INT Power Level", AIC3262_MIC_BIAS_CNTL, 0, 3, 0),
SOC_SINGLE("BEEP_GEN_EN", AIC3262_BEEP_CNTL_R1, 7, 1, 0),
SOC_DOUBLE_R("BEEP_VOL_CNTL", AIC3262_BEEP_CNTL_R1,
AIC3262_BEEP_CNTL_R2, 0, 0x0F, 1),
SOC_SINGLE("BEEP_MAS_VOL", AIC3262_BEEP_CNTL_R2, 6, 3, 0),
SOC_SINGLE("DAC PRB Selection", AIC3262_DAC_PRB, 0, 26, 0),
SOC_SINGLE("ADC PRB Selection", AIC3262_ADC_PRB, 0, 18, 0),
SOC_ENUM("Input CM mode", input_cm_mode),
SOC_ENUM("Output CM mode", output_cm_mode),
// SOC_SINGLE_EXT("FIRMWARE SET MODE", SND_SOC_NOPM, 0, 0xffff, 0,
// aic3262_set_mode_get, aic3262_set_mode_put),
};
/*
*----------------------------------------------------------------------------
* @struct snd_soc_codec_dai |
* It is SoC Codec DAI structure which has DAI capabilities viz.,
* playback and capture, DAI runtime information viz. state of DAI
* and pop wait state, and DAI private data.
* The AIC3262 rates ranges from 8k to 192k
* The PCM bit format supported are 16, 20, 24 and 32 bits
*----------------------------------------------------------------------------
*/
struct snd_soc_dai_ops aic3262_asi1_dai_ops = {
.hw_params = aic3262_hw_params,
.mute_stream = aic3262_mute,
.set_fmt = aic3262_set_dai_fmt,
.set_pll = aic3262_dai_set_pll,
};
struct snd_soc_dai_ops aic3262_asi2_dai_ops = {
.hw_params = aic3262_hw_params,
.mute_stream = aic3262_mute,
.set_fmt = aic3262_set_dai_fmt,
.set_pll = aic3262_dai_set_pll,
};
struct snd_soc_dai_ops aic3262_asi3_dai_ops = {
.hw_params = aic3262_hw_params,
.mute_stream = aic3262_mute,
.set_fmt = aic3262_set_dai_fmt,
.set_pll = aic3262_dai_set_pll,
};
struct snd_soc_dai_driver aic326x_dai_driver[] = {
{
.name = "aic326x-asi1",
.playback = {
.stream_name = "ASI1 Playback",
.channels_min = 1,
.channels_max = 8,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.capture = {
.stream_name = "ASI1 Capture",
.channels_min = 1,
.channels_max = 8,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.ops = &aic3262_asi1_dai_ops,
},
{
.name = "aic326x-asi2",
.playback = {
.stream_name = "ASI2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.capture = {
.stream_name = "ASI2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.ops = &aic3262_asi2_dai_ops,
},
{
.name = "aic326x-asi3",
.playback = {
.stream_name = "ASI3 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.capture = {
.stream_name = "ASI3 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3262_RATES,
.formats = AIC3262_FORMATS,
},
.ops = &aic3262_asi3_dai_ops,
},
};
static const unsigned int adc_ma_tlv[] = {
TLV_DB_RANGE_HEAD(7),
1, 1, TLV_DB_SCALE_ITEM(-3610, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(-3010, 0, 0),
3, 3, TLV_DB_SCALE_ITEM(-2660, 0, 0),
4, 4, TLV_DB_SCALE_ITEM(-2410, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(-2210, 1500, 0),
8, 11, TLV_DB_SCALE_ITEM(-1810, 1000, 0),
12, 41 , TLV_DB_SCALE_ITEM(-1450, 500, 0)
};
static const DECLARE_TLV_DB_SCALE(lo_hp_tlv, -7830, 50, 0);
static const struct snd_kcontrol_new mal_pga_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_MA_CNTL, 5, 1, 0),
SOC_DAPM_SINGLE_TLV("Left MicPGA Volume", AIC3262_LADC_PGA_MAL_VOL,
0, 0x3f, 1, adc_ma_tlv),
};
static const struct snd_kcontrol_new mar_pga_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_MA_CNTL, 4, 1, 0),
SOC_DAPM_SINGLE_TLV("Right MicPGA Volume", AIC3262_RADC_PGA_MAR_VOL,
0, 0x3f, 1, adc_ma_tlv),
};
/* Left HPL Mixer */
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_HP_AMP_CNTL_R1, 7, 1,
0),
SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
5, 1, 0),
SOC_DAPM_SINGLE_TLV("LO Left-B1 Playback Volume",
AIC3262_HP_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
};
/* Right HPR Mixer */
static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("LO Right-B1 Playback Volume",
AIC3262_HP_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
2, 1, 0),
SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
4, 1, 0),
SOC_DAPM_SINGLE("MA Right Playback Switch", AIC3262_HP_AMP_CNTL_R1,
6, 1, 0),
};
/* Left LOL Mixer */
static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_LINE_AMP_CNTL_R2,
7, 1, 0),
SOC_DAPM_SINGLE("IN1 Left-B Capture Switch", AIC3262_LINE_AMP_CNTL_R2,
3, 1, 0),
SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
7, 1, 0),
SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
5, 1, 0),
};
/* Right LOR Mixer */
static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
SOC_DAPM_SINGLE("LO Left Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
2, 1, 0),
SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
6, 1, 0),
SOC_DAPM_SINGLE("MA Right Playback Switch", AIC3262_LINE_AMP_CNTL_R2,
6, 1, 0),
SOC_DAPM_SINGLE("IN1 Right-B Capture Switch", AIC3262_LINE_AMP_CNTL_R2,
0, 1, 0),
};
/* Left SPKL Mixer */
static const struct snd_kcontrol_new spkl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_SPK_AMP_CNTL_R1,
7, 1, 0),
SOC_DAPM_SINGLE_TLV("LO Left Playback Volume",
AIC3262_SPK_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE("SPR_IN Switch", AIC3262_SPK_AMP_CNTL_R1, 2, 1, 0),
};
/* Right SPKR Mixer */
static const struct snd_kcontrol_new spkr_output_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("LO Right Playback Volume",
AIC3262_SPK_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE("MA Right Playback Switch",
AIC3262_SPK_AMP_CNTL_R1, 6, 1, 0),
};
/* REC Mixer */
static const struct snd_kcontrol_new rec_output_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("LO Left-B2 Playback Volume",
AIC3262_RAMP_CNTL_R1, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE_TLV("IN1 Left Capture Volume",
AIC3262_IN1L_SEL_RM, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE_TLV("IN1 Right Capture Volume",
AIC3262_IN1R_SEL_RM, 0, 0x7f, 1, lo_hp_tlv),
SOC_DAPM_SINGLE_TLV("LO Right-B2 Playback Volume",
AIC3262_RAMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
};
/* Left Input Mixer */
static const struct snd_kcontrol_new left_input_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
6, 3, 0),
SOC_DAPM_SINGLE("IN2 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
4, 3, 0),
SOC_DAPM_SINGLE("IN3 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
2, 3, 0),
SOC_DAPM_SINGLE("IN4 Left Capture Switch", AIC3262_LMIC_PGA_PM_IN4,
5, 1, 0),
SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_LMIC_PGA_PIN,
0, 3, 0),
SOC_DAPM_SINGLE("IN2 Right Capture Switch", AIC3262_LMIC_PGA_MIN,
4, 3, 0),
SOC_DAPM_SINGLE("IN3 Right Capture Switch", AIC3262_LMIC_PGA_MIN,
2, 3, 0),
SOC_DAPM_SINGLE("IN4 Right Capture Switch", AIC3262_LMIC_PGA_PM_IN4,
4, 1, 0),
SOC_DAPM_SINGLE("CM2 Left Capture Switch", AIC3262_LMIC_PGA_MIN,
0, 3, 0),
SOC_DAPM_SINGLE("CM1 Left Capture Switch", AIC3262_LMIC_PGA_MIN,
6, 3, 0),
};
/* Right Input Mixer */
static const struct snd_kcontrol_new right_input_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
6, 3, 0),
SOC_DAPM_SINGLE("IN2 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
4, 3, 0),
SOC_DAPM_SINGLE("IN3 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
2, 3, 0),
SOC_DAPM_SINGLE("IN4 Right Capture Switch", AIC3262_RMIC_PGA_PM_IN4,
5, 1, 0),
SOC_DAPM_SINGLE("IN2 Left Capture Switch", AIC3262_RMIC_PGA_PIN,
0, 3, 0),
SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_RMIC_PGA_MIN,
4, 3, 0),
SOC_DAPM_SINGLE("IN3 Left Capture Switch", AIC3262_RMIC_PGA_MIN,
2, 3, 0),
SOC_DAPM_SINGLE("IN4 Left Capture Switch", AIC3262_RMIC_PGA_PM_IN4,
4, 1, 0),
SOC_DAPM_SINGLE("CM1 Right Capture Switch", AIC3262_RMIC_PGA_MIN,
6, 3, 0),
SOC_DAPM_SINGLE("CM2 Right Capture Switch", AIC3262_RMIC_PGA_MIN,
0, 3, 0),
};
static const char * const asi1lin_text[] = {
"Off", "ASI1 Left In", "ASI1 Right In", "ASI1 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi1lin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 6, asi1lin_text);
static const struct snd_kcontrol_new asi1lin_control =
SOC_DAPM_ENUM("ASI1LIN Route", asi1lin_enum);
static const char * const asi1rin_text[] = {
"Off", "ASI1 Right In", "ASI1 Left In", "ASI1 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi1rin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 4, asi1rin_text);
static const struct snd_kcontrol_new asi1rin_control =
SOC_DAPM_ENUM("ASI1RIN Route", asi1rin_enum);
static const char * const asi2lin_text[] = {
"Off", "ASI2 Left In", "ASI2 Right In", "ASI2 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi2lin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 6, asi2lin_text);
static const struct snd_kcontrol_new asi2lin_control =
SOC_DAPM_ENUM("ASI2LIN Route", asi2lin_enum);
static const char * const asi2rin_text[] = {
"Off", "ASI2 Right In", "ASI2 Left In", "ASI2 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi2rin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 4, asi2rin_text);
static const struct snd_kcontrol_new asi2rin_control =
SOC_DAPM_ENUM("ASI2RIN Route", asi2rin_enum);
static const char * const asi3lin_text[] = {
"Off", "ASI3 Left In", "ASI3 Right In", "ASI3 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi3lin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 6, asi3lin_text);
static const struct snd_kcontrol_new asi3lin_control =
SOC_DAPM_ENUM("ASI3LIN Route", asi3lin_enum);
static const char * const asi3rin_text[] = {
"Off", "ASI3 Right In", "ASI3 Left In", "ASI3 MonoMix In"
};
SOC_ENUM_SINGLE_DECL(asi3rin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 4, asi3rin_text);
static const struct snd_kcontrol_new asi3rin_control =
SOC_DAPM_ENUM("ASI3RIN Route", asi3rin_enum);
static const char * const dacminidspin1_text[] = {
"ASI1 In", "ASI2 In", "ASI3 In", "ADC MiniDSP Out"
};
SOC_ENUM_SINGLE_DECL(dacminidspin1_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 4,
dacminidspin1_text);
static const struct snd_kcontrol_new dacminidspin1_control =
SOC_DAPM_ENUM("DAC MiniDSP IN1 Route", dacminidspin1_enum);
static const char * const dacminidspin2_text[] = {
"ASI1 In", "ASI2 In", "ASI3 In"
};
SOC_ENUM_SINGLE_DECL(dacminidspin2_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 2,
dacminidspin2_text);
static const struct snd_kcontrol_new dacminidspin2_control =
SOC_DAPM_ENUM("DAC MiniDSP IN2 Route", dacminidspin2_enum);
static const char * const dacminidspin3_text[] = {
"ASI1 In", "ASI2 In", "ASI3 In"
};
SOC_ENUM_SINGLE_DECL(dacminidspin3_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 0,
dacminidspin3_text);
static const struct snd_kcontrol_new dacminidspin3_control =
SOC_DAPM_ENUM("DAC MiniDSP IN3 Route", dacminidspin3_enum);
static const char * const adcdac_route_text[] = {
"Off",
"On",
};
SOC_ENUM_SINGLE_DECL(adcdac_enum, 0, 2, adcdac_route_text);
static const struct snd_kcontrol_new adcdacroute_control =
SOC_DAPM_ENUM("ADC DAC Route", adcdac_enum);
static const char * const dout1_text[] = {
"ASI1 Out",
"DIN1 Bypass",
"DIN2 Bypass",
"DIN3 Bypass",
};
SOC_ENUM_SINGLE_DECL(dout1_enum, AIC3262_ASI1_DOUT_CNTL, 0, dout1_text);
static const struct snd_kcontrol_new dout1_control =
SOC_DAPM_ENUM("DOUT1 Route", dout1_enum);
static const char * const dout2_text[] = {
"ASI2 Out",
"DIN1 Bypass",
"DIN2 Bypass",
"DIN3 Bypass",
};
SOC_ENUM_SINGLE_DECL(dout2_enum, AIC3262_ASI2_DOUT_CNTL, 0, dout2_text);
static const struct snd_kcontrol_new dout2_control =
SOC_DAPM_ENUM("DOUT2 Route", dout2_enum);
static const char * const dout3_text[] = {
"ASI3 Out",
"DIN1 Bypass",
"DIN2 Bypass",
"DIN3 Bypass",
};
SOC_ENUM_SINGLE_DECL(dout3_enum, AIC3262_ASI3_DOUT_CNTL, 0, dout3_text);
static const struct snd_kcontrol_new dout3_control =
SOC_DAPM_ENUM("DOUT3 Route", dout3_enum);
static const char * const asi1out_text[] = {
"Off",
"ADC MiniDSP Out1",
"ASI1In Bypass",
"ASI2In Bypass",
"ASI3In Bypass",
};
SOC_ENUM_SINGLE_DECL(asi1out_enum, AIC3262_ASI1_ADC_INPUT_CNTL,
0, asi1out_text);
static const struct snd_kcontrol_new asi1out_control =
SOC_DAPM_ENUM("ASI1OUT Route", asi1out_enum);
static const char * const asi2out_text[] = {
"Off",
"ADC MiniDSP Out1",
"ASI1In Bypass",
"ASI2In Bypass",
"ASI3In Bypass",
"ADC MiniDSP Out2",
};
SOC_ENUM_SINGLE_DECL(asi2out_enum, AIC3262_ASI2_ADC_INPUT_CNTL,
0, asi2out_text);
static const struct snd_kcontrol_new asi2out_control =
SOC_DAPM_ENUM("ASI2OUT Route", asi2out_enum);
static const char * const asi3out_text[] = {
"Off",
"ADC MiniDSP Out1",
"ASI1In Bypass",
"ASI2In Bypass",
"ASI3In Bypass",
"Reserved",
"ADC MiniDSP Out3",
};
SOC_ENUM_SINGLE_DECL(asi3out_enum, AIC3262_ASI3_ADC_INPUT_CNTL,
0, asi3out_text);
static const struct snd_kcontrol_new asi3out_control =
SOC_DAPM_ENUM("ASI3OUT Route", asi3out_enum);
static const char * const asibclk_text[] = {
"DAC_CLK",
"DAC_MOD_CLK",
"ADC_CLK",
"ADC_MOD_CLK",
};
SOC_ENUM_SINGLE_DECL(asi1bclk_enum, AIC3262_ASI1_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi1bclk_control =
SOC_DAPM_ENUM("ASI1_BCLK Route", asi1bclk_enum);
SOC_ENUM_SINGLE_DECL(asi2bclk_enum, AIC3262_ASI2_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi2bclk_control =
SOC_DAPM_ENUM("ASI2_BCLK Route", asi2bclk_enum);
SOC_ENUM_SINGLE_DECL(asi3bclk_enum, AIC3262_ASI3_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi3bclk_control =
SOC_DAPM_ENUM("ASI3_BCLK Route", asi3bclk_enum);
static const char * const adc_mux_text[] = {
"Analog",
"Digital",
};
SOC_ENUM_SINGLE_DECL(adcl_enum, AIC3262_ADC_CHANNEL_POW, 4, adc_mux_text);
SOC_ENUM_SINGLE_DECL(adcr_enum, AIC3262_ADC_CHANNEL_POW, 2, adc_mux_text);
static const struct snd_kcontrol_new adcl_mux =
SOC_DAPM_ENUM("Left ADC Route", adcl_enum);
static const struct snd_kcontrol_new adcr_mux =
SOC_DAPM_ENUM("Right ADC Route", adcr_enum);
/**
* aic326x_hp_event: - To handle headphone related task before and after
* headphone powrup and power down
* @w: pointer variable to dapm_widget
* @kcontrol: mixer control
* @event: event element information
*
* Returns 0 for success.
*/
static int aic326x_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
int reg_mask = 0;
int mute_reg = 0;
int ret_wbits = 0;
int hpl_hpr;
if (w->shift == 1) {
reg_mask = AIC3262_HPL_POWER_STATUS_MASK;
mute_reg = AIC3262_HPL_VOL;
}
if (w->shift == 0) {
reg_mask = AIC3262_HPR_POWER_STATUS_MASK;
mute_reg = AIC3262_HPR_VOL;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
AIC3262_DYNAMIC_OFFSET_CALIB);
snd_soc_component_write(codec, mute_reg, 0x80);
snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
AIC3262_HP_STAGE_MASK ,
AIC3262_HP_STAGE_25 << AIC3262_HP_STAGE_SHIFT);
break;
case SND_SOC_DAPM_POST_PMU:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_HP_FLAG, reg_mask,
reg_mask, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
if (!ret_wbits) {
dev_err(codec->dev, "HP POST_PMU timedout\n");
return -1;
}
snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
AIC3262_HP_STAGE_MASK ,
AIC3262_HP_STAGE_100 << AIC3262_HP_STAGE_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
AIC3262_HP_STAGE_MASK ,
AIC3262_HP_STAGE_25 << AIC3262_HP_STAGE_SHIFT);
hpl_hpr = snd_soc_component_read(codec, AIC3262_HP_AMP_CNTL_R1);
if((hpl_hpr & 0x3) == 0x3) {
snd_soc_component_update_bits(codec, AIC3262_HP_AMP_CNTL_R1,
AIC3262_HPL_POWER_MASK, 0x0);
mdelay(1);
snd_soc_component_update_bits(codec, AIC3262_HP_AMP_CNTL_R1,
AIC3262_HPR_POWER_MASK, 0x0);
}
break;
case SND_SOC_DAPM_POST_PMD:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_HP_FLAG, reg_mask, 0,
AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
if (!ret_wbits) {
dev_err(codec->dev, "HP POST_PMD timedout\n");
return -1;
}
snd_soc_component_write(codec, mute_reg, 0xb9);
hpl_hpr = snd_soc_component_read(codec, AIC3262_POWER_CONF);
snd_soc_component_write(codec, AIC3262_POWER_CONF, hpl_hpr);
break;
default:
BUG();
return -EINVAL;
}
return 0;
}
/**
*aic326x_dac_event: Headset popup reduction and powering up dsps together
* when they are in sync mode
* @w: pointer variable to dapm_widget
* @kcontrol: pointer to sound control
* @event: event element information
*
* Returns 0 for success.
*/
static int aic326x_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
int reg_mask = 0;
int ret_wbits = 0;
int run_state_mask;
struct aic3262_priv *aic3262 = control_data->control_data;
int sync_needed = 0, non_sync_state = 0;
int other_dsp = 0, run_state = 0;
if (w->shift == 7) {
reg_mask = AIC3262_LDAC_POWER_STATUS_MASK;
run_state_mask = AIC3XXX_COPS_MDSP_D_L;
}
if (w->shift == 6) {
reg_mask = AIC3262_RDAC_POWER_STATUS_MASK;
run_state_mask = AIC3XXX_COPS_MDSP_D_R;
}
switch (event) {
case SND_SOC_DAPM_POST_PMU:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_DAC_FLAG, reg_mask,
reg_mask, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
sync_needed = aic3xxx_reg_read(control_data,
AIC3262_DAC_PRB);
non_sync_state = dsp_non_sync_mode(aic3262->dsp_runstate);
other_dsp = aic3262->dsp_runstate & AIC3XXX_COPS_MDSP_A;
if (sync_needed && non_sync_state && other_dsp) {
run_state = aic3262_get_runstate(
aic3262->codec);
aic3262_dsp_pwrdwn_status(aic3262->codec);
aic3262_dsp_pwrup(aic3262->codec, run_state);
}
aic3262->dsp_runstate |= run_state_mask;
if (!ret_wbits) {
dev_err(codec->dev, "DAC POST_PMU timedout\n");
return -1;
}
break;
case SND_SOC_DAPM_POST_PMD:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_DAC_FLAG, reg_mask, 0,
AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
aic3262->dsp_runstate = (aic3262->dsp_runstate &
~run_state_mask);
if (!ret_wbits) {
dev_err(codec->dev, "DAC POST_PMD timedout\n");
return -1;
}
break;
default:
BUG();
return -EINVAL;
}
return 0;
}
/**
* aic326x_spk_event: Speaker related task before and after
* headphone powrup and power down$
* @w: pointer variable to dapm_widget,
* @kcontrolr: pointer variable to sound control,
* @event: integer to event,
*
* Return value: 0 for success
*/
static int aic326x_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
int reg_mask;
if (w->shift == 1)
reg_mask = AIC3262_SPKL_POWER_STATUS_MASK;
if (w->shift == 0)
reg_mask = AIC3262_SPKR_POWER_STATUS_MASK;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
mdelay(1);
break;
case SND_SOC_DAPM_POST_PMD:
mdelay(1);
break;
default:
BUG();
return -EINVAL;
}
return 0;
}
/**$
* pll_power_on_event: provide delay after widget power up
* @w: pointer variable to dapm_widget,
* @kcontrolr: pointer variable to sound control,
* @event: integer to event,
*
* Return value: 0 for success
*/
static int pll_power_on_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (event == SND_SOC_DAPM_POST_PMU)
mdelay(10);
return 0;
}
/**
* aic3262_set_mode_get: To get different mode of Firmware through tinymix
* @kcontrolr: pointer to sound control,
* ucontrol: pointer to control element value,
*
* Return value: 0 for success
*/
static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *priv_ds = control_data->control_data;
ucontrol->value.integer.value[0] = ((priv_ds->cfw_p->cur_mode << 8)
| priv_ds->cfw_p->cur_cfg);
return 0;
}
/**
* aic3262_set_mode_put: To set different mode of Firmware through tinymix
* @kcontrolr: pointer to sound control,
* ucontrol: pointer to control element value,
*
* Return value: 0 for success
*/
static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *priv_ds = control_data->control_data;
int next_mode = 0, next_cfg = 0;
int ret = 0;
next_mode = (ucontrol->value.integer.value[0] >> 8);
next_cfg = (ucontrol->value.integer.value[0]) & 0xFF;
if (priv_ds == NULL)
dev_err(codec->dev, "failed to load firmware\n");
else
ret = aic3xxx_cfw_setmode_cfg(priv_ds->cfw_p,
next_mode, next_cfg);
return ret;
}
/**
* aic326x_adc_dsp_event: To get DSP run state to perform synchronization
* @w: pointer variable to dapm_widget
* @kcontrol: pointer to sound control
* @event: event element information
*
* Returns 0 for success.
*/
static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
int run_state = 0;
int non_sync_state = 0, sync_needed = 0;
int other_dsp = 0;
int run_state_mask = 0;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control_data->control_data;
int reg_mask = 0;
int ret_wbits = 0;
if (w->shift == 7) {
reg_mask = AIC3262_LADC_POWER_MASK;
run_state_mask = AIC3XXX_COPS_MDSP_A_L;
}
if (w->shift == 6) {
reg_mask = AIC3262_RADC_POWER_MASK;
run_state_mask = AIC3XXX_COPS_MDSP_A_R;
}
switch (event) {
case SND_SOC_DAPM_POST_PMU:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_ADC_FLAG, reg_mask,
reg_mask, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
sync_needed = aic3xxx_reg_read(control_data,
AIC3262_DAC_PRB);
non_sync_state = dsp_non_sync_mode(aic3262->dsp_runstate);
other_dsp = aic3262->dsp_runstate & AIC3XXX_COPS_MDSP_D;
if (sync_needed && non_sync_state && other_dsp) {
run_state = aic3262_get_runstate(
aic3262->codec);
aic3262_dsp_pwrdwn_status(aic3262->codec);
aic3262_dsp_pwrup(aic3262->codec, run_state);
}
aic3262->dsp_runstate |= run_state_mask;
if (!ret_wbits) {
dev_err(codec->dev, "ADC POST_PMU timedout\n");
return -1;
}
break;
case SND_SOC_DAPM_POST_PMD:
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_ADC_FLAG, reg_mask, 0,
AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
aic3262->dsp_runstate = (aic3262->dsp_runstate &
~run_state_mask);
if (!ret_wbits) {
dev_err(codec->dev, "ADC POST_PMD timedout\n");
return -1;
}
break;
default:
BUG();
return -EINVAL;
}
return 0;
}
static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = {
/* TODO: Can we switch these off ? */
SND_SOC_DAPM_AIF_IN("DIN1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("DIN2", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("DIN3", "ASI3 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC_E("Left DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 7, 0,
aic326x_dac_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("Right DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 6, 0,
aic326x_dac_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
/* dapm widget (path domain) for HPL Output Mixer */
SND_SOC_DAPM_MIXER("HP Left Mixer", SND_SOC_NOPM, 0, 0,
&hpl_output_mixer_controls[0],
ARRAY_SIZE(hpl_output_mixer_controls)),
/* dapm widget (path domain) for HPR Output Mixer */
SND_SOC_DAPM_MIXER("HP Right Mixer", SND_SOC_NOPM, 0, 0,
&hpr_output_mixer_controls[0],
ARRAY_SIZE(hpr_output_mixer_controls)),
SND_SOC_DAPM_PGA_S("HP Left Playback Driver", 3,
AIC3262_HP_AMP_CNTL_R1, 1, 0, aic326x_hp_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_S("HP Right Playback Driver", 3,
AIC3262_HP_AMP_CNTL_R1, 0, 0, aic326x_hp_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* dapm widget (path domain) for LOL Output Mixer */
SND_SOC_DAPM_MIXER("LO Left Mixer", SND_SOC_NOPM, 0, 0,
&lol_output_mixer_controls[0],
ARRAY_SIZE(lol_output_mixer_controls)),
/* dapm widget (path domain) for LOR Output Mixer mixer */
SND_SOC_DAPM_MIXER("LO Right Mixer", SND_SOC_NOPM, 0, 0,
&lor_output_mixer_controls[0],
ARRAY_SIZE(lor_output_mixer_controls)),
SND_SOC_DAPM_PGA_S("LO Left Playback Driver", 2,
AIC3262_LINE_AMP_CNTL_R1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("LO Right Playback Driver", 2,
AIC3262_LINE_AMP_CNTL_R1, 0, 0, NULL, 0),
/* dapm widget (path domain) for SPKL Output Mixer */
SND_SOC_DAPM_MIXER("SPK Left Mixer", SND_SOC_NOPM, 0, 0,
&spkl_output_mixer_controls[0],
ARRAY_SIZE(spkl_output_mixer_controls)),
/* dapm widget (path domain) for SPKR Output Mixer */
SND_SOC_DAPM_MIXER("SPK Right Mixer", SND_SOC_NOPM, 0, 0,
&spkr_output_mixer_controls[0],
ARRAY_SIZE(spkr_output_mixer_controls)),
SND_SOC_DAPM_PGA_S("SPK Left Playback Driver", 3,
AIC3262_SPK_AMP_CNTL_R1, 1, 0, aic326x_spk_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("SPK Right Playback Driver", 3,
AIC3262_SPK_AMP_CNTL_R1, 0, 0, aic326x_spk_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* dapm widget (path domain) for SPKR Output Mixer */
SND_SOC_DAPM_MIXER("REC Mixer", SND_SOC_NOPM, 0, 0,
&rec_output_mixer_controls[0],
ARRAY_SIZE(rec_output_mixer_controls)),
SND_SOC_DAPM_PGA_S("RECP Playback Driver", 3, AIC3262_REC_AMP_CNTL_R5,
7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("RECM Playback Driver", 3, AIC3262_REC_AMP_CNTL_R5,
6, 0, NULL, 0),
SND_SOC_DAPM_MUX("ASI1LIN Route",
SND_SOC_NOPM, 0, 0, &asi1lin_control),
SND_SOC_DAPM_MUX("ASI1RIN Route",
SND_SOC_NOPM, 0, 0, &asi1rin_control),
SND_SOC_DAPM_MUX("ASI2LIN Route",
SND_SOC_NOPM, 0, 0, &asi2lin_control),
SND_SOC_DAPM_MUX("ASI2RIN Route",
SND_SOC_NOPM, 0, 0, &asi2rin_control),
SND_SOC_DAPM_MUX("ASI3LIN Route",
SND_SOC_NOPM, 0, 0, &asi3lin_control),
SND_SOC_DAPM_MUX("ASI3RIN Route",
SND_SOC_NOPM, 0, 0, &asi3rin_control),
SND_SOC_DAPM_PGA("ASI1LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI1RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI2LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI2RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI3LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI3RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI1MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI2MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI3MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
/* TODO: Can we switch the ASIxIN off? */
SND_SOC_DAPM_PGA("ASI1IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI2IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI3IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("DAC MiniDSP IN1 Route",
SND_SOC_NOPM, 0, 0, &dacminidspin1_control),
SND_SOC_DAPM_MUX("DAC MiniDSP IN2 Route",
SND_SOC_NOPM, 0, 0, &dacminidspin2_control),
SND_SOC_DAPM_MUX("DAC MiniDSP IN3 Route",
SND_SOC_NOPM, 0, 0, &dacminidspin3_control),
SND_SOC_DAPM_MUX("ADC DAC Route",
SND_SOC_NOPM, 0, 0, &adcdacroute_control),
SND_SOC_DAPM_PGA("CM", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("CM1 Left Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("CM2 Left Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("CM1 Right Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("CM2 Right Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
/* TODO: Can we switch these off ? */
SND_SOC_DAPM_AIF_OUT("DOUT1", "ASI1 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DOUT2", "ASI2 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DOUT3", "ASI3 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DOUT1 Route",
SND_SOC_NOPM, 0, 0, &dout1_control),
SND_SOC_DAPM_MUX("DOUT2 Route",
SND_SOC_NOPM, 0, 0, &dout2_control),
SND_SOC_DAPM_MUX("DOUT3 Route",
SND_SOC_NOPM, 0, 0, &dout3_control),
SND_SOC_DAPM_PGA("ASI1OUT", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI2OUT", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ASI3OUT", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("ASI1OUT Route",
SND_SOC_NOPM, 0, 0, &asi1out_control),
SND_SOC_DAPM_MUX("ASI2OUT Route",
SND_SOC_NOPM, 0, 0, &asi2out_control),
SND_SOC_DAPM_MUX("ASI3OUT Route",
SND_SOC_NOPM, 0, 0, &asi3out_control),
/* TODO: Can we switch the ASI1 OUT1 off? */
/* TODO: Can we switch them off? */
SND_SOC_DAPM_PGA("ADC MiniDSP OUT1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC MiniDSP OUT2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC MiniDSP OUT3", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM, 0, 0, &adcl_mux),
SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM, 0, 0, &adcr_mux),
SND_SOC_DAPM_ADC_E("Left ADC", NULL, AIC3262_ADC_CHANNEL_POW, 7, 0,
aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_ADC_E("Right ADC", NULL, AIC3262_ADC_CHANNEL_POW, 6, 0,
aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_S("Left MicPGA", 0, AIC3262_MICL_PGA, 7, 1, NULL, 0),
SND_SOC_DAPM_PGA_S("Right MicPGA", 0, AIC3262_MICR_PGA, 7, 1, NULL, 0),
SND_SOC_DAPM_PGA_S("MA Left Playback PGA", 1, AIC3262_MA_CNTL,
3, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("MA Right Playback PGA", 1, AIC3262_MA_CNTL,
2, 0, NULL, 0),
/* dapm widget for MAL PGA Mixer */
SND_SOC_DAPM_MIXER("MA Left PGA Mixer", SND_SOC_NOPM, 0, 0,
&mal_pga_mixer_controls[0],
ARRAY_SIZE(mal_pga_mixer_controls)),
/* dapm widget for MAR PGA Mixer */
SND_SOC_DAPM_MIXER("MA Right PGA Mixer", SND_SOC_NOPM, 0, 0,
&mar_pga_mixer_controls[0],
ARRAY_SIZE(mar_pga_mixer_controls)),
/* dapm widget for Left Input Mixer */
SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
&left_input_mixer_controls[0],
ARRAY_SIZE(left_input_mixer_controls)),
/* dapm widget for Right Input Mixer */
SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
&right_input_mixer_controls[0],
ARRAY_SIZE(right_input_mixer_controls)),
SND_SOC_DAPM_OUTPUT("HP Left Playback"),
SND_SOC_DAPM_OUTPUT("HP Right Playback"),
SND_SOC_DAPM_OUTPUT("LO Left Playback"),
SND_SOC_DAPM_OUTPUT("LO Right Playback"),
SND_SOC_DAPM_OUTPUT("SPK Left Playback"),
SND_SOC_DAPM_OUTPUT("SPK Right Playback"),
SND_SOC_DAPM_OUTPUT("RECP Playback"),
SND_SOC_DAPM_OUTPUT("RECM Playback"),
SND_SOC_DAPM_INPUT("IN1 Left Capture"),
SND_SOC_DAPM_INPUT("IN2 Left Capture"),
SND_SOC_DAPM_INPUT("IN3 Left Capture"),
SND_SOC_DAPM_INPUT("IN4 Left Capture"),
SND_SOC_DAPM_INPUT("IN1 Right Capture"),
SND_SOC_DAPM_INPUT("IN2 Right Capture"),
SND_SOC_DAPM_INPUT("IN3 Right Capture"),
SND_SOC_DAPM_INPUT("IN4 Right Capture"),
SND_SOC_DAPM_INPUT("Left DMIC Capture"),
SND_SOC_DAPM_INPUT("Right DMIC Capture"),
SND_SOC_DAPM_MICBIAS("Mic Bias Ext", AIC3262_MIC_BIAS_CNTL, 6, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias Int", AIC3262_MIC_BIAS_CNTL, 2, 0),
SND_SOC_DAPM_PGA_S("PLLCLK", 0, AIC3262_PLL_PR_POW_REG, 7, 0,
pll_power_on_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("DACCLK", 2, AIC3262_NDAC_DIV_POW_REG, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("CODEC_CLK_IN", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("DAC_MOD_CLK", 3, AIC3262_MDAC_DIV_POW_REG,
7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("ADCCLK", 2, AIC3262_NADC_DIV_POW_REG, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ADC_MOD_CLK", 3, AIC3262_MADC_DIV_POW_REG,
7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("ASI1_BCLK", 4, AIC3262_ASI1_BCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ASI1_WCLK", 4, AIC3262_ASI1_WCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ASI2_BCLK", 4, AIC3262_ASI2_BCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ASI2_WCLK", 4, AIC3262_ASI2_WCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ASI3_BCLK", 4, AIC3262_ASI3_BCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("ASI3_WCLK", 4, AIC3262_ASI3_WCLK_N, 7, 0,
NULL, 0),
SND_SOC_DAPM_MUX("ASI1_BCLK Route",
SND_SOC_NOPM, 0, 0, &asi1bclk_control),
SND_SOC_DAPM_MUX("ASI2_BCLK Route",
SND_SOC_NOPM, 0, 0, &asi2bclk_control),
SND_SOC_DAPM_MUX("ASI3_BCLK Route",
SND_SOC_NOPM, 0, 0, &asi3bclk_control),
};
static const struct snd_soc_dapm_route aic3262_dapm_routes[] = {
/* TODO: Do we need only DACCLK for ASIIN's and ADCCLK for ASIOUT??? */
/* Clock portion */
{"CODEC_CLK_IN", NULL, "PLLCLK"},
{"DACCLK", NULL, "CODEC_CLK_IN"},
{"ADCCLK", NULL, "CODEC_CLK_IN"},
{"DAC_MOD_CLK", NULL, "DACCLK"},
#ifdef AIC3262_SYNC_MODE
{"ADC_MOD_CLK", NULL, "DACCLK"},
#else
{"ADC_MOD_CLK", NULL, "ADCCLK"},
#endif
{"ASI1_BCLK Route", "DAC_CLK", "DACCLK"},
{"ASI1_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
{"ASI1_BCLK Route", "ADC_CLK", "ADCCLK"},
{"ASI1_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},
{"ASI2_BCLK Route", "DAC_CLK", "DACCLK"},
{"ASI2_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
{"ASI2_BCLK Route", "ADC_CLK", "ADCCLK"},
{"ASI2_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},
{"ASI3_BCLK Route", "DAC_CLK", "DACCLK"},
{"ASI3_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
{"ASI3_BCLK Route", "ADC_CLK", "ADCCLK"},
{"ASI3_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},
{"ASI1_BCLK", NULL, "ASI1_BCLK Route"},
{"ASI2_BCLK", NULL, "ASI2_BCLK Route"},
{"ASI3_BCLK", NULL, "ASI3_BCLK Route"},
#ifdef AIC3262_ASI1_MASTER
{"DIN1", NULL, "ASI1_BCLK"},
{"DOUT1", NULL, "ASI1_BCLK"},
{"DIN1", NULL, "ASI1_WCLK"},
{"DOUT1", NULL, "ASI1_WCLK"},
#endif
#ifdef AIC3262_ASI2_MASTER
{"DIN2", NULL, "ASI2_BCLK"},
{"DOUT2", NULL, "ASI2_BCLK"},
{"DIN2", NULL, "ASI2_WCLK"},
{"DOUT2", NULL, "ASI2_WCLK"},
#endif
#ifdef AIC3262_ASI3_MASTER
{"DIN3", NULL, "ASI3_BCLK"},
{"DOUT3", NULL, "ASI3_BCLK"},
{"DIN3", NULL, "ASI3_WCLK"},
{"DOUT3", NULL, "ASI3_WCLK"},
#endif
{"Left DAC", NULL, "DAC_MOD_CLK"},
{"Right DAC", NULL, "DAC_MOD_CLK"},
/* When we are master, ASI bclk and wclk are generated by
* DAC_MOD_CLK, so we put them as dependency for ADC too.
*/
{"Left ADC", NULL, "DAC_MOD_CLK"},
{"Right ADC", NULL, "DAC_MOD_CLK"},
{"Left ADC", NULL, "ADC_MOD_CLK"},
{"Right ADC", NULL, "ADC_MOD_CLK"},
/* Playback (DAC) Portion */
{"HP Left Mixer", "Left DAC Playback Switch", "Left DAC"},
{"HP Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
{"HP Left Mixer", "LO Left-B1 Playback Volume", "LO Left Playback"},
{"HP Right Mixer", "LO Right-B1 Playback Volume", "LO Right Playback"},
{"HP Right Mixer", "Left DAC Playback Switch", "Left DAC"},
{"HP Right Mixer", "Right DAC Playback Switch", "Right DAC"},
{"HP Right Mixer", "MA Right Playback Switch", "MA Right Playback PGA"},
{"HP Left Playback Driver", NULL, "HP Left Mixer"},
{"HP Right Playback Driver", NULL, "HP Right Mixer"},
{"HP Left Playback", NULL, "HP Left Playback Driver"},
{"HP Right Playback", NULL, "HP Right Playback Driver"},
{"LO Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
{"LO Left Mixer", "IN1 Left-B Capture Switch", "IN1 Left Capture"},
{"LO Left Mixer", "Left DAC Playback Switch", "Left DAC"},
{"LO Left Mixer", "Right DAC Playback Switch", "Right DAC"},
{"LO Right Mixer", "LO Left Playback Switch", "LO Left Playback"},
{"LO Right Mixer", "Right DAC Playback Switch", "Right DAC"},
{"LO Right Mixer", "MA Right Playback Switch", "MA Right Playback PGA"},
{"LO Right Mixer", "IN1 Right-B Capture Switch", "IN1 Right Capture"},
{"LO Left Playback Driver", NULL, "LO Left Mixer"},
{"LO Right Playback Driver", NULL, "LO Right Mixer"},
{"LO Left Playback", NULL, "LO Left Playback Driver"},
{"LO Right Playback", NULL, "LO Right Playback Driver"},
{"REC Mixer", "LO Left-B2 Playback Volume", "LO Left Playback"},
{"REC Mixer", "IN1 Left Capture Volume", "IN1 Left Capture"},
{"REC Mixer", "IN1 Right Capture Volume", "IN1 Right Capture"},
{"REC Mixer", "LO Right-B2 Playback Volume", "LO Right Playback"},
{"RECP Playback Driver", NULL, "REC Mixer"},
{"RECM Playback Driver", NULL, "REC Mixer"},
{"RECP Playback", NULL, "RECP Playback Driver"},
{"RECM Playback", NULL, "RECM Playback Driver"},
{"SPK Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
{"SPK Left Mixer", "LO Left Playback Volume", "LO Left Playback"},
{"SPK Left Mixer", "SPR_IN Switch", "SPK Right Mixer"},
{"SPK Right Mixer", "LO Right Playback Volume", "LO Right Playback"},
{"SPK Right Mixer", "MA Right Playback Switch",
"MA Right Playback PGA"},
{"SPK Left Playback Driver", NULL, "SPK Left Mixer"},
{"SPK Right Playback Driver", NULL, "SPK Right Mixer"},
{"SPK Left Playback", NULL, "SPK Left Playback Driver"},
{"SPK Right Playback", NULL, "SPK Right Playback Driver"},
/* ASI Input routing */
{"ASI1LIN", NULL, "DIN1"},
{"ASI1RIN", NULL, "DIN1"},
{"ASI1MonoMixIN", NULL, "DIN1"},
{"ASI2LIN", NULL, "DIN2"},
{"ASI2RIN", NULL, "DIN2"},
{"ASI2MonoMixIN", NULL, "DIN2"},
{"ASI3LIN", NULL, "DIN3"},
{"ASI3RIN", NULL, "DIN3"},
{"ASI3MonoMixIN", NULL, "DIN3"},
{"ASI1LIN Route", "ASI1 Left In", "ASI1LIN"},
{"ASI1LIN Route", "ASI1 Right In", "ASI1RIN"},
{"ASI1LIN Route", "ASI1 MonoMix In", "ASI1MonoMixIN"},
{"ASI1RIN Route", "ASI1 Right In", "ASI1RIN"},
{"ASI1RIN Route", "ASI1 Left In", "ASI1LIN"},
{"ASI1RIN Route", "ASI1 MonoMix In", "ASI1MonoMixIN"},
{"ASI2LIN Route", "ASI2 Left In", "ASI2LIN"},
{"ASI2LIN Route", "ASI2 Right In", "ASI2RIN"},
{"ASI2LIN Route", "ASI2 MonoMix In", "ASI2MonoMixIN"},
{"ASI2RIN Route", "ASI2 Right In", "ASI2RIN"},
{"ASI2RIN Route", "ASI2 Left In", "ASI2LIN"},
{"ASI2RIN Route", "ASI2 MonoMix In", "ASI2MonoMixIN"},
{"ASI3LIN Route", "ASI3 Left In", "ASI3LIN"},
{"ASI3LIN Route", "ASI3 Right In", "ASI3RIN"},
{"ASI3LIN Route", "ASI3 MonoMix In", "ASI3MonoMixIN"},
{"ASI3RIN Route", "ASI3 Right In", "ASI3RIN"},
{"ASI3RIN Route", "ASI3 Left In", "ASI3LIN"},
{"ASI3RIN Route", "ASI3 MonoMix In", "ASI3MonoMixIN"},
{"ASI1IN Port", NULL, "ASI1LIN Route"},
{"ASI1IN Port", NULL, "ASI1RIN Route"},
{"ASI2IN Port", NULL, "ASI2LIN Route"},
{"ASI2IN Port", NULL, "ASI2RIN Route"},
{"ASI3IN Port", NULL, "ASI3LIN Route"},
{"ASI3IN Port", NULL, "ASI3RIN Route"},
{"DAC MiniDSP IN1 Route", "ASI1 In", "ASI1IN Port"},
{"DAC MiniDSP IN1 Route", "ASI2 In", "ASI2IN Port"},
{"DAC MiniDSP IN1 Route", "ASI3 In", "ASI3IN Port"},
{"DAC MiniDSP IN1 Route", "ADC MiniDSP Out", "ADC MiniDSP OUT1"},
{"DAC MiniDSP IN2 Route", "ASI1 In", "ASI1IN Port"},
{"DAC MiniDSP IN2 Route", "ASI2 In", "ASI2IN Port"},
{"DAC MiniDSP IN2 Route", "ASI3 In", "ASI3IN Port"},
{"DAC MiniDSP IN3 Route", "ASI1 In", "ASI1IN Port"},
{"DAC MiniDSP IN3 Route", "ASI2 In", "ASI2IN Port"},
{"DAC MiniDSP IN3 Route", "ASI3 In", "ASI3IN Port"},
/*{"Left DAC", "NULL", "DAC MiniDSP IN1 Route"},
{"Right DAC", "NULL", "DAC MiniDSP IN1 Route"},
{"Left DAC", "NULL", "DAC MiniDSP IN2 Route"},
{"Right DAC", "NULL", "DAC MiniDSP IN2 Route"},
{"Left DAC", "NULL", "DAC MiniDSP IN3 Route"},
{"Right DAC", "NULL", "DAC MiniDSP IN3 Route"},*/
/* Mixer Amplifier */
{"MA Left PGA Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
{"MA Left PGA Mixer", "Left MicPGA Volume", "Left MicPGA"},
{"MA Left Playback PGA", NULL, "MA Left PGA Mixer"},
{"MA Right PGA Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
{"MA Right PGA Mixer", "Right MicPGA Volume", "Right MicPGA"},
{"MA Right Playback PGA", NULL, "MA Right PGA Mixer"},
/* Virtual connection between DAC and ADC for miniDSP IPC */
{"ADC DAC Route", "On", "Left ADC"},
{"ADC DAC Route", "On", "Right ADC"},
{"Left DAC", NULL, "ADC DAC Route"},
{"Right DAC", NULL, "ADC DAC Route"},
/* Capture (ADC) portions */
/* Left Positive PGA input */
{"Left Input Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
{"Left Input Mixer", "IN2 Left Capture Switch", "IN2 Left Capture"},
{"Left Input Mixer", "IN3 Left Capture Switch", "IN3 Left Capture"},
{"Left Input Mixer", "IN4 Left Capture Switch", "IN4 Left Capture"},
{"Left Input Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
/* Left Negative PGA input */
{"Left Input Mixer", "IN2 Right Capture Switch", "IN2 Right Capture"},
{"Left Input Mixer", "IN3 Right Capture Switch", "IN3 Right Capture"},
{"Left Input Mixer", "IN4 Right Capture Switch", "IN4 Right Capture"},
{"Left Input Mixer", "CM2 Left Capture Switch", "CM2 Left Capture"},
{"Left Input Mixer", "CM1 Left Capture Switch", "CM1 Left Capture"},
/* Right Positive PGA Input */
{"Right Input Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
{"Right Input Mixer", "IN2 Right Capture Switch", "IN2 Right Capture"},
{"Right Input Mixer", "IN3 Right Capture Switch", "IN3 Right Capture"},
{"Right Input Mixer", "IN4 Right Capture Switch", "IN4 Right Capture"},
{"Right Input Mixer", "IN2 Left Capture Switch", "IN2 Left Capture"},
/* Right Negative PGA Input */
{"Right Input Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
{"Right Input Mixer", "IN3 Left Capture Switch", "IN3 Left Capture"},
{"Right Input Mixer", "IN4 Left Capture Switch", "IN4 Left Capture"},
{"Right Input Mixer", "CM1 Right Capture Switch", "CM1 Right Capture"},
{"Right Input Mixer", "CM2 Right Capture Switch", "CM2 Right Capture"},
{"CM1 Left Capture", NULL, "CM"},
{"CM2 Left Capture", NULL, "CM"},
{"CM1 Right Capture", NULL, "CM"},
{"CM2 Right Capture", NULL, "CM"},
{"Left MicPGA", NULL, "Left Input Mixer"},
{"Right MicPGA", NULL, "Right Input Mixer"},
{"Left ADC Route", "Analog", "Left MicPGA"},
{"Left ADC Route", "Digital", "Left DMIC Capture"},
{"Right ADC Route", "Analog", "Right MicPGA"},
{"Right ADC Route", "Digital", "Right DMIC Capture"},
{"Left ADC", NULL, "Left ADC Route"},
{"Right ADC", NULL, "Right ADC Route"},
/* ASI Output Routing */
{"ADC MiniDSP OUT1", NULL, "Left ADC"},
{"ADC MiniDSP OUT1", NULL, "Right ADC"},
{"ADC MiniDSP OUT2", NULL, "Left ADC"},
{"ADC MiniDSP OUT2", NULL, "Right ADC"},
{"ADC MiniDSP OUT3", NULL, "Left ADC"},
{"ADC MiniDSP OUT3", NULL, "Right ADC"},
{"ASI1OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
{"ASI1OUT Route", "ASI1In Bypass", "ASI1IN Port"},
{"ASI1OUT Route", "ASI2In Bypass", "ASI2IN Port"},
{"ASI1OUT Route", "ASI3In Bypass", "ASI3IN Port"},
{"ASI2OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
{"ASI2OUT Route", "ASI1In Bypass", "ASI1IN Port"},
{"ASI2OUT Route", "ASI2In Bypass", "ASI2IN Port"},
{"ASI2OUT Route", "ASI3In Bypass", "ASI3IN Port"},
{"ASI2OUT Route", "ADC MiniDSP Out2", "ADC MiniDSP OUT2"},
{"ASI3OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
{"ASI3OUT Route", "ASI1In Bypass", "ASI1IN Port"},
{"ASI3OUT Route", "ASI2In Bypass", "ASI2IN Port"},
{"ASI3OUT Route", "ASI3In Bypass", "ASI3IN Port"},
{"ASI3OUT Route", "ADC MiniDSP Out3", "ADC MiniDSP OUT3"},
{"ASI1OUT", NULL, "ASI1OUT Route"},
{"ASI2OUT", NULL, "ASI2OUT Route"},
{"ASI3OUT", NULL, "ASI3OUT Route"},
{"DOUT1 Route", "ASI1 Out", "ASI1OUT"},
{"DOUT1 Route", "DIN1 Bypass", "DIN1"},
{"DOUT1 Route", "DIN2 Bypass", "DIN2"},
{"DOUT1 Route", "DIN3 Bypass", "DIN3"},
{"DOUT2 Route", "ASI2 Out", "ASI2OUT"},
{"DOUT2 Route", "DIN1 Bypass", "DIN1"},
{"DOUT2 Route", "DIN2 Bypass", "DIN2"},
{"DOUT2 Route", "DIN3 Bypass", "DIN3"},
{"DOUT3 Route", "ASI3 Out", "ASI3OUT"},
{"DOUT3 Route", "DIN1 Bypass", "DIN1"},
{"DOUT3 Route", "DIN2 Bypass", "DIN2"},
{"DOUT3 Route", "DIN3 Bypass", "DIN3"},
{"DOUT1", NULL, "DOUT1 Route"},
{"DOUT2", NULL, "DOUT2 Route"},
{"DOUT3", NULL, "DOUT3 Route"},
};
#define AIC3262_DAPM_ROUTE_NUM (ARRAY_SIZE(aic3262_dapm_routes)/ \
sizeof(struct snd_soc_dapm_route))
/* aic3262_firmware_load: This function is called by the
* request_firmware_nowait function as soon
* as the firmware has been loaded from the file.
* The firmware structure contains the data and$
* the size of the firmware loaded.
* @fw: pointer to firmware file to be dowloaded
* @context: pointer variable to codec
*
* Returns 0 for success.
*/
static void aic3262_firmware_load(const struct firmware *fw,
void *context)
{
struct snd_soc_component *codec = context;
struct aic3xxx *control_data =
snd_soc_component_get_drvdata(codec);
struct aic3262_priv *private_ds = control_data->control_data;
int ret = 0;
aic3xxx_cfw_lock(private_ds->cfw_p, 1);
if (private_ds->cur_fw != NULL) {
release_firmware(private_ds->cur_fw);
private_ds->cur_fw = NULL;
}
if(private_ds->cfw_ps.pjt) {
remove_cfw(private_ds->cfw_ps.pjt);
private_ds->cfw_ps.pjt = NULL;
}
if (fw != NULL) {
dev_info(codec->dev, "Firmware binary load\n");
private_ds->cur_fw = (void *)fw;
ret = aic3xxx_cfw_reload(private_ds->cfw_p, (void *)fw->data,
fw->size);
if (ret < 0) { /* reload failed */
dev_err(codec->dev, "Firmware binary load failed\n");
release_firmware(private_ds->cur_fw);
private_ds->cur_fw = NULL;
fw = NULL;
}
} else {
/* request_firmware failed*/
/* could not locate file tlv320aic3262_fw_v1.bin
under /vendor/firmare
*/
dev_err(codec->dev, "request_firmware failed\n");
ret = -1;
}
aic3xxx_cfw_lock(private_ds->cfw_p, 0);
if (ret >= 0) {
/*init function for transition */
aic3xxx_cfw_transition(private_ds->cfw_p, "INIT");
/* add firmware modes */
aic3xxx_cfw_add_modes(codec, private_ds->cfw_p);
/* add runtime controls */
aic3xxx_cfw_add_controls(codec, private_ds->cfw_p);
/* set the default firmware mode */
aic3xxx_cfw_setmode_cfg(private_ds->cfw_p, 0, 0);
}
}
/*=========================================================
headset work and headphone/headset jack interrupt handlers
========================================================*/
enum headset_accessory_state {
BIT_NO_ACCESSORY = 0,
BIT_HEADSET = (1 << 0),
BIT_HEADPHONE = (1 << 1),
};
/**
* aic3262_hs_jack_report: Report jack notication to upper layor
* @codec: pointer variable to codec having information related to codec
* @jack: Pointer variable to snd_soc_jack having information of codec
* and pin number$
* @report: Provides informaton of whether it is headphone or microphone
*
*/
static void aic3262_hs_jack_report(struct snd_soc_component *codec,
struct snd_soc_jack *jack, int report)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control_data->control_data;
int status, state = 0, switch_state = BIT_NO_ACCESSORY;
mutex_lock(&aic3262->mutex);
/* Sync status */
status = snd_soc_component_read(codec, AIC3262_DAC_FLAG);
/* We will check only stereo MIC and headphone */
switch (status & AIC3262_JACK_TYPE_MASK) {
case AIC3262_JACK_WITH_MIC:
state |= SND_JACK_HEADSET;
break;
case AIC3262_JACK_WITHOUT_MIC:
state |= SND_JACK_HEADPHONE;
}
mutex_unlock(&aic3262->mutex);
snd_soc_jack_report(jack, state, report);
if ((state & SND_JACK_HEADSET) == SND_JACK_HEADSET)
switch_state |= BIT_HEADSET;
else if (state & SND_JACK_HEADPHONE)
switch_state |= BIT_HEADPHONE;
}
/**
* aic3262_hs_jack_detect: Detect headphone jack during boot time
* @codec: pointer variable to codec having information related to codec
* @jack: Pointer variable to snd_soc_jack having information of codec
* and pin number$
* @report: Provides informaton of whether it is headphone or microphone
*
*/
void aic3262_hs_jack_detect(struct snd_soc_component *codec,
struct snd_soc_jack *jack, int report)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control_data->control_data;
struct aic3262_jack_data *hs_jack = &aic3262->hs_jack;
hs_jack->jack = jack;
hs_jack->report = report;
aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
}
EXPORT_SYMBOL_GPL(aic3262_hs_jack_detect);
/**
* aic3262_accessory_work: Finished bottom half work from headphone jack
* insertion interupt
* @work: pionter variable to work_struct which is maintaining work queqe
*
*/
static void aic3262_accessory_work(struct work_struct *work)
{
struct aic3262_priv *aic3262 = container_of(work,
struct aic3262_priv,
delayed_work.work);
struct snd_soc_component *codec = aic3262->codec;
struct aic3262_jack_data *hs_jack = &aic3262->hs_jack;
aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
}
/**
* aic3262_audio_handler: audio interrupt handler called
* when interupt is generated
* @irq: provides interupt number which is assigned by aic3262_request_irq,
* @data having information of data passed by aic3262_request_irq last arg,
*
* Return IRQ_HANDLED(means interupt handeled successfully)
*/
static irqreturn_t aic3262_audio_handler(int irq, void *data)
{
struct snd_soc_component *codec = data;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control_data->control_data;
queue_delayed_work(aic3262->workqueue, &aic3262->delayed_work,
msecs_to_jiffies(200));
return IRQ_HANDLED;
}
/**
* Methods for CFW Operations
*
* Due to incompatibilites between structures used by MFD and CFW
* we need to transform the register format before linking to
* CFW operations.
*/
static inline unsigned int aic3262_ops_cfw2reg(unsigned int reg)
{
union cfw_register *c = (union cfw_register *)
union aic3xxx_reg_union mreg;
mreg.aic3xxx_register.offset = c->offset;
mreg.aic3xxx_register.page = c->page;
mreg.aic3xxx_register.book = c->book;
mreg.aic3xxx_register.reserved = 0;
return mreg.aic3xxx_register_int;
}
static int aic3262_ops_reg_read(struct snd_soc_component *codec, unsigned int reg)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_reg_read(control_data, aic3262_ops_cfw2reg(reg));
}
static int aic3262_ops_reg_write(struct snd_soc_component *codec, unsigned int reg,
unsigned char val)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_reg_write(control_data,
aic3262_ops_cfw2reg(reg), val);
}
static int aic3262_ops_i2c_reg_write(struct snd_soc_component *codec, unsigned char reg,
unsigned char val)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_i2c_reg_write(control_data,
reg, val);
}
static int aic3262_ops_set_bits(struct snd_soc_component *codec, unsigned int reg,
unsigned char mask, unsigned char val)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_set_bits(control_data,
aic3262_ops_cfw2reg(reg), mask, val);
}
static int aic3262_ops_bulk_read(struct snd_soc_component *codec, unsigned int reg,
int count, u8 *buf)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_bulk_read(control_data,
aic3262_ops_cfw2reg(reg), count, buf);
}
static int aic3262_ops_bulk_write(struct snd_soc_component *codec, unsigned int reg,
int count, const u8 *buf)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
return aic3xxx_bulk_write(control_data,
aic3262_ops_cfw2reg(reg), count, buf);
}
/**
* aic3262_ops_lock_lock: To Read the run state of the DAC and ADC
* by reading the codec and returning the run state
* @pv: pointer argument to the codec
*
* Run state Bit format
*
* ------------------------------------------------------
* D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 |
* R R R LADC RADC R R LDAC RDAC
* ------------------------------------------------------
*
* R- Reserved
* LDAC- Left DAC
* RDAC- Right DAC
*
* Return value : Integer
*/
static int aic3262_ops_lock(struct snd_soc_component *codec)
{
mutex_lock(&codec->io_mutex);
/* Reading the run state of adc and dac */
return aic3262_get_runstate(codec);
}
/**
* aic3262_ops_dlock_unlock: To unlock the mutex acqiured for reading
* run state of the codec
* @pv: pointer argument to the codec
*
* Return Value: integer returning 0
*/
static int aic3262_ops_unlock(struct snd_soc_component *codec)
{
/*Releasing the lock of mutex */
mutex_unlock(&codec->io_mutex);
return 0;
}
/**
* aic3262_ops_dlock_stop:
* @pv: pointer Argument to the codec
* @mask: tells us the bit format of the codec running state
*
* Bit Format:
* ------------------------------------------------------
* D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 |
* R R R AL AR R R DL DR
* ------------------------------------------------------
* R - Reserved
* A - minidsp_A
* D - minidsp_D
*
* Return: return run state
*/
static int aic3262_ops_stop(struct snd_soc_component *codec, int mask)
{
int run_state = 0;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
run_state = aic3262_get_runstate(codec);
if (mask & AIC3XXX_COPS_MDSP_A)
aic3xxx_set_bits(control_data,
AIC3262_ADC_DATAPATH_SETUP, 0xC0, 0);
if (mask & AIC3XXX_COPS_MDSP_D)
aic3xxx_set_bits(control_data,
AIC3262_DAC_DATAPATH_SETUP, 0xC0, 0);
if ((mask & AIC3XXX_COPS_MDSP_A) &&
!aic3xxx_wait_bits(control_data,
AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK,
0, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER))
goto err;
if ((mask & AIC3XXX_COPS_MDSP_D) &&
!aic3xxx_wait_bits(control_data,
AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK,
0, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER))
goto err;
return run_state;
err:
dev_err(codec->dev, "Unable to turn off ADCs or DACs at [%s:%d]",
__FILE__, __LINE__);
return -EINVAL;
}
/**
* aic3262_ops_lock_restore: To unlock the mutex acqiured for reading
* @pv: pointer argument to the codec,run_state
* @run_state: run state of the codec and to restore the states of the dsp
*
* Return Value : integer returning 0
*/
static int aic3262_ops_restore(struct snd_soc_component *codec, int run_state)
{
int sync_state;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
/* This is for read the sync mode register state */
sync_state = aic3xxx_reg_read(control_data, AIC3262_DAC_PRB);
/*checking whether the sync mode has been set or
not and checking the current state */
if (((run_state & 0x30) && (run_state & 0x03))&& (sync_state & 0x80))
aic3262_restart_dsps_sync(codec, run_state);
else
aic3262_dsp_pwrup(codec, run_state);
return 0;
}
/**
* aic3262_ops_adaptivebuffer_swap: To swap the coefficient buffers
* of minidsp according to mask
* @pv: pointer argument to the codec,
* @mask: tells us which dsp has to be chosen for swapping
*
* Return Value : returning 0 on success
*/
int aic3262_ops_adaptivebuffer_swap(struct snd_soc_component *codec, int mask)
{
const int sbuf[][2] = {
{ AIC3XXX_ABUF_MDSP_A, AIC3262_ADC_ADAPTIVE_CRAM_REG },
{ AIC3XXX_ABUF_MDSP_D1, AIC3262_DAC_ADAPTIVE_BANK1_REG },
{ AIC3XXX_ABUF_MDSP_D2, AIC3262_DAC_ADAPTIVE_BANK2_REG },
};
int i;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
for (i = 0; i < sizeof(sbuf)/sizeof(sbuf[0]); ++i) {
if ((mask & sbuf[i][0])) {
aic3xxx_set_bits(control_data, sbuf[i][1],
0x1, 0x1);
if (!aic3xxx_wait_bits(control_data,
sbuf[i][1], 0x1, 0, 5, 1)) {
dev_err(codec->dev, "miniDSP buffer swap failure");
return -EINVAL;
}
}
}
return 0;
}
/**
* get_runstate: To read the current state of the dac's and adc's
* @ps: pointer argument to the codec
*
* Return Value : returning the runstate
*/
static int aic3262_get_runstate(struct snd_soc_component *codec)
{
unsigned int dac, adc;
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
/* Read the run state */
dac = aic3xxx_reg_read(control_data, AIC3262_DAC_FLAG);
adc = aic3xxx_reg_read(control_data, AIC3262_ADC_FLAG);
return (((adc>>6)&1)<<5) |
(((adc>>2)&1)<<4) |
(((dac>>7)&1)<<1) |
(((dac>>3)&1)<<0);
}
/**
* aic3262_dsp_pwrdwn_status: To read the status of dsp's
* @pv: pointer argument to the codec , cur_state of dac's and adc's
*
* Return Value : integer returning 0
*/
static int aic3262_dsp_pwrdwn_status(struct snd_soc_component *codec)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
aic3xxx_set_bits(control_data,
AIC3262_ADC_DATAPATH_SETUP, 0XC0, 0);
aic3xxx_set_bits(control_data,
AIC3262_DAC_DATAPATH_SETUP, 0XC0, 0);
if (!aic3xxx_wait_bits(control_data, AIC3262_ADC_FLAG,
AIC3262_ADC_POWER_MASK, 0, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER))
goto err;
if (!aic3xxx_wait_bits(control_data, AIC3262_DAC_FLAG,
AIC3262_DAC_POWER_MASK, 0, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER))
goto err;
return 0;
err:
dev_err(codec->dev, "DAC/ADC Power down timedout at [%s:%d]",
__FILE__, __LINE__);
return -EINVAL;
}
static int aic3262_dsp_pwrup(struct snd_soc_component *codec, int state)
{
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
int adc_reg_mask = 0;
int adc_power_mask = 0;
int dac_reg_mask = 0;
int dac_power_mask = 0;
int ret_wbits;
if (state & AIC3XXX_COPS_MDSP_A_L) {
adc_reg_mask |= 0x80;
adc_power_mask |= AIC3262_LADC_POWER_MASK;
}
if (state & AIC3XXX_COPS_MDSP_A_R) {
adc_reg_mask |= 0x40;
adc_power_mask |= AIC3262_RADC_POWER_MASK;
}
if (state & AIC3XXX_COPS_MDSP_A)
aic3xxx_set_bits(control_data,
AIC3262_ADC_DATAPATH_SETUP, 0XC0,
adc_reg_mask);
if (state & AIC3XXX_COPS_MDSP_D_L) {
dac_reg_mask |= 0x80;
dac_power_mask |= AIC3262_LDAC_POWER_STATUS_MASK;
}
if (state & AIC3XXX_COPS_MDSP_D_R) {
dac_reg_mask |= 0x40;
dac_power_mask |= AIC3262_RDAC_POWER_STATUS_MASK;
}
if (state & AIC3XXX_COPS_MDSP_D)
aic3xxx_set_bits(control_data,
AIC3262_DAC_DATAPATH_SETUP, 0XC0,
dac_reg_mask);
if (state & AIC3XXX_COPS_MDSP_A) {
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK,
adc_power_mask, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
if (!ret_wbits)
dev_err(codec->dev, "ADC Power down timedout\n");
}
if (state & AIC3XXX_COPS_MDSP_D) {
ret_wbits = aic3xxx_wait_bits(control_data,
AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK,
dac_power_mask, AIC326X_TIME_DELAY,
AIC326X_DELAY_COUNTER);
if (!ret_wbits)
dev_err(codec->dev, "ADC Power down timedout\n");
}
return 0;
}
static int aic3262_restart_dsps_sync(struct snd_soc_component *codec, int run_state)
{
aic3262_dsp_pwrdwn_status(codec);
aic3262_dsp_pwrup(codec, run_state);
return 0;
}
static const struct aic3xxx_codec_ops aic3262_cfw_codec_ops = {
.reg_read = aic3262_ops_reg_read,
.reg_write = aic3262_ops_reg_write,
.reg_i2c_write = aic3262_ops_i2c_reg_write,
.set_bits = aic3262_ops_set_bits,
.bulk_read = aic3262_ops_bulk_read,
.bulk_write = aic3262_ops_bulk_write,
.lock = aic3262_ops_lock,
.unlock = aic3262_ops_unlock,
.stop = aic3262_ops_stop,
.restore = aic3262_ops_restore,
.bswap = aic3262_ops_adaptivebuffer_swap,
};
/**
* aic3262_codec_read: provide read api to read aic3262 registe space
* @codec: pointer variable to codec having codec information,
* @reg: register address,
*
* Return: Return value will be value read.
*/
unsigned int aic3262_codec_read(struct snd_soc_component *codec, unsigned int reg)
{
u8 value;
//printk("%s enter", __func__);
union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *)
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
value = aic3xxx_reg_read(control_data, reg);
/*
dev_info(codec->dev, "p %d , r 30 %x %x\n",
aic_reg->aic3xxx_register.page,
aic_reg->aic3xxx_register.offset, value);
*/
//printk("%s exit", __func__);
return value;
}
/**
* aic3262_codec_write: provide write api to write at aic3262 registe space
* @codec: Pointer variable to codec having codec information,
* @reg: Register address,
* @value: Value to be written to address space
*
* Return: Total no of byte written to address space.
*/
int aic3262_codec_write(struct snd_soc_component *codec, unsigned int reg,
unsigned int value)
{
//printk("%s enter", __func__);
union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *)
struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
/*
dev_info(codec->dev, "p %d, w 30 %x %x\n",
aic_reg->aic3xxx_register.page,
aic_reg->aic3xxx_register.offset, value);
*/
//printk("%s exit", __func__);
return aic3xxx_reg_write(control_data, reg, value);
}
/**
* aic3262_set_interface_fmt: Setting interface ASI1/2/3 data format
* @dai: ponter to dai Holds runtime data for a DAI,
* @fmt: asi format info,
* @channel: number of channel,
*
* Return: On success return 0.
*/
static int aic3262_set_interface_fmt(struct snd_soc_dai *dai, unsigned int fmt,
unsigned int channel)
{
int aif_interface_reg;
int aif_bclk_offset_reg;
struct snd_soc_component *codec = dai->component;
u8 iface_val = 0;
u8 dsp_a_val = 0;
switch (dai->id) {
case 0:
aif_interface_reg = AIC3262_ASI1_BUS_FMT;
aif_bclk_offset_reg = AIC3262_ASI1_LCH_OFFSET;
break;
case 1:
aif_interface_reg = AIC3262_ASI2_BUS_FMT;
aif_bclk_offset_reg = AIC3262_ASI2_LCH_OFFSET;
break;
case 2:
aif_interface_reg = AIC3262_ASI3_BUS_FMT;
aif_bclk_offset_reg = AIC3262_ASI3_LCH_OFFSET;
break;
default:
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface_val = 0;
break;
case SND_SOC_DAIFMT_DSP_A:
dsp_a_val = 0x1; /* Intentionally falling through */
case SND_SOC_DAIFMT_DSP_B:
if (channel == 1)
iface_val = 0x80; /* Choose mono PCM */
else if (channel <= 8)
iface_val = 0x20; /* choose multichannel PCM */
else
return -EINVAL;
break;
case SND_SOC_DAIFMT_RIGHT_J:
iface_val = 0x40;
break;
case SND_SOC_DAIFMT_LEFT_J:
iface_val = 0x60;
break;
default:
dev_err(codec->dev, "Invalid DAI interface format\n");
return -EINVAL;
}
snd_soc_component_update_bits(codec, aif_interface_reg,
AIC3262_ASI_INTERFACE_MASK, iface_val);
snd_soc_component_update_bits(codec, aif_bclk_offset_reg,
AIC3262_BCLK_OFFSET_MASK, dsp_a_val);
return 0;
}
/**
* aic3262_hw_params: This function is to set the hardware parameters
* for AIC3262.
* The functions set the sample rate and audio serial data word
* length.
* @substream: pointer variable to sn_pcm_substream,
* @params: pointer to snd_pcm_hw_params structure,
* @dai: ponter to dai Holds runtime data for a DAI,
*
* Return: Return 0 on success.
*/
int aic3262_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *codec = dai->component;
struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control->control_data;
int asi_reg, val = 0, ret = 0;
u8 data = 0, value = 0, wclk_div = 0, bclk_div = 0;
unsigned int channels = params_channels(params);
// dev_info(codec->dev, "%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aic3262->stream_status = 1;
else
aic3262->stream_status = 0;
switch (dai->id) {
case 0:
asi_reg = AIC3262_ASI1_BUS_FMT;
break;
case 1:
asi_reg = AIC3262_ASI2_BUS_FMT;
break;
case 2:
asi_reg = AIC3262_ASI3_BUS_FMT;
break;
default:
return -EINVAL;
}
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
data = data | 0x00;
break;
case SNDRV_PCM_FORMAT_S20_LE:
data |= (0x08);
break;
case SNDRV_PCM_FORMAT_S24_LE:
data |= (0x10);
break;
case SNDRV_PCM_FORMAT_S32_LE:
data |= (0x18);
break;
}
/* Configure TDM for multi chennels */
switch (channels) {
case 4:
value = value | 0x40;
bclk_div = 0x03;
wclk_div = 0x40;
break;
case 6:
bclk_div = 0x02;
wclk_div = 0x60;
value = value | 0x80;
break;
case 8:
bclk_div = 0x01;
wclk_div = 0x00;
value = value | 0xC0;
break;
default:
bclk_div = 0x04;
wclk_div = 0x20;
}
snd_soc_component_update_bits(codec, AIC3262_ASI1_CHNL_SETUP,
AIC3262_ASI1_CHNL_MASK, value);
snd_soc_component_update_bits(codec, AIC3262_ASI1_BCLK_N,
AIC3262_ASI1_BCLK_N_MASK, bclk_div);
snd_soc_component_update_bits(codec, AIC3262_ASI1_WCLK_N,
AIC3262_ASI1_WCLK_N_MASK, wclk_div);
val = snd_soc_component_read(codec, AIC3262_ASI1_BUS_FMT);
val = snd_soc_component_read(codec, AIC3262_ASI1_CHNL_SETUP);
/* configure the respective Registers for the above configuration */
snd_soc_component_update_bits(codec, asi_reg,
AIC3262_ASI_DATA_WORD_LENGTH_MASK, data);
ret = aic3262_set_interface_fmt(dai, aic3262->asi_fmt[dai->id],
channels);
if (ret < 0) {
dev_err(codec->dev, "failed to set hardware params for AIC3262\n");
return ret;
}
// printk("%s exit", __func__);
return 0;
}
/**
* aic3262_mute: This function is to mute or unmute the left and right DAC
* @dai: ponter to dai Holds runtime data for a DAI,
* @mute: integer value one if we using mute else unmute,
*
* Return: return 0 on success.
*/
static int aic3262_mute(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_component *codec = dai->component;
struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control->control_data;
// dev_info(codec->dev, "codec : %s : started\n", __func__);
if (dai->id > 2)
return -EINVAL;
if (mute) {
aic3262->mute_asi &= ~((0x1) << dai->id);
if (aic3262->mute_asi == 0)
/* Mute only when all asi's are muted */
snd_soc_component_update_bits(codec,
AIC3262_DAC_MVOL_CONF,
AIC3262_DAC_LR_MUTE_MASK,
AIC3262_DAC_LR_MUTE);
} else { /* Unmute */
if (aic3262->mute_asi == 0)
/* Unmute for the first asi that need to unmute.
rest unmute will pass */
snd_soc_component_update_bits(codec,
AIC3262_DAC_MVOL_CONF,
AIC3262_DAC_LR_MUTE_MASK,
0x0);
aic3262->mute_asi |= ((0x1) << dai->id);
}
// dev_info(codec->dev, "codec : %s : ended\n", __func__);
// printk("%s exit", __func__);
return 0;
}
/**
* aic3262_set_dai_fmt: This function is to set the DAI format
* @codec_dai: ponter to dai Holds runtime data for a DAI,
* @fmt: asi format info,
*
* return: return 0 on success.
*/
static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *codec = codec_dai->component;
struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
struct aic3262_priv *aic3262 = control->control_data;
u8 iface_val, master;
int aif_bclk_wclk_reg;
dev_info(codec->dev, "%s enter\n", __func__);
iface_val = 0x00;
master = 0x0;
switch (codec_dai->id) {
case 0:
aif_bclk_wclk_reg = AIC3262_ASI1_BWCLK_CNTL_REG;
break;
case 1:
aif_bclk_wclk_reg = AIC3262_ASI2_BWCLK_CNTL_REG;
break;
case 2:
aif_bclk_wclk_reg = AIC3262_ASI3_BWCLK_CNTL_REG;
break;
default:
return -EINVAL;
}
aic3262->asi_fmt[codec_dai->id] = fmt;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
aic3262->master = 1;
master |= (AIC3262_WCLK_OUT_MASK | AIC3262_BCLK_OUT_MASK);
break;
case SND_SOC_DAIFMT_CBS_CFS:
aic3262->master = 0;
break;
case SND_SOC_DAIFMT_CBS_CFM: /* new case..for debug purpose */
master |= (AIC3262_WCLK_OUT_MASK);
aic3262->master = 0;
break;
case SND_SOC_DAIFMT_CBM_CFS:
master |= (AIC3262_BCLK_OUT_MASK);
aic3262->master = 0;
break;
default:
dev_err(codec->dev, "Invalid DAI master/slave" " interface\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
master |= AIC3262_BCLK_INV_MASK;
break;
default:
return -EINVAL;
}
break;
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
master |= AIC3262_BCLK_INV_MASK;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
snd_soc_component_update_bits(codec, aif_bclk_wclk_reg,
AIC3262_WCLK_BCLK_MASTER_MASK, master);
// printk("%s exit", __func__);
return 0;
}
/**
* aic3262_dai_set_pll: This function is to Set pll for aic3262 codec dai
* @dai: ponter to dai Holds runtime data for a DAI,$
* @pll_id: integer pll_id
* @source: DAI specific source for the PLL
* @fin: PLL Input clock frequency in Hz,
* @fout: Frequency out,
*
* Return: return 0 on success
*/
static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int Fin, unsigned int Fout)
{
struct snd_soc_component *codec = dai->component;
struct aic3262_priv *aic3262 = snd_soc_component_get_drvdata(codec);
dev_info(codec->dev, "%s enter\n", __func__);
dev_info(codec->dev, "In aic3262: dai_set_pll\n");
dev_info(codec->dev, "%d, %s, dai->id = %d\n", __LINE__,
__func__, dai->id);
/* select the PLL_CLKIN */
snd_soc_component_update_bits(codec, AIC3262_PLL_CLKIN_REG,
AIC3262_PLL_CLKIN_MASK, source <<
AIC3262_PLL_CLKIN_SHIFT);
/* TODO: How to select low/high clock range? */
aic3xxx_cfw_set_pll(aic3262->cfw_p, dai->id);
// printk("%s exit", __func__);
return 0;
}
/**
*
* aic3262_set_bias_level: This function is to get triggered
* when dapm events occurs.
* @codec: pointer variable to codec having informaton related to codec,
* @level: Bias level-> ON, PREPARE, STANDBY, OFF.
*
* Return: Return 0 on success.
*/
static int aic3262_set_bias_level(struct snd_soc_component *codec,
enum snd_soc_bias_level level)
{
printk("%s enter", __func__);
switch (level) {
/* full On */
case SND_SOC_BIAS_ON:
dev_info(codec->dev, "set_bias_on\n");
break;
/* partial On */
case SND_SOC_BIAS_PREPARE:
dev_info(codec->dev, "set_bias_prepare\n");
break;
/* Off, with power */
case SND_SOC_BIAS_STANDBY:
/*
* all power is driven by DAPM system,
* so output power is safe if bypass was set
*/
dev_info(codec->dev, "set_bias_stby\n");
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
pm_runtime_get_sync(codec->dev);
snd_soc_component_update_bits(codec, AIC3262_POWER_CONF,
(AIC3262_AVDD_TO_DVDD_MASK |
AIC3262_EXT_ANALOG_SUPPLY_MASK),
0x0);
snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
AIC3262_CHIP_REF_PWR_ON_MASK,
AIC3262_CHIP_REF_PWR_ON);
mdelay(40);
snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
AIC3262_DYNAMIC_OFFSET_CALIB);
}
break;
/* Off, without power */
case SND_SOC_BIAS_OFF:
dev_info(codec->dev, "set_bias_off\n");
/* force all power off */
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
AIC3262_CHIP_REF_PWR_ON_MASK, 0x0);
snd_soc_component_update_bits(codec, AIC3262_POWER_CONF,
(AIC3262_AVDD_TO_DVDD_MASK |
AIC3262_EXT_ANALOG_SUPPLY_MASK),
(AIC3262_AVDD_TO_DVDD |
AIC3262_EXT_ANALOG_SUPPLY_OFF));
pm_runtime_put(codec->dev);
}
break;
}
codec->dapm.bias_level = level;
// printk("%s exit", __func__);
return 0;
}
/**
*
* aic3262_suspend; This function is to suspend the AIC3262 driver.
* @codec: pointer variable to codec having informaton related to codec,
*
* Return: Return 0 on success.
*/
static int aic3262_suspend(struct snd_soc_component *codec)
{
// printk("%s enter", __func__);
aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF);
// printk("%s exit", __func__);
return 0;
}
/**
* aic3262_resume: This function is to resume the AIC3262 driver
* from off state to standby
* @codec: pointer variable to codec having informaton related to codec,
*
* Return: Return 0 on success.
*/
static int aic3262_resume(struct snd_soc_component *codec)
{
// printk("%s enter", __func__);
aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
// printk("%s exit", __func__);
return 0;
}
/**
* aic3262_probe: This is first driver function called by the SoC core driver.
* @codec: pointer variable to codec having informaton related to codec,
*
* Return: Return 0 on success.
*/
static int aic3262_codec_probe(struct snd_soc_component *codec)
{
int ret = 0;
struct aic3xxx *control;
struct aic3262_priv *aic3262;
// printk("%s enter", __func__);
if (codec == NULL) {
dev_err(codec->dev, "codec pointer is NULL.\n");
return -EINVAL;
}
control = snd_soc_component_get_drvdata(codec);
aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL);
if (aic3262 == NULL) {
dev_err(codec->dev, "memory pointer is NULL.\n");
return -ENOMEM;
}
control->control_data = aic3262;
aic3262->pdata = dev_get_platdata(codec->dev->parent);
aic3262->codec = codec;
aic3262->cur_fw = NULL;
aic3262->cfw_p = &(aic3262->cfw_ps);
aic3xxx_cfw_init(aic3262->cfw_p, &aic3262_cfw_codec_ops,
aic3262->codec);
aic3262->workqueue = create_singlethread_workqueue("aic3262-codec");
if (!aic3262->workqueue) {
ret = -ENOMEM;
goto work_err;
}
INIT_DELAYED_WORK(&aic3262->delayed_work, aic3262_accessory_work);
mutex_init(&aic3262->mutex);
mutex_init(&codec->io_mutex);
mutex_init(&aic3262->cfw_mutex);
pm_runtime_enable(codec->dev);
pm_runtime_resume(codec->dev);
aic3262->dsp_runstate = 0;
if (control->irq) {
ret = aic3xxx_request_irq(control,
AIC3262_IRQ_HEADSET_DETECT,
aic3262_audio_handler,
IRQF_NO_SUSPEND,
"aic3262_irq_headset", codec);
if (ret) {
dev_err(codec->dev,
"HEADSET detect irq request failed:%d\n", ret);
goto irq_err;
} else {
/* Dynamic Headset Detection Enabled */
snd_soc_component_update_bits(codec, AIC3262_HP_DETECT,
AIC3262_HEADSET_IN_MASK,
AIC3262_HEADSET_IN_MASK);
}
}
/* Keep the reference voltage ON while in
STANDBY mode for fast power up */
snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
AIC3262_CHIP_REF_PWR_ON_MASK,
AIC3262_CHIP_REF_PWR_ON);
mdelay(40);
snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
AIC3262_DYNAMIC_OFFSET_CALIB);
//aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
aic3262->mute_asi = 0;
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
"tlv320aic3262_fw_v1.bin", codec->dev,
GFP_KERNEL, codec, aic3262_firmware_load);
if (ret < 0) {
dev_err(codec->dev, "Firmware request failed\n");
goto firm_err;
}
// printk("%s exit", __func__);
return 0;
firm_err:
aic3xxx_free_irq(control,
AIC3262_IRQ_HEADSET_DETECT, codec);
irq_err:
destroy_workqueue(aic3262->workqueue);
work_err:
kfree(aic3262);
return 0;
}
/*
* aic3262_remove: Cleans up and Remove aic3262 soc device
* @codec: pointer variable to codec having informaton related to codec,
*
* Return: Return 0 on success.
*/
static void aic3262_codec_remove(struct snd_soc_component *codec)
{
/* power down chip */
struct aic3xxx *control;
struct aic3262_priv *aic3262;
// printk("%s enter", __func__);
if (codec == NULL) {
dev_err(codec->dev, "codec pointer is NULL.\n");
return;
}
control = snd_soc_component_get_drvdata(codec);
aic3262 = control->control_data;
aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF);
/* free_irq if any */
switch (control->type) {
case TLV320AIC3262:
if (control->irq)
aic3xxx_free_irq(control,
AIC3262_IRQ_HEADSET_DETECT, codec);
break;
default:
dev_info(codec->dev, "Coded is not TLV320AIC3262\n");
}
/* release firmware if any */
if (aic3262->cur_fw != NULL)
release_firmware(aic3262->cur_fw);
if(aic3262->cfw_ps.pjt) {
remove_cfw(aic3262->cfw_ps.pjt);
aic3262->cfw_ps.pjt = NULL;
}
/* destroy workqueue for jac dev */
destroy_workqueue(aic3262->workqueue);
kfree(aic3262);
// printk("%s exit", __func__);
}
static struct snd_soc_component_driver soc_codec_driver_aic326x = {
.probe = aic3262_codec_probe,
.remove = aic3262_codec_remove,
.suspend = aic3262_suspend,
.resume = aic3262_resume,
.read = aic3262_codec_read,
.write = aic3262_codec_write,
.controls = aic3262_snd_controls,
.num_controls = ARRAY_SIZE(aic3262_snd_controls),
.dapm_widgets = aic3262_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aic3262_dapm_widgets),
.dapm_routes = aic3262_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(aic3262_dapm_routes),
.set_bias_level = aic3262_set_bias_level,
};
int aic326x_register_codec(struct aic3xxx *pdev)
{
return snd_soc_register_component(pdev->dev, &soc_codec_driver_aic326x,
aic326x_dai_driver,
ARRAY_SIZE(aic326x_dai_driver));
}
int aic326x_deregister_codec(struct aic3xxx *pdev)
{
snd_soc_unregister_component(pdev->dev);
return 0;
}
MODULE_ALIAS("platform:tlv320aic326x-codec");
MODULE_DESCRIPTION("ASoC TLV320AIC326X codec driver");
MODULE_AUTHOR("Y Preetam Sashank Reddy ");
MODULE_AUTHOR("Barani Prashanth ");
MODULE_AUTHOR("Mukund Navada K <navada@ti.com>");
MODULE_AUTHOR("Naren Vasanad <naren.vasanad@ti.com>");
MODULE_LICENSE("GPL");
暂停恢复的步骤:
步骤1 :当我们在一个终端并行运行 aplay -DHW:0 3.wav 如果我们做 echo mem >/sys/power/state (命令暂停到 RAM 状态)在另一个终端, aplay 无法恢复时系统恢复。 我们观察到 I/O 错误、请参阅以下日志。
长 OGS:
[ 356.715868] aic3262_SET_BIAS_LEVEL 输入
[ 356.715885] tlv320aic3262-codec 2-0018:set_bias_preparate
[ 356.716033] aic3262_SET_BIAS_LEVEL 输入
[ 356.716044] tlv320aic3262-codec 2-0018:set_bias_stby
[ 356.716050] aic3262_SET_BIAS_LEVEL 输入
[ 356.716055] tlv320aic3262-codec 2-0018:SET_BIAS_OFF
root:~# aplay -DHW:0 3.wav
播放波形"3.wav":有符号16位小端序,速率44100Hz,立体声
已暂停。 正在尝试恢复。 停止更新。
Aplay:PCM_WRITE:2061:写入错误:输入/输出错误
请告诉我们是否可以打电话进行讨论。
谢谢。此致、
日照市赤阔地