diff options
-rw-r--r-- | sound/soc/tegra/tegra_aic326x.c | 344 |
1 files changed, 330 insertions, 14 deletions
diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c index b64ab41c97d0..6d38934138bc 100644 --- a/sound/soc/tegra/tegra_aic326x.c +++ b/sound/soc/tegra/tegra_aic326x.c @@ -73,6 +73,16 @@ extern int g_is_call_mode; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +const char *tegra_i2s_dai_name[TEGRA30_NR_I2S_IFC] = { + "tegra30-i2s.0", + "tegra30-i2s.1", + "tegra30-i2s.2", + "tegra30-i2s.3", + "tegra30-i2s.4", +}; +#endif + struct tegra_aic326x { struct tegra_asoc_utils_data util_data; struct tegra_aic326x_platform_data *pdata; @@ -81,6 +91,10 @@ struct tegra_aic326x { bool init_done; int is_call_mode; int is_device_bt; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct codec_config codec_info[NUM_I2S_DEVICES]; + struct snd_soc_card *pcard; +#endif }; static int tegra_aic326x_call_mode_info(struct snd_kcontrol *kcontrol, @@ -110,6 +124,9 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol, int is_call_mode_new = ucontrol->value.integer.value[0]; #ifdef CONFIG_ARCH_TEGRA_2x_SOC int codec_dap_id, codec_dap_sel, bb_dap_id, bb_dap_sel; +#else /*assumes tegra3*/ + int codec_index; + unsigned int i; #endif if (machine->is_call_mode == is_call_mode_new) @@ -127,6 +144,11 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol, codec_dap_id = TEGRA20_DAS_DAP_ID_2; codec_dap_sel = TEGRA20_DAS_DAP_SEL_DAP2; } +#else /*assumes tegra3*/ + if (machine->is_device_bt) + codec_index = BT_SCO; + else + codec_index = HIFI_CODEC; #endif if (is_call_mode_new) { @@ -139,6 +161,17 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol, codec_dap_sel, 1, 0, 0); tegra20_das_set_tristate(codec_dap_id, 0); tegra20_das_set_tristate(bb_dap_id, 0); +#else /*assumes tegra3*/ + if (machine->codec_info[codec_index].rate == 0 || + machine->codec_info[codec_index].channels == 0) + return -EINVAL; + + for (i = 0; i < machine->pcard->num_links; i++) + machine->pcard->dai_link[i].ignore_suspend = 1; + + tegra30_make_voice_call_connections( + &machine->codec_info[codec_index], + &machine->codec_info[BASEBAND]); #endif } else { #ifdef CONFIG_ARCH_TEGRA_2x_SOC @@ -150,6 +183,13 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol, codec_dap_sel, 0, 0, 0); tegra20_das_set_tristate(codec_dap_id, 0); tegra20_das_set_tristate(bb_dap_id, 0); +#else /*assumes tegra3*/ + tegra30_break_voice_call_connections( + &machine->codec_info[codec_index], + &machine->codec_info[BASEBAND]); + + for (i = 0; i < machine->pcard->num_links; i++) + machine->pcard->dai_link[i].ignore_suspend = 0; #endif } @@ -196,6 +236,35 @@ static int tegra_aic326x_get_mclk(int srate) return mclk; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +static int tegra_aic326x_set_dam_cif(int dam_ifc, int srate, + int channels, int bit_size, int src_on, int src_srate, + int src_channels, int src_bit_size) +{ + tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN1, 0x1000); + tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHOUT, + srate); + tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN1, + srate); + tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN1, + channels, bit_size, channels, + bit_size); + tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT, + channels, bit_size, channels, + bit_size); + + if (src_on) { + tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN0_SRC, 0x1000); + tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN0_SRC, + src_srate); + tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN0_SRC, + src_channels, src_bit_size, 1, 16); + } + + return 0; +} +#endif + static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -207,6 +276,9 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card); int srate, mclk, sample_size, daifmt; int err; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); +#endif switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -271,6 +343,10 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, dev_err(card->dev, "failed to set dac-dap path\n"); return err; } +#else /*assumes tegra3*/ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && i2s->is_dam_used) + tegra_aic326x_set_dam_cif(i2s->dam_ifc, srate, + params_channels(params), sample_size, 0, 0, 0, 0); #endif return 0; @@ -315,6 +391,9 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream, struct snd_soc_card *card = rtd->card; struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card); int err, srate, mclk, min_mclk, sample_size; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai); +#endif switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -368,11 +447,164 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream, dev_err(card->dev, "failed to set dac-dap path\n"); return err; } +#else + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && i2s->is_dam_used) + tegra_aic326x_set_dam_cif(i2s->dam_ifc, params_rate(params), + params_channels(params), sample_size, 0, 0, 0, 0); #endif return 0; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +static int tegra_aic326x_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); + struct tegra_aic326x *machine = snd_soc_card_get_drvdata(rtd->card); + struct codec_config *codec_info; + struct codec_config *bb_info; + int codec_index; + + if (!i2s->is_dam_used) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*dam configuration*/ + if (!i2s->dam_ch_refcount) + i2s->dam_ifc = tegra30_dam_allocate_controller(); + + tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1); + i2s->dam_ch_refcount++; + tegra30_dam_enable_clock(i2s->dam_ifc); + + tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 + + (i2s->dam_ifc*2), i2s->txcif); + + /* + *make the dam tx to i2s rx connection if this is the only client + *using i2s for playback + */ + if (i2s->playback_ref_count == 1) + tegra30_ahub_set_rx_cif_source( + TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->id, + TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->dam_ifc); + + /* enable the dam*/ + tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE, + TEGRA30_DAM_CHIN1); + } else { + + i2s->is_call_mode_rec = machine->is_call_mode; + + if (!i2s->is_call_mode_rec) + return 0; + + if (machine->is_device_bt) + codec_index = BT_SCO; + else + codec_index = HIFI_CODEC; + + codec_info = &machine->codec_info[codec_index]; + bb_info = &machine->codec_info[BASEBAND]; + + /* allocate a dam for voice call recording */ + + i2s->call_record_dam_ifc = tegra30_dam_allocate_controller(); + tegra30_dam_allocate_channel(i2s->call_record_dam_ifc, + TEGRA30_DAM_CHIN0_SRC); + tegra30_dam_allocate_channel(i2s->call_record_dam_ifc, + TEGRA30_DAM_CHIN1); + tegra30_dam_enable_clock(i2s->call_record_dam_ifc); + + /* configure the dam */ + tegra_aic326x_set_dam_cif(i2s->call_record_dam_ifc, + codec_info->rate, codec_info->channels, + codec_info->bitsize, 1, bb_info->rate, + bb_info->channels, bb_info->bitsize); + + /* setup the connections for voice call record */ + + tegra30_ahub_unset_rx_cif_source(i2s->rxcif); + tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 + + (i2s->call_record_dam_ifc*2), + TEGRA30_AHUB_TXCIF_I2S0_TX0 + bb_info->i2s_id); + tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 + + (i2s->call_record_dam_ifc*2), + TEGRA30_AHUB_TXCIF_I2S0_TX0 + codec_info->i2s_id); + tegra30_ahub_set_rx_cif_source(i2s->rxcif, + TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->call_record_dam_ifc); + + /* enable the dam*/ + + tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE, + TEGRA30_DAM_CHIN1); + tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE, + TEGRA30_DAM_CHIN0_SRC); + } + + return 0; +} + +static void tegra_aic326x_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); + + if (!i2s->is_dam_used) + return; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* disable the dam*/ + tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_DISABLE, + TEGRA30_DAM_CHIN1); + + /* disconnect the ahub connections*/ + tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 + + (i2s->dam_ifc*2)); + + /* disable the dam and free the controller */ + tegra30_dam_disable_clock(i2s->dam_ifc); + tegra30_dam_free_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1); + i2s->dam_ch_refcount--; + if (!i2s->dam_ch_refcount) + tegra30_dam_free_controller(i2s->dam_ifc); + } else { + + if (!i2s->is_call_mode_rec) + return 0; + + i2s->is_call_mode_rec = 0; + + /* disable the dam*/ + tegra30_dam_enable(i2s->call_record_dam_ifc, + TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN1); + tegra30_dam_enable(i2s->call_record_dam_ifc, + TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN0_SRC); + + /* disconnect the ahub connections*/ + tegra30_ahub_unset_rx_cif_source(i2s->rxcif); + tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 + + (i2s->call_record_dam_ifc*2)); + tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 + + (i2s->call_record_dam_ifc*2)); + + /* free the dam channels and dam controller */ + tegra30_dam_disable_clock(i2s->call_record_dam_ifc); + tegra30_dam_free_channel(i2s->call_record_dam_ifc, + TEGRA30_DAM_CHIN1); + tegra30_dam_free_channel(i2s->call_record_dam_ifc, + TEGRA30_DAM_CHIN0_SRC); + tegra30_dam_free_controller(i2s->call_record_dam_ifc); + } + + return; +} +#endif + + static int tegra_aic326x_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -412,10 +644,18 @@ static int tegra_aic326x_voice_call_hw_params( tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai, + if(machine_is_tegra_enterprise()) { + err = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } else { + err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + } + if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; @@ -428,23 +668,39 @@ static int tegra_aic326x_voice_call_hw_params( return err; } - if (params_rate(params) == 8000) { - /* Change these Settings for 8KHz*/ - pcmdiv = 1; - /* BB expecting 2048Khz bclk */ - vxclkdiv = 27; - } else if (params_rate(params) == 16000) { - pcmdiv = 1; - /* BB expecting 2048Khz bclk */ - vxclkdiv = 27; - } else { - dev_err(card->dev, "codec_dai unsupported voice rate\n"); - return -EINVAL; + if(!machine_is_tegra_enterprise()) { + if (params_rate(params) == 8000) { + /* Change these Settings for 8KHz*/ + pcmdiv = 1; + /* BB expecting 2048Khz bclk */ + vxclkdiv = 27; + } else if (params_rate(params) == 16000) { + pcmdiv = 1; + /* BB expecting 2048Khz bclk */ + vxclkdiv = 27; + } else { + dev_err(card->dev, "codec_dai unsupported voice rate\n"); + return -EINVAL; + } } //snd_soc_dai_set_clkdiv(codec_dai, ASI2_BCLK_N, vxclkdiv); //snd_soc_dai_set_clkdiv(codec_dai, ASI2_WCLK_N, pcmdiv); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* codec configuration */ + machine->codec_info[HIFI_CODEC].rate = params_rate(params); + machine->codec_info[HIFI_CODEC].channels = params_channels(params); + machine->codec_info[HIFI_CODEC].bitsize = 16; + machine->codec_info[HIFI_CODEC].is_i2smaster = 1; + machine->codec_info[HIFI_CODEC].is_format_dsp = 0; + + /* baseband configuration */ + machine->codec_info[BASEBAND].bitsize = 16; + machine->codec_info[BASEBAND].is_i2smaster = 1; + machine->codec_info[BASEBAND].is_format_dsp = 1; +#endif + machine->is_device_bt = 0; return 0; @@ -457,6 +713,11 @@ static void tegra_aic326x_voice_call_shutdown( struct tegra_aic326x *machine = snd_soc_card_get_drvdata(rtd->codec->card); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + machine->codec_info[HIFI_CODEC].rate = 0; + machine->codec_info[HIFI_CODEC].channels = 0; +#endif + machine->is_device_bt = 0; } @@ -489,6 +750,20 @@ static int tegra_aic326x_bt_voice_call_hw_params( tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* codec configuration */ + machine->codec_info[BT_SCO].rate = params_rate(params); + machine->codec_info[BT_SCO].channels = params_channels(params); + machine->codec_info[BT_SCO].bitsize = 16; + machine->codec_info[BT_SCO].is_i2smaster = 1; + machine->codec_info[BT_SCO].is_format_dsp = 1; + + /* baseband configuration */ + machine->codec_info[BASEBAND].bitsize = 16; + machine->codec_info[BASEBAND].is_i2smaster = 1; + machine->codec_info[BASEBAND].is_format_dsp = 1; +#endif + machine->is_device_bt = 1; return 0; @@ -501,12 +776,21 @@ static void tegra_aic326x_bt_voice_call_shutdown( struct tegra_aic326x *machine = snd_soc_card_get_drvdata(rtd->codec->card); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + machine->codec_info[BT_SCO].rate = 0; + machine->codec_info[BT_SCO].channels = 0; +#endif + machine->is_device_bt = 0; } static struct snd_soc_ops tegra_aic326x_hifi_ops = { .hw_params = tegra_aic326x_hw_params, .hw_free = tegra_aic326x_hw_free, +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + .startup = tegra_aic326x_startup, + .shutdown = tegra_aic326x_shutdown, +#endif }; static struct snd_soc_ops tegra_aic326x_spdif_ops = { @@ -529,6 +813,10 @@ static struct snd_soc_ops tegra_aic326x_bt_voice_call_ops = { static struct snd_soc_ops tegra_aic326x_bt_ops = { .hw_params = tegra_aic326x_bt_hw_params, .hw_free = tegra_aic326x_hw_free, +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + .startup = tegra_aic326x_startup, + .shutdown = tegra_aic326x_shutdown, +#endif }; static struct snd_soc_jack tegra_aic326x_hp_jack; @@ -657,13 +945,25 @@ static int tegra_aic326x_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = codec->card; struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card); struct tegra_aic326x_platform_data *pdata = machine->pdata; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai); +#endif int ret; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + if (machine->codec_info[BASEBAND].i2s_id != -1) + i2s->is_dam_used = true; +#endif + if (machine->init_done) return 0; machine->init_done = true; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + machine->pcard = card; +#endif + if (machine_is_whistler()) { machine->audio_reg = regulator_get(NULL, "avddio_audio"); if (IS_ERR(machine->audio_reg)) { @@ -808,6 +1108,7 @@ static struct snd_soc_dai_link tegra_aic326x_dai[] = { .cpu_dai_name = "tegra30-i2s.3", #endif .codec_dai_name = "dit-hifi", + .init = tegra_aic326x_init, .ops = &tegra_aic326x_bt_ops, }, [DAI_LINK_VOICE_CALL] = { @@ -841,7 +1142,7 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev) struct snd_soc_card *card = &snd_soc_tegra_aic326x; struct tegra_aic326x *machine; struct tegra_aic326x_platform_data *pdata; - int ret; + int ret, i; pdata = pdev->dev.platform_data; if (!pdata) { @@ -875,9 +1176,24 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev) } #endif +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + for (i = 0; i < NUM_I2S_DEVICES ; i++) + machine->codec_info[i].i2s_id = pdata->audio_port_id[i]; + + machine->codec_info[BASEBAND].rate = pdata->baseband_param.rate; + machine->codec_info[BASEBAND].channels = pdata->baseband_param.channels; + + tegra_aic326x_dai[DAI_LINK_HIFI].cpu_dai_name = + tegra_i2s_dai_name[machine->codec_info[HIFI_CODEC].i2s_id]; + + tegra_aic326x_dai[DAI_LINK_BTSCO].cpu_dai_name = + tegra_i2s_dai_name[machine->codec_info[BT_SCO].i2s_id]; +#endif + if(machine_is_tegra_enterprise()) { tegra_aic326x_dai[DAI_LINK_HIFI].codec_name = "aic3262-codec.0-0018"; tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_name = "aic3262-codec.0-0018"; + tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_dai_name = "aic3262-asi1"; } ret = snd_soc_register_card(card); |