From 94dc32841c85c112ca24dfbbe76dea7f3ed371c3 Mon Sep 17 00:00:00 2001 From: Nitin Pai Date: Fri, 6 Jul 2012 16:31:11 +0530 Subject: asoc: tegra: p852: Added machine driver for P852 Renamed P1852 machine driver to VCM so that can be used for P1852/P852 and E1853 as well. Bug 1008391 Signed-off-by: Nitin Pai Reviewed-on: http://git-master/r/113886 (cherry picked from commit 5eb23e30bab716b28146b85438989e58761c7136) Change-Id: I8d1363a6419c6381b8d23ebd38d625fb482084dc Reviewed-on: http://git-master/r/116056 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Scott Peterson --- sound/soc/tegra/Makefile | 5 +- sound/soc/tegra/tegra_p1852.c | 303 ---------------------------------------- sound/soc/tegra/tegra_vcm.c | 312 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+), 305 deletions(-) delete mode 100644 sound/soc/tegra/tegra_p1852.c create mode 100644 sound/soc/tegra/tegra_vcm.c (limited to 'sound/soc') diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 7af02a69b8ae..83f30054a085 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -33,7 +33,7 @@ snd-soc-tegra-max98088-objs := tegra_max98088.o snd-soc-tegra-aic326x-objs := tegra_aic326x.o snd-soc-tegra-rt5640-objs := tegra_rt5640.o snd-soc-tegra-max98095-objs := tegra_max98095.o -snd-soc-tegra-p1852-objs := tegra_p1852.o +snd-soc-tegra-vcm-objs := tegra_vcm.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o @@ -42,4 +42,5 @@ obj-$(CONFIG_SND_SOC_TEGRA_MAX98088) += snd-soc-tegra-max98088.o obj-$(CONFIG_SND_SOC_TEGRA_TLV320AIC326X) += snd-soc-tegra-aic326x.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_MAX98095) += snd-soc-tegra-max98095.o -obj-$(CONFIG_SND_SOC_TEGRA_P1852) += snd-soc-tegra-p1852.o +obj-$(CONFIG_SND_SOC_TEGRA_P1852) += snd-soc-tegra-vcm.o +obj-$(CONFIG_SND_SOC_TEGRA_P852) += snd-soc-tegra-vcm.o diff --git a/sound/soc/tegra/tegra_p1852.c b/sound/soc/tegra/tegra_p1852.c deleted file mode 100644 index 199bb8046636..000000000000 --- a/sound/soc/tegra/tegra_p1852.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * tegra_p1852.c - Tegra machine ASoC driver for P1852 Boards. - * - * Author: Nitin Pai - * Copyright (C) 2010-2012 - NVIDIA, Inc. - * - * Based on code copyright/by: - * Copyright (c) 2009-2010, NVIDIA Corporation. - * Stephen Warren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "tegra_pcm.h" -#include "tegra_asoc_utils.h" - -#define DRV_NAME "tegra-snd-p1852" - -struct tegra_p1852 { - struct tegra_asoc_utils_data util_data; - struct tegra_p1852_platform_data *pdata; -}; - -static int tegra_p1852_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - int codec_id) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_card *card = codec->card; - struct tegra_p1852 *machine = snd_soc_card_get_drvdata(card); - int srate, mclk; - int i2s_daifmt = 0; - int err; - struct tegra_p1852_platform_data *pdata; - - pdata = machine->pdata; - - srate = params_rate(params); - switch (srate) { - case 64000: - case 88200: - case 96000: - mclk = 128 * srate; - break; - default: - mclk = 256 * srate; - break; - } - - err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); - if (err < 0) { - if (!(machine->util_data.set_mclk % mclk)) - mclk = machine->util_data.set_mclk; - else { - dev_err(card->dev, "Can't configure clocks\n"); - return err; - } - } - - tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - - if (pdata->codec_info[codec_id].master) - i2s_daifmt |= SND_SOC_DAIFMT_CBM_CFM; - else - i2s_daifmt |= SND_SOC_DAIFMT_CBS_CFS; - - switch (pdata->codec_info[codec_id].i2s_format) { - case format_tdm: - i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; - break; - case format_i2s: - i2s_daifmt |= SND_SOC_DAIFMT_I2S; - break; - case format_rjm: - i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; - break; - case format_ljm: - i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; - break; - default: - break; - } - - err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); - if (err < 0) - dev_info(card->dev, "codec_dai fmt not set\n"); - - err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); - if (err < 0) { - dev_err(card->dev, "cpu_dai fmt not set\n"); - return err; - } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); - if (err < 0) - dev_info(card->dev, "codec_dai clock not set\n"); - - if (pdata->codec_info[codec_id].i2s_format == - format_tdm) { - err = snd_soc_dai_set_tdm_slot(cpu_dai, - pdata->codec_info[codec_id].rx_mask, - pdata->codec_info[codec_id].tx_mask, - pdata->codec_info[codec_id].num_slots, - pdata->codec_info[codec_id].slot_width); - if (err < 0) - dev_err(card->dev, "cpu_dai tdm mode setting not done\n"); - } - - return 0; -} - -static int tegra_p1852_hw_params_controller1( - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - return tegra_p1852_hw_params(substream, params, 0); -} - -static int tegra_p1852_hw_params_controller2( - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - return tegra_p1852_hw_params(substream, params, 1); -} - -static int tegra_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct tegra_p1852 *machine = snd_soc_card_get_drvdata(rtd->card); - - tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0); - - return 0; -} - -static struct snd_soc_ops tegra_p1852_ops_controller1 = { - .hw_params = tegra_p1852_hw_params_controller1, - .hw_free = tegra_hw_free, -}; -static struct snd_soc_ops tegra_p1852_ops_controller2 = { - .hw_params = tegra_p1852_hw_params_controller2, - .hw_free = tegra_hw_free, -}; - -static struct snd_soc_dai_link tegra_p1852_dai_link[] = { - { - .name = "I2S-TDM-1", - .stream_name = "TEGRA PCM", - .platform_name = "tegra-pcm-audio", - .ops = &tegra_p1852_ops_controller1, - }, - { - .name = "I2S-TDM-2", - .stream_name = "TEGRA PCM", - .platform_name = "tegra-pcm-audio", - .ops = &tegra_p1852_ops_controller2, - } -}; - -static struct snd_soc_card snd_soc_tegra_p1852 = { - .name = "tegra-p1852", - .dai_link = tegra_p1852_dai_link, - .num_links = ARRAY_SIZE(tegra_p1852_dai_link), -}; - -static __devinit int tegra_p1852_driver_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &snd_soc_tegra_p1852; - struct tegra_p1852 *machine; - struct tegra_p1852_platform_data *pdata; - int ret; - int i; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "No platform data supplied\n"); - return -EINVAL; - } - - machine = kzalloc(sizeof(struct tegra_p1852), GFP_KERNEL); - if (!machine) { - dev_err(&pdev->dev, "Can't allocate tegra_p1852 struct\n"); - return -ENOMEM; - } - - machine->pdata = pdata; - - /* The codec driver and codec dai have to come from the system - * level board configuration file - * */ - for (i = 0; i < ARRAY_SIZE(tegra_p1852_dai_link); i++) { - tegra_p1852_dai_link[i].codec_name = - pdata->codec_info[i].codec_name; - tegra_p1852_dai_link[i].cpu_dai_name = - pdata->codec_info[i].cpu_dai_name; - tegra_p1852_dai_link[i].codec_dai_name = - pdata->codec_info[i].codec_dai_name; - tegra_p1852_dai_link[i].name = - pdata->codec_info[i].name; - if (pdata->codec_info[i].pcm_driver) - tegra_p1852_dai_link[i].platform_name = - pdata->codec_info[i].pcm_driver; - } - - ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card); - if (ret) - goto err_free_machine; - - card->dev = &pdev->dev; - platform_set_drvdata(pdev, card); - snd_soc_card_set_drvdata(card, machine); - - ret = snd_soc_register_card(card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - } - - if (!card->instantiated) { - ret = -ENODEV; - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - goto err_unregister_card; - } - - return 0; - -err_unregister_card: - snd_soc_unregister_card(card); - tegra_asoc_utils_fini(&machine->util_data); -err_free_machine: - kfree(machine); - return ret; -} - -static int __devexit tegra_p1852_driver_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct tegra_p1852 *machine = snd_soc_card_get_drvdata(card); - - snd_soc_unregister_card(card); - tegra_asoc_utils_fini(&machine->util_data); - kfree(machine); - - return 0; -} - -static struct platform_driver tegra_p1852_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .pm = &snd_soc_pm_ops, - }, - .probe = tegra_p1852_driver_probe, - .remove = __devexit_p(tegra_p1852_driver_remove), -}; - -static int __init tegra_p1852_modinit(void) -{ - return platform_driver_register(&tegra_p1852_driver); -} -module_init(tegra_p1852_modinit); - -static void __exit tegra_p1852_modexit(void) -{ - platform_driver_unregister(&tegra_p1852_driver); -} -module_exit(tegra_p1852_modexit); - -MODULE_AUTHOR("Nitin Pai "); -MODULE_DESCRIPTION("Tegra+P1852 machine ASoC driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_vcm.c b/sound/soc/tegra/tegra_vcm.c new file mode 100644 index 000000000000..afb444e3f19f --- /dev/null +++ b/sound/soc/tegra/tegra_vcm.c @@ -0,0 +1,312 @@ +/* + * tegra_vcm.c - Tegra machine ASoC driver for P852/P1852/P1853 Boards. + * + * Author: Nitin Pai + * Copyright (C) 2010-2012 - NVIDIA, Corporation. All rights reserved. + * + * Based on code copyright/by: + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Stephen Warren + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "tegra_pcm.h" +#include "tegra_asoc_utils.h" + +#ifdef CONFIG_MACH_P1852 +#define DRV_NAME "tegra-snd-p1852" +#endif +#ifdef CONFIG_MACH_E1853 +#define DRV_NAME "tegra-snd-e1853" +#endif +#ifdef CONFIG_MACH_P852 +#define DRV_NAME "tegra-snd-p852" +#endif + + +struct tegra_vcm { + struct tegra_asoc_utils_data util_data; + struct tegra_p1852_platform_data *pdata; +}; + +static int tegra_vcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + int codec_id) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct tegra_vcm *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int i2s_daifmt = 0; + int err; + struct tegra_p1852_platform_data *pdata; + + pdata = machine->pdata; + + srate = params_rate(params); + switch (srate) { + case 64000: + case 88200: + case 96000: + mclk = 128 * srate; + break; + default: + mclk = 256 * srate; + break; + } + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + if (!(machine->util_data.set_mclk % mclk)) + mclk = machine->util_data.set_mclk; + else { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + } + + tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); + + if (pdata->codec_info[codec_id].master) + i2s_daifmt |= SND_SOC_DAIFMT_CBM_CFM; + else + i2s_daifmt |= SND_SOC_DAIFMT_CBS_CFS; + + switch (pdata->codec_info[codec_id].i2s_format) { + case format_tdm: + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case format_i2s: + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case format_rjm: + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + case format_ljm: + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + default: + break; + } + + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); + if (err < 0) + dev_info(card->dev, "codec_dai fmt not set\n"); + + err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); + if (err < 0) { + dev_err(card->dev, "cpu_dai fmt not set\n"); + return err; + } + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) + dev_info(card->dev, "codec_dai clock not set\n"); + + if (pdata->codec_info[codec_id].i2s_format == + format_tdm) { + err = snd_soc_dai_set_tdm_slot(cpu_dai, + pdata->codec_info[codec_id].rx_mask, + pdata->codec_info[codec_id].tx_mask, + pdata->codec_info[codec_id].num_slots, + pdata->codec_info[codec_id].slot_width); + if (err < 0) + dev_err(card->dev, "cpu_dai tdm mode setting not done\n"); + } + + return 0; +} + +static int tegra_vcm_hw_params_controller1( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return tegra_vcm_hw_params(substream, params, 0); +} + +static int tegra_vcm_hw_params_controller2( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return tegra_vcm_hw_params(substream, params, 1); +} + +static int tegra_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct tegra_vcm *machine = snd_soc_card_get_drvdata(rtd->card); + + tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0); + + return 0; +} + +static struct snd_soc_ops tegra_vcm_ops_controller1 = { + .hw_params = tegra_vcm_hw_params_controller1, + .hw_free = tegra_hw_free, +}; +static struct snd_soc_ops tegra_vcm_ops_controller2 = { + .hw_params = tegra_vcm_hw_params_controller2, + .hw_free = tegra_hw_free, +}; + +static struct snd_soc_dai_link tegra_vcm_dai_link[] = { + { + .name = "I2S-TDM-1", + .stream_name = "TEGRA PCM", + .platform_name = "tegra-pcm-audio", + .ops = &tegra_vcm_ops_controller1, + }, + { + .name = "I2S-TDM-2", + .stream_name = "TEGRA PCM", + .platform_name = "tegra-pcm-audio", + .ops = &tegra_vcm_ops_controller2, + } +}; + +static struct snd_soc_card snd_soc_tegra_vcm = { + .name = "tegra-vcm", + .dai_link = tegra_vcm_dai_link, + .num_links = ARRAY_SIZE(tegra_vcm_dai_link), +}; + +static __devinit int tegra_vcm_driver_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_tegra_vcm; + struct tegra_vcm *machine; + struct tegra_p1852_platform_data *pdata; + int ret; + int i; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + machine = kzalloc(sizeof(struct tegra_vcm), GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_vcm struct\n"); + return -ENOMEM; + } + + machine->pdata = pdata; + + /* The codec driver and codec dai have to come from the system + * level board configuration file + * */ + for (i = 0; i < ARRAY_SIZE(tegra_vcm_dai_link); i++) { + tegra_vcm_dai_link[i].codec_name = + pdata->codec_info[i].codec_name; + tegra_vcm_dai_link[i].cpu_dai_name = + pdata->codec_info[i].cpu_dai_name; + tegra_vcm_dai_link[i].codec_dai_name = + pdata->codec_info[i].codec_dai_name; + tegra_vcm_dai_link[i].name = + pdata->codec_info[i].name; + if (pdata->codec_info[i].pcm_driver) + tegra_vcm_dai_link[i].platform_name = + pdata->codec_info[i].pcm_driver; + } + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card); + if (ret) + goto err_free_machine; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + } + + if (!card->instantiated) { + ret = -ENODEV; + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_unregister_card; + } + + return 0; + +err_unregister_card: + snd_soc_unregister_card(card); + tegra_asoc_utils_fini(&machine->util_data); +err_free_machine: + kfree(machine); + return ret; +} + +static int __devexit tegra_vcm_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_vcm *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + tegra_asoc_utils_fini(&machine->util_data); + kfree(machine); + + return 0; +} + +static struct platform_driver tegra_vcm_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = tegra_vcm_driver_probe, + .remove = __devexit_p(tegra_vcm_driver_remove), +}; + +static int __init tegra_vcm_modinit(void) +{ + return platform_driver_register(&tegra_vcm_driver); +} +module_init(tegra_vcm_modinit); + +static void __exit tegra_vcm_modexit(void) +{ + platform_driver_unregister(&tegra_vcm_driver); +} +module_exit(tegra_vcm_modexit); + +MODULE_AUTHOR("Nitin Pai "); +MODULE_DESCRIPTION("Tegra+VCM machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3 From 4b70cc537cadc787b748c7c246d703a240b08985 Mon Sep 17 00:00:00 2001 From: Ankit Gupta Date: Wed, 11 Jul 2012 20:50:01 +0530 Subject: asoc: tegra: integration of slave mode support. Add master/slave configurability support interface for all codecs. Currently, complete slave mode functionality is added only for MAX98088 and WM8903 codec only. By default, board parameters will set i2s master mode for all codecs. asoc: tegra: utils: add support for i2s master/slave mode. Support for i2s in master or slave mode is made generic by obtaining information from platform data. Signed-off-by: Ankit Gupta Change-Id: Ided0fcefb6cdb124b78aab423bfd8c7dccd4bf6e Reviewed-on: http://git-master/r/111553 (cherry picked from commit bb1ad7222f9c75424a45976d16de418bc927dc04) asoc: tegra: wm8903 machine: add support for i2s in slave mode. Add support for i2s as slave for playback and capture use cases. Signed-off-by: Ankit Gupta Change-Id: I1f6b73a0a1b690ecd311b0ff4107aadbb1f037d8 Reviewed-on: http://git-master/r/111472 (cherry picked from commit 0434f8ba74adefe60597d95d30a455b9b2ece9b8) asoc: tegra: wm8753 machine: add support for i2s in slave mode. Add support for i2s as slave for playback and capture use cases. Signed-off-by: Ankit Gupta Change-Id: Ib0596955cd0c6ac5ec57b0f3c6ecc9e4ed41268c Reviewed-on: http://git-master/r/113208 (cherry picked from commit 0fc6b5e3a98d9f8866f73d7914b0c590334ce862) asoc: tegra: aic326x machine: add support for i2s in slave mode. Support for i2s in slave mode is added for playabck and capture use cases. Signed-off-by: Ankit Gupta Change-Id: I41f6459765f075703ad7f5f8dc9d4628dd853820 Reviewed-on: http://git-master/r/112874 (cherry picked from commit 9a89ede36a1dca6f53250444e819443fb6f28d09) asoc: tegra: rt5640 machine: add support for i2s in slave mode. Add support for i2s as slave for playback and capture use cases. Signed-off-by: Ankit Gupta Change-Id: I850ec62149b8a8d244445b70658b632dbce06558 Reviewed-on: http://git-master/r/112878 (cherry picked from commit 1d51561c8edf47d8557a825450a48ee8743a185b) asoc: tegra: max98088 machine: add support for codec i2s as slave. Add support for codec i2s as slave during voice Call. (Bug 998682) Signed-off-by: Ankit Gupta Change-Id: I8fc54d367e9acd5417d270869cb5a9398b3b527f Reviewed-on: http://git-master/r/110559 (cherry picked from commit 78a490867e131b1cc892094ddd844c2b892cafb6) asoc: tegra: max98095 machine: add support for i2s in slave mode. Add support for i2s as slave for playback and capture use cases. Signed-off-by: Ankit Gupta Change-Id: I80944d403be94c55ad2ce31aea921d80ea7c088a Reviewed-on: http://git-master/r/112875 Reviewed-on: http://git-master/r/118080 Reviewed-by: Scott Peterson Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sumit Bhattacharya --- sound/soc/tegra/tegra_aic326x.c | 173 +++++++++++++++++++++++---------- sound/soc/tegra/tegra_asoc_utils.c | 53 +++++----- sound/soc/tegra/tegra_asoc_utils.h | 11 ++- sound/soc/tegra/tegra_max98088.c | 193 ++++++++++++++++++++++++------------- sound/soc/tegra/tegra_max98095.c | 57 ++++++++--- sound/soc/tegra/tegra_rt5640.c | 103 +++++++++++++++----- sound/soc/tegra/tegra_wm8753.c | 88 ++++++++++++++--- sound/soc/tegra/tegra_wm8903.c | 132 ++++++++++++++++--------- 8 files changed, 570 insertions(+), 240 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c index 4cacb6758eb8..a809d4c74145 100644 --- a/sound/soc/tegra/tegra_aic326x.c +++ b/sound/soc/tegra/tegra_aic326x.c @@ -8,6 +8,7 @@ * * (c) 2010, 2011 Nvidia Graphics Pvt. Ltd. * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -26,6 +27,7 @@ #include +#include #include #include #include @@ -274,8 +276,9 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card); - int srate, mclk, sample_size, daifmt; - int err; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, sample_size, i2s_daifmt; + int err, rate; #ifndef CONFIG_ARCH_TEGRA_2x_SOC struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); #endif @@ -294,9 +297,30 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, if (mclk < 0) return mclk; - daifmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { @@ -310,20 +334,21 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai, daifmt); + rate = clk_get_rate(machine->util_data.clk_cdev1); + + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_fmt(cpu_dai, daifmt); + err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -390,7 +415,8 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; 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; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int err, srate, mclk, min_mclk, sample_size, i2s_daifmt; #ifndef CONFIG_ARCH_TEGRA_2x_SOC struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai); #endif @@ -423,10 +449,32 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[BT_SCO].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[BT_SCO].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + + err = snd_soc_dai_set_fmt(rtd->cpu_dai, i2s_daifmt); if (err < 0) { dev_err(rtd->codec->card->dev, "cpu_dai fmt not set\n"); @@ -624,7 +672,8 @@ static int tegra_aic326x_voice_call_hw_params( struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card); - int srate, mclk; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, rate, i2s_daifmt; int err, pcmdiv, vxclkdiv;; srate = params_rate(params); @@ -632,6 +681,36 @@ static int tegra_aic326x_voice_call_hw_params( if (mclk < 0) return mclk; + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + if (machine_is_tegra_enterprise()) { + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, + "Can't configure i2s format\n"); + return -EINVAL; + } + } else { + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + } + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -644,25 +723,15 @@ static int tegra_aic326x_voice_call_hw_params( tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - 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); - } + rate = clk_get_rate(machine->util_data.clk_cdev1); + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -691,14 +760,6 @@ static int tegra_aic326x_voice_call_hw_params( /* 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; @@ -754,14 +815,6 @@ static int tegra_aic326x_bt_voice_call_hw_params( /* 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; @@ -1183,11 +1236,23 @@ 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; + for (i = 0; i < NUM_I2S_DEVICES ; i++) { + machine->codec_info[i].i2s_id = + pdata->i2s_param[i].audio_port_id; + machine->codec_info[i].bitsize = + pdata->i2s_param[i].sample_size; + machine->codec_info[i].is_i2smaster = + pdata->i2s_param[i].is_i2s_master; + machine->codec_info[i].rate = + pdata->i2s_param[i].rate; + machine->codec_info[i].channels = + pdata->i2s_param[i].channels; + if ((pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_A) || + (pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_B)) + machine->codec_info[i].is_format_dsp = 1; + else + machine->codec_info[i].is_format_dsp = 0; + } tegra_aic326x_dai[DAI_LINK_HIFI].cpu_dai_name = tegra_i2s_dai_name[machine->codec_info[HIFI_CODEC].i2s_id]; @@ -1214,6 +1279,16 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev) goto err_unregister_card; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_unregister_card; + } +#endif + return 0; err_unregister_card: diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 6ab5b2d46a1f..2c18a71b8b52 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -2,8 +2,7 @@ * tegra_asoc_utils.c - Harmony machine ASoC driver * * Author: Stephen Warren - * Copyright (C) 2010 - NVIDIA, Inc. - * + * Copyright (c) 2010-12, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -340,28 +339,6 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, ret = PTR_ERR(data->clk_out1); goto err_put_cdev1; } -#endif - -#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) -#if TEGRA30_I2S_MASTER_PLAYBACK - ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0); - if (ret) { - dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); - goto err_put_out1; - } -#else - rate = clk_get_rate(data->clk_m); - - if(rate == 26000000) - clk_set_rate(data->clk_cdev1, 13000000); - - ret = clk_set_parent(data->clk_cdev1, data->clk_m); - if (ret) { - dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); - goto err_put_out1; - } -#endif - #endif ret = clk_enable(data->clk_cdev1); @@ -402,6 +379,34 @@ err: } EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) +int tegra_asoc_utils_set_parent (struct tegra_asoc_utils_data *data, + int is_i2s_master) +{ + int ret = -ENODEV; + + if (is_i2s_master) { + ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0); + if (ret) { + dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); + return ret; + } + } else { + if(clk_get_rate(data->clk_m) == 26000000) + clk_set_rate(data->clk_cdev1, 13000000); + + ret = clk_set_parent(data->clk_cdev1, data->clk_m); + if (ret) { + dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_parent); +#endif + void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) { if (!IS_ERR(data->clk_out1)) diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 0423f02b76cc..ce379e3e513d 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -2,7 +2,7 @@ * tegra_asoc_utils.h - Definitions for Tegra DAS driver * * Author: Stephen Warren - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (c) 2010-12, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,7 +24,6 @@ #define __TEGRA_ASOC_UTILS_H_ -#define TEGRA30_I2S_MASTER_PLAYBACK 1 #define TEGRA_ALSA_MAX_DEVICES 6 #define TEGRA_DMA_MAX_CHANNELS 32 @@ -47,11 +46,13 @@ struct tegra_asoc_utils_data { }; int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, - int mclk); + int mclk); void tegra_asoc_utils_lock_clk_rate(struct tegra_asoc_utils_data *data, - int lock); + int lock); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, - struct device *dev, struct snd_soc_card *card); + struct device *dev, struct snd_soc_card *card); +int tegra_asoc_utils_set_parent(struct tegra_asoc_utils_data *data, + int is_i2s_master); void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data); diff --git a/sound/soc/tegra/tegra_max98088.c b/sound/soc/tegra/tegra_max98088.c index 82c2b930a39e..88098fcd99d9 100644 --- a/sound/soc/tegra/tegra_max98088.c +++ b/sound/soc/tegra/tegra_max98088.c @@ -12,6 +12,7 @@ * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -213,14 +214,16 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_max98088 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; #ifndef CONFIG_ARCH_TEGRA_2x_SOC struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); #endif - int srate, mclk, sample_size, i2s_daifmt; + int srate, mclk, sample_size, i2s_daifmt, i2s_master; int err; - struct clk *clk; int rate; + i2s_master = pdata->i2s_param[HIFI_CODEC].is_i2s_master; + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: sample_size = 16; @@ -251,32 +254,31 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, break; } + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= i2s_master ? SND_SOC_DAIFMT_CBS_CFS : + SND_SOC_DAIFMT_CBM_CFM; -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) - clk = clk_get_sys(NULL, "cdev1"); -#else - clk = clk_get_sys("extern1", NULL); -#endif - if (IS_ERR(clk)) { - dev_err(card->dev, "Can't retrieve clk cdev1\n"); - err = PTR_ERR(clk); - return err; + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; } - rate = clk_get_rate(clk); - printk("extern1 rate=%d\n",rate); - -#if TEGRA30_I2S_MASTER_PLAYBACK - i2s_daifmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; -#else - i2s_daifmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - mclk = rate; -#endif - err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -289,7 +291,9 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai,i2s_daifmt); + rate = clk_get_rate(machine->util_data.clk_cdev1); + + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; @@ -301,8 +305,17 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); + + /* ULP specific use case for 44.1kHz stream. */ + if ((!i2s_master) && (srate == 44100) && + machine_is_tegra_enterprise()) { + clk_set_rate(machine->util_data.clk_cdev1, (256 * srate)); + rate = clk_get_rate(machine->util_data.clk_cdev1); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, + SND_SOC_CLOCK_IN); + } + if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -371,7 +384,9 @@ static int tegra_bt_hw_params(struct snd_pcm_substream *substream, #endif struct snd_soc_card *card = rtd->card; struct tegra_max98088 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; int err, srate, mclk, min_mclk, sample_size; + int i2s_daifmt; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -414,10 +429,32 @@ static int tegra_bt_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[BT_SCO].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[BT_SCO].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + + err = snd_soc_dai_set_fmt(rtd->cpu_dai, i2s_daifmt); if (err < 0) { dev_err(rtd->codec->card->dev, "cpu_dai fmt not set\n"); return err; @@ -598,8 +635,9 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_max98088 *machine = snd_soc_card_get_drvdata(card); - int srate, mclk; - int err; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, i2s_daifmt; + int err, rate; srate = params_rate(params); switch (srate) { @@ -623,6 +661,31 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream, break; } + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -635,17 +698,15 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + rate = clk_get_rate(machine->util_data.clk_cdev1); + + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -655,14 +716,6 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream, /* 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; @@ -729,14 +782,6 @@ static int tegra_bt_voice_call_hw_params(struct snd_pcm_substream *substream, /* 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; @@ -1148,16 +1193,22 @@ static __devinit int tegra_max98088_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; - machine->codec_info[BASEBAND].is_format_dsp = 0; - - if ((pdata->baseband_param.bit_format == TEGRA_DAIFMT_DSP_A) || - (pdata->baseband_param.bit_format == TEGRA_DAIFMT_DSP_B)) { - machine->codec_info[BASEBAND].is_format_dsp = 1; + for (i = 0; i < NUM_I2S_DEVICES ; i++) { + machine->codec_info[i].i2s_id = + pdata->i2s_param[i].audio_port_id; + machine->codec_info[i].bitsize = + pdata->i2s_param[i].sample_size; + machine->codec_info[i].is_i2smaster = + pdata->i2s_param[i].is_i2s_master; + machine->codec_info[i].rate = + pdata->i2s_param[i].rate; + machine->codec_info[i].channels = + pdata->i2s_param[i].channels; + if ((pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_A) || + (pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_B)) + machine->codec_info[i].is_format_dsp = 1; + else + machine->codec_info[i].is_format_dsp = 0; } tegra_max98088_dai[DAI_LINK_HIFI].cpu_dai_name = @@ -1180,6 +1231,16 @@ static __devinit int tegra_max98088_driver_probe(struct platform_device *pdev) goto err_unregister_card; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_unregister_card; + } +#endif + return 0; err_unregister_card: diff --git a/sound/soc/tegra/tegra_max98095.c b/sound/soc/tegra/tegra_max98095.c index d065b78164ac..f77c327b4b00 100644 --- a/sound/soc/tegra/tegra_max98095.c +++ b/sound/soc/tegra/tegra_max98095.c @@ -8,7 +8,7 @@ * * Based on code copyright/by: * - * (c) 2010, 2011, 2012 Nvidia Graphics Pvt. Ltd. + * Copyright (c) 2010-12, NVIDIA CORPORATION. All rights reserved. * * Copyright 2007 Wolfson Microelectronics PLC. * Author: Graeme Gregory @@ -32,6 +32,7 @@ #include +#include #include #include #include @@ -118,11 +119,12 @@ static int tegra_max98095_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; #ifndef CONFIG_ARCH_TEGRA_2x_SOC struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); #endif - unsigned int srate, mclk, sample_size; - int err; + unsigned int srate, mclk, sample_size, i2s_daifmt; + int err, rate; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -154,6 +156,31 @@ static int tegra_max98095_hw_params(struct snd_pcm_substream *substream, break; } + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master; ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -166,26 +193,20 @@ static int tegra_max98095_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + rate = clk_get_rate(machine->util_data.clk_cdev1); + err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -667,6 +688,16 @@ static __devinit int tegra_max98095_driver_probe(struct platform_device *pdev) goto err_unregister_card; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_switch_unregister; + } +#endif + return 0; err_unregister_card: diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index 765eb59fabae..a99711fb9234 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -10,6 +10,7 @@ * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -28,6 +29,7 @@ #include +#include #include #include #include @@ -37,7 +39,7 @@ #include #endif -#include +#include #include #include @@ -61,7 +63,7 @@ struct tegra_rt5640 { struct tegra_asoc_utils_data util_data; - struct tegra_rt5640_platform_data *pdata; + struct tegra_asoc_platform_data *pdata; struct regulator *spk_reg; struct regulator *dmic_reg; struct regulator *cdc_en; @@ -82,11 +84,38 @@ static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; int srate, mclk, i2s_daifmt; - int err; + int err, rate; srate = params_rate(params); mclk = 256 * srate; + + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) { @@ -99,10 +128,7 @@ static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - i2s_daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - - i2s_daifmt |= SND_SOC_DAIFMT_I2S; + rate = clk_get_rate(machine->util_data.clk_cdev1); err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { @@ -116,8 +142,7 @@ static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream, return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -130,10 +155,10 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_card *card = rtd->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - int srate, mclk, min_mclk; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, min_mclk, i2s_daifmt; int err; srate = params_rate(params); @@ -169,10 +194,32 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[BT_SCO].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[BT_SCO].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + + err = snd_soc_dai_set_fmt(rtd->cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; @@ -279,7 +326,7 @@ static int tegra_rt5640_jack_notifier(struct notifier_block *self, struct snd_soc_codec *codec = jack->codec; struct snd_soc_card *card = codec->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; enum headset_state state = BIT_NO_HEADSET; unsigned char status_jack = 0; @@ -357,7 +404,7 @@ static int tegra_rt5640_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->spk_reg) { if (SND_SOC_DAPM_EVENT_ON(event)) @@ -381,7 +428,7 @@ static int tegra_rt5640_event_hp(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (!(machine->gpio_requested & GPIO_HP_MUTE)) return 0; @@ -398,7 +445,7 @@ static int tegra_rt5640_event_int_mic(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->dmic_reg) { if (SND_SOC_DAPM_EVENT_ON(event)) @@ -422,7 +469,7 @@ static int tegra_rt5640_event_ext_mic(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (!(machine->gpio_requested & GPIO_EXT_MIC_EN)) return 0; @@ -467,7 +514,7 @@ static int tegra_rt5640_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = codec->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; int ret; if (gpio_is_valid(pdata->gpio_spkr_en)) { @@ -650,7 +697,7 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev) { struct snd_soc_card *card = &snd_soc_tegra_rt5640; struct tegra_rt5640 *machine; - struct tegra_rt5640_platform_data *pdata; + struct tegra_asoc_platform_data *pdata; int ret; pdata = pdev->dev.platform_data; @@ -716,6 +763,16 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev) goto err_unregister_card; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_unregister_card; + } +#endif + return 0; err_unregister_card: @@ -735,7 +792,7 @@ static int __devexit tegra_rt5640_driver_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); - struct tegra_rt5640_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->gpio_requested & GPIO_HP_DET) snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f7c7a4c6b5a1..f1b9335ab473 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -12,6 +12,7 @@ * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -30,6 +31,7 @@ #include +#include #include #include #include @@ -83,8 +85,10 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; int srate, mclk, i2s_daifmt; - int err; + int err, rate; + srate = params_rate(params); switch (srate) { case 8000: @@ -119,14 +123,38 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - i2s_daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; + rate = clk_get_rate(machine->util_data.clk_cdev1); + + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; /* Use DSP mode for mono on Tegra20 */ - if ((params_channels(params) != 2) && machine_is_whistler()) + if ((params_channels(params) != 2) && machine_is_whistler()) { i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; - else - i2s_daifmt |= SND_SOC_DAIFMT_I2S; + } else { + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, + "Can't configure i2s format\n"); + return -EINVAL; + } + } err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { @@ -140,8 +168,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -173,7 +200,8 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); - int srate, mclk, min_mclk; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, min_mclk, i2s_daifmt; int err; srate = params_rate(params); @@ -209,10 +237,32 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[BT_SCO].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[BT_SCO].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + + err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; @@ -873,8 +923,6 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) return -ENOMEM; } - machine->pdata = pdata; - ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card); if (ret) goto err_free_machine; @@ -904,6 +952,16 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) } #endif +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_unregister_card; + } +#endif + return 0; err_unregister_card: diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 063aefe50507..dcd031209172 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -12,6 +12,8 @@ * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -40,7 +42,7 @@ #include #endif -#include +#include #include #include @@ -67,7 +69,7 @@ struct tegra_wm8903 { struct tegra_asoc_utils_data util_data; - struct tegra_wm8903_platform_data *pdata; + struct tegra_asoc_platform_data *pdata; struct regulator *spk_reg; struct regulator *dmic_reg; int gpio_requested; @@ -86,9 +88,9 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; int srate, mclk, i2s_daifmt; int err; - struct clk *clk_m; int rate; srate = params_rate(params); @@ -103,31 +105,17 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, break; } + if(pdata->i2s_param[HIFI_CODEC].is_i2s_master) { + /* FIXME: Codec only requires >= 3MHz if OSR==0 */ + while (mclk < 6000000) + mclk *= 2; - - clk_m = clk_get_sys(NULL, "clk_m"); - if (IS_ERR(clk_m)) { - dev_err(card->dev, "Can't retrieve clk clk_m\n"); - err = PTR_ERR(clk_m); - return err; + i2s_daifmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + } else { + i2s_daifmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; } - rate = clk_get_rate(clk_m); - printk("extern1 rate=%d\n",rate); - -#if TEGRA30_I2S_MASTER_PLAYBACK - /* FIXME: Codec only requires >= 3MHz if OSR==0 */ - while (mclk < 6000000) - mclk *= 2; - - i2s_daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; -#else - mclk = rate; - - i2s_daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; -#endif - err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { @@ -141,13 +129,36 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); + rate = clk_get_rate(machine->util_data.clk_cdev1); + /* Use DSP mode for mono on Tegra20 */ if ((params_channels(params) != 2) && - (machine_is_ventana() || machine_is_harmony() || - machine_is_kaen() || machine_is_aebl())) + (machine_is_ventana() || machine_is_harmony() || + machine_is_kaen() || machine_is_aebl())) { i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; - else - i2s_daifmt |= SND_SOC_DAIFMT_I2S; + } else { + switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, + "Can't configure i2s format\n"); + return -EINVAL; + } + } err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt); if (err < 0) { @@ -161,8 +172,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; @@ -190,10 +200,10 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_card *card = rtd->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - int srate, mclk, min_mclk; + struct tegra_asoc_platform_data *pdata = machine->pdata; + int srate, mclk, min_mclk, i2s_daifmt; int err; srate = params_rate(params); @@ -229,10 +239,32 @@ static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + i2s_daifmt = SND_SOC_DAIFMT_NB_NF; + i2s_daifmt |= pdata->i2s_param[BT_SCO].is_i2s_master ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + + switch (pdata->i2s_param[BT_SCO].i2s_mode) { + case TEGRA_DAIFMT_I2S : + i2s_daifmt |= SND_SOC_DAIFMT_I2S; + break; + case TEGRA_DAIFMT_DSP_A : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_A; + break; + case TEGRA_DAIFMT_DSP_B : + i2s_daifmt |= SND_SOC_DAIFMT_DSP_B; + break; + case TEGRA_DAIFMT_LEFT_J : + i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J; + break; + case TEGRA_DAIFMT_RIGHT_J : + i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J; + break; + default : + dev_err(card->dev, "Can't configure i2s format\n"); + return -EINVAL; + } + + err = snd_soc_dai_set_fmt(rtd->cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; @@ -408,7 +440,7 @@ static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->spk_reg) { if (SND_SOC_DAPM_EVENT_ON(event)) @@ -432,7 +464,7 @@ static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (!(machine->gpio_requested & GPIO_HP_MUTE)) return 0; @@ -449,7 +481,7 @@ static int tegra_wm8903_event_int_mic(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->dmic_reg) { if (SND_SOC_DAPM_EVENT_ON(event)) @@ -473,7 +505,7 @@ static int tegra_wm8903_event_ext_mic(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (!(machine->gpio_requested & GPIO_EXT_MIC_EN)) return 0; @@ -578,7 +610,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = codec->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; int ret; machine->bias_level = SND_SOC_BIAS_STANDBY; @@ -792,7 +824,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) { struct snd_soc_card *card = &snd_soc_tegra_wm8903; struct tegra_wm8903 *machine; - struct tegra_wm8903_platform_data *pdata; + struct tegra_asoc_platform_data *pdata; int ret; pdata = pdev->dev.platform_data; @@ -895,6 +927,16 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) goto err_unregister_card; } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + ret = tegra_asoc_utils_set_parent(&machine->util_data, + pdata->i2s_param[HIFI_CODEC].is_i2s_master); + if (ret) { + dev_err(&pdev->dev, "tegra_asoc_utils_set_parent failed (%d)\n", + ret); + goto err_unregister_card; + } +#endif + return 0; err_unregister_card: @@ -914,7 +956,7 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = machine->pdata; + struct tegra_asoc_platform_data *pdata = machine->pdata; if (machine->gpio_requested & GPIO_HP_DET) snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, -- cgit v1.2.3 From cfb86bf491e3cf6baad2f164fae4880185f0e22c Mon Sep 17 00:00:00 2001 From: Nitin Pai Date: Mon, 2 Jul 2012 14:02:31 +0530 Subject: asoc: tegra: i2s: Fixes for audio_clock_2x rate audio_clock_2x parent needs to be set first before changing the rate, else it will not work. Bug 1019017 Signed-off-by: Nitin Pai Reviewed-on: http://git-master/r/116682 (cherry picked from commit fdc4da6d96e7be4aa2accf098de48543e745cf11) Change-Id: I93aeb1afed8732e8a1b1cff4ab03b9a9f654804f Reviewed-on: http://git-master/r/118250 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bob Johnston Reviewed-by: Varun Wadekar --- sound/soc/tegra/tegra30_i2s.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 72e64470008a..03ddff5e8b95 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -557,6 +557,13 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, return ret; } + ret = clk_set_parent(clk_get_parent(i2s->clk_audio_2x), + i2s->clk_i2s_sync); + if (ret) { + dev_err(dev, "Can't set parent of audio2x clock\n"); + return ret; + } + ret = clk_set_rate(i2s->clk_audio_2x, i2sclock); if (ret) { dev_err(dev, "Can't set I2S sync clock rate\n"); -- cgit v1.2.3 From 7c0739af09b7d0c0233587942180e83e77d3b921 Mon Sep 17 00:00:00 2001 From: Sayak Ghosh Choudhury Date: Thu, 2 Aug 2012 14:59:53 +0530 Subject: asoc: codecs: max98088: fix no audio after reset 98088 codec register value is altered to fix no audio issue after reset. Previously after reset the register value was getting wiped out. Bug 1017782 Change-Id: I060929bfb868c48dc4db9def171a3bdd167a3913 Reviewed-on: http://git-master/r/120319 Tested-by: Sayak Choudhury Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Vijay Mali Reviewed-by: Ravindra Lokhande Reviewed-by: Scott Peterson --- sound/soc/codecs/max98088.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index b2b31f912b74..31ca26388642 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -389,7 +389,7 @@ static struct { { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */ { 0xFF, 0xFF, 0 }, /* 2D SPK control */ { 0xFF, 0xFF, 0 }, /* 2E sidetone */ - { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */ + { 0xFF, 0xFF, 1 }, /* 2F DAI1 playback level */ { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */ { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */ -- cgit v1.2.3 From c5dea888253285e23107585805b7728ebe46e17e Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Thu, 2 Aug 2012 15:27:53 -0400 Subject: asoc: tegra: fix compilation error in max98095 change 4b70cc537cadc787b748c7c246d703a240b08985 introduced a compilation error. Signed-off-by: Rhyland Klein Change-Id: I59e54537d9bb4b30e807478e5b7634db0813a739 Reviewed-on: http://git-master/r/120443 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Ankit Gupta (Engrg-SW) Reviewed-by: Scott Peterson --- sound/soc/tegra/tegra_max98095.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_max98095.c b/sound/soc/tegra/tegra_max98095.c index f77c327b4b00..1d03980f276f 100644 --- a/sound/soc/tegra/tegra_max98095.c +++ b/sound/soc/tegra/tegra_max98095.c @@ -157,7 +157,7 @@ static int tegra_max98095_hw_params(struct snd_pcm_substream *substream, } i2s_daifmt = SND_SOC_DAIFMT_NB_NF; - i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master; ? + i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) { -- cgit v1.2.3 From e9d6b70e5987491b9ae033e0553ea4f13824ff7e Mon Sep 17 00:00:00 2001 From: Manoj Gangwal Date: Wed, 22 Aug 2012 15:28:01 +0530 Subject: asoc: codecs: Add TI aic326x driver Add latest TI aic326x driver. Bug 1034241 Change-Id: I8457538025b2989c4baa5eaefef48bdd5535def9 Signed-off-by: Manoj Gangwal Reviewed-on: http://git-master/r/125167 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Lokesh Pathak --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/Makefile | 3 +- sound/soc/codecs/aic3262_codec_ops.c | 424 ++ sound/soc/codecs/aic3262_codec_ops.h | 44 + sound/soc/codecs/aic326x_tiload.c | 231 +- sound/soc/codecs/aic326x_tiload.h | 5 +- sound/soc/codecs/aic3xxx_cfw.h | 427 ++ sound/soc/codecs/aic3xxx_cfw_ops.c | 922 +++++ sound/soc/codecs/aic3xxx_cfw_ops.h | 76 + sound/soc/codecs/tlv320aic3262_default_fw.h | 330 ++ sound/soc/codecs/tlv320aic326x.c | 5647 +++++++++------------------ sound/soc/codecs/tlv320aic326x.h | 645 +-- 12 files changed, 4201 insertions(+), 4554 deletions(-) create mode 100644 sound/soc/codecs/aic3262_codec_ops.c create mode 100644 sound/soc/codecs/aic3262_codec_ops.h create mode 100644 sound/soc/codecs/aic3xxx_cfw.h create mode 100644 sound/soc/codecs/aic3xxx_cfw_ops.c create mode 100644 sound/soc/codecs/aic3xxx_cfw_ops.h create mode 100644 sound/soc/codecs/tlv320aic3262_default_fw.h (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7d5a66122eec..8946925ae41a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -256,6 +256,7 @@ config SND_SOC_TLV320AIC3X tristate config SND_SOC_TLV320AIC326X + select AIC3262_CODEC tristate "TI AIC326x Codec" config SND_SOC_TLV320DAC33 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d80b7bbff2e8..dc89ee430031 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -35,7 +35,8 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o -snd-soc-tlv320aic326x-objs := tlv320aic326x.o +snd-soc-tlv320aic326x-objs := tlv320aic326x.o aic3xxx_cfw_ops.o +snd-soc-tlv320aic326x-objs += aic3262_codec_ops.o aic326x_tiload.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o diff --git a/sound/soc/codecs/aic3262_codec_ops.c b/sound/soc/codecs/aic3262_codec_ops.c new file mode 100644 index 000000000000..45686c7aa460 --- /dev/null +++ b/sound/soc/codecs/aic3262_codec_ops.c @@ -0,0 +1,424 @@ +#include +#include +#include +#include +#include +#include +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" +#include "tlv320aic326x.h" +#include "aic3262_codec_ops.h" + +int aic3262_ops_reg_read(void *p, unsigned int reg) +{ + struct aic3262_priv *ps = p; + union cfw_register *c = (union cfw_register *) ® + union aic326x_reg_union mreg; + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + + return aic3262_reg_read(ps->codec->control_data, + mreg.aic326x_register_int); + +} +int aic3262_ops_reg_write(void *p, unsigned int reg, unsigned char mval) +{ + struct aic3262_priv *ps = p; + union aic326x_reg_union mreg; + union cfw_register *c = (union cfw_register *) ® + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + mval = c->data; + DBG("reg_write:page %d book %d offset %d mval : %#x\n", + mreg.aic326x_register.page, mreg.aic326x_register.book, + mreg.aic326x_register.offset, mval); + return aic3262_reg_write(ps->codec->control_data, + mreg.aic326x_register_int, mval); +} + +int aic3262_ops_set_bits(void *p, unsigned int reg, + unsigned char mask, unsigned char val) +{ + struct aic3262_priv *ps = p; + + union aic326x_reg_union mreg; + union cfw_register *c = (union cfw_register *) ® + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + DBG("set_bits:page %d book %d offset %d mask %#x val %#x\n", + mreg.aic326x_register.page, mreg.aic326x_register.book, + mreg.aic326x_register.offset, mask, val); + + return aic3262_set_bits(ps->codec->control_data, + mreg.aic326x_register_int, mask, val); + +} + +int aic3262_ops_bulk_read(void *p, unsigned int reg, int count, u8 *buf) +{ + struct aic3262_priv *ps = p; + + union aic326x_reg_union mreg; + union cfw_register *c = (union cfw_register *) ® + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + + return aic3262_bulk_read(ps->codec->control_data, + mreg.aic326x_register_int, count, buf); +} + +int aic3262_ops_bulk_write(void *p, unsigned int reg, int count, const u8 *buf) +{ + struct aic3262_priv *ps = p; + union aic326x_reg_union mreg; + union cfw_register *c = (union cfw_register *) ® + + mreg.aic326x_register.offset = c->offset; + mreg.aic326x_register.page = c->page; + mreg.aic326x_register.book = c->book; + mreg.aic326x_register.reserved = 0; + DBG("bulk_write: ncmd %d page %d book %d offset %d data[0] %d\n", + count, mreg.aic326x_register.page, mreg.aic326x_register.book, + mreg.aic326x_register.offset, buf[0]); + + return aic3262_bulk_write(ps->codec->control_data, + mreg.aic326x_register_int, count, buf); +} +/***************************************************************************** +Function Name : aic3262_ops_dlock_lock +Argument : pointer argument to the codec +Return value : Integer +Purpose : To Read the run state of the DAC and ADC +by reading the codec and returning the run state + +Run state Bit format + +------------------------------------------------------ +D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 | +R R R LADC RADC R R LDAC RDAC +------------------------------------------------------ + +*******************************************************************************/ + +int aic3262_ops_lock(void *pv) +{ + int run_state = 0; + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + mutex_lock(&aic3262->codec->mutex); + + /* Reading the run state of adc and dac */ + run_state = get_runstate(aic3262->codec->control_data); + + return run_state; +} +/******************************************************************************* +Function name : aic3262_ops_dlock_unlock +Argument : pointer argument to the codec +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec + ******************************************************************************/ +int aic3262_ops_unlock(void *pv) +{ + /*Releasing the lock of mutex */ + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + + mutex_unlock(&aic3262->codec->mutex); + return 0; +} +/******************************************************************************* +Function Name : aic3262_ops_dlock_stop +Argument : 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 + ******************************************************************************/ +int aic3262_ops_stop(void *pv, int mask) +{ + int run_state = 0; + int limask = 0; + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + int ret_wbits = 0; + + mutex_lock(&aic3262->codec->mutex); + run_state = get_runstate(aic3262->codec->control_data); + + limask = mask & AIC3XX_COPS_MDSP_A; + if (limask != 0) + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_ADC_DATAPATH_SETUP, 0xC0, 0); + + limask = mask & AIC3XX_COPS_MDSP_D; + if (limask != 0) + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_DAC_DATAPATH_SETUP, 0xC0, 0); + + limask = mask & AIC3XX_COPS_MDSP_A; + if (limask != 0) { + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK, + 0, TIME_DELAY, DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "at line %d function %s, ADC powerdown" + "wait_bits timedout\n", + __LINE__, __func__); + } + + limask = mask & AIC3XX_COPS_MDSP_D; + if (limask != 0) { + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK, + 0, TIME_DELAY, DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "at line %d function %s, DAC powerdown" + "wait_bits timedout\n", + __LINE__, __func__); + } + + return run_state; + +} +/**************************************************************************** +Function name : aic3262_ops_dlock_restore +Argument : pointer argument to the codec,run_state +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec and to restore the states of the dsp +******************************************************************************/ +int aic3262_ops_restore(void *pv, int run_state) +{ + int sync_state; + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + + /* This is for read the sync mode register state */ + sync_state = SYNC_STATE(aic3262); + + /*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(pv, run_state); + else + aic3262_dsp_pwrup(pv, run_state); + + mutex_unlock(&aic3262->codec->mutex); + + return 0; +} + +/***************************************************************************** +Function name : aic3262_ops_adaptivebuffer_swap +Argument : pointer argument to the codec,mask tells us which dsp has to +be chosen for swapping +Return Value : integer returning 0 +Purpose : To swap the coefficient buffers of minidsp according to mask +******************************************************************************/ + +int aic3262_ops_adaptivebuffer_swap(void *pv, int mask) +{ + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + int ret_wbits = 0; + + if (mask & AIC3XX_ABUF_MDSP_A) { + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_ADC_ADAPTIVE_CRAM_REG, 0x1, 0x1); + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_ADC_ADAPTIVE_CRAM_REG, 0x1, 0, 15, + 1); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "at line %d function %s, miniDSP_A buffer swap failed\n", + __LINE__, __func__); + } + + if (mask & AIC3XX_ABUF_MDSP_D1) { + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_DAC_ADAPTIVE_BANK1_REG, 0x1, 0x1); + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_DAC_ADAPTIVE_BANK1_REG, 0x1, 0, + 15, 1); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "at line %d function %s, miniDSP_D buffer1 swap failed\n", + __LINE__, __func__); + } + + if (mask & AIC3XX_ABUF_MDSP_D2) { + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_DAC_ADAPTIVE_BANK2_REG, 0x1, 0x1); + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_DAC_ADAPTIVE_BANK2_REG, 0x1, 0, + 15, 1); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "at line %d function %s, miniDSP_D buffer2 swap failed\n", + __LINE__, __func__); + } + + return 0; +} + +/***************************************************************************** +Function name : get_runstate +Argument : pointer argument to the codec +Return Value : integer returning the runstate +Purpose : To read the current state of the dac's and adc's +******************************************************************************/ + +int get_runstate(void *ps) +{ + struct aic3262 *pr = ps; + int run_state = 0; + int DAC_state = 0, ADC_state = 0; + /* Read the run state */ + DAC_state = aic3262_reg_read(pr, AIC3262_DAC_FLAG); + ADC_state = aic3262_reg_read(pr, AIC3262_ADC_FLAG); + + DSP_STATUS(run_state, ADC_state, 6, 5); + DSP_STATUS(run_state, ADC_state, 2, 4); + DSP_STATUS(run_state, DAC_state, 7, 1); + DSP_STATUS(run_state, DAC_state, 3, 0); + + return run_state; + +} +/**************************************************************************** +Function name : aic3262_dsp_pwrdwn_status +Argument : pointer argument to the codec , cur_state of dac's and adc's +Return Value : integer returning 0 +Purpose : To read the status of dsp's +******************************************************************************/ + +int aic3262_dsp_pwrdwn_status(void *pv) +{ + struct aic3262_priv *aic3262 = pv; + int ret_wbits = 0; + + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_ADC_DATAPATH_SETUP, 0XC0, 0); + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_DAC_DATAPATH_SETUP, 0XC0, 0); + + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, AIC3262_ADC_FLAG, + AIC3262_ADC_POWER_MASK, 0, TIME_DELAY, + DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, "ADC Power down timedout\n"); + + aic3262_wait_bits(aic3262->codec->control_data, AIC3262_DAC_FLAG, + AIC3262_DAC_POWER_MASK, 0, TIME_DELAY, DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, "DAC Power down timedout\n"); + + return 0; +} + +int aic3262_dsp_pwrup(void *pv, int state) +{ + struct aic3262_priv *aic3262 = (struct aic3262_priv *)pv; + 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 & AIC3262_COPS_MDSP_A_L) { + adc_reg_mask |= 0x80; + adc_power_mask |= AIC3262_LADC_POWER_MASK; + } + if (state & AIC3262_COPS_MDSP_A_R) { + adc_reg_mask |= 0x40; + adc_power_mask |= AIC3262_RADC_POWER_MASK; + } + + if (state & AIC3262_COPS_MDSP_A) + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_ADC_DATAPATH_SETUP, 0XC0, + adc_reg_mask); + + if (state & AIC3262_COPS_MDSP_D_L) { + dac_reg_mask |= 0x80; + dac_power_mask |= AIC3262_LDAC_POWER_MASK; + } + if (state & AIC3262_COPS_MDSP_D_R) { + dac_reg_mask |= 0x40; + dac_power_mask |= AIC3262_RDAC_POWER_MASK; + } + + if (state & AIC3262_COPS_MDSP_D) + aic3262_set_bits(aic3262->codec->control_data, + AIC3262_DAC_DATAPATH_SETUP, 0XC0, + dac_reg_mask); + + if (state & AIC3262_COPS_MDSP_A) { + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK, + adc_power_mask, TIME_DELAY, + DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "ADC Power down timedout\n"); + } + + if (state & AIC3262_COPS_MDSP_D) { + ret_wbits = + aic3262_wait_bits(aic3262->codec->control_data, + AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK, + dac_power_mask, TIME_DELAY, + DELAY_COUNTER); + if (!ret_wbits) + dev_err(aic3262->codec->dev, + "ADC Power down timedout\n"); + } + + return 0; +} + +int aic3262_restart_dsps_sync(void *pv, int run_state) +{ + + aic3262_dsp_pwrdwn_status(pv); + aic3262_dsp_pwrup(pv, run_state); + + return 0; +} + +const struct aic3xxx_codec_ops aic3262_cfw_codec_ops = { + .reg_read = aic3262_ops_reg_read, + .reg_write = aic3262_ops_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, +}; + diff --git a/sound/soc/codecs/aic3262_codec_ops.h b/sound/soc/codecs/aic3262_codec_ops.h new file mode 100644 index 000000000000..bec0dc817e34 --- /dev/null +++ b/sound/soc/codecs/aic3262_codec_ops.h @@ -0,0 +1,44 @@ +#define SYNC_STATE(p) aic3262_reg_read(p->codec->control_data, AIC3262_DAC_PRB) + +#define DSP_STATUS(rs, adc_dac, rpos, rspos) (rs |= \ + (((adc_dac>>rpos) & 1) << rspos)) + +#define AIC3262_COPS_MDSP_A 0x30 +#define AIC3262_COPS_MDSP_A_L 0x20 +#define AIC3262_COPS_MDSP_A_R 0x10 + +#define AIC3262_COPS_MDSP_D 0x03 +#define AIC3262_COPS_MDSP_D_L 0x02 +#define AIC3262_COPS_MDSP_D_R 0x01 + +int get_runstate(void *); + +int aic3262_dsp_pwrup(void *, int); + +int aic3262_pwr_down(void *, int, int, int, int); + +int aic3262_dsp_pwrdwn_status(void *); + +int aic3262_ops_reg_read(void *p, unsigned int reg); + +int aic3262_ops_reg_write(void *p, unsigned int reg, unsigned char mval); + +int aic3262_ops_set_bits(void *p, unsigned int reg, unsigned char mask, + unsigned char val); + +int aic3262_ops_bulk_read(void *p, unsigned int reg, int count, u8 *buf); + +int aic3262_ops_bulk_write(void *p, unsigned int reg, int count, + const u8 *buf); + +int aic3262_ops_lock(void *pv); + +int aic3262_ops_unlock(void *pv); + +int aic3262_ops_stop(void *pv, int mask); + +int aic3262_ops_restore(void *pv, int run_state); + +int aic3262_ops_adaptivebuffer_swap(void *pv, int mask); + +int aic3262_restart_dsps_sync(void *pv, int run_state); diff --git a/sound/soc/codecs/aic326x_tiload.c b/sound/soc/codecs/aic326x_tiload.c index 07615dc5ebd7..781a6cb3456e 100644 --- a/sound/soc/codecs/aic326x_tiload.c +++ b/sound/soc/codecs/aic326x_tiload.c @@ -16,9 +16,9 @@ * * History: * - * Rev 0.1 Tiload support 16-09-2010 + * Rev 0.1 Tiload support TI 16-09-2010 * - * The Tiload programming support is added to AIC3262. + * The Tiload programming support is added to AIC3262. * */ @@ -37,16 +37,21 @@ #include #include #include +#include +#include +#include +#include +#include #include "tlv320aic326x.h" #include "aic326x_tiload.h" /* enable debug prints in the driver */ -#define DEBUG -//#undef DEBUG +/*#define DEBUG */ +#undef DEBUG #ifdef DEBUG -#define dprintk(x...) printk(x) +#define dprintk(x...) printk(x) #else #define dprintk(x...) #endif @@ -58,17 +63,12 @@ static void aic3262_dump_page(struct i2c_client *i2c, u8 page); #endif -/* externs */ -extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page); -extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book); -extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value); -int aic3262_driver_init(struct snd_soc_codec *codec); /************** Dynamic aic3262 driver, TI LOAD support ***************/ static struct cdev *aic3262_cdev; -static int aic3262_major = 0; /* Dynamic allocation of Mjr No. */ -static int aic3262_opened = 0; /* Dynamic allocation of Mjr No. */ +static union aic326x_reg_union aic_reg; +static int aic3262_major; /* Dynamic allocation of Mjr No. */ +static int aic3262_opened; /* Dynamic allocation of Mjr No. */ static struct snd_soc_codec *aic3262_codec; struct class *tiload_class; static unsigned int magic_num = 0xE0; @@ -77,10 +77,12 @@ static unsigned int magic_num = 0xE0; #ifdef REG_DUMP_aic3262 /* - *---------------------------------------------------------------------------- - * Function : aic3262_dump_page - * Purpose : Read and display one codec register page, for debugging purpose - *---------------------------------------------------------------------------- + * aic3262_dump_page: Read and display one codec register page, for + debugging purpose + * @i2c; i2c_client identifies a single device (i.e. aic3262) connected + to an i2c bus. + * @page: page number + * */ static void aic3262_dump_page(struct i2c_client *i2c, u8 page) { @@ -88,103 +90,99 @@ static void aic3262_dump_page(struct i2c_client *i2c, u8 page) u8 data; u8 test_page_array[8]; - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); - aic3262_change_page(codec, page); + dprintk("TiLoad DRIVER : %s\n", __func__); data = 0x0; - i2c_master_send(i2c, data, 1); i2c_master_recv(i2c, test_page_array, 8); - printk("\n------- aic3262 PAGE %d DUMP --------\n", page); - for (i = 0; i < 8; i++) { - printk(" [ %d ] = 0x%x\n", i, test_page_array[i]); - } + dprintk("\n------- aic3262 PAGE %d DUMP --------\n", page); + for (i = 0; i < 8; i++) + dprintk(" [ %d ] = 0x%x\n", i, test_page_array[i]); } #endif -/* - *---------------------------------------------------------------------------- - * Function : tiload_open +/** + * tiload_open: open method for aic3262-tiload programming interface + * @in: Pointer to inode + * @filp: pointer to file * - * Purpose : open method for aic3262-tiload programming interface - *---------------------------------------------------------------------------- + * Return: Return 0 if success. */ static int tiload_open(struct inode *in, struct file *filp) { - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); if (aic3262_opened) { - printk("%s device is already opened\n", "aic3262"); - printk("%s: only one instance of driver is allowed\n", - "aic3262"); + dprintk("%s device is already opened\n", "aic3262"); + dprintk("%s: only one instance of driver is allowed\n", + "aic3262"); return -1; } aic3262_opened++; return 0; } -/* - *---------------------------------------------------------------------------- - * Function : tiload_release +/** + * tiload_release close method for aic3262_tilaod programming interface + * @in: Pointer to inode + * @filp: pointer to file * - * Purpose : close method for aic3262_tilaod programming interface - *---------------------------------------------------------------------------- + * Return: Return 0 if success. */ static int tiload_release(struct inode *in, struct file *filp) { - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); aic3262_opened--; return 0; } -/* - *---------------------------------------------------------------------------- - * Function : tiload_read +/** + * tiload_read: read method for mini dsp programming interface + * @file: pointer to file + * @buf: pointer to user + * @count: number of byte to be read + * @offset: offset address * - * Purpose : read method for mini dsp programming interface - *---------------------------------------------------------------------------- + * Return: return value read */ -static ssize_t tiload_read(struct file *file, char __user * buf, - size_t count, loff_t * offset) +static ssize_t tiload_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) { - static char rd_data[8]; + static char rd_data[128]; char reg_addr; size_t size; - #ifdef DEBUG +#ifdef DEBUG int i; - #endif - struct i2c_client *i2c = aic3262_codec->control_data; +#endif + struct aic3262 *control = aic3262_codec->control_data; - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); if (count > 128) { - printk("Max 128 bytes can be read\n"); + dprintk("Max 128 bytes can be read\n"); count = 128; } /* copy register address from user space */ size = copy_from_user(®_addr, buf, 1); if (size != 0) { - printk("read: copy_from_user failure\n"); + dprintk("read: copy_from_user failure\n"); return -1; } /* Send the address to device thats is to be read */ - if (i2c_master_send(i2c, ®_addr, 1) != 1) { - dprintk("Can not write register address\n"); - return -1; - } - /* read the codec device registers */ - size = i2c_master_recv(i2c, rd_data, count); + aic_reg.aic326x_register.offset = reg_addr; + size = + aic3262_bulk_read(control, aic_reg.aic326x_register_int, count, + rd_data); + #ifdef DEBUG - printk(KERN_ERR "read size = %d, reg_addr= %x , count = %d\n", - (int)size, reg_addr, (int)count); - for (i = 0; i < (int)size; i++) { - printk(KERN_ERR "rd_data[%d]=%x\n", i, rd_data[i]); - } + pr_err(KERN_ERR "read size = %d, reg_addr= %x , count = %d\n", + (int)size, reg_addr, (int)count); + for (i = 0; i < (int)size; i++) + dprintk("rd_data[%d]=%x\n", i, rd_data[i]); #endif - if (size != count) { - printk("read %d registers from the codec\n", size); - } + if (size != count) + dprintk("read %d registers from the codec\n", size); if (copy_to_user(buf, rd_data, size) != 0) { dprintk("copy_to_user failed\n"); @@ -194,58 +192,71 @@ static ssize_t tiload_read(struct file *file, char __user * buf, return size; } -/* - *---------------------------------------------------------------------------- - * Function : tiload_write +/** + * tiload_write: write method for aic3262_tiload programming interface + * @file: pointer to file + * @buf: pointer to user + * @count: number of byte to be read + * @offset: offset address * - * Purpose : write method for aic3262_tiload programming interface - *---------------------------------------------------------------------------- + * Return: return byte written */ -static ssize_t tiload_write(struct file *file, const char __user * buf, - size_t count, loff_t * offset) +static ssize_t tiload_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) { - static char wr_data[8]; - u8 pg_no; - #ifdef DEBUG + static char wr_data[128]; +#ifdef DEBUG int i; - #endif - struct i2c_client *i2c = aic3262_codec->control_data; - struct aic3262_priv *aic3262_private = snd_soc_codec_get_drvdata(aic3262_codec); +#endif + struct aic3262 *control = aic3262_codec->control_data; - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); /* copy buffer from user space */ if (copy_from_user(wr_data, buf, count)) { - printk("copy_from_user failure\n"); + dprintk("copy_from_user failure\n"); return -1; } #ifdef DEBUG - printk(KERN_ERR "write size = %d\n", (int)count); - for (i = 0; i < (int)count; i++) { - printk(KERN_INFO "\nwr_data[%d]=%x\n", i, wr_data[i]); - } + dprintk("write size = %d\n", (int)count); + for (i = 0; i < (int)count; i++) + dprintk("\nwr_data[%d]=%x\n", i, wr_data[i]); #endif if (wr_data[0] == 0) { - aic3262_change_page(aic3262_codec, wr_data[1]); + /*change of page seen, but will only be registered */ + aic_reg.aic326x_register.page = wr_data[1]; + return count; + + } else + if (wr_data[0] == 127) { + /* change of book seen, but will not be sent for I2C write */ + aic_reg.aic326x_register.book = wr_data[1]; return count; - } - pg_no = aic3262_private->page_no; - if ((wr_data[0] == 127) && (pg_no == 0)) { - aic3262_change_book(aic3262_codec, wr_data[1]); + } else { + aic_reg.aic326x_register.offset = wr_data[0]; + aic3262_bulk_write(control, aic_reg.aic326x_register_int, + count - 1, &wr_data[1]); return count; } - return i2c_master_send(i2c, wr_data, count); } -static long tiload_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) +/** + * tiload_ioctl: copy data to user and from user + * @filp: pointer to file + * @cmd: integer of type command + * @arg: argument type + * + * Return: Return 0 on success + */ +static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int num = 0; void __user *argp = (void __user *)arg; + if (_IOC_TYPE(cmd) != aic3262_IOC_MAGIC) return -ENOTTY; - dprintk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); switch (cmd) { case aic3262_IOMAGICNUM_GET: num = copy_to_user(argp, &magic_num, sizeof(int)); @@ -257,7 +268,7 @@ static long tiload_ioctl(struct file *filp, return num; } -/*********** File operations structure for aic3262-tiload programming *************/ +/******* File operations structure for aic3262-tiload programming *********/ static struct file_operations aic3262_fops = { .owner = THIS_MODULE, .open = tiload_open, @@ -267,46 +278,48 @@ static struct file_operations aic3262_fops = { .unlocked_ioctl = tiload_ioctl, }; -/* - *---------------------------------------------------------------------------- - * Function : aic3262_driver_init +/** + * aic3262_driver_init: Register a char driver for dynamic aic3262-tiload programming + * @codec: pointer variable to codec having codec information * - * Purpose : Register a char driver for dynamic aic3262-tiload programming - *---------------------------------------------------------------------------- + * Return: Return 0 on seccess */ int aic3262_driver_init(struct snd_soc_codec *codec) { int result; dev_t dev = MKDEV(aic3262_major, 0); - printk("TiLoad DRIVER : %s\n", __FUNCTION__); + dprintk("TiLoad DRIVER : %s\n", __func__); aic3262_codec = codec; - printk("allocating dynamic major number\n"); + dprintk("allocating dynamic major number\n"); result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); if (result < 0) { - printk("cannot allocate major number %d\n", aic3262_major); + dprintk("cannot allocate major number %d\n", aic3262_major); return result; } + tiload_class = class_create(THIS_MODULE, DEVICE_NAME); aic3262_major = MAJOR(dev); - printk("allocated Major Number: %d\n", aic3262_major); + dprintk("allocated Major Number: %d\n", aic3262_major); aic3262_cdev = cdev_alloc(); cdev_init(aic3262_cdev, &aic3262_fops); aic3262_cdev->owner = THIS_MODULE; aic3262_cdev->ops = &aic3262_fops; + aic_reg.aic326x_register.page = 0; + aic_reg.aic326x_register.book = 0; + if (cdev_add(aic3262_cdev, dev, 1) < 0) { - dprintk("aic3262_driver: cdev_add failed \n"); + dprintk("aic3262_driver: cdev_add failed\n"); unregister_chrdev_region(dev, 1); aic3262_cdev = NULL; return 1; } - printk("Registered aic3262 TiLoad driver, Major number: %d \n", - aic3262_major); - //class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); + dprintk("Registered aic3262 TiLoad driver, Major number: %d\n", + aic3262_major); return 0; } diff --git a/sound/soc/codecs/aic326x_tiload.h b/sound/soc/codecs/aic326x_tiload.h index 6621a4127b16..c11d8a2d06f7 100644 --- a/sound/soc/codecs/aic326x_tiload.h +++ b/sound/soc/codecs/aic326x_tiload.h @@ -2,7 +2,7 @@ * linux/sound/soc/codecs/aic3262_tiload.h * * - * Copyright (C) 2012 Texas Instruments, Inc. + * Copyright (C) 2011 Texas Instruments Inc. * * * @@ -24,9 +24,6 @@ #ifndef _AIC3262_TILOAD_H #define _AIC3262_TILOAD_H -/* typedefs required for the included header files */ -typedef char *string; - /* defines */ #define DEVICE_NAME "tiload_node" #define aic3262_IOC_MAGIC 0xE0 diff --git a/sound/soc/codecs/aic3xxx_cfw.h b/sound/soc/codecs/aic3xxx_cfw.h new file mode 100644 index 000000000000..12bc85575195 --- /dev/null +++ b/sound/soc/codecs/aic3xxx_cfw.h @@ -0,0 +1,427 @@ +/** + * \file Codec Firmware Declarations + */ + +#ifndef CFW_FIRMWARE_H_ +#define CFW_FIRMWARE_H_ +/** \defgroup bt Basic Types */ +/* @{ */ +#ifndef AIC3XXX_CFW_HOST_BLD +#include +#else +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long int u32; +#endif + +#define CFW_FW_MAGIC 0xC0D1F1ED + +/** defgroup pd Arbitrary Limitations */ + +#ifndef CFW_MAX_ID +#define CFW_MAX_ID (64) /* Max length of string identifies */ +#endif + +#ifndef CFW_MAX_DESC +#define CFW_MAX_DESC (512) /* Max length of description */ +#endif +/* +# include +#include +#include +#include +#include +#include +# define warn(fmt, ...) printk(fmt "\n", ##__VA_ARGS__) +# define error(fmt, ...) printk(fmt "\n", ##__VA_ARGS__) + +#else +# define _GNU_SOURCE +# include +# include "utils.h" +# include +# include +# define EINVAL 1 + +#endif + +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" +#ifndef AIC3XXX_CFW_HOST_BLD +static struct cfw_project *aic3xxx_cfw_unpickle(void *pcfw, int n); +#endif + + +/* + * Firmware version numbers are used to make sure that the + * host and target code stay in sync. It is _not_ recommended + * to provide this number from the outside (E.g., from a makefile) + * Instead, a set of automated tools are relied upon to keep the numbers + * in sync at the time of host testing. + */ +#define CFW_FW_VERSION 0x000100B3 + +static int aic3xxx_cfw_dlimage(struct cfw_state *ps, struct cfw_image *pim); +static int aic3xxx_cfw_dlcfg(struct cfw_state *ps, struct cfw_image *pim); +static int aic3xxx_cfw_dlctl(struct cfw_state *ps, struct cfw_block *pb, + u32 mute_flags); +static void aic3xxx_cfw_dlcmds(struct cfw_state *ps, struct cfw_block *pb); +static void aic3xxx_wait(struct cfw_state *ps, unsigned int reg, u8 mask, + u8 data); +static int aic3xxx_cfw_set_mode_id(struct cfw_state *ps); +static int aic3xxx_cfw_mute(struct cfw_state *ps, int mute, u32 flags); +static int aic3xxx_cfw_setmode_cfg_u(struct cfw_state *ps, int mode, int cfg); +static int aic3xxx_cfw_setcfg_u(struct cfw_state *ps, int cfg); +static int aic3xxx_cfw_transition_u(struct cfw_state *ps, char *ttype); +static int aic3xxx_cfw_set_pll_u(struct cfw_state *ps, int asi); +static int aic3xxx_cfw_control_u(struct cfw_state *ps, char *cname, int param); + + +#if defined(AIC3XXX_CFW_HOST_BLD) + +static int mutex_init(struct mutex *m) +{ + m->lock = 0; + return 0; +} + +static int mutex_lock(struct mutex *m) +{ + assert(m->lock == 0); + m->lock = 1; + return 0; +} + +static int mutex_unlock(struct mutex *m) +{ + assert(m->lock == 1); + m->lock = 0; + return 0; +} +/* +static void mdelay(int val) +{ + int i; + for (i = 0; i < (val * 10); i++); +} +*/ +#endif + +int aic3xxx_cfw_init(struct cfw_state *ps, struct aic3xxx_codec_ops const *ops, + void *ops_obj) +{ + ps->ops = ops; + ps->ops_obj = ops_obj; + ps->pjt = NULL; + mutex_init(&ps->mutex); + return 0; +} + +int aic3xxx_cfw_lock(struct cfw_state *ps, int lock) +{ + if (lock) + mutex_lock(&ps->mutex); + else + mutex_unlock(&ps->mutex); + return 0; +} + +int aic3xxx_cfw_reload(struct cfw_state *ps, void *pcfw, int n) +{ + ps->pjt = aic3xxx_cfw_unpickle(pcfw, n); + ps->cur_mode_id = + ps->cur_mode = ps->cur_pfw = ps->cur_ovly = ps->cur_cfg = -1; + if (ps->pjt == NULL) + return -1; + return 0; +} + +int aic3xxx_cfw_setmode(struct cfw_state *ps, int mode) +{ + struct cfw_project *pjt; + int ret; + + aic3xxx_cfw_lock(ps, 1); + pjt = ps->pjt; + if (pjt == NULL) { + aic3xxx_cfw_lock(ps, 0); + return -1; + } + ret = aic3xxx_cfw_setmode_cfg_u(ps, mode, pjt->mode[mode]->cfg); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +int aic3xxx_cfw_setcfg(struct cfw_state *ps, int cfg) +{ + int ret; + + aic3xxx_cfw_lock(ps, 1); + ret = aic3xxx_cfw_setcfg_u(ps, cfg); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +static int aic3xxx_cfw_setcfg_u(struct cfw_state *ps, int cfg) +{ + struct cfw_project *pjt = ps->pjt; + struct cfw_pfw *pfw; + + if (pjt == NULL) + return -1; + if (ps->cur_pfw < 0 || ps->cur_pfw >= pjt->npfw) + return -1; + if (ps->cur_cfg == cfg) + return 0; + pfw = pjt->pfw[ps->cur_pfw]; + if (pfw->ncfg == 0 && cfg != 0) + return -1; + if (cfg > 0 && cfg >= pfw->ncfg) + return -1; + ps->cur_cfg = cfg; + aic3xxx_cfw_set_mode_id(ps); + if (pfw->ncfg != 0) + return aic3xxx_cfw_dlcfg(ps, + pfw->ovly_cfg[ps->cur_ovly][ps-> + cur_cfg]); + return 0; +} + +int aic3xxx_cfw_setmode_cfg(struct cfw_state *ps, int mode, int cfg) +{ + int ret; + + aic3xxx_cfw_lock(ps, 1); + ret = aic3xxx_cfw_setmode_cfg_u(ps, mode, cfg); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +static int aic3xxx_cfw_setmode_cfg_u(struct cfw_state *ps, int mode, int cfg) +{ + struct cfw_project *pjt = ps->pjt; + int which = 0; + struct cfw_pfw *pfw; + struct cfw_image *im; + + if (pjt == NULL) + return -1; + if ((mode < 0) || (mode >= pjt->nmode)) + return -1; + if (cfg < 0) + return -1; + if (mode == ps->cur_mode) + return aic3xxx_cfw_setcfg_u(ps, cfg); + + /* Apply exit sequence for previous mode if present */ + if (ps->cur_mode >= 0 && pjt->mode[ps->cur_mode]->exit) + aic3xxx_cfw_dlcmds(ps, pjt->mode[ps->cur_mode]->exit); + + if (pjt->mode[mode]->pfw < pjt->npfw) { + /* New mode uses miniDSP */ + pfw = pjt->pfw[pjt->mode[mode]->pfw]; + /* Make sure cfg is valid and supported in this mode */ + if (pfw->ncfg == 0 && cfg != 0) + return -1; + if (cfg > 0 && cfg >= pfw->ncfg) + return -1; + /* + * Decisions about which miniDSP to stop/restart are taken + * on the basis of sections present in the _base_ image + * This allows for correct sync mode operation even in cases + * where the base PFW uses both miniDSPs where a particular + * overlay applies only to one + */ + im = pfw->base; + if (im->block[CFW_BLOCK_A_INST]) + which |= AIC3XX_COPS_MDSP_A; + if (im->block[CFW_BLOCK_D_INST]) + which |= AIC3XX_COPS_MDSP_D; + + /* New mode requires different PFW */ + if (pjt->mode[mode]->pfw != ps->cur_pfw) { + ps->cur_pfw = pjt->mode[mode]->pfw; + ps->cur_ovly = 0; + ps->cur_cfg = 0; + + which = ps->ops->stop(ps->ops_obj, which); + aic3xxx_cfw_dlimage(ps, im); + if (pjt->mode[mode]->ovly + && pjt->mode[mode]->ovly < pfw->novly) { + /* New mode uses ovly */ + if (pfw->ovly_cfg[pjt->mode[mode] + ->ovly][cfg] != NULL) + aic3xxx_cfw_dlimage(ps, + pfw->ovly_cfg[pjt-> + mode[mode]-> + ovly][cfg]); + } else if (pfw->ncfg > 0) { + /* new mode needs only a cfg change */ + aic3xxx_cfw_dlimage(ps, pfw->ovly_cfg[0][cfg]); + } + ps->ops->restore(ps->ops_obj, which); + + } else if (pjt->mode[mode]->ovly != ps->cur_ovly) { + /* New mode requires only an ovly change */ + which = ps->ops->stop(ps->ops_obj, which); + aic3xxx_cfw_dlimage(ps, + pfw->ovly_cfg[pjt->mode[mode]-> + ovly][cfg]); + ps->ops->restore(ps->ops_obj, which); + } else if (pfw->ncfg > 0 && cfg != ps->cur_cfg) { + /* New mode requires only a cfg change */ + aic3xxx_cfw_dlcfg(ps, + pfw->ovly_cfg[pjt->mode[mode]-> + ovly][cfg]); + } + ps->cur_ovly = pjt->mode[mode]->ovly; + ps->cur_cfg = cfg; + + ps->cur_mode = mode; + aic3xxx_cfw_set_pll_u(ps, 0); + + } else if (pjt->mode[mode]->pfw != 0xFF) { + warn("Bad pfw setting detected (%d). Max pfw=%d", + pjt->mode[mode]->pfw, pjt->npfw); + } + ps->cur_mode = mode; + aic3xxx_cfw_set_mode_id(ps); + /* Transition to netural mode */ + aic3xxx_cfw_transition_u(ps, "NEUTRAL"); + /* Apply entry sequence if present */ + if (pjt->mode[mode]->entry) + aic3xxx_cfw_dlcmds(ps, pjt->mode[mode]->entry); + DBG("setmode_cfg: DONE (mode=%d pfw=%d ovly=%d cfg=%d)", ps->cur_mode, + ps->cur_pfw, ps->cur_ovly, ps->cur_cfg); + return 0; +} + +int aic3xxx_cfw_transition(struct cfw_state *ps, char *ttype) +{ + int ret; + + aic3xxx_cfw_lock(ps, 1); + ret = aic3xxx_cfw_transition_u(ps, ttype); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +static int aic3xxx_cfw_transition_u(struct cfw_state *ps, char *ttype) +{ + int i; + + if (ps->pjt == NULL) + return -1; + for (i = 0; i < CFW_TRN_N; ++i) { + if (!strcasecmp(ttype, cfw_transition_id[i])) { + DBG("Sending transition %s[%d]", ttype, i); + if (ps->pjt->transition[i]) { + aic3xxx_cfw_dlcmds(ps, + ps->pjt->transition[i]-> + block); + } + return 0; + } + } + warn("Transition %s not present or invalid", ttype); + return 0; +} + +int aic3xxx_cfw_set_pll(struct cfw_state *ps, int asi) +{ + int ret; + + aic3xxx_cfw_lock(ps, 1); + ret = aic3xxx_cfw_set_pll_u(ps, asi); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +static int aic3xxx_cfw_set_pll_u(struct cfw_state *ps, int asi) +{ + struct cfw_project *pjt = ps->pjt; + struct cfw_pfw *pfw; + + if (pjt == NULL) + return -1; + if (ps->cur_mode < 0) + return -EINVAL; + pfw = pjt->pfw[pjt->mode[ps->cur_mode]->pfw]; + if (pfw->pll) { + DBG("Configuring PLL for ASI%d using PFW%d", asi, + pjt->mode[ps->cur_mode]->pfw); + aic3xxx_cfw_dlcmds(ps, pfw->pll); + } + return 0; +} + +int aic3xxx_cfw_control(struct cfw_state *ps, char *cname, int param) +{ + int ret; + + aic3xxx_cfw_lock(ps, 1); + ret = aic3xxx_cfw_control_u(ps, cname, param); + aic3xxx_cfw_lock(ps, 0); + return ret; +} + +static int aic3xxx_cfw_control_u(struct cfw_state *ps, char *cname, int param) +{ + struct cfw_pfw *pfw; + int i; + + if (ps->cur_pfw < 0 || ps->cur_pfw >= ps->pjt->npfw) { + warn("Not in MiniDSP mode"); + return 0; + } + pfw = ps->pjt->pfw[ps->cur_pfw]; + for (i = 0; i < pfw->nctrl; ++i) { + if (!strcasecmp(cname, pfw->ctrl[i]->name)) { + struct cfw_control *pc = pfw->ctrl[i]; + if (param < 0 || param > pc->imax) { + warn("Parameter out of range\n"); + return -EINVAL; + } + DBG("Sending control %s[%d]", cname, param); + pc->icur = param; + aic3xxx_cfw_dlctl(ps, pc->output[param], + pc->mute_flags); + return 0; + } + } + warn("Control named %s nort found in pfw %s", cname, pfw->name); + + return 0; +} + +static void aic3xxx_cfw_dlcmds(struct cfw_state *ps, struct cfw_block *pb) +{ + int i = 0, lock = 0; + + while (i < pb->ncmds) { + if (CFW_BLOCK_BURSTS(pb->type)) + ps->ops->bulk_write(ps->ops_obj, + pb->cmd[i].burst->reg.bpod, + pb->cmd[i].burst->length, + pb->cmd[i].burst->data); + else { + struct cfw_meta_delay d = pb->cmd[i].reg.meta.delay; + struct cfw_meta_bitop b = pb->cmd[i].reg.meta.bitop; + switch (pb->cmd[i].reg.meta.mcmd) { + case CFW_META_DELAY: + mdelay(d.delay); + break; + case CFW_META_UPDTBITS: + ps->ops->set_bits(ps->ops_obj, + pb->cmd[i + 1].reg.bpod, + b.mask, + pb->cmd[i + 1].reg.data); + i++; + break; + case CFW_META_WAITBITS: + aic3xxx_wait(ps, pb->cmd[i + 1].reg.bpod, + b.mask, pb->cmd[i + 1].reg.data); + i++; + break; + case CFW_META_LOCK: + if (d.delay) { + ps->ops->lock(ps->ops_obj); + lock = 1; + } else { + if (!lock) + error("already lock\n"); + ps->ops->unlock(ps->ops_obj); + lock = 0; + } + break; + default: + ps->ops->reg_write(ps->ops_obj, + pb->cmd[i].reg.bpod, + pb->cmd[i].reg.data); + } + } + ++i; + } + if (lock) + error("exiting blkcmds with lock ON"); +} + +static void aic3xxx_wait(struct cfw_state *ps, unsigned int reg, u8 mask, + u8 data) +{ + while ((ps->ops->reg_read(ps->ops_obj, reg) & mask) != data) + mdelay(2); +} + +static const struct { + u32 mdsp; + int buf_a, buf_b; + u32 swap; +} csecs[] = { + { + .mdsp = AIC3XX_COPS_MDSP_A, + .swap = AIC3XX_ABUF_MDSP_A, + .buf_a = CFW_BLOCK_A_A_COEF, + .buf_b = CFW_BLOCK_A_B_COEF + }, + { + .mdsp = AIC3XX_COPS_MDSP_D, + .swap = AIC3XX_ABUF_MDSP_D1, + .buf_a = CFW_BLOCK_D_A1_COEF, + .buf_b = CFW_BLOCK_D_B1_COEF + }, + { + .mdsp = AIC3XX_COPS_MDSP_D, + .swap = AIC3XX_ABUF_MDSP_D2, + .buf_a = CFW_BLOCK_D_A2_COEF, + .buf_b = CFW_BLOCK_D_B2_COEF + }, +}; + +static int aic3xxx_cfw_dlctl(struct cfw_state *ps, struct cfw_block *pb, + u32 mute_flags) +{ + int i, btype = CFW_BLOCK_TYPE(pb->type); + int run_state = ps->ops->lock(ps->ops_obj); + + DBG("Download CTL"); + for (i = 0; i < sizeof(csecs) / sizeof(csecs[0]); ++i) { + if (csecs[i].buf_a == btype || csecs[i].buf_b == btype) { + DBG("\tDownload once to %d", btype); + aic3xxx_cfw_dlcmds(ps, pb); + if (run_state & csecs[i].mdsp) { + DBG("Download again %d", btype); + aic3xxx_cfw_mute(ps, 1, run_state & mute_flags); + ps->ops->bswap(ps->ops_obj, csecs[i].swap); + aic3xxx_cfw_mute(ps, 0, run_state & mute_flags); + aic3xxx_cfw_dlcmds(ps, pb); + } + break; + } + } + ps->ops->unlock(ps->ops_obj); + return 0; +} + +static int aic3xxx_cfw_dlcfg(struct cfw_state *ps, struct cfw_image *pim) +{ + int i, run_state, swap; + + DBG("Download CFG %s", pim->name); + run_state = ps->ops->lock(ps->ops_obj); + swap = 0; + for (i = 0; i < sizeof(csecs) / sizeof(csecs[0]); ++i) { + if (pim->block[csecs[i].buf_a]) { + if (run_state & csecs[i].mdsp) { + aic3xxx_cfw_dlcmds(ps, + pim->block[csecs[i].buf_a]); + swap |= csecs[i].swap; + } else { + aic3xxx_cfw_dlcmds(ps, + pim->block[csecs[i].buf_a]); + aic3xxx_cfw_dlcmds(ps, + pim->block[csecs[i].buf_b]); + } + } + } + if (swap) { + aic3xxx_cfw_mute(ps, 1, run_state & pim->mute_flags); + ps->ops->bswap(ps->ops_obj, swap); + aic3xxx_cfw_mute(ps, 0, run_state & pim->mute_flags); + for (i = 0; i < sizeof(csecs) / sizeof(csecs[0]); ++i) { + if (pim->block[csecs[i].buf_a]) { + if (run_state & csecs[i].mdsp) + aic3xxx_cfw_dlcmds(ps, + pim->block[csecs[i]. + buf_a]); + } + } + } + ps->ops->unlock(ps->ops_obj); + return 0; +} + +static int aic3xxx_cfw_dlimage(struct cfw_state *ps, struct cfw_image *pim) +{ + int i; + + DBG("Download IMAGE %s", pim->name); + for (i = 0; i < CFW_BLOCK_N; ++i) + if (pim->block[i]) + aic3xxx_cfw_dlcmds(ps, pim->block[i]); + return 0; +} + +static int aic3xxx_cfw_mute(struct cfw_state *ps, int mute, u32 flags) +{ + if ((flags & AIC3XX_COPS_MDSP_D) && (flags & AIC3XX_COPS_MDSP_A)) + aic3xxx_cfw_transition_u(ps, mute ? "AD_MUTE" : "AD_UNMUTE"); + else if (flags & AIC3XX_COPS_MDSP_D) + aic3xxx_cfw_transition_u(ps, mute ? "D_MUTE" : "D_UNMUTE"); + else if (flags & AIC3XX_COPS_MDSP_A) + aic3xxx_cfw_transition_u(ps, mute ? "A_MUTE" : "A_UNMUTE"); + return 0; +} + +#define FW_NDX2PTR(x, b) do { \ +x = (void *)((u8 *)(b) + ((int)(x))); \ +} while (0) + +static void aic3xxx_cfw_unpickle_block(struct cfw_block *pb, void *p) +{ + int i; + + if (CFW_BLOCK_BURSTS(pb->type)) + for (i = 0; i < pb->ncmds; ++i) + FW_NDX2PTR(pb->cmd[i].burst, p); +} + +static void aic3xxx_cfw_unpickle_image(struct cfw_image *im, void *p) +{ + int i; + for (i = 0; i < CFW_BLOCK_N; ++i) + if (im->block[i]) { + FW_NDX2PTR(im->block[i], p); + aic3xxx_cfw_unpickle_block(im->block[i], p); + } +} + +static void aic3xxx_cfw_unpickle_control(struct cfw_control *ct, void *p) +{ + int i; + FW_NDX2PTR(ct->output, p); + for (i = 0; i <= ct->imax; ++i) { + FW_NDX2PTR(ct->output[i], p); + aic3xxx_cfw_unpickle_block(ct->output[i], p); + } +} +#ifndef AIC3XXX_CFW_HOST_BLD +static +#endif +unsigned int crc32(unsigned int *pdata, int n) +{ + u32 crc = 0, i, crc_poly = 0x04C11DB7; /* CRC - 32 */ + u32 msb; + u32 residue_value; + int bits; + + for (i = 0; i < (n >> 2); i++) { + bits = 32; + while (--bits >= 0) { + msb = crc & 0x80000000; + crc = (crc << 1) ^ ((*pdata >> bits) & 1); + if (msb) + crc = crc ^ crc_poly; + } + pdata++; + } + + switch (n & 3) { + case 0: + break; + case 1: + residue_value = (*pdata & 0xFF); + bits = 8; + break; + case 2: + residue_value = (*pdata & 0xFFFF); + bits = 16; + break; + case 3: + residue_value = (*pdata & 0xFFFFFF); + bits = 24; + break; + } + + if (n & 3) { + while (--bits >= 0) { + msb = crc & 0x80000000; + crc = (crc << 1) ^ ((residue_value >> bits) & 1); + if (msb) + crc = crc ^ crc_poly; + } + } + return crc; +} + +static int crc_chk(void *p, int n) +{ + struct cfw_project *pjt = (void *)p; + u32 crc = pjt->cksum, crc_comp; + + pjt->cksum = 0; + DBG("Entering crc %d", n); + crc_comp = crc32(p, n); + if (crc_comp != crc) { + DBG("CRC mismatch 0x%08X != 0x%08X", crc, crc_comp); + return 0; + } + DBG("CRC pass"); + pjt->cksum = crc; + return 1; +} +#ifndef AIC3XXX_CFW_HOST_BLD +static +#endif +struct cfw_project *aic3xxx_cfw_unpickle(void *p, int n) +{ + struct cfw_project *pjt = p; + int i, j, k; + + if (pjt->magic != CFW_FW_MAGIC || + pjt->size != n || pjt->bmagic != CFW_FW_VERSION || + !crc_chk(p, n)) { + error + ("magic:0x%08X!=0x%08X || size:%d!=%d ||version:0x%08X!=0x%08X", + pjt->magic, CFW_FW_MAGIC, pjt->size, n, pjt->cksum, + CFW_FW_VERSION); + + return NULL; + } + DBG("Loaded firmware inside unpickle\n"); + + for (i = 0; i < CFW_MAX_TRANSITIONS; i++) { + if (pjt->transition[i]) { + FW_NDX2PTR(pjt->transition[i], p); + FW_NDX2PTR(pjt->transition[i]->block, p); + aic3xxx_cfw_unpickle_block(pjt->transition[i]->block, + p); + } + } + + for (i = 0; i < pjt->npfw; i++) { + DBG("loading pfw %d\n", i); + FW_NDX2PTR(pjt->pfw[i], p); + if (pjt->pfw[i]->base) { + FW_NDX2PTR(pjt->pfw[i]->base, p); + aic3xxx_cfw_unpickle_image(pjt->pfw[i]->base, p); + } + if (pjt->pfw[i]->pll) { + FW_NDX2PTR(pjt->pfw[i]->pll, p); + aic3xxx_cfw_unpickle_block(pjt->pfw[i]->pll, p); + } + for (j = 0; j < pjt->pfw[i]->novly; ++j) + for (k = 0; k < pjt->pfw[i]->ncfg; ++k) { + FW_NDX2PTR(pjt->pfw[i]->ovly_cfg[j][k], p); + aic3xxx_cfw_unpickle_image(pjt->pfw[i]-> + ovly_cfg[j][k], p); + } + for (j = 0; j < pjt->pfw[i]->nctrl; ++j) { + FW_NDX2PTR(pjt->pfw[i]->ctrl[j], p); + aic3xxx_cfw_unpickle_control(pjt->pfw[i]->ctrl[j], p); + } + } + + DBG("loaded pfw's\n"); + for (i = 0; i < pjt->nmode; i++) { + FW_NDX2PTR(pjt->mode[i], p); + if (pjt->mode[i]->entry) { + FW_NDX2PTR(pjt->mode[i]->entry, p); + aic3xxx_cfw_unpickle_block(pjt->mode[i]->entry, p); + } + if (pjt->mode[i]->exit) { + FW_NDX2PTR(pjt->mode[i]->exit, p); + aic3xxx_cfw_unpickle_block(pjt->mode[i]->exit, p); + } + } + if (pjt->asoc_toc) + FW_NDX2PTR(pjt->asoc_toc, p); + else { + warn("asoc_toc not defined. FW version mismatch?"); + return NULL; + } + DBG("loaded modes"); + return pjt; +} + +#ifndef AIC3XXX_CFW_HOST_BLD +static int aic3xxx_get_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cfw_state *ps = + (struct cfw_state *) kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cfw_pfw *pfw; + int i; + + if (ps->cur_pfw >= ps->pjt->npfw) { + DBG("Not in MiniDSP mode"); + return 0; + } + pfw = ps->pjt->pfw[ps->cur_pfw]; + for (i = 0; i < pfw->nctrl; ++i) { + if (!strcasecmp(kcontrol->id.name, pfw->ctrl[i]->name)) { + struct cfw_control *pc = pfw->ctrl[i]; + ucontrol->value.integer.value[0] = pc->icur; + return 0; + } + } + return 0; +} + +static int aic3xxx_put_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cfw_state *ps = + (struct cfw_state *) kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + + aic3xxx_cfw_control(ps, kcontrol->id.name, + ucontrol->value.integer.value[0]); + return 0; +} + +static int aic3xxx_info_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct cfw_state *ps = + (struct cfw_state *) kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cfw_pfw *pfw; + int i; + + if (ps->cur_pfw >= ps->pjt->npfw) { + DBG("Not in MiniDSP mode"); + return 0; + } + pfw = ps->pjt->pfw[ps->cur_pfw]; + for (i = 0; i < pfw->nctrl; ++i) { + if (!strcasecmp(kcontrol->id.name, pfw->ctrl[i]->name)) { + struct cfw_control *pc = pfw->ctrl[i]; + ucontrol->value.integer.min = 0; + ucontrol->value.integer.max = pc->imax; + if (pc->imax == 1) + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + ucontrol->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + } + } + + ucontrol->count = 1; + return 0; +} +#endif +int aic3xxx_cfw_add_controls(struct snd_soc_codec *codec, struct cfw_state *ps) +{ + int i, j; + struct cfw_pfw *pfw; + + for (j = 0; j < ps->pjt->npfw; ++j) { + pfw = ps->pjt->pfw[j]; + + for (i = 0; i < pfw->nctrl; ++i) { + struct cfw_control *pc = pfw->ctrl[i]; +#ifndef AIC3XXX_CFW_HOST_BLD + struct snd_kcontrol_new *generic_control = + kzalloc(sizeof(struct snd_kcontrol_new), + GFP_KERNEL); + unsigned int *tlv_array = + kzalloc(4 * sizeof(unsigned int), GFP_KERNEL); + + if (generic_control == NULL) + return -ENOMEM; + generic_control->access = + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + tlv_array[0] = SNDRV_CTL_TLVT_DB_SCALE; + tlv_array[1] = 2 * sizeof(unsigned int); + tlv_array[2] = pc->min; + tlv_array[3] = ((pc->step) & TLV_DB_SCALE_MASK); + if (pc->step > 0) + generic_control->tlv.p = tlv_array; + generic_control->name = pc->name; + generic_control->private_value = (unsigned long) ps; + generic_control->get = aic3xxx_get_control; + generic_control->put = aic3xxx_put_control; + generic_control->info = aic3xxx_info_control; + generic_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; +#endif + DBG("Adding control %s", pc->name); +#ifndef AIC3XXX_CFW_HOST_BLD + snd_soc_add_controls(codec, generic_control, 1); +#endif + } + } + return 0; + +} + +static int aic3xxx_cfw_set_mode_id(struct cfw_state *ps) +{ + struct cfw_asoc_toc *toc = ps->pjt->asoc_toc; + int i; + + for (i = 0; i < toc->nentries; ++i) { + if (toc->entry[i].cfg == ps->cur_cfg && + toc->entry[i].mode == ps->cur_mode) { + ps->cur_mode_id = i; + return 0; + } + } + DBG("Unknown mode, cfg combination [%d, %d]", + ps->cur_mode, ps->cur_cfg); + return -1; +} +#ifndef AIC3XXX_CFW_HOST_BLD +static int aic3xxx_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct cfw_state *ps = (struct cfw_state *)e->mask; + + ucontrol->value.enumerated.item[0] = ps->cur_mode_id; + + return 0; +} +#endif +#ifndef AIC3XXX_CFW_HOST_BLD +static int aic3xxx_put_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct cfw_state *ps = (struct cfw_state *)e->mask; + struct cfw_asoc_toc *toc; + int index, ret; + + aic3xxx_cfw_lock(ps, 1); + toc = ps->pjt->asoc_toc; + + index = ucontrol->value.enumerated.item[0]; + if (index < 0 || index >= toc->nentries) { + aic3xxx_cfw_lock(ps, 0); + return -EINVAL; + } + ret = + aic3xxx_cfw_setmode_cfg_u(ps, toc->entry[index].mode, + toc->entry[index].cfg); + aic3xxx_cfw_lock(ps, 0); + return ret; +} +#endif + +int aic3xxx_cfw_add_modes(struct snd_soc_codec *codec, struct cfw_state *ps) +{ +#ifndef AIC3XXX_CFW_HOST_BLD + int j; + struct cfw_asoc_toc *toc = ps->pjt->asoc_toc; + struct soc_enum *mode_cfg_enum = + kzalloc(sizeof(struct soc_enum), GFP_KERNEL); + struct snd_kcontrol_new *mode_cfg_control = + kzalloc(sizeof(struct snd_kcontrol_new), GFP_KERNEL); + char **enum_texts; + + if (mode_cfg_enum == NULL) + goto mem_err; + if (mode_cfg_control == NULL) + goto mem_err; + + mode_cfg_enum->texts = + kzalloc(toc->nentries * sizeof(char *), GFP_KERNEL); + if (mode_cfg_enum->texts == NULL) + goto mem_err; + /* Hack to overwrite the const * const pointer */ + enum_texts = (char **)mode_cfg_enum->texts; + + for (j = 0; j < toc->nentries; j++) + enum_texts[j] = toc->entry[j].etext; + mode_cfg_enum->reg = j; + mode_cfg_enum->max = toc->nentries; + mode_cfg_enum->mask = (unsigned int)ps; + mode_cfg_control->name = "Codec Firmware Setmode"; + mode_cfg_control->get = aic3xxx_get_mode; + mode_cfg_control->put = aic3xxx_put_mode; + mode_cfg_control->info = snd_soc_info_enum_ext; + mode_cfg_control->private_value = (unsigned long)mode_cfg_enum; + mode_cfg_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + snd_soc_add_controls(codec, mode_cfg_control, 1); + return 0; +mem_err: + kfree(mode_cfg_control); + kfree(mode_cfg_enum); + kfree(mode_cfg_enum->texts); + return -ENOMEM; +#else + return 0; +#endif + +} diff --git a/sound/soc/codecs/aic3xxx_cfw_ops.h b/sound/soc/codecs/aic3xxx_cfw_ops.h new file mode 100644 index 000000000000..81f6bda3581b --- /dev/null +++ b/sound/soc/codecs/aic3xxx_cfw_ops.h @@ -0,0 +1,76 @@ +#ifndef AIC3XXX_CFW_OPS_H_ +#define AIC3XXX_CFW_OPS_H_ +#define DEBUG +#ifdef AIC3XXX_CFW_HOST_BLD +struct mutex { + int lock; +}; +#endif + +struct cfw_state { + struct cfw_project *pjt; + struct aic3xxx_codec_ops const *ops; + void *ops_obj; + struct mutex mutex; + int cur_mode_id; + int cur_mode; + int cur_pfw; + int cur_ovly; + int cur_cfg; +}; + +#ifdef AIC3XXX_CFW_HOST_BLD +struct cfw_project *aic3xxx_cfw_unpickle(void *pcfw, int n); +unsigned int crc32(unsigned int *pdata, int n); +struct snd_soc_codec; +#else +#ifdef DEBUG +#define DBG(fmt, ...) printk(KERN_DEBUG "CFW[%s:%d]: " fmt "\n", \ + __FILE__, __LINE__, ##__VA_ARGS__) +#else + #define DBG(fmt, ...) +#endif +#endif +int aic3xxx_cfw_init(struct cfw_state *ps, struct aic3xxx_codec_ops const *ops, + void *ops_obj); +int aic3xxx_cfw_lock(struct cfw_state *ps, int lock); +int aic3xxx_cfw_reload(struct cfw_state *ps, void *pcfw, int n); +int aic3xxx_cfw_setmode(struct cfw_state *ps, int mode); +int aic3xxx_cfw_setmode_cfg(struct cfw_state *ps, int mode, int cfg); +int aic3xxx_cfw_setcfg(struct cfw_state *ps, int cfg); +int aic3xxx_cfw_transition(struct cfw_state *ps, char *ttype); +int aic3xxx_cfw_set_pll(struct cfw_state *ps, int asi); +int aic3xxx_cfw_control(struct cfw_state *ps, char *cname, int param); +int aic3xxx_cfw_add_controls(struct snd_soc_codec *codec, struct cfw_state *ps); +int aic3xxx_cfw_add_modes(struct snd_soc_codec *codec, struct cfw_state *ps); + + +#define AIC3XX_COPS_MDSP_D (0x00000003u) +#define AIC3XX_COPS_MDSP_A (0x00000030u) +#define AIC3XX_COPS_MDSP_ALL (AIC3XX_COPS_MDSP_D|AIC3XX_COPS_MDSP_A) + +#define AIC3XX_ABUF_MDSP_D1 (0x00000001u) +#define AIC3XX_ABUF_MDSP_D2 (0x00000002u) +#define AIC3XX_ABUF_MDSP_A (0x00000010u) +#define AIC3XX_ABUF_MDSP_ALL \ + (AIC3XX_ABUF_MDSP_D1 | AIC3XX_ABUF_MDSP_D2 | AIC3XX_ABUF_MDSP_A) + +struct aic3xxx_codec_ops { + int (*reg_read)(void *p, unsigned int reg); + int (*reg_write)(void *p, unsigned int reg, + unsigned int val); + int (*set_bits)(void *p, unsigned int reg, + unsigned char mask, unsigned char val); + int (*bulk_read)(void *p, unsigned int reg, + int count, u8 *buf); + int (*bulk_write)(void *p, unsigned int reg, + int count, const u8 *buf); + + int (*lock) (void *p); + int (*unlock) (void *p); + int (*stop) (void *p, int mask); + int (*restore) (void *p, int runstate); + int (*bswap) (void *p, int mask); +}; + +#endif diff --git a/sound/soc/codecs/tlv320aic3262_default_fw.h b/sound/soc/codecs/tlv320aic3262_default_fw.h new file mode 100644 index 000000000000..55606d76f854 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3262_default_fw.h @@ -0,0 +1,330 @@ +unsigned char default_firmware[] = { + 237, 241, 209, 192, 179, 0, 1, 0, 123, 20, 0, 0, 238, 201, 253, 6, + 0, 0, 0, 0, 12, 220, 206, 79, 70, 105, 114, 109, 119, 97, 114, 101, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 13, 0, 0, 8, 2, 0, 0, + 168, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 231, 19, 0, 0, 105, 110, 105, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 73, 110, 105, 116, 105, 97, 108, 105, + 122, 97, 116, 105, 111, 110, 32, 115, 101, 113, 117, 101, 110, + 99, 101, 32, 40, 97, 112, 112, 108, 105, 101, 100, 32, 111, 110, + 108, 121, 32, 111, 110, 32, 115, 121, 115, 116, 101, 109, 32, 98, + 111, 111, 116, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 236, 5, 0, 0, 0, 0, 0, 0, + 7, 0, 0, 0, 100, 0, 128, 0, 0, 1, 1, 0, 0, 1, 122, 1, + 0, 0, 4, 51, 0, 0, 82, 0, 0, 0, 67, 151, 0, 1, 119, 127, + 100, 101, 102, 97, 117, 108, 116, 0, 98, 97, 115, 101, 95, 109, 97, + 105, 110, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 53, 12, 0, 0, 172, 9, 0, 0, 133, 12, 0, 0, + 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 97, + 117, 108, 116, 47, 98, 97, 115, 101, 95, 109, 97, 105, 110, 95, 82, 97, + 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 0, 1, 0, 0, 0, 44, 12, 0, 0, 2, 0, 0, 0, + 0, 0, 60, 1, 1, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 129, + 112, 0, 0, 6, 16, 0, 0, 129, 15, 0, 0, 6, 1, 0, 0, 7, + 8, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 129, 127, 0, 0, 11, + 2, 0, 0, 129, 127, 0, 0, 12, 8, 0, 0, 129, 127, 0, 0, 18, + 2, 0, 0, 129, 127, 0, 0, 19, 16, 0, 0, 20, 64, 0, 0, 13, + 0, 0, 0, 14, 128, 98, 97, 115, 101, 95, 109, 97, 105, 110, 0, 116, + 99, 104, 95, 98, 97, 115, 101, 95, 109, 97, 105, 110, 95, 82, 97, 116, + 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, + 0, 5, 15, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, 98, 97, + 115, 101, 95, 115, 112, 107, 0, 97, 116, 99, 104, 95, 98, 97, 115, 101, + 95, 115, 112, 107, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 130, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, 142, 17, 0, 0, 2, 0, + 0, 0, 0, 0, 60, 1, 1, 100, 101, 102, 97, 117, 108, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 100, 101, 102, 97, 117, + 108, 116, 32, 98, 97, 115, 101, 95, 109, 97, 105, 110, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 100, 101, 102, 97, 117, 108, 116, 32, 98, 97, 115, 101, 95, + 115, 112, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, +}; diff --git a/sound/soc/codecs/tlv320aic326x.c b/sound/soc/codecs/tlv320aic326x.c index 5d6b92904d56..06f813bbc0ca 100644 --- a/sound/soc/codecs/tlv320aic326x.c +++ b/sound/soc/codecs/tlv320aic326x.c @@ -1,636 +1,198 @@ /* -* linux/sound/soc/codecs/tlv320aic3262.c -* -* Copyright (C) 2012 Texas Instruments, Inc. -* -* Based on sound/soc/codecs/tlv320aic3262.c -* -* 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 20-01-2011 -* -* The AIC325x ASoC driver is ported for the codec AIC3262. -* Rev 0.2 ASoC driver support 21-03-2011 -* The AIC326x ASoC driver is updated abe changes. -* -* Rev 0.3 ASoC driver support 12.09.2011 -* fixed the compilation issues for Whistler support -* -* Rev 0.4 ASoC driver support 27.09.2011 -* The AIC326x driver ported for Nvidia cardhu. -* -* Rev 0.5 Modified to support Multiple ASI Ports 08-Nov-2011 -* Driver updated to support ASI Ports of AIC3262 -* -* Modified by Nvidia 23-Nov-2011 for K39 ASoC changes. -*/ + * 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 #include -#include #include #include #include #include +#include #include +#include +#include +#include +#include +#include #include +#include +#include + +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" #include "tlv320aic326x.h" -#include -/* - ***************************************************************************** - * Global Variable - ***************************************************************************** - */ -static u8 aic3262_reg_ctl; - -#ifdef AIC3262_TiLoad - extern int aic3262_driver_init(struct snd_soc_codec *codec); -#endif - - - -/* whenever aplay/arecord is run, aic3262_hw_params() function gets called. - * This function reprograms the clock dividers etc. this flag can be used to - * disable this when the clock dividers are programmed by pps config file - */ -static struct snd_soc_codec *aic3262_codec; +#include "aic3262_codec_ops.h" +#include "tlv320aic3262_default_fw.h" -/* - ***************************************************************************** - * Macros - ***************************************************************************** - */ -/* ASoC Widget Control definition for a single Register based Control */ -#define SOC_SINGLE_AIC3262(xname) \ -{\ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = __new_control_info, .get = __new_control_get,\ - .put = __new_control_put, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ -} -#define SOC_SINGLE_N(xname, xreg, xshift, xmax, xinvert) \ -{\ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = n_control_info, .get = n_control_get,\ - .put = n_control_put, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .private_value = ((unsigned long)&(struct soc_mixer_control)) \ - {.reg = xreg, .shift = xshift, .rshift = xshift, .max = xmax, \ - .invert = xinvert} } - -/* ASoC Widget Control definition for a Double Register based Control */ - -#define SOC_DOUBLE_R_N(xname, reg_left, reg_right, xshift, xmax, xinvert) \ +#define SOC_DOUBLE_R_SX_TLV3262(xname, xreg_left, xreg_right, xshift,\ + xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw_2r_n, \ - .get = snd_soc_get_volsw_2r_n, .put = snd_soc_put_volsw_2r_n, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ - .max = xmax, .invert = xinvert} } - -#define SND_SOC_DAPM_SWITCH_N(wname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift,\ - .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} -/* - ***************************************************************************** - * Function Prototype - ***************************************************************************** - */ -static int aic3262_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level); - -static int __new_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); - -static int __new_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -static int __new_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -static inline int aic3262_get_divs(int mclk, int rate); - -static int aic3262_multi_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai); - -static int aic3262_multi_i2s_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir); -static int aic3262_multi_i2s_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, int source, unsigned int freq_in, - unsigned int freq_out); - -static int aic3262_multi_i2s_asi1_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt); - -static int aic3262_multi_i2s_asi2_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt); - -static int aic3262_multi_i2s_asi3_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt); - -static int aic3262_multi_i2s_asi1_mute(struct snd_soc_dai *dai, int mute); - -static int aic3262_multi_i2s_asi2_mute(struct snd_soc_dai *dai, int mute); - -static int aic3262_multi_i2s_asi3_mute(struct snd_soc_dai *dai, int mute); - -#if 0 -static const char *wclk1_pincontrol[] = { - "ASI1 Word Clock Input/Output", "CLKOUT output"}; -static const char *dout1_pincontrol[] = { - "disabled", "ASI1 data output", "gpio", "clock out", - "INT1", "INT2", "SAR ADC interrupt"}; - -static const char *din1_pincontrol[] = {"disabled", "enabled"}; + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_2r_sx, \ + .get = snd_soc_get_volsw_2r_sx, \ + .put = snd_soc_put_volsw_2r_sx_aic3262, \ + .private_value = (unsigned long) &(struct soc_mixer_control) \ + {.reg = xreg_left, \ + .rreg = xreg_right, .shift = xshift, \ + .min = xmin, .max = xmax} } -static const char *wclk2_pincontrol[] = { - "diabled", "ASI1 secondary wclk", "general purpose input", - "general purpose output", "clkout", "INT1 interrupt", - "IN2 interrupt", "output digital microphone", - "SAR ADC interrupt", "data output for ASI1"}; +/***************************************************************************** + Macros +****************************************************************************** -static const char *bclk2_pincontrol[] = { - "diabled", "ASI1 secondary wclk", "general purpose input", - "general purpose output", "clkout", "INT1 interrupt", - "IN2 interrupt", "output digital microphone", - "SAR ADC interrupt", "data output for ASI1"}; +****************************************************************************** + Function Prototype +******************************************************************************/ -static const char *dout2_pincontrol[] = { - "disabled", "ASI2 Data Output", "General Purpose Output", - "INT1 Interrupt", "INT2 Interrupt", "SAR ADC interrupt", - "Output for digital microphone", "Data Output for ASI1"}; +static int aic3262_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); -static const char *din2_pincontrol[] = {"disabled", "enabled"}; +static int aic3262_mute(struct snd_soc_dai *dai, int mute); -static const char *wclk3_pincontrol[] = { - "Disabled", "ASI3 WCLK", "General Purpose Input", - "General Purpose output", "Data Output for ASI1"}; +static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir); -static const char *bclk3_pincontrol[] = { - "Disabled", "ASI3 BCLK", "General Purpose Input", - "General Purpose output", "Data Output for ASI1"}; +static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); -static const char *dout3_pincontrol[] = { - "disabled", "ASI3 data ooutput", "General Purpose Output", - "ASI1 Word Clock Output", "Data Output for ASI1"}; +static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int Fin, unsigned int Fout); -static const char *din3_pincontrol[] = {"disabled", "enabled"}; - -static const char *clkin[] = { - "mclk1", "bclk1", "gpio1", "pll_clk", "bclk2", "gpi1", - "hf_ref_clk", "hf_osc_clk", "mclk2", "gpio2", "gpi2"}; - -#endif -#ifdef DAC_INDEPENDENT_VOL -/* - *---------------------------------------------------------------------------- - * Function : n_control_info - * Purpose : This function is to initialize data for new control required to - * program the AIC3262 registers. - * - *---------------------------------------------------------------------------- - */ -static int n_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - - if (max == 1) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = shift == rshift ? 1 : 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} - -/* - *---------------------------------------------------------------------------- - * Function : n_control_get - * Purpose : This function is to read data of new control for - * program the AIC3262 registers. - * - *---------------------------------------------------------------------------- - */ -static int n_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - u32 val; - unsigned short mask, shift; - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - if (!strcmp(kcontrol->id.name, "Left DAC Volume")) { - mask = AIC3262_8BITS_MASK; - shift = 0; - val = snd_soc_read(codec, mc->reg); - ucontrol->value.integer.value[0] = - (val <= 48) ? (val + 127) : (val - 129); - } - if (!strcmp(kcontrol->id.name, "Right DAC Volume")) { - mask = AIC3262_8BITS_MASK; - shift = 0; - val = snd_soc_read(codec, mc->reg); - ucontrol->value.integer.value[0] = - (val <= 48) ? (val + 127) : (val - 129); - } +static int aic3262_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level); - return 0; -} +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); -/* - *---------------------------------------------------------------------------- - * Function : __new_control_put - * Purpose : new_control_put is called to pass data from user/application to - * the driver. - * - *---------------------------------------------------------------------------- - */ -static int n_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - u8 val, val_mask; - int reg, err; - unsigned int invert = mc->invert; - int max = mc->max; - DBG("n_control_put\n"); - reg = mc->reg; - val = ucontrol->value.integer.value[0]; - if (invert) - val = max - val; - if (!strcmp(kcontrol->id.name, "Left DAC Volume")) { - DBG("LDAC\n"); - val = (val >= 127) ? (val - 127) : (val + 129); - val_mask = AIC3262_8BITS_MASK; - } - if (!strcmp(kcontrol->id.name, "Right DAC Volume")) { - DBG("RDAC\n"); - val = (val >= 127) ? (val - 127) : (val + 129); - val_mask = AIC3262_8BITS_MASK; - } +static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); - if (err < 0) { - printk(KERN_ERR "Error while updating bits\n"); - return err; - } +static long debug_level; +module_param(debug_level, long, 0); +MODULE_PARM_DESC(debug_level, "Debug level for printing"); - return 0; -} -#endif /*#ifdef DAC_INDEPENDENT_VOL*/ -/* - *------------------------------------------------------------------------------ - * snd_soc_info_volsw_2r_n - double mixer info callback +/** + * snd_soc_put_volsw_2r_sx - double with tlv and variable data size + * mixer put callback * @kcontrol: mixer control * @uinfo: control element information * - * Callback to provide information about a double mixer control that - * spans 2 codec registers. - * - * Returns 0 for success. - *------------------------------------------------------------------------------ - */ -int snd_soc_info_volsw_2r_n(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - - if (max == 1) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} - -/* - *------------------------------------------------------------------------------ - * snd_soc_get_volsw_2r_n - double mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a double mixer control that spans 2 registers. - * * Returns 0 for success. - *------------------------------------------------------------------------------ */ -int snd_soc_get_volsw_2r_n(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - int max = mc->max; - unsigned int mask; - unsigned int invert = mc->invert; - unsigned short val, val2; - - if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) { - mask = AIC3262_8BITS_MASK; - shift = 0; - } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) { - mask = 0x3F; - shift = 0; - } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { - mask = 0x7F; - shift = 0; - } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) { - mask = 0x3F; - shift = 0; - } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) { - mask = 0x7F; - shift = 0; - } else if (!strcmp(kcontrol->id.name, "MA Volume")) { - mask = 0x7F; - shift = 0; - } else { - printk(KERN_ERR "Invalid kcontrol name\n"); - return -1; - } - - /* Read, update the corresponding Registers */ - val = (snd_soc_read(codec, reg) >> shift) & mask; - val2 = (snd_soc_read(codec, reg2) >> shift) & mask; - - if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) { - ucontrol->value.integer.value[0] = - (val <= 48) ? (val + 127) : (val - 129); - ucontrol->value.integer.value[1] = - (val2 <= 48) ? (val2 + 127) : (val2 - 129); - } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) { - ucontrol->value.integer.value[0] = - (val >= 57) ? (val - 57) : (val + 7); - ucontrol->value.integer.value[1] = - (val2 >= 57) ? (val2 - 57) : (val2 + 7); - } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { - ucontrol->value.integer.value[0] = - (val <= 40) ? (val + 24) : (val - 104); - ucontrol->value.integer.value[1] = - (val2 <= 40) ? (val2 + 24) : (val2 - 104); - } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) { - ucontrol->value.integer.value[0] = ((val >= 0) & (val <= 29)) ? - (val + 7) : (val - 57); - ucontrol->value.integer.value[1] = ((val2 >= 0) & - (val2 <= 29)) ? (val2 + 7) : (val2 - 57); - - } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) { - ucontrol->value.integer.value[0] = ((val >= 0) & (val <= 116)) ? - (val + 1) : ((val == 127) ? (0) : (117)); - ucontrol->value.integer.value[1] = ((val2 >= 0) & (val2 <= 116)) - ? (val2 + 1) : ((val2 == 127) ? (0) : (117)); - - } else if (!strcmp(kcontrol->id.name, "MA Volume")) { - ucontrol->value.integer.value[0] = (val <= 40) ? - (41 - val) : (val = 0); - ucontrol->value.integer.value[1] = (val2 <= 40) ? - (41 - val2) : (val2 = 0); - } - - if (invert) { - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - } - - return 0; -} -/* -*------------------------------------------------------------------------------- -* snd_soc_put_volsw_2r_n - double mixer set callback -* @kcontrol: mixer control -* @ucontrol: control element information -* -* Callback to set the value of a double mixer control that spans 2 registers. -* -* Returns 0 for success. -*------------------------------------------------------------------------------- -*/ -int snd_soc_put_volsw_2r_n(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int snd_soc_put_volsw_2r_sx_aic3262(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - int max = mc->max; - unsigned int mask; - unsigned int invert = mc->invert; - int err; - unsigned short val, val2, val_mask; - - mask = 0x00FF; - - val = (ucontrol->value.integer.value[0] & mask); - val2 = (ucontrol->value.integer.value[1] & mask); - if (invert) { - val = max - val; - val2 = max - val2; - } - - /* Check for the string name of the kcontrol */ - if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) { - val = (val >= 127) ? (val - 127) : (val + 129); - val2 = (val2 >= 127) ? (val2 - 127) : (val2 + 129); - val_mask = AIC3262_8BITS_MASK; /* 8 bits */ - } else if ((!strcmp(kcontrol->id.name, "HP Driver Gain")) || - (!strcmp(kcontrol->id.name, "LO Driver Gain"))) { - val = (val <= 6) ? (val + 57) : (val - 7); - val2 = (val2 <= 6) ? (val2 + 57) : (val2 - 7); - val_mask = 0x3F; /* 6 bits */ - DBG("val=%d, val2=%d", val, val2); - } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { - val = (val >= 24) ? ((val <= 64) ? - (val-24) : (40)) : (val + 104); - val2 = (val2 >= 24) ? - ((val2 <= 64) ? (val2 - 24) : (40)) : (val2 + 104); - val_mask = 0x7F; /* 7 bits */ - } else if (!strcmp(kcontrol->id.name, "LO to REC Volume")) { - - val = (val <= 116) ? - (val % 116) : ((val == 117) ? (127) : (117)); - val2 = (val2 <= 116) ? - (val2 % 116) : ((val2 == 117) ? (127) : (117)); - val_mask = 0x7F; - } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) { - - val = (val <= 7) ? (val + 57) : ((val < 36) ? (val - 7) : (29)); - val2 = (val2 <= 7) ? - (val2 + 57) : ((val2 < 36) ? (val2 - 7) : (29)); - val_mask = 0x3F; - } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) { - - val = ((val > 0) & (val <= 117)) ? - (val - 1) : ((val == 0) ? (127) : (116)); - val2 = ((val2 > 0) & (val2 <= 117)) ? - (val2 - 1) : ((val2 == 0) ? (127) : (116)); - val_mask = 0x7F; - } else if (!strcmp(kcontrol->id.name, "MA Volume")) { - - val = ((val <= 41) & (val > 0)) ? - (41 - val) : ((val > 41) ? (val = 41) : (63)); - val2 = ((val2 <= 41) & (val2 > 0)) ? - (41 - val2) : ((val2 > 41) ? (val2 = 41) : (63)); - val_mask = 0x7F; - } else { - printk(KERN_ERR "Invalid control name\n"); - return -1; - } - - val = val << shift; - val2 = val2 << shift; - - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); - if (err < 0) - return err; - - err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2); - return err; -} + unsigned int mask = (1 << mc->shift) - 1; + int min = mc->min; + int ret; + unsigned int val, valr; -static int __new_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65535; + val = ((ucontrol->value.integer.value[0] + min) & 0xff); + val &= mask; + valr = ((ucontrol->value.integer.value[1] + min) & 0xff); + valr &= mask; + ret = 0; + ret = snd_soc_update_bits_locked(codec, mc->reg, mask, val); + if (ret < 0) + return ret; + ret = snd_soc_update_bits_locked(codec, mc->rreg, mask, valr); + if (ret < 0) + return ret; return 0; } -/* - *---------------------------------------------------------------------------- - * Function : __new_control_get - * Purpose : This function is to read data of new control for - * program the AIC3262 registers. - * - *---------------------------------------------------------------------------- - */ -static int __new_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static ssize_t debug_level_show(struct device *dev, + struct device_attribute *attr, + char *buf, size_t count) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - u32 val; - val = snd_soc_read(codec, aic3262_reg_ctl); - ucontrol->value.integer.value[0] = val; - return 0; + return sprintf(buf, "%ld\n", debug_level); } -/* - *---------------------------------------------------------------------------- - * Function : __new_control_put - * Purpose : new_control_put is called to pass data from user/application to - * the driver. - * - *---------------------------------------------------------------------------- - */ -static int __new_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static ssize_t debug_level_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 data[2]; - int ret = 0; - - u32 data_from_user = ucontrol->value.integer.value[0]; - - aic3262_change_book(codec, 0); - aic3262_reg_ctl = data[0] = (u8) ((data_from_user & 0xFF00) >> 8); - data[1] = (u8) ((data_from_user & 0x00FF)); - - if (!data[0]) - aic3262->page_no = data[1]; + int ret; - DBG("reg = %d val = %x\n", data[0], data[1]); -#if defined(LOCAL_REG_ACCESS) - if (codec->hw_write(codec->control_data, data, 2) != 2) - ret = -EIO; -#else - ret = snd_soc_write(codec, data[0], data[1]); -#endif + ret = kstrtol(buf, 10, &debug_level); if (ret) - printk(KERN_ERR "Error in i2c write\n"); - - return ret; + return ret; + return count; } +static DEVICE_ATTR(debug_level, 0644, debug_level_show, debug_level_set); -/* - ***************************************************************************** - * Structure Initialization - ***************************************************************************** - */ 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, 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 *input_common_mode_text[] = { - "0.9v", "0.75v" }; +static const char * const input_common_mode_text[] = { + "0.9v", "0.75v" +}; -static const char *output_common_mode_text[] = { - "Input CM", "1.25v", "1.5v", "1.65v" }; +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(CM_REG, 2, 2, input_common_mode_text); +SOC_ENUM_SINGLE(AIC3262_CM_REG, 2, 2, input_common_mode_text); static const struct soc_enum output_cm_mode = - SOC_ENUM_SINGLE(CM_REG, 0, 4, output_common_mode_text); - +SOC_ENUM_SINGLE(AIC3262_CM_REG, 0, 4, output_common_mode_text); /* ***************************************************************************** * Structure Initialization @@ -641,2388 +203,874 @@ static const struct snd_kcontrol_new aic3262_snd_controls[] = { #ifndef DAC_INDEPENDENT_VOL /* sound new kcontrol for PCM Playback volume control */ - SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", - DAC_LVOL, DAC_RVOL, 8,0xffffff81, 0x30, dac_vol_tlv), + SOC_DOUBLE_R_SX_TLV3262("PCM Playback Volume", + AIC3262_DAC_LVOL, AIC3262_DAC_RVOL, 8, + 0xffffff81, + 0x30, dac_vol_tlv), #endif - /*HP Driver Gain Control*/ - SOC_DOUBLE_R_SX_TLV("HeadPhone Driver Amplifier Volume", - HPL_VOL, HPR_VOL, 6, 0xfffffffa, 0xe, output_gain_tlv), - - /*LO Driver Gain Control*/ - SOC_DOUBLE_TLV("Speaker Amplifier Volume", - SPK_AMP_CNTL_R4, 4, 0, 5, 0, spk_gain_tlv), - - SOC_DOUBLE_R_SX_TLV("Receiver Amplifier Volume", - REC_AMP_CNTL_R5, RAMPR_VOL, 6, 0xfffffffa, 0x1d, output_gain_tlv), + /*HP Driver Gain Control */ + SOC_DOUBLE_R_SX_TLV3262("HeadPhone Driver Amplifier Volume", + AIC3262_HPL_VOL, AIC3262_HPR_VOL, 6, 0xffffffb9, + 0xffffffce, 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("PCM Capture Volume", - LADC_VOL, RADC_VOL, 7,0xffffff68, 0x24, adc_vol_tlv), + SOC_DOUBLE_R_SX_TLV3262("Receiver Amplifier Volume", + AIC3262_REC_AMP_CNTL_R5, AIC3262_RAMPR_VOL, 6, + 0xffffffb9, 0xffffffd6, output_gain_tlv), + SOC_DOUBLE_R_SX_TLV3262("PCM Capture Volume", AIC3262_LADC_VOL, + AIC3262_RADC_VOL, 7, 0xffffff68, 0xffffffa8, + adc_vol_tlv), - SOC_DOUBLE_R_TLV ("MicPGA Volume Control", - MICL_PGA, MICR_PGA, 0, 0x5F, 0, micpga_gain_tlv), - SOC_DOUBLE_TLV("PCM Capture Fine Gain Volume", - ADC_FINE_GAIN, 4, 0, 5, 1, adc_fine_gain_tlv), + SOC_DOUBLE_R_TLV("MicPGA Volume Control", AIC3262_MICL_PGA, + AIC3262_MICR_PGA, 0, 0x5F, 0, micpga_gain_tlv), - SOC_DOUBLE("ADC channel mute", ADC_FINE_GAIN, 7, 3, 1, 0), + SOC_DOUBLE_TLV("PCM Capture Fine Gain Volume", AIC3262_ADC_FINE_GAIN, + 4, 0, 5, 1, adc_fine_gain_tlv), - SOC_DOUBLE("DAC MUTE", DAC_MVOL_CONF, 2, 3, 1, 1), + SOC_DOUBLE("ADC channel mute", AIC3262_ADC_FINE_GAIN, 7, 3, 1, 0), - /* sound new kcontrol for Programming the registers from user space */ - SOC_SINGLE_AIC3262("Program Registers"), + SOC_DOUBLE("DAC MUTE", AIC3262_DAC_MVOL_CONF, 2, 3, 1, 1), - SOC_SINGLE("RESET", RESET_REG, 0,1,0), + SOC_SINGLE("RESET", AIC3262_RESET_REG, 0, 1, 0), - SOC_SINGLE("DAC VOL SOFT STEPPING", DAC_MVOL_CONF, 0, 2, 0), + SOC_SINGLE("DAC VOL SOFT STEPPING", AIC3262_DAC_MVOL_CONF, 0, 2, 0), -#ifdef DAC_INDEPENDENT_VOL - /*SOC_SINGLE_N("Left DAC Volume", DAC_LVOL, 0, 0xAF, 0), - SOC_SINGLE_N("Right DAC Volume", DAC_RVOL, 0, 0xAF, 0),*/ -#endif - - SOC_SINGLE("DAC AUTO MUTE CONTROL", DAC_MVOL_CONF, 4, 7, 0), - SOC_SINGLE("RIGHT MODULATOR SETUP", DAC_MVOL_CONF, 7, 1, 0), - - SOC_SINGLE("ADC Volume soft stepping", ADC_CHANNEL_POW, 0, 3, 0), - - SOC_DOUBLE_R("MICPGA enable/disable",MICL_PGA,MICR_PGA,7, 1, 0), + SOC_SINGLE("DAC AUTO MUTE CONTROL", AIC3262_DAC_MVOL_CONF, 4, 7, 0), - SOC_SINGLE("Mic Bias ext independent enable", MIC_BIAS_CNTL, 7, 1, 0), - SOC_SINGLE("MICBIAS_EXT ON", MIC_BIAS_CNTL, 6, 1, 0), - SOC_SINGLE("MICBIAS EXT Power Level", MIC_BIAS_CNTL, 4, 3, 0), + SOC_SINGLE("RIGHT MODULATOR SETUP", AIC3262_DAC_MVOL_CONF, 7, 1, 0), - SOC_SINGLE("MICBIAS_INT ON", MIC_BIAS_CNTL, 2, 1, 0), - SOC_SINGLE("MICBIAS INT Power Level", MIC_BIAS_CNTL, 0, 3, 0), + SOC_SINGLE("ADC Volume soft stepping", AIC3262_ADC_CHANNEL_POW, + 0, 3, 0), - SOC_DOUBLE("DRC_EN_CTL", DRC_CNTL_R1, 6, 5, 1, 0), - SOC_SINGLE("DRC_THRESHOLD_LEVEL", DRC_CNTL_R1, 2, 7, 1), - SOC_SINGLE("DRC_HYSTERISIS_LEVEL", DRC_CNTL_R1, 0, 7, 0), + SOC_SINGLE("Mic Bias ext independent enable", AIC3262_MIC_BIAS_CNTL, + 7, 1, 0), - SOC_SINGLE("DRC_HOLD_LEVEL", DRC_CNTL_R2, 3, 0x0F, 0), - SOC_SINGLE("DRC_GAIN_RATE", DRC_CNTL_R2, 0, 4, 0), - SOC_SINGLE("DRC_ATTACK_RATE", DRC_CNTL_R3, 4, 0x0F, 1), - SOC_SINGLE("DRC_DECAY_RATE", DRC_CNTL_R3, 0, 0x0F, 1), + SOC_SINGLE("MICBIAS EXT Power Level", AIC3262_MIC_BIAS_CNTL, 4, 3, 0), - SOC_SINGLE("BEEP_GEN_EN", BEEP_CNTL_R1, 7, 1, 0), - SOC_DOUBLE_R("BEEP_VOL_CNTL", BEEP_CNTL_R1, BEEP_CNTL_R2, 0, 0x0F, 1), - SOC_SINGLE("BEEP_MAS_VOL", BEEP_CNTL_R2, 6, 3, 0), + SOC_SINGLE("MICBIAS INT Power Level", AIC3262_MIC_BIAS_CNTL, 0, 3, 0), - SOC_DOUBLE_R("AGC_EN", LAGC_CNTL, RAGC_CNTL, 7, 1, 0), - SOC_DOUBLE_R("AGC_TARGET_LEVEL", LAGC_CNTL, RAGC_CNTL, 4, 7, 1), + SOC_SINGLE("BEEP_GEN_EN", AIC3262_BEEP_CNTL_R1, 7, 1, 0), - SOC_DOUBLE_R("AGC_GAIN_HYSTERESIS", LAGC_CNTL, RAGC_CNTL, 0, 3, 0), - SOC_DOUBLE_R("AGC_HYSTERESIS", LAGC_CNTL_R2, RAGC_CNTL_R2, 6, 3, 0), - SOC_DOUBLE_R("AGC_NOISE_THRESHOLD", LAGC_CNTL_R2, - RAGC_CNTL_R2, 1, 31, 1), + SOC_DOUBLE_R("BEEP_VOL_CNTL", AIC3262_BEEP_CNTL_R1, + AIC3262_BEEP_CNTL_R2, 0, 0x0F, 1), - SOC_DOUBLE_R("AGC_MAX_GAIN", LAGC_CNTL_R3, RAGC_CNTL_R3, 0, 116, 0), - SOC_DOUBLE_R("AGC_ATCK_TIME", LAGC_CNTL_R4, RAGC_CNTL_R4, 3, 31, 0), - SOC_DOUBLE_R("AGC_ATCK_SCALE_FACTOR", - LAGC_CNTL_R4, RAGC_CNTL_R4, 0, 7, 0), + SOC_SINGLE("BEEP_MAS_VOL", AIC3262_BEEP_CNTL_R2, 6, 3, 0), - SOC_DOUBLE_R("AGC_DECAY_TIME", LAGC_CNTL_R5, RAGC_CNTL_R5, 3, 31, 0), - SOC_DOUBLE_R("AGC_DECAY_SCALE_FACTOR", - LAGC_CNTL_R5, RAGC_CNTL_R5, 0, 7, 0), - SOC_DOUBLE_R("AGC_NOISE_DEB_TIME", LAGC_CNTL_R6, - RAGC_CNTL_R6, 0, 31, 0), + SOC_SINGLE("DAC PRB Selection", AIC3262_DAC_PRB, 0, 26, 0), - SOC_DOUBLE_R("AGC_SGL_DEB_TIME", LAGC_CNTL_R7, - RAGC_CNTL_R7, 0, 0x0F, 0), + SOC_SINGLE("ADC PRB Selection", AIC3262_ADC_PRB, 0, 18, 0), - SOC_SINGLE("DAC PRB Selection",DAC_PRB, 0, 25, 0), - SOC_SINGLE("HP_DEPOP", HP_DEPOP, 0, 255,0), - SOC_DOUBLE("IN1 LO BYPASS VOLUME" , LINE_AMP_CNTL_R2, 3, 0, 3, 1), 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), +}; -/* the sturcture contains the different values for mclk */ -static const struct aic3262_rate_divs aic3262_divs[] = { /* - * mclk, rate, p_val, pll_j, pll_d, dosr, ndac, mdac, aosr, nadc, madc, blck_N, - * codec_speficic_initializations + *---------------------------------------------------------------------------- + * @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 + *---------------------------------------------------------------------------- */ - /* 8k rate */ -#ifdef CONFIG_MINI_DSP - {12000000, 8000, 1, 8, 1920, 768, 8, 2, 128, 8, 12, 4, - {{0, 60, 0}, {0, 61, 0} } }, -#else - {12000000, 8000, 1, 8, 1920, 128, 12, 8, 128, 8, 6, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 8000, 1, 1, 3333, 128, 12, 8, 128, 8, 6, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 8000, 1, 4, 96, 128, 12, 8, 128, 12, 8, 4, - {{0, 60, 1}, {0, 61, 1} } }, -#endif - /* 11.025k rate */ - {12000000, 11025, 1, 1, 8816, 1024, 8, 2, 128, 8, 2, 48, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 11025, 1, 1, 8375, 1024, 8, 2, 128, 8, 2, 48, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 11025, 1, 3, 7632, 128, 8, 8, 128, 8, 8, 4, - {{0, 60, 1}, {0, 61, 1} } }, - - /* 16k rate */ -#ifdef CONFIG_MINI_DSP - {12000000, 16000, 1, 8, 1920, 384, 4, 4, 128, 4, 12, 12, - {{0, 60, 0}, {0, 61, 0} } }, - {12288000, 16000, 1, 9, 0, 216, 2, 16, 72, 2, 48, 27, - {{0, 60, 0}, {0, 61, 0} } }, -#else - {12000000, 16000, 1, 8, 1920, 128, 8, 6, 128, 8, 6, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 16000, 1, 2, 6667, 128, 8, 6, 128, 8, 6, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 16000, 1, 4, 96, 128, 8, 6, 128, 8, 6, 4, - {{0, 60, 1}, {0, 61, 1} } }, -#endif - /* 22.05k rate */ - {12000000, 22050, 1, 3, 7632, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 22050, 1, 3, 675, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 22050, 1, 3, 7632, 128, 8, 3, 128, 8, 3, 4, - {{0, 60, 1}, {0, 61, 1} } }, - /* 32k rate */ - {12000000, 32000, 1, 5, 4613, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 32000, 1, 5, 3333, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 32000, 1, 4, 96, 128, 6, 4, 128, 6, 4, 4, - {{0, 60, 1}, {0, 61, 1} } }, - -#ifdef CONFIG_MINI_DSP - {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4, - {{0, 60, 0}, {0, 61, 0} } }, - {12288000, 44100, 1, 7, 3548, 128, 2, 8, 128, 8, 2, 4, - {{0, 60, 0}, {0, 61, 0} } }, -#else - /* 44.1k rate */ - {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 44100, 1, 7, 3548, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 44100, 1, 3, 7632, 128, 4, 4, 64, 4, 4, 4, - {{0, 60, 1}, {0, 61, 1} } }, -#endif +struct snd_soc_dai_ops aic3262_asi1_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .set_fmt = aic3262_set_dai_fmt, + .set_pll = aic3262_dai_set_pll, +}; -#ifdef CONFIG_MINI_DSP - {12288000, 48000, 1, 8, 52, 128, 2, 8, 128, 2, 8, 4, - {{0, 60, 0}, {0, 61, 0} } }, - {12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4, - {{0, 60, 0}, {0, 61, 0}}}, -#else - /* 48k rate */ - {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {12288000, 48000, 1, 8, 52, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 1}, {0, 61, 1} } }, - {24000000, 48000, 1, 4, 960, 128, 4, 4, 128, 4, 4, 4, - {{0, 60, 1}, {0, 61, 1} } }, -#endif +struct snd_soc_dai_ops aic3262_asi2_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .set_fmt = aic3262_set_dai_fmt, + .set_pll = aic3262_dai_set_pll, +}; - /*96k rate */ - {12000000, 96000, 1, 16, 3840, 128, 8, 2, 128, 8, 2 , 4, - {{0, 60, 7}, {0, 61, 7} } }, - {24000000, 96000, 1, 4, 960, 128, 4, 2, 128, 4, 2, 2, - {{0, 60, 7}, {0, 61, 7} } }, - /*192k */ - {12000000, 192000, 1, 32, 7680, 128, 8, 2, 128, 8, 2, 4, - {{0, 60, 17}, {0, 61, 13} } }, - {24000000, 192000, 1, 4, 960, 128, 2, 2, 128, 2, 2, 4, - {{0, 60, 17}, {0, 61, 13} } }, +struct snd_soc_dai_ops aic3262_asi3_dai_ops = { + .hw_params = aic3262_hw_params, + .digital_mute = aic3262_mute, + .set_sysclk = aic3262_set_dai_sysclk, + .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 = 2, + .rates = AIC3262_RATES, + .formats = AIC3262_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 1, + .channels_max = 2, + .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, + }, +}; -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_dump_regs -* Purpose : This function is to mute or unmute the left and right DAC -* -*---------------------------------------------------------------------------- -*/ -static void aic3262_multi_i2s_dump_regs(struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - unsigned int counter; - DBG(KERN_INFO "#%s: Dai Active %d ASI%d REGS DUMP\n", - __func__, aic3262->active_count, dai->id); +static const unsigned int adc_ma_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 29, TLV_DB_SCALE_ITEM(-1450, 500, 0), + 30, 35, TLV_DB_SCALE_ITEM(-2060, 1000, 0), + 36, 38, TLV_DB_SCALE_ITEM(-2660, 2000, 0), + 39, 40, TLV_DB_SCALE_ITEM(-3610, 5000, 0), +}; - aic3262_change_page(codec, 0); - aic3262_change_book(codec, 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("IN1L 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), +}; - DBG(KERN_INFO "#Page0 REGS..\n"); - for (counter = 0; counter < 85; counter++) { - DBG(KERN_INFO "#%2d -> 0x%x\n", counter, - snd_soc_read(codec, counter)); - } +static const struct snd_kcontrol_new mar_pga_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1R 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), +}; - DBG(KERN_INFO "#Page1 REGS..\n"); - for (counter = 128; counter < 176; counter++) { - DBG(KERN_INFO "#%2d -> 0x%x\n", (counter % 128), - snd_soc_read(codec, counter)); - } +/* Left HPL Mixer */ +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_HP_AMP_CNTL_R1, 7, 1, + 0), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_HP_AMP_CNTL_R1, + 5, 1, 0), + SOC_DAPM_SINGLE_TLV("LOL-B1 Volume", + AIC3262_HP_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv), +}; - DBG(KERN_INFO "#Page4 REGS..\n"); - for (counter = 512; counter < 631; counter++) { - DBG(KERN_INFO "#%2d -> 0x%x\n", - (counter % 128), snd_soc_read(codec, counter)); - } +/* Right HPR Mixer */ +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("LOR-B1 Volume", + AIC3262_HP_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_HP_AMP_CNTL_R1, + 2, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_HP_AMP_CNTL_R1, + 4, 1, 0), + SOC_DAPM_SINGLE("MAR Switch", AIC3262_HP_AMP_CNTL_R1, + 6, 1, 0), +}; - for (counter = 0; counter < MAX_ASI_COUNT; counter++) { - DBG(KERN_INFO "#ASI%d Frame %s @ %dHz Playback %d Record %d\n", - (counter + 1), - (aic3262->asiCtxt[counter].master == 1) ? "Master" : "Slave", - aic3262->asiCtxt[counter].sampling_rate, - aic3262->asiCtxt[counter].playback_mode, - aic3262->asiCtxt[counter].capture_mode); - DBG(KERN_INFO "#DAC Option [%d,%d] ADC Option %d WLEN %d\n\n", - aic3262->asiCtxt[counter].left_dac_output, - aic3262->asiCtxt[counter].right_dac_output, - aic3262->asiCtxt[counter].adc_input, - aic3262->asiCtxt[counter].word_len); - } - return; -} +/* Left LOL Mixer */ +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_LINE_AMP_CNTL_R2, + 7, 1, 0), + SOC_DAPM_SINGLE("IN1L-B Switch", AIC3262_LINE_AMP_CNTL_R2, + 3, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", AIC3262_LINE_AMP_CNTL_R1, + 7, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_LINE_AMP_CNTL_R1, + 5, 1, 0), +}; -/* - *---------------------------------------------------------------------------- - * Function : aic3262_multi_i2s_mute - * Purpose : This function is to mute or unmute the left and right DAC - * - *---------------------------------------------------------------------------- - */ -static int aic3262_multi_i2s_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); +/* Right LOR Mixer */ +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("LOL Switch", AIC3262_LINE_AMP_CNTL_R1, + 2, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", AIC3262_LINE_AMP_CNTL_R1, + 6, 1, 0), + SOC_DAPM_SINGLE("MAR Switch", AIC3262_LINE_AMP_CNTL_R2, + 6, 1, 0), + SOC_DAPM_SINGLE("IN1R-B Switch", AIC3262_LINE_AMP_CNTL_R2, + 0, 1, 0), +}; - DBG(KERN_INFO "#%s : mute entered with %d\n", __func__, mute); - - /* If we are doing both Recording and Playback on this DAI interface, - * do not MUTE the Codec. - */ - if (mute && (aic3262->asiCtxt[dai->id - 1].asi_active > 1)) { - DBG("#%s Cannot Mute the ASI%d Now..\n", - __func__, dai->id); - } else { - switch (dai->id) { - case 1: - aic3262_multi_i2s_asi1_mute(dai, mute); - break; - case 2: - aic3262_multi_i2s_asi2_mute(dai, mute); - break; - case 3: - aic3262_multi_i2s_asi3_mute(dai, mute); - break; - default: - printk(KERN_ERR "#%s: Invalid DAI id\n", __func__); - return -EINVAL; - } - } - DBG(KERN_INFO "#%s : mute ended\n", __func__); - return 0; -} - - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi1_mute -* Purpose : This function is to mute or unmute the left and right DAC -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi1_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute); - - if (mute && !aic3262->asiCtxt[0].port_muted ) { - DBG(KERN_INFO "Mute if part\n"); - - - snd_soc_update_bits(codec, DAC_MVOL_CONF, DAC_LR_MUTE_MASK,DAC_LR_MUTE); - - /* First check if both Playback and Recording is going on - * this interface. - */ - if (aic3262->asiCtxt[0].asi_active > 1) { - DBG("#%s Cannot Mute the ASI Now..\n", __func__); - } else if (!(aic3262->asiCtxt[1].playback_mode) && - !(aic3262->asiCtxt[2].playback_mode)) { - /* Before Muting, please check if any other - * ASI is active. if so, we cannot simply mute the - * DAC and ADC Registers. - */ - DBG("#%s None of the ASI's are active now..\n", __func__); - snd_soc_write(codec, DAC_MVOL_CONF, - ((aic3262->dac_reg & 0xF3) | 0x0C)); - snd_soc_write(codec, ADC_FINE_GAIN, - ((aic3262->adc_gain & 0x77) | 0x88)); - snd_soc_write(codec, HPL_VOL, 0xB9); - snd_soc_write(codec, HPR_VOL, 0xB9); - snd_soc_write(codec, REC_AMP_CNTL_R5, 0x39); - snd_soc_write(codec, RAMPR_VOL, 0x39); - snd_soc_write(codec, SPK_AMP_CNTL_R4, 0x00); - aic3262->asiCtxt[0].port_muted = 1; - } - } else { - DBG(KERN_INFO "Mute else part\n"); - snd_soc_update_bits(codec, DAC_MVOL_CONF, - DAC_LR_MUTE_MASK, 0x0); - snd_soc_write(codec, ADC_FINE_GAIN,(0X00 & 0x77) | 0x0); - aic3262_multi_i2s_dump_regs(dai); - } - - DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute); - - return 0; -} - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi2_maic3262_asi3_clk_configute -* Purpose : This function is to mute or unmute the left and right DAC -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi2_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute); - - if (mute && !aic3262->asiCtxt[1].port_muted ) { - DBG(KERN_INFO "Mute if part\n"); - snd_soc_update_bits(codec, DAC_MVOL_CONF, DAC_LR_MUTE_MASK,DAC_LR_MUTE); - - /* First check if both Playback and Recording is going on - * this interface. - */ - if (aic3262->asiCtxt[1].asi_active > 1) { - DBG("#%s Cannot Mute the ASI Now..\n", __func__); - } else if (!(aic3262->asiCtxt[0].playback_mode) && - !(aic3262->asiCtxt[2].playback_mode)) { - /* Before Muting, please check if any other - * ASI is active. if so, we cannot simply mute the - * DAC and ADC Registers. - */ - snd_soc_write(codec, DAC_MVOL_CONF, - ((aic3262->dac_reg & 0xF3) | 0x0C)); - snd_soc_write(codec, ADC_FINE_GAIN, - ((aic3262->adc_gain & 0x77) | 0x88)); - snd_soc_write(codec, HPL_VOL, 0xB9); - snd_soc_write(codec, HPR_VOL, 0xB9); - snd_soc_write(codec, REC_AMP_CNTL_R5, 0x39); - snd_soc_write(codec, RAMPR_VOL, 0x39); - snd_soc_write(codec, SPK_AMP_CNTL_R4, 0x00); - aic3262->asiCtxt[1].port_muted = 1; - } - } else { - DBG(KERN_INFO "Mute else part\n"); - snd_soc_update_bits(codec, DAC_MVOL_CONF, - DAC_LR_MUTE_MASK, 0x0); - - /*aic3262_multi_i2s_dump_regs(dai);*/ - } - - DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute); - - return 0; -} - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi3_mute -* Purpose : This function is to mute or unmute the left and right DAC -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi3_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute); - - if (mute && !aic3262->asiCtxt[2].port_muted) { - DBG("Mute if part\n"); - snd_soc_update_bits(codec, DAC_MVOL_CONF, DAC_LR_MUTE_MASK,DAC_LR_MUTE); - - /* First check if both Playback and Recording is going on - * this interface. - */ - if (aic3262->asiCtxt[2].asi_active > 1) { - DBG("#%s Cannot Mute the ASI Now..\n", __func__); - } else if (!(aic3262->asiCtxt[0].playback_mode) && - !(aic3262->asiCtxt[1].playback_mode)) { - /* Before Muting, please check if any other - * ASI is active. if so, we cannot simply mute the - * DAC and ADC Registers. - */ - snd_soc_write(codec, DAC_MVOL_CONF, - ((aic3262->dac_reg & 0xF3) | 0x0C)); - snd_soc_write(codec, ADC_FINE_GAIN, - ((aic3262->adc_gain & 0x77) | 0x88)); - snd_soc_write(codec, HPL_VOL, 0xB9); - snd_soc_write(codec, HPR_VOL, 0xB9); - snd_soc_write(codec, REC_AMP_CNTL_R5, 0x39); - snd_soc_write(codec, RAMPR_VOL, 0x39); - snd_soc_write(codec, SPK_AMP_CNTL_R4, 0x00); - aic3262->asiCtxt[2].port_muted = 1; - } - } else { - DBG("Mute else part\n"); - snd_soc_update_bits(codec, DAC_MVOL_CONF, - DAC_LR_MUTE_MASK, 0x0); - - /*aic3262_multi_i2s_dump_regs(dai);*/ - - } - - DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute); - - return 0; -} - -/* - *---------------------------------------------------------------------------- - * Function : aic3262_multi_i2s_set_dai_fmt - * Purpose : This function is to set the DAI format - * - *---------------------------------------------------------------------------- - */ -static int aic3262_multi_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - /* Check the DAI Id and based on that switch the configuration for - * the Individual ASI Port. - */ - switch (codec_dai->id) { - case 1: - aic3262_multi_i2s_asi1_set_dai_fmt(codec_dai, fmt); - break; - case 2: - aic3262_multi_i2s_asi2_set_dai_fmt(codec_dai, fmt); - break; - case 3: - aic3262_multi_i2s_asi3_set_dai_fmt(codec_dai, fmt); - break; - default: - printk(KERN_ERR - "#%s: Invalid DAI interface format\n", __func__); - return -EINVAL; - } - return 0; -} - - - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi1_set_dai_fmt -* Purpose : This function is to set the DAI format for ASI1 Port -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi1_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_codec *codec = codec_dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 iface_reg, clk_reg; - u8 regvalue; - - DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n", - __func__, codec_dai->id, fmt); - - /* Read the B0_P4_R4 and B0_P4_R10 Registers to configure the - * ASI1 Bus and Clock Formats depending on the PCM Format. - */ - iface_reg = snd_soc_read(codec, ASI1_BUS_FMT); - clk_reg = snd_soc_read(codec, ASI1_BWCLK_CNTL_REG); - - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n", - __func__, codec_dai->id); - aic3262->asiCtxt[0].master = 1; - clk_reg |= (BIT5 | BIT2); /* Codec Interface as Master */ - break; - case SND_SOC_DAIFMT_CBS_CFS: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n", - __func__, codec_dai->id); - clk_reg &= ~0xFC; /* Reset bits D[7:5] and D[4:2] to zero */ - aic3262->asiCtxt[0].master = 0; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /* new case..just for debugging */ - DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__); - aic3262->asiCtxt[0].master = 0; - clk_reg |= BIT5; /* Only WCLK1 Output from Codec */ - clk_reg &= ~0x1C; /* BCLK1 Input to Codec */ - break; - default: - printk(KERN_ERR "#%s: Invalid DAI master/slave interface\n", - __func__); - return -EINVAL; - } - aic3262->asiCtxt[0].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - DBG(KERN_INFO "#%s: Configuring ASI%d for I2s Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f); - break; - case SND_SOC_DAIFMT_DSP_A: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x20; - break; - case SND_SOC_DAIFMT_RIGHT_J: - iface_reg = (iface_reg & 0x1f) | 0x40; - break; - case SND_SOC_DAIFMT_LEFT_J: - iface_reg = (iface_reg & 0x1f) | 0x60; - break; - case SND_SOC_DAIFMT_DSP_B: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_B Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x80; - /* voice call need data offset in 1 bitclock */ - snd_soc_write(codec, ASI1_LCH_OFFSET, 1); - break; - default: - printk(KERN_ERR - "#%s: Invalid DAI interface format\n", __func__); - return -EINVAL; - } - /* Also Configure the Pin Control Registers before writing into - * the ASI specific Clock Control and Format Registers - */ - - /* Configure B0_P4_R65_D[5:2] to 001 This configures the - * WCLK1 Pin to ASI1 - */ - regvalue = snd_soc_read(codec, WCLK1_PIN_CNTL_REG); - snd_soc_write(codec, WCLK1_PIN_CNTL_REG, (regvalue | BIT2)); - - /* Configure B0_P4_R68_d[6:5] = 01 and B0_P4_R67_D[4:1] to 0001 - * to ensure that the DIN1 and DOUT1 Pins are configured - * correctly - */ - regvalue = snd_soc_read(codec, DIN1_PIN_CNTL_REG); - snd_soc_write(codec, DIN1_PIN_CNTL_REG, (regvalue | BIT5)); - regvalue = snd_soc_read(codec, DOUT1_PIN_CNTL_REG); - snd_soc_write(codec, DOUT1_PIN_CNTL_REG, (regvalue | BIT1)); - - snd_soc_write(codec, ASI1_BWCLK_CNTL_REG, clk_reg); - - snd_soc_write(codec, ASI1_BUS_FMT, iface_reg); - - return 0; -} - - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi2_set_dai_fmt -* Purpose : This function is to set the DAI format for ASI2 Port -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi2_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_codec *codec = codec_dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 iface_reg, clk_reg; - u8 regvalue; - - DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n", - __func__, codec_dai->id, fmt); - - /* Read the B0_P4_R17 and B0_P4_R26 Registers to configure the - * ASI1 Bus and Clock Formats depending on the PCM Format. - */ - iface_reg = snd_soc_read(codec, ASI2_BUS_FMT); - clk_reg = snd_soc_read(codec, ASI2_BWCLK_CNTL_REG); - - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n", - __func__, codec_dai->id); - aic3262->asiCtxt[1].master = 1; - clk_reg |= (BIT5 | BIT2); - break; - case SND_SOC_DAIFMT_CBS_CFS: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n", - __func__, codec_dai->id); - - clk_reg &= ~0xFC; - aic3262->asiCtxt[1].master = 0; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /*new case..just for debugging */ - DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__); - aic3262->asiCtxt[1].master = 0; - clk_reg |= BIT5; - clk_reg &= ~0x1C; - break; - default: - printk(KERN_ERR "#%s:Invalid DAI master/slave interface\n", - __func__); - return -EINVAL; - } - aic3262->asiCtxt[1].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - DBG(KERN_INFO "#%s: Configuring ASI%d for I2S Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f); - break; - case SND_SOC_DAIFMT_DSP_A: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x20; - break; - case SND_SOC_DAIFMT_RIGHT_J: - iface_reg = (iface_reg & 0x1f) | 0x40; - break; - case SND_SOC_DAIFMT_LEFT_J: - iface_reg = (iface_reg & 0x1f) | 0x60; - break; - case SND_SOC_DAIFMT_DSP_B: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x80; - /* voice call need data offset in 1 bitclock */ - snd_soc_write(codec, ASI2_LCH_OFFSET, 1); - break; - default: - printk(KERN_ERR "#%s:Invalid DAI interface format\n", __func__); - return -EINVAL; - } - - /* Also Configure the Pin Control Registers before writing into - * the ASI2 specific Clock Control and Format Registers - */ - - /* Configure B0_P4_R69_D[5:2] to 001 This configures the - * WCLK2 Pin to ASI2 - */ - - regvalue = snd_soc_read(codec, WCLK2_PIN_CNTL_REG); - snd_soc_write(codec, WCLK2_PIN_CNTL_REG, (regvalue | BIT2)); - - regvalue = snd_soc_read(codec, BCLK2_PIN_CNTL_REG); - snd_soc_write(codec, BCLK2_PIN_CNTL_REG, (regvalue | BIT2)); - - /* Configure B0_P4_R72_d[6:5] = 01 and B0_P4_R71_D[4:1] to 0001 - * to ensure that the DIN2 and DOUT2 Pins are configured - * correctly - */ - regvalue = snd_soc_read(codec, DIN2_PIN_CNTL_REG); - snd_soc_write(codec, DIN2_PIN_CNTL_REG, (regvalue | BIT5)); - - regvalue = snd_soc_read(codec, DOUT2_PIN_CNTL_REG); - snd_soc_write(codec, DOUT2_PIN_CNTL_REG, (regvalue | BIT5 | BIT1)); - - snd_soc_write(codec, ASI2_BWCLK_CNTL_REG, clk_reg); - - snd_soc_write(codec, ASI2_BUS_FMT, iface_reg); - - return 0; -} - -/* -*---------------------------------------------------------------------------- -* Function : aic3262_multi_i2s_asi3_set_dai_fmt -* Purpose : This function is to set the DAI format for ASI3 Port -* -*---------------------------------------------------------------------------- -*/ -static int aic3262_multi_i2s_asi3_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_codec *codec = codec_dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 iface_reg, clk_reg; - u8 regvalue; - - DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n", - __func__, codec_dai->id, fmt); - - /* Read the B0_P4_R33 and B0_P4_R42 Registers to configure the - * ASI1 Bus and Clock Formats depending on the PCM Format. - */ - iface_reg = snd_soc_read(codec, ASI3_BUS_FMT); - clk_reg = snd_soc_read(codec, ASI3_BWCLK_CNTL_REG); - - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n", - __func__, codec_dai->id); - aic3262->asiCtxt[2].master = 1; - clk_reg |= (BIT5 | BIT2); - break; - case SND_SOC_DAIFMT_CBS_CFS: - DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n", - __func__, codec_dai->id); - clk_reg &= ~0xFC; - aic3262->asiCtxt[2].master = 0; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /* new case..just for debugging */ - DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__); - aic3262->asiCtxt[2].master = 0; - clk_reg |= BIT5; - clk_reg &= ~0x1C; - break; - default: - printk(KERN_ERR "Invalid DAI master/slave interface\n"); - return -EINVAL; - } - aic3262->asiCtxt[2].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - DBG(KERN_INFO "#%s: Configuring ASI%d for I2S Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f); - break; - case SND_SOC_DAIFMT_DSP_A: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x20; - break; - case SND_SOC_DAIFMT_RIGHT_J: - iface_reg = (iface_reg & 0x1f) | 0x40; - break; - case SND_SOC_DAIFMT_LEFT_J: - iface_reg = (iface_reg & 0x1f) | 0x60; - break; - case SND_SOC_DAIFMT_DSP_B: - DBG(KERN_INFO "#%s: Configuring ASI%d for DSP Mode..\n", - __func__, codec_dai->id); - iface_reg = (iface_reg & 0x1f) | 0x80; - /* voice call need data offset in 1 bitclock */ - snd_soc_write(codec, ASI3_LCH_OFFSET, 1); - break; - default: - printk(KERN_ERR - "#%s: Invalid DAI interface format\n", __func__); - return -EINVAL; - } - - /* Also Configure the Pin Control Registers before writing into - * the ASI specific Clock Control and Format Registers - */ - /* Configure B0_P4_R73_D[5:2] to 0001 This configures the - * WCLK1 Pin to ASI1 - */ - regvalue = snd_soc_read(codec, WCLK3_PIN_CNTL_REG); - snd_soc_write(codec, WCLK3_PIN_CNTL_REG, (regvalue | BIT2)); - - regvalue = snd_soc_read(codec, BCLK3_PIN_CNTL_REG); - snd_soc_write(codec, BCLK3_PIN_CNTL_REG, (regvalue | BIT2)); - - /* Configure B0_P4_R76_d[6:5] = 01 and B0_P4_R75_D[4:1] to 0001 - * to ensure that the DIN1 and DOUT1 Pins are configured - * correctly - */ - regvalue = snd_soc_read(codec, DIN3_PIN_CNTL_REG); - snd_soc_write(codec, DIN3_PIN_CNTL_REG, (regvalue | BIT5)); - regvalue = snd_soc_read(codec, DOUT3_PIN_CNTL_REG); - snd_soc_write(codec, DOUT3_PIN_CNTL_REG, (regvalue | BIT1)); - - snd_soc_write(codec, ASI3_BWCLK_CNTL_REG, clk_reg); - - snd_soc_write(codec, ASI3_BUS_FMT, iface_reg); - - return 0; -} - -/* - * Clock after PLL and dividers - */ -static int aic3262_multi_i2s_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_codec *codec = codec_dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "#%s: DAI ID %d Freq %d Direction %d\n", - __func__, codec_dai->id, freq, dir); - switch (freq) { - case AIC3262_FREQ_12000000: - case AIC3262_FREQ_12288000: - case AIC3262_FREQ_24000000: - aic3262->sysclk = freq; - return 0; - break; - } - printk(KERN_ERR "Invalid frequency to set DAI system clock\n"); - return -EINVAL; -} - -/* -* aic3262_multi_i2s_set_pll -* -* This function is invoked as part of the PLL call-back -* handler from the ALSA layer. -*/ -static int aic3262_multi_i2s_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, int source, unsigned int freq_in, - unsigned int freq_out) -{ - - printk(KERN_INFO "%s: DAI ID %d PLL_ID %d InFreq %d OutFreq %d\n", - __func__, pll_id, codec_dai->id, freq_in, freq_out); - - return 0; -} - -/* -* aic3262_asi1_clk_config -* -* This function is used to configure the BCLK1, WCLK1 pins which -* are specific to ASI1 Interface. This function just enables the -* BCLk and WCLK along with the miniDSP Port Control Registers. -* However, depending on the user requirement, this function can also be -* extended to configure the sourc for the BCLK and WCLK on a ASI basis. -*/ -static int aic3262_asi1_clk_config(struct snd_soc_codec *codec, - struct snd_pcm_hw_params *params) -{ - u8 bclk_N_value, wclk_N_value; - u8 minidspD_data, minidspA_data; - u8 regval; - - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "%s: Invoked\n", __func__); - - /* Configure the BCLK and WCLK Output Mux Options */ - regval = snd_soc_read(codec, ASI1_BWCLK_OUT_CNTL); - regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK); - - regval |= (aic3262->asiCtxt[0].bclk_output << - AIC3262_ASI_BCLK_MUX_SHIFT); - regval |= aic3262->asiCtxt[0].wclk_output; - snd_soc_write(codec, ASI1_BWCLK_OUT_CNTL, regval); - - /* Configure the corresponding miniDSP Data Ports */ - minidspD_data = snd_soc_read(codec, MINIDSP_PORT_CNTL_REG); - minidspD_data &= ~(BIT5 | BIT4); - snd_soc_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data); - - minidspA_data = snd_soc_read(codec, ASI1_ADC_INPUT_CNTL); - minidspA_data &= ~(BIT2 | BIT1 | BIT0); - minidspA_data |= aic3262->asiCtxt[0].adc_input; - snd_soc_write(codec, ASI1_ADC_INPUT_CNTL, minidspA_data); - - - if (aic3262->asiCtxt[0].master == 1) { - DBG(KERN_INFO - "#%s: Codec Master on ASI1 Port. Enabling BCLK WCLK Divider.\n", - __func__); - bclk_N_value = aic3262->asiCtxt[0].bclk_div; - snd_soc_write(codec, ASI1_BCLK_N, (bclk_N_value | 0x80)); - - wclk_N_value = snd_soc_read(codec, ASI1_WCLK_N); - snd_soc_write(codec, ASI1_WCLK_N, (wclk_N_value | 0xA0)); - } - return 0; - -} - -/* -* aic3262_asi2_clk_config -* -* This function is used to configure the BCLK2, WCLK2 pins which -* are specific to ASI2 Interface. This function just enables the -* BCLk and WCLK along with the miniDSP Port Control Registers. -* However, depending on the user requirement, this function can also be -* extended to configure the sourc for the BCLK and WCLK on a ASI basis. -*/ -static int aic3262_asi2_clk_config(struct snd_soc_codec *codec, - struct snd_pcm_hw_params *params) -{ - u8 bclk_N_value, wclk_N_value, minidspD_data, minidspA_data; - u8 regval; - - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "%s: Invoked\n", __func__); - - - /* Configure the BCLK and WCLK Output Mux Options */ - regval = snd_soc_read(codec, ASI2_BWCLK_OUT_CNTL); - regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK); - regval |= (aic3262->asiCtxt[1].bclk_output << - AIC3262_ASI_BCLK_MUX_SHIFT); - regval |= aic3262->asiCtxt[1].wclk_output; - - snd_soc_write(codec, ASI2_BWCLK_OUT_CNTL, regval); - /* Configure the corresponding miniDSP Data Ports */ - minidspD_data = snd_soc_read(codec, MINIDSP_PORT_CNTL_REG); - minidspD_data |= (BIT2); - snd_soc_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data); - - minidspA_data = snd_soc_read(codec, ASI2_ADC_INPUT_CNTL); - minidspA_data &= ~(BIT2 | BIT1 | BIT0); - minidspA_data |= aic3262->asiCtxt[1].adc_input; - snd_soc_write(codec, ASI2_ADC_INPUT_CNTL, minidspA_data); - - /* NO Manual configuration of WCLK and BCLK for Master Mode. - * DAPM Handles all the required modifications. - */ - if (aic3262->asiCtxt[1].master == 1) { - DBG(KERN_INFO - "#%s: Codec Master on ASI2 Port. Enabling BCLK WCLK Divider.\n", - __func__); - bclk_N_value = aic3262->asiCtxt[1].bclk_div; - snd_soc_write(codec, ASI2_BCLK_N, (bclk_N_value | 0x80)); - - wclk_N_value = snd_soc_read(codec, ASI2_WCLK_N); - snd_soc_write(codec, ASI2_WCLK_N, (wclk_N_value | 0xA0)); - } - - return 0; - -} - -/* -* aic3262_asi3_clk_config -* -* This function is used to configure the BCLK3, WCLK3 pins which -* are specific to ASI3 Interface. This function just enables the -* BCLk and WCLK along with the miniDSP Port Control Registers. -* However, depending on the user requirement, this function can also be -* extended to configure the sourc for the BCLK and WCLK on a ASI basis. -*/ -static int aic3262_asi3_clk_config(struct snd_soc_codec *codec, - struct snd_pcm_hw_params *params) -{ - u8 bclk_N_value, wclk_N_value, minidspD_data, minidspA_data; - u8 regval; - - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "%s:\n", __func__); - - - /* Configure the BCLK and WCLK Output Mux Options */ - regval = snd_soc_read(codec, ASI3_BWCLK_OUT_CNTL); - regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK); - regval |= (aic3262->asiCtxt[2].bclk_output << - AIC3262_ASI_BCLK_MUX_SHIFT); - regval |= aic3262->asiCtxt[2].wclk_output; - snd_soc_write(codec, ASI3_BWCLK_OUT_CNTL, regval); - - minidspD_data = snd_soc_read(codec, MINIDSP_PORT_CNTL_REG); - minidspD_data |= (BIT1); - snd_soc_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data); - - minidspA_data = snd_soc_read(codec, ASI3_ADC_INPUT_CNTL); - minidspA_data &= ~(BIT2 | BIT1 | BIT0); - minidspA_data |= aic3262->asiCtxt[2].adc_input; - snd_soc_write(codec, ASI3_ADC_INPUT_CNTL, minidspA_data); - - if (aic3262->asiCtxt[2].master == 1) { - DBG(KERN_INFO - "#%s: Codec Master on ASI3 Port. Enabling BCLK WCLK Divider.\n", - __func__); - bclk_N_value = aic3262->asiCtxt[2].bclk_div; - snd_soc_write(codec, ASI2_BCLK_N, (bclk_N_value | 0x80)); - - wclk_N_value = snd_soc_read(codec, ASI3_WCLK_N); - snd_soc_write(codec, ASI3_WCLK_N, (wclk_N_value | 0xA0)); - } - return 0; - -} - -/* -* aic3262_multi_i2s_hw_params -* -* This function is used to configure the individual ASI port registers -* depending on the configuration passed on by the snd_pcm_hw_params -* structure. -* This function internally configures the ASI specific pins and clock -* Control Registers. -*/ -static int aic3262_multi_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - int i, j; - u8 data; - u16 regoffset = 0; - u8 dacpath = 0; - u8 adcpath = 0; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - DBG(KERN_INFO "#%s: Invoked for ASI%d Port for %s Mode\n", - __func__, dai->id, - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ? "Playback" : "Record"); - - i = aic3262_get_divs(aic3262->sysclk, params_rate(params)); - - i2c_verify_book0(codec); - - if (i < 0) { - printk(KERN_ERR "#%s: Sampling rate %d not supported\n", - __func__, params_rate(params)); - return i; - } - - aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* Configure the PLL J, R D values only if none of the ASI - * Interfaces are Active. - */ - - if (1) { - DBG(KERN_INFO "#%s: None of the ASIs active yet...\n", - __func__); - /*We will fix R value to 1 and make P & J=K.D as variable */ - /* Setting P & R values are set to 1 and 1 at init*/ - - /* J value */ - snd_soc_write(codec, PLL_J_REG, aic3262_divs[i].pll_j); - - /* MSB & LSB for D value */ - - snd_soc_write(codec, PLL_D_MSB, (aic3262_divs[i].pll_d >> 8)); - snd_soc_write(codec, PLL_D_LSB, - (aic3262_divs[i].pll_d & AIC3262_8BITS_MASK)); - - /* NDAC divider value */ - data = snd_soc_read(codec, NDAC_DIV_POW_REG); - DBG(KERN_INFO "# reading NDAC = %d , NDAC_DIV_POW_REG = %x\n", - aic3262_divs[i].ndac, data); - snd_soc_write(codec, NDAC_DIV_POW_REG, - ((data & 0x80)|(aic3262_divs[i].ndac))); - DBG(KERN_INFO "# writing NDAC = %d , NDAC_DIV_POW_REG = %x\n", - aic3262_divs[i].ndac, - ((data & 0x80)|(aic3262_divs[i].ndac))); - - /* MDAC divider value */ - data = snd_soc_read(codec, MDAC_DIV_POW_REG); - DBG(KERN_INFO "# reading MDAC = %d , MDAC_DIV_POW_REG = %x\n", - aic3262_divs[i].mdac, data); - snd_soc_write(codec, MDAC_DIV_POW_REG, - ((data & 0x80)|(aic3262_divs[i].mdac))); - DBG(KERN_INFO "# writing MDAC = %d , MDAC_DIV_POW_REG = %x\n", - aic3262_divs[i].mdac, ((data & 0x80)|(aic3262_divs[i].mdac))); - - /* DOSR MSB & LSB values */ - snd_soc_write(codec, DOSR_MSB_REG, aic3262_divs[i].dosr >> 8); - DBG(KERN_INFO "# writing DOSR_MSB_REG = %d\n", - (aic3262_divs[i].dosr >> 8)); - snd_soc_write(codec, DOSR_LSB_REG, - aic3262_divs[i].dosr & AIC3262_8BITS_MASK); - DBG(KERN_INFO "# writing DOSR_LSB_REG = %d\n", - (aic3262_divs[i].dosr & AIC3262_8BITS_MASK)); - - /* NADC divider value */ - data = snd_soc_read(codec, NADC_DIV_POW_REG); - snd_soc_write(codec, NADC_DIV_POW_REG, - ((data & 0x80)|(aic3262_divs[i].nadc))); - DBG(KERN_INFO "# writing NADC_DIV_POW_REG = %d\n", - aic3262_divs[i].nadc); - - /* MADC divider value */ - data = snd_soc_read(codec, MADC_DIV_POW_REG); - snd_soc_write(codec, MADC_DIV_POW_REG, - ((data & 0x80)|(aic3262_divs[i].madc))); - DBG(KERN_INFO "# writing MADC_DIV_POW_REG = %d\n", - aic3262_divs[i].madc); - - /* AOSR value */ - snd_soc_write(codec, AOSR_REG, aic3262_divs[i].aosr); - DBG(KERN_INFO "# writing AOSR = %d\n", aic3262_divs[i].aosr); - } else { - DBG(KERN_INFO "#Atleast 1 ASI Active. Cannot Program PLL..\n"); - } - /* Check for the DAI ID to know which ASI needs - * Configuration. - */ - switch (dai->id) { - case 1: - regoffset = ASI1_BUS_FMT; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - DBG(KERN_INFO "#%s: ASI1 DAC Inputs enabled..\n", - __func__); - /* Read the DAC Control Register and configure it - * as per the ASIContext Structure Settings. - */ - dacpath = snd_soc_read(codec, ASI1_DAC_OUT_CNTL); - dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK | - AIC3262_ASI_RDAC_PATH_MASK); - dacpath |= (aic3262->asiCtxt[0].left_dac_output - << AIC3262_ASI_LDAC_PATH_SHIFT); - - dacpath |= (aic3262->asiCtxt[0].right_dac_output - << AIC3262_ASI_RDAC_PATH_SHIFT); - snd_soc_write(codec, ASI1_DAC_OUT_CNTL, dacpath); - - aic3262->asiCtxt[0].playback_mode = 1; - aic3262->asiCtxt[0].bclk_div = - aic3262_divs[i].blck_N; - } else { - /* For Recording, Configure the DOUT Pin as per - * ASIContext Structure Settings. - */ - adcpath = snd_soc_read(codec, ASI1_DATA_OUT); - adcpath &= ~(AIC3262_ASI_DOUT_MASK); - - adcpath |= aic3262->asiCtxt[0].dout_option; - snd_soc_write(codec, ASI1_DATA_OUT, adcpath); - - aic3262->asiCtxt[0].capture_mode = 1; - } - break; - case 2: - regoffset = ASI2_BUS_FMT; - - /* Since we are configuring ASI2, please check if Playback - * is expected. If so, enable ASI2 Inputs to Left and - * Right DACs - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - DBG(KERN_INFO "#%s: ASI2 DAC Inputs enabled..\n", - __func__); - /* Read the DAC Control Register and configure it - * as per theASIContext Structure Settings. - */ - dacpath = snd_soc_read(codec, ASI2_DAC_OUT_CNTL); - dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK | - AIC3262_ASI_RDAC_PATH_MASK); - dacpath |= (aic3262->asiCtxt[1].left_dac_output - << AIC3262_ASI_LDAC_PATH_SHIFT); - - dacpath |= (aic3262->asiCtxt[1].right_dac_output - << AIC3262_ASI_RDAC_PATH_SHIFT); - snd_soc_write(codec, ASI2_DAC_OUT_CNTL, dacpath); - aic3262->asiCtxt[1].playback_mode = 1; - - aic3262->asiCtxt[1].bclk_div = - aic3262_divs[i].blck_N; - } else { - /* For Recording, Configure the DOUT Pin as per - * ASIContext Structure Settings. - */ - adcpath = snd_soc_read(codec, ASI2_DATA_OUT); - adcpath &= ~(AIC3262_ASI_DOUT_MASK); - adcpath |= aic3262->asiCtxt[1].dout_option; - snd_soc_write(codec, ASI2_DATA_OUT, adcpath); - - aic3262->asiCtxt[1].capture_mode = 1; - } - break; - case 3: - regoffset = ASI3_BUS_FMT; - /* Since we are configuring ASI3, please check if Playback - * is expected. If so, enable ASI3 Inputs to Left and - * Right DACs - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - DBG(KERN_INFO "#%s:ASI3 DAC Inputs enabled.\n", - __func__); - /* Read the DAC Control Register and configure - * it as per the ASIContext Structure Settings. - */ - dacpath = snd_soc_read(codec, ASI3_DAC_OUT_CNTL); - dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK | - AIC3262_ASI_RDAC_PATH_MASK); - dacpath |= (aic3262->asiCtxt[2].left_dac_output - << AIC3262_ASI_LDAC_PATH_SHIFT); - dacpath |= (aic3262->asiCtxt[2].right_dac_output - << AIC3262_ASI_RDAC_PATH_SHIFT); - snd_soc_write(codec, - ASI3_DAC_OUT_CNTL, dacpath); - - aic3262->asiCtxt[2].playback_mode = 1; - - aic3262->asiCtxt[2].bclk_div = - aic3262_divs[i].blck_N; - } else { - /* For Recording, Configure the DOUT Pin as per - * ASIContext Structure Settings. - */ - adcpath &= ~(AIC3262_ASI_DOUT_MASK); - adcpath |= aic3262->asiCtxt[2].dout_option; - snd_soc_write(codec, ASI3_DATA_OUT, adcpath); - - aic3262->asiCtxt[2].capture_mode = 1; - } - break; - default: - printk(KERN_ERR "Invalid Dai ID %d in %s", - dai->id, __func__); - break; - } - DBG(KERN_INFO "#%s: Reading Pg %d Reg %d for Bus Format Control.\n", - __func__, (regoffset/128), (regoffset % 128)); - - /* Read the correspondig ASI DAI Interface Register */ - data = snd_soc_read(codec, regoffset); - - data = data & 0xe7; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - DBG(KERN_INFO "#%s: Configuring ASI%d S16_LE Fmt..\n", - __func__, dai->id); - data = data | 0x00; - aic3262->asiCtxt[dai->id - 1].word_len = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - data |= (0x08); - aic3262->asiCtxt[dai->id - 1].word_len = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - DBG(KERN_INFO "#%s: Configuring ASI%d S24_LE Fmt..\n", - __func__, dai->id); - data |= (0x10); - aic3262->asiCtxt[dai->id - 1].word_len = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - DBG(KERN_INFO "#%s: Configuring ASI%d S32_LE Fmt..\n", - __func__, dai->id); - data |= (0x18); - aic3262->asiCtxt[dai->id - 1].word_len = 32; - break; - } - - /* configure the respective Registers for the above configuration */ - snd_soc_write(codec, regoffset, data); - - for (j = 0; j < NO_FEATURE_REGS; j++) { - snd_soc_write(codec, - aic3262_divs[i].codec_specific_regs[j].reg_offset, - aic3262_divs[i].codec_specific_regs[j].reg_val); - } - - /* Enable the PLL, MDAC, NDAC, NADC, MADC and BCLK Dividers */ - aic3262_set_bias_level(codec, SND_SOC_BIAS_ON); - - /* Based on the DAI ID we enable the corresponding pins related to the - * ASI Port. - */ - switch (dai->id) { - case 1: - aic3262_asi1_clk_config(codec, params); - break; - case 2: - aic3262_asi2_clk_config(codec, params); - break; - case 3: - aic3262_asi3_clk_config(codec, params); - break; - default: - printk(KERN_ERR "Invalid Dai ID %d in %s", - dai->id, __func__); - break; - } - /* Depending on the DAI->ID update the local Flags */ - aic3262->asiCtxt[dai->id - 1].asi_active++; - aic3262->asiCtxt[dai->id - 1].sampling_rate = params_rate(params); - /* Update the active_count flag */ - aic3262->active_count++; - - return 0; -} - -/* -* -* aic3262_multi_i2s_hw_free -* -* This function is used to configure the Codec after the usage is completed. -* We can use this function to disable the DAC and ADC specific inputs from the -* individual ASI Ports of the Audio Codec. -*/ -static void aic3262_multi_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - u8 value; - u8 dacpath; - u8 adcpath; - u16 dacregoffset = 0; - u16 adcregoffset = 0; - - DBG(KERN_INFO "#%s: ASI%d Port for %s Mode\n", - __func__, dai->id, - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - "Playback" : "Record"); - - /* Check if this function was already executed earlier for the same - * ASI Port - */ - if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && - (aic3262->asiCtxt[dai->id - 1].playback_mode == 0)) { - DBG(KERN_INFO "#%s: Function Already Executed. Exiting..\n", - __func__); - goto err; - } else if ((substream->stream != SNDRV_PCM_STREAM_PLAYBACK) && - (aic3262->asiCtxt[dai->id - 1].capture_mode == 0)) { - DBG(KERN_INFO "#%s: Function Already Executed. Exiting..\n", - __func__); - goto err; - } - - switch (dai->id) { - case 1: - /* In case we are Frame Master on this Interface, Switch off - * the Bit Clock Divider and Word Clock Dividers - */ - if (aic3262->asiCtxt[0].master == 1) { - /* Also check if either Playback or Recording is still - * going on this ASI Interface - */ - - value = snd_soc_read(codec, ASI1_BCLK_N); - snd_soc_write(codec, ASI1_BCLK_N, (value & 0x7f)); - - value = snd_soc_read(codec, ASI1_WCLK_N); - snd_soc_write(codec, ASI1_WCLK_N, (value & 0x7f)); - } - - dacregoffset = ASI1_DAC_OUT_CNTL; - adcregoffset = ASI1_ADC_INPUT_CNTL; - break; - case 2: - /* In case we are Frame Master on this Interface, Switch off - * the Bit Clock Divider and Word Clock Dividers - */ - if (aic3262->asiCtxt[1].master == 1) { - value = snd_soc_read(codec, ASI2_BCLK_N); - snd_soc_write(codec, ASI2_BCLK_N, (value & 0x7f)); - - value = snd_soc_read(codec, ASI2_WCLK_N); - snd_soc_write(codec, ASI2_WCLK_N, (value & 0x7f)); - } - dacregoffset = ASI2_DAC_OUT_CNTL; - adcregoffset = ASI2_ADC_INPUT_CNTL; - break; - case 3: - /* In case we are Frame Master on this Interface, Switch off - * the Bit Clock Divider and Word Clock Dividers - */ - if (aic3262->asiCtxt[2].master == 1) { - value = snd_soc_read(codec, ASI3_BCLK_N); - snd_soc_write(codec, ASI3_BCLK_N, (value & 0x7f)); - - value = snd_soc_read(codec, ASI3_WCLK_N); - snd_soc_write(codec, ASI3_WCLK_N, (value & 0x7f)); - } - dacregoffset = ASI3_DAC_OUT_CNTL; - adcregoffset = ASI3_ADC_INPUT_CNTL; - break; - default: - printk(KERN_ERR "#%s: Invalid dai id\n", __func__); - } - /* If this was a Playback Stream Stop, then only - * switch off the DAC Inputs - */ - if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && - (dacregoffset != 0)) { - DBG(KERN_INFO "#%s: Disabling Pg %d Reg %d DAC Inputs ..\n", - __func__, (dacregoffset/128), (dacregoffset % 128)); - - dacpath = snd_soc_read(codec, dacregoffset); - snd_soc_write(codec, dacregoffset, (dacpath & ~(BIT6 | BIT4))); - - aic3262->asiCtxt[dai->id - 1].playback_mode = 0; - } else { - /* Switch off the ADC Input Control Registers here */ - DBG(KERN_INFO "#%s: Disabling Pg %d Reg %d for ADC Inputs..\n", - __func__, (adcregoffset/128), (adcregoffset % 128)); - - adcpath = snd_soc_read(codec, adcregoffset); - snd_soc_write(codec, adcregoffset, - (adcpath & ~(BIT2 | BIT1 | BIT0))); - - aic3262->asiCtxt[dai->id - 1].capture_mode = 0; - } - - /* If we were configured in mono PCM Mode earlier, then reset the - * Left Channel and Right Channel offset Registers here. - */ - switch (dai->id) { - case 1: - if (aic3262->asiCtxt[0].pcm_format == SND_SOC_DAIFMT_DSP_B) { - snd_soc_write(codec, ASI1_LCH_OFFSET, 0x00); - snd_soc_write(codec, ASI1_RCH_OFFSET, 0x00); - } - break; - case 2: - if (aic3262->asiCtxt[1].pcm_format == SND_SOC_DAIFMT_DSP_B) { - snd_soc_write(codec, ASI2_LCH_OFFSET, 0x00); - snd_soc_write(codec, ASI2_RCH_OFFSET, 0x00); - } - - break; - case 3: - if (aic3262->asiCtxt[2].pcm_format == SND_SOC_DAIFMT_DSP_B) { - snd_soc_write(codec, ASI3_LCH_OFFSET, 0x00); - snd_soc_write(codec, ASI3_RCH_OFFSET, 0x00); - } - break; - } - /* Depending on the DAI->ID update the asi_active Flags */ - if (aic3262->asiCtxt[dai->id - 1].asi_active) { - aic3262->asiCtxt[dai->id - 1].asi_active--; - - /* Update the active_count flag */ - if (aic3262->active_count) - aic3262->active_count--; - } -err: - return; -} - - -/* -* -* aic3262_multi_i2s_set_clkdiv -* -*/ -static int aic3262_multi_i2s_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) -{ - int value; - struct snd_soc_codec *codec = codec_dai->codec; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - - value = snd_soc_read(codec, div_id); - snd_soc_write(codec, div_id, (value | div)); - - printk(KERN_INFO "#%s: DAI ID %d Page %d Register %d Divider_Val %d Final_Value 0x%x\n", - __func__, codec_dai->id, (div_id /128), (div_id%128), div, - (value | div)); - - /* Store the Clock Divider inside the Private Structure */ - switch(codec_dai->id) { - case 1: - if (div_id == ASI1_BCLK_N) - aic3262->asiCtxt[0].bclk_div = div; - if (div_id == ASI1_WCLK_N) - aic3262->asiCtxt[0].wclk_div = div; - break; - case 2: - if (div_id == ASI2_BCLK_N) - aic3262->asiCtxt[1].bclk_div = div; - if (div_id == ASI2_WCLK_N) - aic3262->asiCtxt[1].wclk_div = div; - break; - case 3: - if (div_id == ASI3_BCLK_N) - aic3262->asiCtxt[2].bclk_div = div; - if (div_id == ASI3_WCLK_N) - aic3262->asiCtxt[2].wclk_div = div; - break; - } - return 0; -} - - -/* - *---------------------------------------------------------------------------- - * @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_multi_i2s_dai_ops = { - .hw_params = aic3262_multi_i2s_hw_params, - .digital_mute = aic3262_multi_i2s_mute, - .set_fmt = aic3262_multi_i2s_set_dai_fmt, - .set_pll = aic3262_multi_i2s_set_dai_pll, - .set_sysclk = aic3262_multi_i2s_set_dai_sysclk, - .shutdown = aic3262_multi_i2s_shutdown, - .set_clkdiv = aic3262_multi_i2s_set_clkdiv, -}; - - -static struct snd_soc_dai_driver tlv320aic3262_dai[] = { -/* AIC3262 ASI1 DAI */ -{ - .name = "aic3262-asi1", - .id = 1, - .playback = { - .stream_name = "ASI1 Playback", - .channels_min = 1, - .channels_max = 2, - .rates = AIC3262_RATES, - .formats = AIC3262_FORMATS}, - .capture = { /* dummy for fast DAI switching */ - .stream_name = "ASI1 Capture", - .channels_min = 1, - .channels_max = 2, - .rates = AIC3262_RATES, - .formats = AIC3262_FORMATS}, - .ops = &aic3262_multi_i2s_dai_ops, -}, -/* AIC3262 ASI2 DAI */ -{ - .name = "aic3262-asi2", - .id = 2, - .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_multi_i2s_dai_ops, - -}, -/* AIC3262 ASI3 DAI */ -{ - .name = "aic3262-asi3", - .id = 3, - .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_multi_i2s_dai_ops, - -}, -}; - -/* - ***************************************************************************** - * Initializations - ***************************************************************************** - */ -/* - * AIC3262 register cache - * We are caching the registers here. - * There is no point in caching the reset register. - * - * NOTE: In AIC3262, there are 127 registers supported in both page0 and page1 - * The following table contains the page0 and page 1 and page 3 - * registers values. - */ -static const u8 aic3262_reg[AIC3262_CACHEREGNUM] = { - 0x00, 0x00, 0x10, 0x00, /* 0 */ - 0x03, 0x40, 0x11, 0x08, /* 4 */ - 0x00, 0x00, 0x00, 0x82, /* 8 */ - 0x88, 0x00, 0x80, 0x02, /* 12 */ - 0x00, 0x08, 0x01, 0x01, /* 16 */ - 0x80, 0x01, 0x00, 0x04, /* 20 */ - 0x00, 0x00, 0x01, 0x00, /* 24 */ - 0x00, 0x00, 0x01, 0x00, /* 28 */ - 0x00, 0x00, 0x00, 0x00, /* 32 */ - 0x00, 0x00, 0x00, 0x00, /* 36 */ - 0x00, 0x00, 0x00, 0x00, /* 40 */ - 0x00, 0x00, 0x00, 0x00, /* 44 */ - 0x00, 0x00, 0x00, 0x00, /* 48 */ - 0x00, 0x42, 0x02, 0x02, /* 52 */ - 0x42, 0x02, 0x02, 0x02, /* 56 */ - 0x00, 0x00, 0x00, 0x01, /* 60 */ - 0x01, 0x00, 0x14, 0x00, /* 64 */ - 0x0C, 0x00, 0x00, 0x00, /* 68 */ - 0x00, 0x00, 0x00, 0xEE, /* 72 */ - 0x10, 0xD8, 0x10, 0xD8, /* 76 */ - 0x00, 0x00, 0x88, 0x00, /* 80 */ - 0x00, 0x00, 0x00, 0x00, /* 84 */ - 0x7F, 0x00, 0x00, 0x00, /* 88 */ - 0x00, 0x00, 0x00, 0x00, /* 92 */ - 0x7F, 0x00, 0x00, 0x00, /* 96 */ - 0x00, 0x00, 0x00, 0x00, /* 100 */ - 0x00, 0x00, 0x00, 0x00, /* 104 */ - 0x00, 0x00, 0x00, 0x00, /* 108 */ - 0x00, 0x00, 0x00, 0x00, /* 112 */ - 0x00, 0x00, 0x00, 0x00, /* 116 */ - 0x00, 0x00, 0x00, 0x00, /* 120 */ - 0x00, 0x00, 0x00, 0x00, /* 124 - PAGE0 Registers(127) ends here */ - 0x01, 0x00, 0x08, 0x00, /* 128, PAGE1-0 */ - 0x00, 0x00, 0x00, 0x00, /* 132, PAGE1-4 */ - 0x00, 0x00, 0x00, 0x10, /* 136, PAGE1-8 */ - 0x00, 0x00, 0x00, 0x00, /* 140, PAGE1-12 */ - 0x40, 0x40, 0x40, 0x40, /* 144, PAGE1-16 */ - 0x00, 0x00, 0x00, 0x00, /* 148, PAGE1-20 */ - 0x00, 0x00, 0x00, 0x00, /* 152, PAGE1-24 */ - 0x00, 0x00, 0x00, 0x00, /* 156, PAGE1-28 */ - 0x00, 0x00, 0x00, 0x00, /* 160, PAGE1-32 */ - 0x00, 0x00, 0x00, 0x00, /* 164, PAGE1-36 */ - 0x00, 0x00, 0x00, 0x00, /* 168, PAGE1-40 */ - 0x00, 0x00, 0x00, 0x00, /* 172, PAGE1-44 */ - 0x00, 0x00, 0x00, 0x00, /* 176, PAGE1-48 */ - 0x00, 0x00, 0x00, 0x00, /* 180, PAGE1-52 */ - 0x00, 0x00, 0x00, 0x80, /* 184, PAGE1-56 */ - 0x80, 0x00, 0x00, 0x00, /* 188, PAGE1-60 */ - 0x00, 0x00, 0x00, 0x00, /* 192, PAGE1-64 */ - 0x00, 0x00, 0x00, 0x00, /* 196, PAGE1-68 */ - 0x00, 0x00, 0x00, 0x00, /* 200, PAGE1-72 */ - 0x00, 0x00, 0x00, 0x00, /* 204, PAGE1-76 */ - 0x00, 0x00, 0x00, 0x00, /* 208, PAGE1-80 */ - 0x00, 0x00, 0x00, 0x00, /* 212, PAGE1-84 */ - 0x00, 0x00, 0x00, 0x00, /* 216, PAGE1-88 */ - 0x00, 0x00, 0x00, 0x00, /* 220, PAGE1-92 */ - 0x00, 0x00, 0x00, 0x00, /* 224, PAGE1-96 */ - 0x00, 0x00, 0x00, 0x00, /* 228, PAGE1-100 */ - 0x00, 0x00, 0x00, 0x00, /* 232, PAGE1-104 */ - 0x00, 0x00, 0x00, 0x00, /* 236, PAGE1-108 */ - 0x00, 0x00, 0x00, 0x00, /* 240, PAGE1-112 */ - 0x00, 0x00, 0x00, 0x00, /* 244, PAGE1-116 */ - 0x00, 0x00, 0x00, 0x00, /* 248, PAGE1-120 */ - 0x00, 0x00, 0x00, 0x00, /* 252, PAGE1-124 Page 1 Registers Ends Here */ - 0x00, 0x00, 0x00, 0x00, /* 256, PAGE2-0 */ - 0x00, 0x00, 0x00, 0x00, /* 260, PAGE2-4 */ - 0x00, 0x00, 0x00, 0x00, /* 264, PAGE2-8 */ - 0x00, 0x00, 0x00, 0x00, /* 268, PAGE2-12 */ - 0x00, 0x00, 0x00, 0x00, /* 272, PAGE2-16 */ - 0x00, 0x00, 0x00, 0x00, /* 276, PAGE2-20 */ - 0x00, 0x00, 0x00, 0x00, /* 280, PAGE2-24 */ - 0x00, 0x00, 0x00, 0x00, /* 284, PAGE2-28 */ - 0x00, 0x00, 0x00, 0x00, /* 288, PAGE2-32 */ - 0x00, 0x00, 0x00, 0x00, /* 292, PAGE2-36 */ - 0x00, 0x00, 0x00, 0x00, /* 296, PAGE2-40 */ - 0x00, 0x00, 0x00, 0x00, /* 300, PAGE2-44 */ - 0x00, 0x00, 0x00, 0x00, /* 304, PAGE2-48 */ - 0x00, 0x00, 0x00, 0x00, /* 308, PAGE2-52 */ - 0x00, 0x00, 0x00, 0x00, /* 312, PAGE2-56 */ - 0x00, 0x00, 0x00, 0x00, /* 316, PAGE2-60 */ - 0x00, 0x00, 0x00, 0x00, /* 320, PAGE2-64 */ - 0x00, 0x00, 0x00, 0x00, /* 324, PAGE2-68 */ - 0x00, 0x00, 0x00, 0x00, /* 328, PAGE2-72 */ - 0x00, 0x00, 0x00, 0x00, /* 332, PAGE2-76 */ - 0x00, 0x00, 0x00, 0x00, /* 336, PAGE2-80 */ - 0x00, 0x00, 0x00, 0x00, /* 340, PAGE2-84 */ - 0x00, 0x00, 0x00, 0x00, /* 344, PAGE2-88 */ - 0x00, 0x00, 0x00, 0x00, /* 348, PAGE2-92 */ - 0x00, 0x00, 0x00, 0x00, /* 352, PAGE2-96 */ - 0x00, 0x00, 0x00, 0x00, /* 356, PAGE2-100 */ - 0x00, 0x00, 0x00, 0x00, /* 360, PAGE2-104 */ - 0x00, 0x00, 0x00, 0x00, /* 364, PAGE2-108 */ - 0x00, 0x00, 0x00, 0x00, /* 368, PAGE2-112*/ - 0x00, 0x00, 0x00, 0x00, /* 372, PAGE2-116*/ - 0x00, 0x00, 0x00, 0x00, /* 376, PAGE2-120*/ - 0x00, 0x00, 0x00, 0x00, /* 380, PAGE2-124 Page 2 Registers Ends Here */ - 0x00, 0x00, 0x00, 0x00, /* 384, PAGE3-0 */ - 0x00, 0x00, 0x00, 0x00, /* 388, PAGE3-4 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-8 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-12 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-16 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-20 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-24 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-28 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-32 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-36 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-40 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-44 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-48 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-52 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-56 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-60 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-64 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-68 */ - 0x00, 0x00, 0x00, 0x00, /* 328, PAGE3-72 */ - 0x00, 0x00, 0x00, 0x00, /* 332, PAGE3-76 */ - 0x00, 0x00, 0x00, 0x00, /* 336, PAGE3-80 */ - 0x00, 0x00, 0x00, 0x00, /* 340, PAGE3-84 */ - 0x00, 0x00, 0x00, 0x00, /* 344, PAGE3-88 */ - 0x00, 0x00, 0x00, 0x00, /* 348, PAGE3-92 */ - 0x00, 0x00, 0x00, 0x00, /* 352, PAGE3-96 */ - 0x00, 0x00, 0x00, 0x00, /* 356, PAGE3-100 */ - 0x00, 0x00, 0x00, 0x00, /* 360, PAGE3-104 */ - 0x00, 0x00, 0x00, 0x00, /* 364, PAGE3-108 */ - 0x00, 0x00, 0x00, 0x00, /* 368, PAGE3-112*/ - 0x00, 0x00, 0x00, 0x00, /* 372, PAGE3-116*/ - 0x00, 0x00, 0x00, 0x00, /* 376, PAGE3-120*/ - 0x00, 0x00, 0x00, 0x00, /* 380, PAGE3-124 Page 3 Registers Ends Here */ - 0x00, 0x00, 0x00, 0x00, /* 384, PAGE4-0 */ - 0x00, 0x00, 0x00, 0x00, /* 388, PAGE4-4 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-8 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-12 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-16 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-20 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-24 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-28 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-32 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-36 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-40 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-44 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-48 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-52 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-56 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-60 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-64 */ - 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-68 */ - 0x00, 0x00, 0x00, 0x00, /* 328, PAGE4-72 */ - 0x00, 0x00, 0x00, 0x00, /* 332, PAGE4-76 */ - 0x00, 0x00, 0x00, 0x00, /* 336, PAGE4-80 */ - 0x00, 0x00, 0x00, 0x00, /* 340, PAGE4-84 */ - 0x00, 0x00, 0x00, 0x00, /* 344, PAGE4-88 */ - 0x00, 0x00, 0x00, 0x00, /* 348, PAGE4-92 */ - 0x00, 0x00, 0x00, 0x00, /* 352, PAGE4-96 */ - 0x00, 0x00, 0x00, 0x00, /* 356, PAGE4-100 */ - 0x00, 0x00, 0x00, 0x00, /* 360, PAGE4-104 */ - 0x00, 0x00, 0x00, 0x00, /* 364, PAGE4-108 */ - 0x00, 0x00, 0x00, 0x00, /* 368, PAGE4-112*/ - 0x00, 0x00, 0x00, 0x00, /* 372, PAGE4-116*/ - 0x00, 0x00, 0x00, 0x00, /* 376, PAGE4-120*/ - 0x00, 0x00, 0x00, 0x00, /* 380, PAGE4-124 Page 2 Registers Ends Here */ - -}; - -/* - *------------------------------------------------------------------------------ - * aic3262 initialization data - * This structure initialization contains the initialization required for - * AIC326x. - * These registers values (reg_val) are written into the respective AIC3262 - * register offset (reg_offset) to initialize AIC326x. - * These values are used in aic3262_init() function only. - *------------------------------------------------------------------------------ - */ -static const struct aic3262_configs aic3262_reg_init[] = { - /* CLOCKING */ - - {0, RESET_REG, 1}, - {0, RESET_REG, 0}, - - {0, PASI_DAC_DP_SETUP, 0xc0}, /*DAC */ - {0, DAC_MVOL_CONF, 0x00}, /*DAC un-muted*/ - /* set default volumes */ - {0, DAC_LVOL, 0x01}, - {0, DAC_RVOL, 0x01}, - {0, HPL_VOL, 0x80}, - {0, HPR_VOL, 0x80}, - {0, SPK_AMP_CNTL_R2, 0x14}, - {0, SPK_AMP_CNTL_R3, 0x14}, - {0, SPK_AMP_CNTL_R4, 0x33}, - {0, REC_AMP_CNTL_R5, 0x82}, - {0, RAMPR_VOL, 20}, - {0, RAMP_CNTL_R1, 70}, - {0, RAMP_CNTL_R2, 70}, - - /* DRC Defaults */ - {0, DRC_CNTL_R1, 0x6c}, - {0, DRC_CNTL_R2, 16}, - - /* DEPOP SETTINGS */ - {0, HP_DEPOP, 0x14}, - {0, RECV_DEPOP, 0x14}, - - {0, POWER_CONF, 0x00}, /* Disconnecting AVDD-DVD weak link*/ - {0, REF_PWR_DLY, 0x01}, - {0, CM_REG, 0x00}, /*CM - default*/ - {0, LDAC_PTM, 0}, /*LDAC_PTM - default*/ - {0, RDAC_PTM, 0}, /*RDAC_PTM - default*/ - {0, HP_CTL, 0x30}, /*HP output percentage - at 75%*/ - {0, LADC_VOL, 0x01}, /*LADC volume*/ - {0, RADC_VOL, 0x01}, /*RADC volume*/ - - {0, DAC_ADC_CLKIN_REG, 0x33}, /*DAC ADC CLKIN*/ - {0, PLL_CLKIN_REG, 0x00}, /*PLL CLKIN*/ - {0, PLL_PR_POW_REG, 0x11}, /*PLL Power=0-down, P=1, R=1 vals*/ - {0, 0x3d, 1}, - - {0, LMIC_PGA_PIN, 0x0}, /*IN1_L select - - 10k -LMICPGA_P*/ - {0, LMIC_PGA_MIN, 0x40}, /*CM to LMICPGA-M*/ - {0, RMIC_PGA_PIN, 0x0}, /*IN1_R select - - 10k -RMIC_PGA_P*/ - {0, RMIC_PGA_MIN, 0x0}, /*CM to RMICPGA_M*/ - {0, MIC_PWR_DLY , 33}, /*LMIC-PGA-POWERUP-DELAY - default*/ - {0, REF_PWR_DLY, 1}, /*FIXMELATER*/ - - - {0, ADC_CHANNEL_POW, 0x0}, /*ladc, radc ON , SOFT STEP disabled*/ - {0, ADC_FINE_GAIN, 0x00}, /*ladc - unmute, radc - unmute*/ - {0, MICL_PGA, 0x3f}, - {0, MICR_PGA, 0x3f}, - /*controls MicBias ext power based on B0_P1_R51_D6*/ - {0, MIC_BIAS_CNTL, 0x80}, - /* ASI1 Configuration */ - {0, ASI1_BUS_FMT, 0}, - {0, ASI1_BWCLK_CNTL_REG, 0x00}, /* originaly 0x24*/ - {0, ASI1_BCLK_N_CNTL, 1}, - {0, ASI1_BCLK_N, 0x04}, - - {0, MA_CNTL, 0}, /* Mixer Amp disabled */ - {0, LINE_AMP_CNTL_R2, 0x00}, /* Line Amp Cntl disabled */ - - /* ASI2 Configuration */ - {0, ASI2_BUS_FMT, 0}, - {0, ASI2_BCLK_N_CNTL, 0x01}, - {0, ASI2_BCLK_N, 0x04}, - {0, ASI2_BWCLK_OUT_CNTL, 0x20}, - - {0, BEEP_CNTL_R1, 0x05}, - {0, BEEP_CNTL_R2, 0x04}, - - /* Interrupt config for headset detection */ - {0,HEADSET_TUNING1_REG,0x7f}, - {0, INT1_CNTL, 0x40}, - /*{0, TIMER_REG, 0x8c},*/ - {0, INT_FMT, 0x40}, - {0, GPIO1_IO_CNTL, 0x14}, - {0, HP_DETECT, 0x96}, - -#if defined(CONFIG_MINI_DSP) - {0, 60, 0}, - {0, 61, 0}, - /* Added the below set of values after consulting the miniDSP - * Program Section Array - */ - {0, MINIDSP_ACCESS_CTRL, 0x00}, -#endif - - -}; - -static int reg_init_size = - sizeof(aic3262_reg_init) / sizeof(struct aic3262_configs); - -static const unsigned int adc_ma_tlv[] = { -TLV_DB_RANGE_HEAD(4), - 0, 29, TLV_DB_SCALE_ITEM(-1450, 500, 0), - 30, 35, TLV_DB_SCALE_ITEM(-2060, 1000, 0), - 36, 38, TLV_DB_SCALE_ITEM(-2660, 2000, 0), - 39, 40, TLV_DB_SCALE_ITEM(-3610, 5000, 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("IN1L Switch", MA_CNTL, 5, 1, 0), - SOC_DAPM_SINGLE_TLV("Left MicPGA Volume", LADC_PGA_MAL_VOL, 0, - 0x3f, 1, adc_ma_tlv), - -}; - -static const struct snd_kcontrol_new mar_pga_mixer_controls[] = { - SOC_DAPM_SINGLE("IN1R Switch", MA_CNTL, 4, 1, 0), - SOC_DAPM_SINGLE_TLV("Right MicPGA Volume", 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("MAL Switch", HP_AMP_CNTL_R1, 7, 1, 0), - SOC_DAPM_SINGLE("LDAC Switch", HP_AMP_CNTL_R1, 5, 1, 0), - SOC_DAPM_SINGLE_TLV("LOL-B1 Volume", HP_AMP_CNTL_R2, 0, - 0x7f, 0, lo_hp_tlv), -}; - -/* Right HPR Mixer */ -static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { - SOC_DAPM_SINGLE_TLV("LOR-B1 Volume", HP_AMP_CNTL_R3, 0, - 0x7f, 0, lo_hp_tlv), - SOC_DAPM_SINGLE("LDAC Switch", HP_AMP_CNTL_R1, 2, 1, 0), - SOC_DAPM_SINGLE("RDAC Switch", HP_AMP_CNTL_R1, 4, 1, 0), - SOC_DAPM_SINGLE("MAR Switch", HP_AMP_CNTL_R1, 6, 1, 0), -}; - -/* Left LOL Mixer */ -static const struct snd_kcontrol_new lol_output_mixer_controls[] = { - SOC_DAPM_SINGLE("MAL Switch", LINE_AMP_CNTL_R2, 7, 1, 0), - SOC_DAPM_SINGLE("IN1L-B Switch", LINE_AMP_CNTL_R2, 3, 1,0), - SOC_DAPM_SINGLE("LDAC Switch", LINE_AMP_CNTL_R1, 7, 1, 0), - SOC_DAPM_SINGLE("RDAC Switch", LINE_AMP_CNTL_R1, 5, 1, 0), -}; - -/* Right LOR Mixer */ -static const struct snd_kcontrol_new lor_output_mixer_controls[] = { - SOC_DAPM_SINGLE("LOL Switch", LINE_AMP_CNTL_R1, 2, 1, 0), - SOC_DAPM_SINGLE("RDAC Switch", LINE_AMP_CNTL_R1, 6, 1, 0), - SOC_DAPM_SINGLE("MAR Switch", LINE_AMP_CNTL_R2, 6, 1, 0), - SOC_DAPM_SINGLE("IN1R-B Switch", LINE_AMP_CNTL_R2, 0, 1,0), -}; - -/* Left SPKL Mixer */ -static const struct snd_kcontrol_new spkl_output_mixer_controls[] = { - SOC_DAPM_SINGLE("MAL Switch", SPK_AMP_CNTL_R1, 7, 1, 0), - SOC_DAPM_SINGLE_TLV("LOL Volume", SPK_AMP_CNTL_R2, 0, 0x7f,0, - lo_hp_tlv), - SOC_DAPM_SINGLE("SPR_IN Switch", SPK_AMP_CNTL_R1, 2, 1, 0), -}; +/* Left SPKL Mixer */ +static const struct snd_kcontrol_new spkl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("MAL Switch", AIC3262_SPK_AMP_CNTL_R1, + 7, 1, 0), + SOC_DAPM_SINGLE_TLV("LOL 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("LOR Volume", SPK_AMP_CNTL_R3, 0, 0x7f, 0, - lo_hp_tlv), - SOC_DAPM_SINGLE("MAR Switch", SPK_AMP_CNTL_R1, 6, 1, 0), + SOC_DAPM_SINGLE_TLV("LOR Volume", + AIC3262_SPK_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE("MAR 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("LOL-B2 Volume", RAMP_CNTL_R1, 0, 0x7f,0, - lo_hp_tlv), - SOC_DAPM_SINGLE_TLV("IN1L Volume", IN1L_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), - SOC_DAPM_SINGLE_TLV("IN1R Volume", IN1R_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), - SOC_DAPM_SINGLE_TLV("LOR-B2 Volume", RAMP_CNTL_R2, 0,0x7f, 0,lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("LOL-B2 Volume", + AIC3262_RAMP_CNTL_R1, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("IN1L Volume", + AIC3262_IN1L_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("IN1R Volume", + AIC3262_IN1R_SEL_RM, 0, 0x7f, 1, lo_hp_tlv), + SOC_DAPM_SINGLE_TLV("LOR-B2 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("IN1L Switch", LMIC_PGA_PIN, 6, 1, 0), - SOC_DAPM_SINGLE("IN2L Switch", LMIC_PGA_PIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3L Switch", LMIC_PGA_PIN, 2, 1, 0), - SOC_DAPM_SINGLE("IN4L Switch", LMIC_PGA_PM_IN4, 5, 1, 0), - SOC_DAPM_SINGLE("IN1R Switch", LMIC_PGA_PIN, 0, 1, 0), - SOC_DAPM_SINGLE("IN2R Switch", LMIC_PGA_MIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3R Switch", LMIC_PGA_MIN, 2, 1, 0), - SOC_DAPM_SINGLE("IN4R Switch", LMIC_PGA_PM_IN4, 4, 1, 0), - SOC_DAPM_SINGLE("CM2L Switch", LMIC_PGA_MIN, 0, 1, 0), - SOC_DAPM_SINGLE("CM1L Switch", LMIC_PGA_MIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN1L Switch", AIC3262_LMIC_PGA_PIN, + 6, 3, 0), + SOC_DAPM_SINGLE("IN2L Switch", AIC3262_LMIC_PGA_PIN, + 4, 3, 0), + SOC_DAPM_SINGLE("IN3L Switch", AIC3262_LMIC_PGA_PIN, + 2, 3, 0), + SOC_DAPM_SINGLE("IN4L Switch", AIC3262_LMIC_PGA_PM_IN4, + 5, 1, 0), + SOC_DAPM_SINGLE("IN1R Switch", AIC3262_LMIC_PGA_PIN, + 0, 3, 0), + SOC_DAPM_SINGLE("IN2R Switch", AIC3262_LMIC_PGA_MIN, + 4, 3, 0), + SOC_DAPM_SINGLE("IN3R Switch", AIC3262_LMIC_PGA_MIN, + 2, 3, 0), + SOC_DAPM_SINGLE("IN4R Switch", AIC3262_LMIC_PGA_PM_IN4, + 4, 1, 0), + SOC_DAPM_SINGLE("CM2L Switch", AIC3262_LMIC_PGA_MIN, + 0, 3, 0), + SOC_DAPM_SINGLE("CM1L Switch", AIC3262_LMIC_PGA_MIN, + 6, 3, 0), }; /* Right Input Mixer */ static const struct snd_kcontrol_new right_input_mixer_controls[] = { - SOC_DAPM_SINGLE("IN1R Switch", RMIC_PGA_PIN, 6, 1, 0), - SOC_DAPM_SINGLE("IN2R Switch", RMIC_PGA_PIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3R Switch", RMIC_PGA_PIN, 2, 1, 0), - SOC_DAPM_SINGLE("IN4R Switch", RMIC_PGA_PM_IN4, 5, 1, 0), - SOC_DAPM_SINGLE("IN2L Switch", RMIC_PGA_PIN, 0, 1, 0), - SOC_DAPM_SINGLE("IN1L Switch", RMIC_PGA_MIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3L Switch", RMIC_PGA_MIN, 2, 1, 0), - SOC_DAPM_SINGLE("IN4L Switch", RMIC_PGA_PM_IN4, 4, 1, 0), - SOC_DAPM_SINGLE("CM1R Switch", RMIC_PGA_MIN, 6, 1, 0), - SOC_DAPM_SINGLE("CM2R Switch", RMIC_PGA_MIN, 0, 1, 0), + SOC_DAPM_SINGLE("IN1R Switch", AIC3262_RMIC_PGA_PIN, + 6, 3, 0), + SOC_DAPM_SINGLE("IN2R Switch", AIC3262_RMIC_PGA_PIN, + 4, 3, 0), + SOC_DAPM_SINGLE("IN3R Switch", AIC3262_RMIC_PGA_PIN, + 2, 3, 0), + SOC_DAPM_SINGLE("IN4R Switch", AIC3262_RMIC_PGA_PM_IN4, + 5, 1, 0), + SOC_DAPM_SINGLE("IN2L Switch", AIC3262_RMIC_PGA_PIN, + 0, 3, 0), + SOC_DAPM_SINGLE("IN1L Switch", AIC3262_RMIC_PGA_MIN, + 4, 3, 0), + SOC_DAPM_SINGLE("IN3L Switch", AIC3262_RMIC_PGA_MIN, + 2, 3, 0), + SOC_DAPM_SINGLE("IN4L Switch", AIC3262_RMIC_PGA_PM_IN4, + 4, 1, 0), + SOC_DAPM_SINGLE("CM1R Switch", AIC3262_RMIC_PGA_MIN, + 6, 3, 0), + SOC_DAPM_SINGLE("CM2R Switch", AIC3262_RMIC_PGA_MIN, + 0, 3, 0), }; - -static const char *asi1lin_text[] = { - "Off", "ASI1 Left In","ASI1 Right In","ASI1 MonoMix In" +static const char * const asi1lin_text[] = { + "Off", "ASI1 Left In", "ASI1 Right In", "ASI1 MonoMix In" }; -SOC_ENUM_SINGLE_DECL(asi1lin_enum, ASI1_DAC_OUT_CNTL, 6, asi1lin_text); +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); +SOC_DAPM_ENUM("ASI1LIN Route", asi1lin_enum); - -static const char *asi1rin_text[] = { - "Off", "ASI1 Right In","ASI1 Left In","ASI1 MonoMix In" +static const char * const asi1rin_text[] = { + "Off", "ASI1 Right In", "ASI1 Left In", "ASI1 MonoMix In" }; -SOC_ENUM_SINGLE_DECL(asi1rin_enum, ASI1_DAC_OUT_CNTL, 4, asi1rin_text); +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); +SOC_DAPM_ENUM("ASI1RIN Route", asi1rin_enum); -static const char *asi2lin_text[] = { - "Off", "ASI2 Left In","ASI2 Right In","ASI2 MonoMix In" +static const char * const asi2lin_text[] = { + "Off", "ASI2 Left In", "ASI2 Right In", "ASI2 MonoMix In" }; -SOC_ENUM_SINGLE_DECL(asi2lin_enum, ASI2_DAC_OUT_CNTL, 6, asi2lin_text); +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); +SOC_DAPM_ENUM("ASI2LIN Route", asi2lin_enum); -static const char *asi2rin_text[] = { - "Off", "ASI2 Right In","ASI2 Left In","ASI2 MonoMix In" +static const char * const asi2rin_text[] = { + "Off", "ASI2 Right In", "ASI2 Left In", "ASI2 MonoMix In" }; - -SOC_ENUM_SINGLE_DECL(asi2rin_enum, ASI2_DAC_OUT_CNTL, 4, asi2rin_text); +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); +SOC_DAPM_ENUM("ASI2RIN Route", asi2rin_enum); -static const char *asi3lin_text[] = { - "Off", "ASI3 Left In","ASI3 Right In","ASI3 MonoMix In" +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); -SOC_ENUM_SINGLE_DECL(asi3lin_enum, ASI3_DAC_OUT_CNTL, 6, asi3lin_text); static const struct snd_kcontrol_new asi3lin_control = - SOC_DAPM_ENUM("ASI3LIN Route", asi3lin_enum); - +SOC_DAPM_ENUM("ASI3LIN Route", asi3lin_enum); -static const char *asi3rin_text[] = { - "Off", "ASI3 Right In","ASI3 Left In","ASI3 MonoMix In" +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); -SOC_ENUM_SINGLE_DECL(asi3rin_enum, ASI3_DAC_OUT_CNTL, 4, asi3rin_text); static const struct snd_kcontrol_new asi3rin_control = - SOC_DAPM_ENUM("ASI3RIN Route", asi3rin_enum); +SOC_DAPM_ENUM("ASI3RIN Route", asi3rin_enum); - -static const char *dacminidspin1_text[] = { - "ASI1 In", "ASI2 In","ASI3 In","ADC MiniDSP Out" +static const char * const dacminidspin1_text[] = { + "ASI1 In", "ASI2 In", "ASI3 In", "ADC MiniDSP Out" }; -SOC_ENUM_SINGLE_DECL(dacminidspin1_enum, MINIDSP_PORT_CNTL_REG, 4, dacminidspin1_text); +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); +SOC_DAPM_ENUM("DAC MiniDSP IN1 Route", dacminidspin1_enum); -static const char *dacminidspin2_text[] = { - "ASI1 In", "ASI2 In","ASI3 In" +static const char * const dacminidspin2_text[] = { + "ASI1 In", "ASI2 In", "ASI3 In" }; -//static const struct soc_enum dacminidspin1_enum = -// SOC_ENUM_SINGLE(MINIDSP_DATA_PORT_CNTL, 5, 2, dacminidspin1_text); -SOC_ENUM_SINGLE_DECL(dacminidspin2_enum, MINIDSP_PORT_CNTL_REG, 2, dacminidspin2_text); +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); +SOC_DAPM_ENUM("DAC MiniDSP IN2 Route", dacminidspin2_enum); -static const char *dacminidspin3_text[] = { - "ASI1 In", "ASI2 In","ASI3 In" +static const char * const dacminidspin3_text[] = { + "ASI1 In", "ASI2 In", "ASI3 In" }; -//static const struct soc_enum dacminidspin1_enum = -// SOC_ENUM_SINGLE(MINIDSP_DATA_PORT_CNTL, 5, 2, dacminidspin1_text); -SOC_ENUM_SINGLE_DECL(dacminidspin3_enum, MINIDSP_PORT_CNTL_REG, 0, dacminidspin3_text); +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 *asi1out_text[] = { +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_VIRT("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, ASI1_ADC_INPUT_CNTL, 0, asi1out_text); + +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); +SOC_DAPM_ENUM("ASI1OUT Route", asi1out_enum); -static const char *asi2out_text[] = { +static const char * const asi2out_text[] = { "Off", - "ASI1 Out", + "ADC MiniDSP Out1", "ASI1In Bypass", "ASI2In Bypass", "ASI3In Bypass", - "ASI2 Out", + "ADC MiniDSP Out2", }; -SOC_ENUM_SINGLE_DECL(asi2out_enum, ASI2_ADC_INPUT_CNTL, 0, asi2out_text); + +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 *asi3out_text[] = { +SOC_DAPM_ENUM("ASI2OUT Route", asi2out_enum); +static const char * const asi3out_text[] = { "Off", - "ASI1 Out", + "ADC MiniDSP Out1", "ASI1In Bypass", "ASI2In Bypass", "ASI3In Bypass", - "ASI3 Out", + "Reserved", + "ADC MiniDSP Out3", }; -SOC_ENUM_SINGLE_DECL(asi3out_enum, 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 *asi1bclk_text[] = { +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, ASI1_BCLK_N_CNTL, 0, asi1bclk_text); +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_DAPM_ENUM("ASI1_BCLK Route", asi1bclk_enum); -static const char *asi2bclk_text[] = { - "DAC_CLK", - "DAC_MOD_CLK", - "ADC_CLK", - "ADC_MOD_CLK", -}; -SOC_ENUM_SINGLE_DECL(asi2bclk_enum, ASI2_BCLK_N_CNTL, 0, asi2bclk_text); +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); -static const char *asi3bclk_text[] = { - "DAC_CLK", - "DAC_MOD_CLK", - "ADC_CLK", - "ADC_MOD_CLK", -}; -SOC_ENUM_SINGLE_DECL(asi3bclk_enum, ASI3_BCLK_N_CNTL, 0, asi3bclk_text); +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); +SOC_DAPM_ENUM("ASI3_BCLK Route", asi3bclk_enum); -static int aic326x_hp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - return 0; -} -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; -} +static const char * const adc_mux_text[] = { + "Analog", + "Digital", +}; -static int polling_loop(struct snd_soc_codec *codec, unsigned int reg, - int mask, int on_off) -{ - unsigned int counter, status; - - counter = 0; - switch(on_off) { - case 0: /*off*/ - do { - status = snd_soc_read(codec, reg); - counter++; - } while ((counter < 500) && ((status & mask) == mask)); - break; - case 1: /*on*/ - do { - status = snd_soc_read(codec, reg); - counter++; - } while ((counter < 500) && ((status & mask) != mask)); - break; - default: - printk("%s: unknown arguement\n", __func__); - break; - } +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); - printk("%s: exiting with count value %d \n", __func__, counter); - if(counter >= 500) - return -1; - return 0; -} +static const struct snd_kcontrol_new adcl_mux = +SOC_DAPM_ENUM("Left ADC Route", adcl_enum); -int poll_dac(struct snd_soc_codec *codec, int left_right, int on_off) -{ - int ret = 0; +static const struct snd_kcontrol_new adcr_mux = +SOC_DAPM_ENUM("Right ADC Route", adcr_enum); - aic3262_change_page(codec, 0); - aic3262_change_book(codec, 0); - - switch(on_off) { - - case 0:/*power off polling*/ - /*DAC power polling logic*/ - switch(left_right) { - case 0: /*left dac polling*/ - ret = polling_loop(codec, DAC_FLAG_R1, LDAC_POW_FLAG_MASK, 0); - break; - case 1:/*right dac polling*/ - ret = polling_loop(codec, DAC_FLAG_R1, RDAC_POW_FLAG_MASK, 0); - break; - } - break; - case 1:/*power on polling*/ - /*DAC power polling logic*/ - switch(left_right) { - case 0: /*left dac polling*/ - ret = polling_loop(codec, DAC_FLAG_R1, LDAC_POW_FLAG_MASK, 1); - break; - case 1:/*right dac polling*/ - ret = polling_loop(codec, DAC_FLAG_R1, RDAC_POW_FLAG_MASK, 1); - break; - } +/** + * 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) +{ + int reg_mask = 0; + int ret_wbits = 0; + + if (w->shift == 1) + reg_mask = AIC3262_HPL_POWER_MASK; + if (w->shift == 0) + reg_mask = AIC3262_HPR_POWER_MASK; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_HP_FLAG, reg_mask, + reg_mask, TIME_DELAY, + DELAY_COUNTER); + if (!ret_wbits) { + dev_err(w->codec->dev, "HP POST_PMU timedout\n"); + return -1; + } break; - default: - printk("%s:unknown arguement\n", __func__); - break; + case SND_SOC_DAPM_POST_PMD: + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_HP_FLAG, reg_mask, 0, + TIME_DELAY, DELAY_COUNTER); + if (!ret_wbits) { + dev_err(w->codec->dev, "HP POST_PMD timedout\n"); + return -1; } - if(ret) - printk("%s: power %s %s failure", __func__, left_right?"right":"left", on_off?"on":"off"); - return ret; + break; + default: + BUG(); + return -EINVAL; + } + return 0; } -int poll_adc(struct snd_soc_codec *codec, int left_right, int on_off) -{ - int ret = 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) +{ + int reg_mask = 0; + int ret_wbits = 0; + int run_state_mask; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(w->codec); + int sync_needed = 0, non_sync_state = 0; + int other_dsp = 0, run_state = 0; + + if (w->shift == 7) { + reg_mask = AIC3262_LDAC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_D_L; + } + if (w->shift == 6) { + reg_mask = AIC3262_RDAC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_D_R; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_DAC_FLAG, reg_mask, + reg_mask, TIME_DELAY, + DELAY_COUNTER); + + sync_needed = SYNC_STATE(aic3262); + non_sync_state = DSP_NON_SYNC_MODE(aic3262->dsp_runstate); + other_dsp = aic3262->dsp_runstate & AIC3262_COPS_MDSP_A; + + if (sync_needed && non_sync_state && other_dsp) { + run_state = get_runstate(aic3262->codec->control_data); + aic3262_dsp_pwrdwn_status(aic3262); + aic3262_dsp_pwrup(aic3262, run_state); + } + aic3262->dsp_runstate |= run_state_mask; - aic3262_change_page(codec, 0); - aic3262_change_book(codec, 0); + if (!ret_wbits) { + dev_err(w->codec->dev, "DAC POST_PMU timedout\n"); + return -1; + } + break; + case SND_SOC_DAPM_POST_PMD: - switch(on_off) { + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_DAC_FLAG, reg_mask, 0, + TIME_DELAY, DELAY_COUNTER); - case 0:/*power off polling*/ - /*DAC power polling logic*/ - switch(left_right) { - case 0: /*left dac polling*/ - ret = polling_loop(codec, ADC_FLAG_R1, LADC_POW_FLAG_MASK, 0); - break; - case 1:/*right dac polling*/ - ret = polling_loop(codec, ADC_FLAG_R1, RADC_POW_FLAG_MASK, 0); - break; - } - break; - case 1:/*power on polling*/ - /*DAC power polling logic*/ - switch(left_right) { - case 0: /*left dac polling*/ - ret = polling_loop(codec, ADC_FLAG_R1, LADC_POW_FLAG_MASK, 1); - break; - case 1:/*right dac polling*/ - ret = polling_loop(codec, ADC_FLAG_R1, RADC_POW_FLAG_MASK, 1); - break; + aic3262->dsp_runstate = (aic3262->dsp_runstate & + ~run_state_mask); + if (!ret_wbits) { + dev_err(w->codec->dev, "DAC POST_PMD timedout\n"); + return -1; } - break; - default: - printk("%s:unknown arguement\n", __func__); - break; + break; + default: + BUG(); + return -EINVAL; } + return 0; +} - if(ret) - printk("%s: power %s %s failure", __func__, left_right?"right":"left", on_off?"on":"off"); - return ret; +/** + * 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_MASK; + if (w->shift == 0) + reg_mask = AIC3262_SPKR_POWER_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; } -static int slave_dac_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +/**$ + * 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_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3262_priv *priv_ds = snd_soc_codec_get_drvdata(codec); - if (event & SND_SOC_DAPM_POST_PMU) { - /* Poll for DAC Power-up first */ - poll_dac(codec, 0, 1); - poll_dac(codec, 1, 1); - } + ucontrol->value.integer.value[0] = ((priv_ds->cfw_p->cur_mode << 8) + | priv_ds->cfw_p->cur_cfg); - if (event & SND_SOC_DAPM_POST_PMD) { - poll_dac(codec, 0, 0); - poll_dac(codec, 1, 0); - } return 0; } - -static int slave_adc_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) - +/** + * 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_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3262_priv *priv_ds = snd_soc_codec_get_drvdata(codec); - if (event & SND_SOC_DAPM_POST_PMU) { + int next_mode = 0, next_cfg = 0; + int ret = 0; - /* Poll for ADC Power-up first */ - poll_adc(codec, 0, 1); - poll_adc(codec, 1, 1); - } + 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; +} - if (event & SND_SOC_DAPM_POST_PMD) { - poll_adc(codec, 0, 0); - poll_adc(codec, 1, 0); +/** + * 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) +{ + int run_state = 0; + int non_sync_state = 0, sync_needed = 0; + int other_dsp = 0; + int run_state_mask = 0; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(w->codec); + int reg_mask = 0; + int ret_wbits = 0; + + if (w->shift == 7) { + reg_mask = AIC3262_LADC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_A_L; + } + if (w->shift == 6) { + reg_mask = AIC3262_RADC_POWER_MASK; + run_state_mask = AIC3262_COPS_MDSP_A_R; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_ADC_FLAG, reg_mask, + reg_mask, TIME_DELAY, + DELAY_COUNTER); + sync_needed = SYNC_STATE(aic3262); + non_sync_state = DSP_NON_SYNC_MODE(aic3262->dsp_runstate); + other_dsp = aic3262->dsp_runstate & AIC3262_COPS_MDSP_D; + if (sync_needed && non_sync_state && other_dsp) { + run_state = get_runstate(aic3262->codec->control_data); + aic3262_dsp_pwrdwn_status(aic3262); + aic3262_dsp_pwrup(aic3262, run_state); + } + aic3262->dsp_runstate |= run_state_mask; + if (!ret_wbits) { + dev_err(w->codec->dev, "ADC POST_PMU timedout\n"); + return -1; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret_wbits = aic3262_wait_bits(w->codec->control_data, + AIC3262_ADC_FLAG, reg_mask, 0, + TIME_DELAY, DELAY_COUNTER); + aic3262->dsp_runstate = (aic3262->dsp_runstate & + ~run_state_mask); + if (!ret_wbits) { + dev_err(w->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("ASI1IN", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_IN("ASI2IN", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_IN("ASI3IN", "ASI3 Playback", 0, SND_SOC_NOPM, 0, 0), + 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, PASI_DAC_DP_SETUP, 7, 0, - slave_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_DAC_E("Right DAC", NULL, PASI_DAC_DP_SETUP, 6, 0, - slave_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMD), + 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("HPL Output Mixer", SND_SOC_NOPM, 0, 0, - &hpl_output_mixer_controls[0], - ARRAY_SIZE(hpl_output_mixer_controls)), + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), /* dapm widget (path domain) for HPR Output Mixer */ SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, - &hpr_output_mixer_controls[0], - ARRAY_SIZE(hpr_output_mixer_controls)), + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), - SND_SOC_DAPM_PGA_E("HPL Driver", HP_AMP_CNTL_R1, 1, 0, NULL, 0, - aic326x_hp_event, SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_PGA_E("HPR Driver", HP_AMP_CNTL_R1, 0, 0, NULL, 0, - aic326x_hp_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("HPL Driver", AIC3262_HP_AMP_CNTL_R1, + 1, 0, NULL, 0, aic326x_hp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPR Driver", AIC3262_HP_AMP_CNTL_R1, + 0, 0, NULL, 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("LOL Output Mixer", SND_SOC_NOPM, 0, 0, - &lol_output_mixer_controls[0], - ARRAY_SIZE(lol_output_mixer_controls)), + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), /* dapm widget (path domain) for LOR Output Mixer mixer */ SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, - &lor_output_mixer_controls[0], - ARRAY_SIZE(lor_output_mixer_controls)), + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), - SND_SOC_DAPM_PGA("LOL Driver", LINE_AMP_CNTL_R1, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("LOR Driver", LINE_AMP_CNTL_R1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("LOL Driver", AIC3262_LINE_AMP_CNTL_R1, + 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("LOR Driver", AIC3262_LINE_AMP_CNTL_R1, + 0, 0, NULL, 0), /* dapm widget (path domain) for SPKL Output Mixer */ SND_SOC_DAPM_MIXER("SPKL Output Mixer", SND_SOC_NOPM, 0, 0, - &spkl_output_mixer_controls[0], - ARRAY_SIZE(spkl_output_mixer_controls)), + &spkl_output_mixer_controls[0], + ARRAY_SIZE(spkl_output_mixer_controls)), /* dapm widget (path domain) for SPKR Output Mixer */ SND_SOC_DAPM_MIXER("SPKR Output Mixer", SND_SOC_NOPM, 0, 0, - &spkr_output_mixer_controls[0], - ARRAY_SIZE(spkr_output_mixer_controls)), + &spkr_output_mixer_controls[0], + ARRAY_SIZE(spkr_output_mixer_controls)), - SND_SOC_DAPM_PGA("SPKL Driver", SPK_AMP_CNTL_R1, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("SPKR Driver", SPK_AMP_CNTL_R1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("SPKL Driver", AIC3262_SPK_AMP_CNTL_R1, + 1, 0, NULL, 0, aic326x_spk_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("SPKR Driver", AIC3262_SPK_AMP_CNTL_R1, + 0, 0, NULL, 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 Output Mixer", SND_SOC_NOPM, 0, 0, - &rec_output_mixer_controls[0], - ARRAY_SIZE(rec_output_mixer_controls)), + &rec_output_mixer_controls[0], + ARRAY_SIZE(rec_output_mixer_controls)), - SND_SOC_DAPM_PGA("RECP Driver", REC_AMP_CNTL_R5, 7, 0, NULL, 0), - SND_SOC_DAPM_PGA("RECM Driver", REC_AMP_CNTL_R5, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("RECP Driver", AIC3262_REC_AMP_CNTL_R5, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("RECM Driver", AIC3262_REC_AMP_CNTL_R5, + 6, 0, NULL, 0), SND_SOC_DAPM_MUX("ASI1LIN Route", - SND_SOC_NOPM, 0, 0, &asi1lin_control), + SND_SOC_NOPM, 0, 0, &asi1lin_control), SND_SOC_DAPM_MUX("ASI1RIN Route", - SND_SOC_NOPM, 0, 0, &asi1rin_control), + SND_SOC_NOPM, 0, 0, &asi1rin_control), SND_SOC_DAPM_MUX("ASI2LIN Route", - SND_SOC_NOPM, 0, 0, &asi2lin_control), + SND_SOC_NOPM, 0, 0, &asi2lin_control), SND_SOC_DAPM_MUX("ASI2RIN Route", - SND_SOC_NOPM, 0, 0, &asi2rin_control), + SND_SOC_NOPM, 0, 0, &asi2rin_control), SND_SOC_DAPM_MUX("ASI3LIN Route", - SND_SOC_NOPM, 0, 0, &asi3lin_control), + SND_SOC_NOPM, 0, 0, &asi3lin_control), SND_SOC_DAPM_MUX("ASI3RIN Route", - SND_SOC_NOPM, 0, 0, &asi3rin_control), + 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), @@ -3030,14 +1078,6 @@ static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = { 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("ASI1LOUT", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ASI1ROUT", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ASI2LOUT", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ASI2ROUT", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ASI3LOUT", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ASI3ROUT", 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), @@ -3046,13 +1086,15 @@ static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = { 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_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_NOPM, 0, 0, &dacminidspin3_control), + + SND_SOC_DAPM_VIRT_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("CM1L", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -3061,59 +1103,74 @@ SND_SOC_DAPM_MUX("DAC MiniDSP IN2 Route", SND_SOC_DAPM_PGA("CM2R", SND_SOC_NOPM, 0, 0, NULL, 0), /* TODO: Can we switch these off ? */ - SND_SOC_DAPM_AIF_OUT("ASI1OUT","ASI1 Capture", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("ASI2OUT", "ASI2 Capture",0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("ASI3OUT", "ASI3 Capture",0, SND_SOC_NOPM, 0, 0), + 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_NOPM, 0, 0, &asi1out_control), SND_SOC_DAPM_MUX("ASI2OUT Route", - SND_SOC_NOPM, 0, 0, &asi2out_control), + SND_SOC_NOPM, 0, 0, &asi2out_control), SND_SOC_DAPM_MUX("ASI3OUT Route", - SND_SOC_NOPM, 0, 0, &asi3out_control), + SND_SOC_NOPM, 0, 0, &asi3out_control), - /* TODO: Will be used during MINIDSP programming */ + /* 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("DMICDAT Input Route", + SND_SOC_NOPM, 0, 0, &dmicinput_control),*/ + 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, ADC_CHANNEL_POW, 7, 0, - slave_adc_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("Right ADC", NULL, ADC_CHANNEL_POW, 6, 0, - slave_adc_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_PGA("Left MicPGA",MICL_PGA, 7, 1, NULL, 0), - SND_SOC_DAPM_PGA("Right MicPGA",MICR_PGA, 7, 1, NULL, 0), + 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("MAL PGA", MA_CNTL, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("MAR PGA", MA_CNTL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left MicPGA", AIC3262_MICL_PGA, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right MicPGA", AIC3262_MICR_PGA, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("MAL PGA", AIC3262_MA_CNTL, + 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("MAR PGA", AIC3262_MA_CNTL, + 2, 0, NULL, 0), - /* dapm widget for MAL PGA Mixer*/ + /* dapm widget for MAL PGA Mixer */ SND_SOC_DAPM_MIXER("MAL PGA Mixer", SND_SOC_NOPM, 0, 0, - &mal_pga_mixer_controls[0], - ARRAY_SIZE(mal_pga_mixer_controls)), + &mal_pga_mixer_controls[0], + ARRAY_SIZE(mal_pga_mixer_controls)), - /* dapm widget for MAR PGA Mixer*/ + /* dapm widget for MAR PGA Mixer */ SND_SOC_DAPM_MIXER("MAR PGA Mixer", SND_SOC_NOPM, 0, 0, - &mar_pga_mixer_controls[0], - ARRAY_SIZE(mar_pga_mixer_controls)), + &mar_pga_mixer_controls[0], + ARRAY_SIZE(mar_pga_mixer_controls)), - /* dapm widget for Left Input Mixer*/ + /* 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)), + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), - /* dapm widget for Right Input Mixer*/ + /* 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)), - + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), @@ -3132,31 +1189,36 @@ SND_SOC_DAPM_MUX("DAC MiniDSP IN2 Route", SND_SOC_DAPM_INPUT("IN2R"), SND_SOC_DAPM_INPUT("IN3R"), SND_SOC_DAPM_INPUT("IN4R"), - - - SND_SOC_DAPM_MICBIAS("Mic Bias Ext", MIC_BIAS_CNTL, 6, 0), - SND_SOC_DAPM_MICBIAS("Mic Bias Int", MIC_BIAS_CNTL, 2, 0), - - SND_SOC_DAPM_SUPPLY("PLLCLK",PLL_PR_POW_REG,7,0,pll_power_on_event, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_SUPPLY("DACCLK",NDAC_DIV_POW_REG,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("CODEC_CLK_IN",SND_SOC_NOPM,0,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("DAC_MOD_CLK",MDAC_DIV_POW_REG,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADCCLK",NADC_DIV_POW_REG,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK",MADC_DIV_POW_REG,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI1_BCLK",ASI1_BCLK_N,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI1_WCLK",ASI1_WCLK_N,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI2_BCLK",ASI2_BCLK_N,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI2_WCLK",ASI2_WCLK_N,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI3_BCLK",ASI3_BCLK_N,7,0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ASI3_WCLK",ASI3_WCLK_N,7,0, NULL, 0), + SND_SOC_DAPM_INPUT("Left DMIC"), + SND_SOC_DAPM_INPUT("Right DMIC"), + + 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_SUPPLY("PLLCLK", AIC3262_PLL_PR_POW_REG, 7, 0, + pll_power_on_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DACCLK", AIC3262_NDAC_DIV_POW_REG, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CODEC_CLK_IN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC_MOD_CLK", AIC3262_MDAC_DIV_POW_REG, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCCLK", AIC3262_NADC_DIV_POW_REG, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", AIC3262_MADC_DIV_POW_REG, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI1_BCLK", AIC3262_ASI1_BCLK_N, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI1_WCLK", AIC3262_ASI1_WCLK_N, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI2_BCLK", AIC3262_ASI2_BCLK_N, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI2_WCLK", AIC3262_ASI2_WCLK_N, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI3_BCLK", AIC3262_ASI3_BCLK_N, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASI3_WCLK", 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), + 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[] ={ +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"}, @@ -3164,185 +1226,179 @@ static const struct snd_soc_dapm_route aic3262_dapm_routes[] ={ {"ADCCLK", NULL, "CODEC_CLK_IN"}, {"DAC_MOD_CLK", NULL, "DACCLK"}, #ifdef AIC3262_SYNC_MODE - {"ADC_MOD_CLK", NULL,"DACCLK"}, + {"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"}, + {"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"}, + {"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"}, + {"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"}, - - {"ASI1IN", NULL , "PLLCLK"}, - {"ASI1IN", NULL , "DACCLK"}, - {"ASI1IN", NULL , "ADCCLK"}, - {"ASI1IN", NULL , "DAC_MOD_CLK"}, - {"ASI1IN", NULL , "ADC_MOD_CLK"}, - - {"ASI1OUT", NULL , "PLLCLK"}, - {"ASI1OUT", NULL , "DACCLK"}, - {"ASI1OUT", NULL , "ADCCLK"}, - {"ASI1OUT", NULL , "DAC_MOD_CLK"}, - {"ASI1OUT", NULL , "ADC_MOD_CLK"}, + {"DIN1", NULL, "PLLCLK"}, + {"DIN1", NULL, "DACCLK"}, + {"DIN1", NULL, "ADCCLK"}, + {"DIN1", NULL, "DAC_MOD_CLK"}, + {"DIN1", NULL, "ADC_MOD_CLK"}, + + {"DOUT1", NULL, "PLLCLK"}, + {"DOUT1", NULL, "DACCLK"}, + {"DOUT1", NULL, "ADCCLK"}, + {"DOUT1", NULL, "DAC_MOD_CLK"}, + {"DOUT1", NULL, "ADC_MOD_CLK"}, #ifdef AIC3262_ASI1_MASTER - {"ASI1IN", NULL , "ASI1_BCLK"}, - {"ASI1OUT", NULL , "ASI1_BCLK"}, - {"ASI1IN", NULL , "ASI1_WCLK"}, - {"ASI1OUT", NULL , "ASI1_WCLK"}, + {"DIN1", NULL, "ASI1_BCLK"}, + {"DOUT1", NULL, "ASI1_BCLK"}, + {"DIN1", NULL, "ASI1_WCLK"}, + {"DOUT1", NULL, "ASI1_WCLK"}, #else #endif - - {"ASI2IN", NULL , "PLLCLK"}, - {"ASI2IN", NULL , "DACCLK"}, - {"ASI2IN", NULL , "ADCCLK"}, - {"ASI2IN", NULL , "DAC_MOD_CLK"}, - {"ASI2IN", NULL , "ADC_MOD_CLK"}, - - {"ASI2OUT", NULL , "PLLCLK"}, - {"ASI2OUT", NULL , "DACCLK"}, - {"ASI2OUT", NULL , "ADCCLK"}, - {"ASI2OUT", NULL , "DAC_MOD_CLK"}, - {"ASI2OUT", NULL , "ADC_MOD_CLK"}, + {"DIN2", NULL, "PLLCLK"}, + {"DIN2", NULL, "DACCLK"}, + {"DIN2", NULL, "ADCCLK"}, + {"DIN2", NULL, "DAC_MOD_CLK"}, + {"DIN2", NULL, "ADC_MOD_CLK"}, + + {"DOUT2", NULL, "PLLCLK"}, + {"DOUT2", NULL, "DACCLK"}, + {"DOUT2", NULL, "ADCCLK"}, + {"DOUT2", NULL, "DAC_MOD_CLK"}, + {"DOUT2", NULL, "ADC_MOD_CLK"}, #ifdef AIC3262_ASI2_MASTER - {"ASI2IN", NULL , "ASI2_BCLK"}, - {"ASI2OUT", NULL , "ASI2_BCLK"}, - {"ASI2IN", NULL , "ASI2_WCLK"}, - {"ASI2OUT", NULL , "ASI2_WCLK"}, + {"DIN2", NULL, "ASI2_BCLK"}, + {"DOUT2", NULL, "ASI2_BCLK"}, + {"DIN2", NULL, "ASI2_WCLK"}, + {"DOUT2", NULL, "ASI2_WCLK"}, #else #endif - {"ASI3IN", NULL , "PLLCLK"}, - {"ASI3IN", NULL , "DACCLK"}, - {"ASI3IN", NULL , "ADCCLK"}, - {"ASI3IN", NULL , "DAC_MOD_CLK"}, - {"ASI3IN", NULL , "ADC_MOD_CLK"}, - - - {"ASI3OUT", NULL , "PLLCLK"}, - {"ASI3OUT", NULL , "DACCLK"}, - {"ASI3OUT", NULL , "ADCCLK"}, - {"ASI3OUT", NULL , "DAC_MOD_CLK"}, - {"ASI3OUT", NULL , "ADC_MOD_CLK"}, + {"DIN3", NULL, "PLLCLK"}, + {"DIN3", NULL, "DACCLK"}, + {"DIN3", NULL, "ADCCLK"}, + {"DIN3", NULL, "DAC_MOD_CLK"}, + {"DIN3", NULL, "ADC_MOD_CLK"}, + + {"DOUT3", NULL, "PLLCLK"}, + {"DOUT3", NULL, "DACCLK"}, + {"DOUT3", NULL, "ADCCLK"}, + {"DOUT3", NULL, "DAC_MOD_CLK"}, + {"DOUT3", NULL, "ADC_MOD_CLK"}, #ifdef AIC3262_ASI3_MASTER - {"ASI3IN", NULL , "ASI3_BCLK"}, - {"ASI3OUT", NULL , "ASI3_BCLK"}, - {"ASI3IN", NULL , "ASI3_WCLK"}, - {"ASI3OUT", NULL , "ASI3_WCLK"}, + {"DIN3", NULL, "ASI3_BCLK"}, + {"DOUT3", NULL, "ASI3_BCLK"}, + {"DIN3", NULL, "ASI3_WCLK"}, + {"DOUT3", NULL, "ASI3_WCLK"}, #else -#endif -/* Playback (DAC) Portion */ - {"HPL Output Mixer","LDAC Switch","Left DAC"}, - {"HPL Output Mixer","MAL Switch","MAL PGA"}, - {"HPL Output Mixer","LOL-B1 Volume","LOL"}, - - {"HPR Output Mixer","LOR-B1 Volume","LOR"}, - {"HPR Output Mixer","LDAC Switch","Left DAC"}, - {"HPR Output Mixer","RDAC Switch","Right DAC"}, - {"HPR Output Mixer","MAR Switch","MAR PGA"}, - - {"HPL Driver",NULL,"HPL Output Mixer"}, - {"HPR Driver",NULL,"HPR Output Mixer"}, - - {"HPL",NULL,"HPL Driver"}, - {"HPR",NULL,"HPR Driver"}, +#endif + /* Playback (DAC) Portion */ + {"HPL Output Mixer", "LDAC Switch", "Left DAC"}, + {"HPL Output Mixer", "MAL Switch", "MAL PGA"}, + {"HPL Output Mixer", "LOL-B1 Volume", "LOL"}, - {"LOL Output Mixer","MAL Switch","MAL PGA"}, - {"LOL Output Mixer","IN1L-B Switch","IN1L"}, - {"LOL Output Mixer","LDAC Switch","Left DAC"}, - {"LOL Output Mixer","RDAC Switch","Right DAC"}, + {"HPR Output Mixer", "LOR-B1 Volume", "LOR"}, + {"HPR Output Mixer", "LDAC Switch", "Left DAC"}, + {"HPR Output Mixer", "RDAC Switch", "Right DAC"}, + {"HPR Output Mixer", "MAR Switch", "MAR PGA"}, - {"LOR Output Mixer","LOL Switch","LOL"}, - {"LOR Output Mixer","RDAC Switch","Right DAC"}, - {"LOR Output Mixer","MAR Switch","MAR PGA"}, - {"LOR Output Mixer","IN1R-B Switch","IN1R"}, + {"HPL Driver", NULL, "HPL Output Mixer"}, + {"HPR Driver", NULL, "HPR Output Mixer"}, - {"LOL Driver",NULL,"LOL Output Mixer"}, - {"LOR Driver",NULL,"LOR Output Mixer"}, + {"HPL", NULL, "HPL Driver"}, + {"HPR", NULL, "HPR Driver"}, - {"LOL",NULL,"LOL Driver"}, - {"LOR",NULL,"LOR Driver"}, + {"LOL Output Mixer", "MAL Switch", "MAL PGA"}, + {"LOL Output Mixer", "IN1L-B Switch", "IN1L"}, + {"LOL Output Mixer", "LDAC Switch", "Left DAC"}, + {"LOL Output Mixer", "RDAC Switch", "Right DAC"}, - {"REC Output Mixer","LOL-B2 Volume","LOL"}, - {"REC Output Mixer","IN1L Volume","IN1L"}, - {"REC Output Mixer","IN1R Volume","IN1R"}, - {"REC Output Mixer","LOR-B2 Volume","LOR"}, + {"LOR Output Mixer", "LOL Switch", "LOL"}, + {"LOR Output Mixer", "RDAC Switch", "Right DAC"}, + {"LOR Output Mixer", "MAR Switch", "MAR PGA"}, + {"LOR Output Mixer", "IN1R-B Switch", "IN1R"}, - {"RECP Driver",NULL,"REC Output Mixer"}, - {"RECM Driver",NULL,"REC Output Mixer"}, + {"LOL Driver", NULL, "LOL Output Mixer"}, + {"LOR Driver", NULL, "LOR Output Mixer"}, - {"RECP",NULL,"RECP Driver"}, - {"RECM",NULL,"RECM Driver"}, + {"LOL", NULL, "LOL Driver"}, + {"LOR", NULL, "LOR Driver"}, - {"SPKL Output Mixer","MAL Switch","MAL PGA"}, - {"SPKL Output Mixer","LOL Volume","LOL"}, - {"SPKL Output Mixer","SPR_IN Switch","SPKR Output Mixer"}, + {"REC Output Mixer", "LOL-B2 Volume", "LOL"}, + {"REC Output Mixer", "IN1L Volume", "IN1L"}, + {"REC Output Mixer", "IN1R Volume", "IN1R"}, + {"REC Output Mixer", "LOR-B2 Volume", "LOR"}, - {"SPKR Output Mixer", "LOR Volume","LOR"}, - {"SPKR Output Mixer", "MAR Switch","MAR PGA"}, + {"RECP Driver", NULL, "REC Output Mixer"}, + {"RECM Driver", NULL, "REC Output Mixer"}, + {"RECP", NULL, "RECP Driver"}, + {"RECM", NULL, "RECM Driver"}, - {"SPKL Driver",NULL,"SPKL Output Mixer"}, - {"SPKR Driver",NULL,"SPKR Output Mixer"}, + {"SPKL Output Mixer", "MAL Switch", "MAL PGA"}, + {"SPKL Output Mixer", "LOL Volume", "LOL"}, + {"SPKL Output Mixer", "SPR_IN Switch", "SPKR Output Mixer"}, - {"SPKL",NULL,"SPKL Driver"}, - {"SPKR",NULL,"SPKR Driver"}, -/* ASI Input routing */ - {"ASI1LIN", NULL, "ASI1IN"}, - {"ASI1RIN", NULL, "ASI1IN"}, - {"ASI2LIN", NULL, "ASI2IN"}, - {"ASI2RIN", NULL, "ASI2IN"}, - {"ASI3LIN", NULL, "ASI3IN"}, - {"ASI3RIN", NULL, "ASI3IN"}, + {"SPKR Output Mixer", "LOR Volume", "LOR"}, + {"SPKR Output Mixer", "MAR Switch", "MAR PGA"}, - {"ASI1MonoMixIN", NULL, "ASI1IN"}, - {"ASI2MonoMixIN", NULL, "ASI2IN"}, - {"ASI3MonoMixIN", NULL, "ASI3IN"}, - {"ASI1LIN Route","ASI1 Left In","ASI1LIN"}, - {"ASI1LIN Route","ASI1 Right In","ASI1RIN"}, - {"ASI1LIN Route","ASI1 MonoMix In","ASI1MonoMixIN"}, + {"SPKL Driver", NULL, "SPKL Output Mixer"}, + {"SPKR Driver", NULL, "SPKR Output Mixer"}, - {"ASI1RIN Route", "ASI1 Right In","ASI1RIN"}, - {"ASI1RIN Route","ASI1 Left In","ASI1LIN"}, - {"ASI1RIN Route","ASI1 MonoMix In","ASI1MonoMixIN"}, + {"SPKL", NULL, "SPKL Driver"}, + {"SPKR", NULL, "SPKR 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"}, - {"ASI2LIN Route","ASI2 Left In","ASI2LIN"}, - {"ASI2LIN Route","ASI2 Right In","ASI2RIN"}, - {"ASI2LIN Route","ASI2 MonoMix In","ASI2MonoMixIN"}, + {"ASI1RIN Route", "ASI1 Right In", "ASI1RIN"}, + {"ASI1RIN Route", "ASI1 Left In", "ASI1LIN"}, + {"ASI1RIN Route", "ASI1 MonoMix In", "ASI1MonoMixIN"}, - {"ASI2RIN Route","ASI2 Right In","ASI2RIN"}, - {"ASI2RIN Route","ASI2 Left In","ASI2LIN"}, - {"ASI2RIN Route","ASI2 MonoMix In","ASI2MonoMixIN"}, + {"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"}, + {"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"}, + {"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"}, @@ -3351,83 +1407,94 @@ static const struct snd_soc_dapm_route aic3262_dapm_routes[] ={ {"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 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 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"}, + {"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"}, - {"Left DAC", "NULL","DAC MiniDSP IN2 Route"}, - {"Right DAC", "NULL","DAC MiniDSP IN2 Route"}, + /* Mixer Amplifier */ - {"Left DAC", "NULL","DAC MiniDSP IN3 Route"}, - {"Right DAC", "NULL","DAC MiniDSP IN3 Route"}, - - -/* Mixer Amplifier */ - - {"MAL PGA Mixer", "IN1L Switch","IN1L"}, - {"MAL PGA Mixer", "Left MicPGA Volume","Left MicPGA"}, + {"MAL PGA Mixer", "IN1L Switch", "IN1L"}, + {"MAL PGA Mixer", "Left MicPGA Volume", "Left MicPGA"}, {"MAL PGA", NULL, "MAL PGA Mixer"}, - {"MAR PGA Mixer", "IN1R Switch","IN1R"}, - {"MAR PGA Mixer", "Right MicPGA Volume","Right MicPGA"}, + {"MAR PGA Mixer", "IN1R Switch", "IN1R"}, + {"MAR PGA Mixer", "Right MicPGA Volume", "Right MicPGA"}, {"MAR PGA", NULL, "MAR PGA Mixer"}, -/* Capture (ADC) portions */ + /* 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","IN1L Switch","IN1L"}, - {"Left Input Mixer","IN2L Switch","IN2L"}, - {"Left Input Mixer","IN3L Switch","IN3L"}, - {"Left Input Mixer","IN4L Switch","IN4L"}, - {"Left Input Mixer","IN1R Switch","IN1R"}, + {"Left Input Mixer", "IN1L Switch", "IN1L"}, + {"Left Input Mixer", "IN2L Switch", "IN2L"}, + {"Left Input Mixer", "IN3L Switch", "IN3L"}, + {"Left Input Mixer", "IN4L Switch", "IN4L"}, + {"Left Input Mixer", "IN1R Switch", "IN1R"}, /* Left Negative PGA input */ - {"Left Input Mixer","IN2R Switch","IN2R"}, - {"Left Input Mixer","IN3R Switch","IN3R"}, - {"Left Input Mixer","IN4R Switch","IN4R"}, - {"Left Input Mixer","CM2L Switch","CM2L"}, - {"Left Input Mixer","CM1L Switch","CM1L"}, + {"Left Input Mixer", "IN2R Switch", "IN2R"}, + {"Left Input Mixer", "IN3R Switch", "IN3R"}, + {"Left Input Mixer", "IN4R Switch", "IN4R"}, + {"Left Input Mixer", "CM2L Switch", "CM2L"}, + {"Left Input Mixer", "CM1L Switch", "CM1L"}, - /* Right Positive PGA Input */ - {"Right Input Mixer","IN1R Switch","IN1R"}, - {"Right Input Mixer","IN2R Switch","IN2R"}, - {"Right Input Mixer","IN3R Switch","IN3R"}, - {"Right Input Mixer","IN4R Switch","IN4R"}, - {"Right Input Mixer","IN2L Switch","IN2L"}, + /* Right Positive PGA Input */ + {"Right Input Mixer", "IN1R Switch", "IN1R"}, + {"Right Input Mixer", "IN2R Switch", "IN2R"}, + {"Right Input Mixer", "IN3R Switch", "IN3R"}, + {"Right Input Mixer", "IN4R Switch", "IN4R"}, + {"Right Input Mixer", "IN2L Switch", "IN2L"}, /* Right Negative PGA Input */ - {"Right Input Mixer","IN1L Switch","IN1L"}, - {"Right Input Mixer","IN3L Switch","IN3L"}, - {"Right Input Mixer","IN4L Switch","IN4L"}, - {"Right Input Mixer","CM1R Switch","CM1R"}, - {"Right Input Mixer","CM2R Switch","CM2R"}, + {"Right Input Mixer", "IN1L Switch", "IN1L"}, + {"Right Input Mixer", "IN3L Switch", "IN3L"}, + {"Right Input Mixer", "IN4L Switch", "IN4L"}, + {"Right Input Mixer", "CM1R Switch", "CM1R"}, + {"Right Input Mixer", "CM2R Switch", "CM2R"}, + {"CM1L", NULL, "CM"}, {"CM2L", NULL, "CM"}, {"CM1R", NULL, "CM"}, {"CM2R", NULL, "CM"}, - {"Left MicPGA",NULL,"Left Input Mixer"}, - {"Right MicPGA",NULL,"Right Input Mixer"}, + {"Left MicPGA", NULL, "Left Input Mixer"}, + {"Right MicPGA", NULL, "Right Input Mixer"}, - {"Left ADC", NULL, "Left MicPGA"}, - {"Right ADC", NULL, "Right MicPGA"}, + {"Left ADC Route", "Analog", "Left MicPGA"}, + {"Left ADC Route", "Digital", "Left DMIC"}, -/* ASI Output Routing */ + {"Right ADC Route", "Analog", "Right MicPGA"}, + {"Right ADC Route", "Digital", "Right DMIC"}, + + {"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"}, @@ -3435,459 +1502,673 @@ static const struct snd_soc_dapm_route aic3262_dapm_routes[] ={ {"ADC MiniDSP OUT3", NULL, "Left ADC"}, {"ADC MiniDSP OUT3", NULL, "Right ADC"}, - {"ASI1OUT Route", "ASI1 Out","ADC MiniDSP OUT1"},// Port 1 - {"ASI1OUT Route", "ASI1In Bypass","ASI1IN Port"}, - {"ASI1OUT Route", "ASI2In Bypass","ASI2IN Port"}, - {"ASI1OUT Route", "ASI3In Bypass","ASI3IN Port"}, - - {"ASI2OUT Route", "ASI1 Out","ADC MiniDSP OUT1"},// Port 1 - {"ASI2OUT Route", "ASI1In Bypass","ASI1IN Port"}, - {"ASI2OUT Route", "ASI2In Bypass","ASI2IN Port"}, - {"ASI2OUT Route", "ASI3In Bypass","ASI3IN Port"}, - {"ASI2OUT Route", "ASI2 Out","ADC MiniDSP OUT2"},// Port 2 - - {"ASI3OUT Route", "ASI1 Out","ADC MiniDSP OUT1"},// Port 1 - {"ASI3OUT Route", "ASI1In Bypass","ASI1IN Port"}, - {"ASI3OUT Route", "ASI2In Bypass","ASI2IN Port"}, - {"ASI3OUT Route", "ASI3In Bypass","ASI3IN Port"}, - {"ASI3OUT Route", "ASI3 Out","ADC MiniDSP OUT3"},// Port 3 - - {"ASI1OUT",NULL,"ASI1OUT Route"}, - {"ASI2OUT",NULL,"ASI2OUT Route"}, - {"ASI3OUT",NULL,"ASI3OUT Route"}, - + {"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. + */ +void aic3262_firmware_load(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct aic3262_priv *private_ds = snd_soc_codec_get_drvdata(codec); + 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 (fw != NULL) { + dev_dbg(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 + private_ds->isdefault_fw = 0; + } + + if (fw == NULL) { + /* either request_firmware or reload failed */ + dev_dbg(codec->dev, "Default firmware load\n"); + ret = aic3xxx_cfw_reload(private_ds->cfw_p, default_firmware, + sizeof(default_firmware)); + if (ret < 0) + dev_err(codec->dev, "Default firmware load failed\n"); + else + private_ds->isdefault_fw = 1; + } + aic3xxx_cfw_lock(private_ds->cfw_p, 0); + if (ret >= 0) { + /* init function for transition */ + aic3xxx_cfw_transition(private_ds->cfw_p, "INIT"); + if (!private_ds->isdefault_fw) { + aic3xxx_cfw_add_modes(codec, private_ds->cfw_p); + aic3xxx_cfw_add_controls(codec, private_ds->cfw_p); + } + aic3xxx_cfw_setmode_cfg(private_ds->cfw_p, 0, 0); + } +} -#define AIC3262_DAPM_ROUTE_NUM (sizeof(aic3262_dapm_routes)/sizeof(struct snd_soc_dapm_route)) +/*========================================================= -/* - ***************************************************************************** - * Function Definitions - ***************************************************************************** - */ + headset work and headphone/headset jack interrupt handlers + ========================================================*/ -/* - *---------------------------------------------------------------------------- - * Function : aic3262_change_page - * Purpose : This function is to switch between page 0 and page 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 * - *---------------------------------------------------------------------------- - */ -int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page) +*/ +static void aic3262_hs_jack_report(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) { struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 data[2]; - int ret = 0; + int status, state = 0; - data[0] = 0; - data[1] = new_page; - aic3262->page_no = new_page; + mutex_lock(&aic3262->mutex); -#if defined(LOCAL_REG_ACCESS) - if (codec->hw_write(codec->control_data, data, 2) != 2) - ret = -EIO; -#else - ret = snd_soc_write(codec, data[0], data[1]); -#endif - if (ret) - printk(KERN_ERR "Error in changing page to %d\n", new_page); + /* Sync status */ + status = snd_soc_read(codec, AIC3262_DAC_FLAG); + /* We will check only stereo MIC and headphone */ + if (status & AIC3262_JACK_WITH_STEREO_HS) + state |= SND_JACK_HEADPHONE; + if (status & AIC3262_JACK_WITH_MIC) + state |= SND_JACK_MICROPHONE; - /*DBG("# Changing page to %d\r\n", new_page);*/ + mutex_unlock(&aic3262->mutex); + + snd_soc_jack_report(jack, state, report); - return ret; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_change_book - * Purpose : This function is to switch between books + +/** + * 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 * - *---------------------------------------------------------------------------- - */ -int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book) +*/ +void aic3262_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) { struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 data[2]; - int ret = 0; + struct aic3262_jack_data *hs_jack = &aic3262->hs_jack; - data[0] = 0x7F; - data[1] = new_book; - aic3262->book_no = new_book; + 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_codec *codec = aic3262->codec; + struct aic3262_jack_data *hs_jack = &aic3262->hs_jack; + aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} - ret = aic3262_change_page(codec, 0); - if (ret) - return ret; +/** + * 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_codec *codec = data; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); -#if defined(LOCAL_REG_ACCESS) - if (codec->hw_write(codec->control_data, data, 2) != 2) - ret = -EIO; -#else - ret = snd_soc_write(codec, data[0], data[1]); -#endif - if (ret) - printk(KERN_ERR "Error in changing Book\n"); + queue_delayed_work(aic3262->workqueue, &aic3262->delayed_work, + msecs_to_jiffies(200)); + return IRQ_HANDLED; +} - /*DBG("# Changing book to %d\r\n", new_book);*/ +static irqreturn_t aic3262_button_handler(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + input_report_key(aic3262->idev, KEY_MEDIA, 1); + mdelay(50); + input_report_key(aic3262->idev, KEY_MEDIA, 0); + input_sync(aic3262->idev); - return ret; + return IRQ_HANDLED; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_write_reg_cache - * Purpose : This function is to write aic3262 register cache + +/** + * 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. */ -void aic3262_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, u8 value) +unsigned int aic3262_codec_read(struct snd_soc_codec *codec, unsigned int reg) { -#if defined(EN_REG_CACHE) - u8 *cache = codec->reg_cache; - if (reg >= AIC3262_CACHEREGNUM) - return; + u8 value; - if (cache) - cache[reg] = value; -#endif + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + value = aic3262_reg_read(codec->control_data, reg); + dev_dbg(codec->dev, "p %d , r 30 %x %x\n", + aic_reg->aic326x_register.page, + aic_reg->aic326x_register.offset, value); + return value; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_read - * Purpose : This function is to read the aic3262 register space. +/** + * 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_codec *codec, unsigned int reg, + unsigned int value) +{ + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + dev_dbg(codec->dev, "p %d, w 30 %x %x\n", + aic_reg->aic326x_register.page, + aic_reg->aic326x_register.offset, value); + return aic3262_reg_write(codec->control_data, reg, value); +} -unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg) +/** + * aic3262_add_widget: This function is to add the dapm widgets + * The following are the main widgets supported + * # Left DAC to Left Outputs + * # Right DAC to Right Outputs + * # Left Inputs to Left ADC + * # Right Inputs to Right ADC + * @codec: pointer variable to codec having informaton related to codec, + * + * Return: return 0 on success. + */ +static int aic3262_add_widgets(struct snd_soc_codec *codec) { - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 value; - u8 page = reg / 128; - u16 *cache = codec->reg_cache; - u16 cmd; - u8 buffer[2]; - int rc = 0; - reg = reg % 128; - - if (reg >= AIC3262_CACHEREGNUM) { - return 0; - } - if (aic3262->control_type == SND_SOC_I2C) { - if (aic3262->page_no != page) { - aic3262_change_page(codec, page); - } - i2c_master_send(codec->control_data, (char *)®, 1); - i2c_master_recv(codec->control_data, &value, 1); - /*DBG("r %2x %02x\r\n", reg, value); */ - } else if (aic3262->control_type == SND_SOC_SPI) { - u16 value; - - /* Do SPI transfer; first 16bits are command; remaining is - * register contents */ - cmd = AIC3262_READ_COMMAND_WORD(reg); - buffer[0] = (cmd >> 8) & 0xff; - buffer[1] = cmd & 0xff; - //rc = spi_write_then_read(aic3262->spi, buffer, 2, buffer, 2); - - if (rc) { - dev_err(&aic3262->spi->dev, "AIC26 reg read error\n"); - return -EIO; - } - value = (buffer[0] << 8) | buffer[1]; - } else { - printk(KERN_ERR "Unknown Interface Type in aic3262_read\n"); - } + snd_soc_dapm_new_controls(&codec->dapm, aic3262_dapm_widgets, + ARRAY_SIZE(aic3262_dapm_widgets)); + /* set up audio path interconnects */ + dev_dbg(codec->dev, "#Completed adding new dapm widget" + " controls size=%d\n", ARRAY_SIZE(aic3262_dapm_widgets)); - /* Update the cache before returning with the value */ - cache[reg] = value; - return value; + snd_soc_dapm_add_routes(&codec->dapm, aic3262_dapm_routes, + ARRAY_SIZE(aic3262_dapm_routes)); + dev_dbg(codec->dev, "#Completed adding DAPM routes\n"); + snd_soc_dapm_new_widgets(&codec->dapm); + dev_dbg(codec->dev, "#Completed updating dapm\n"); + return 0; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_write - * Purpose : This function is to write to the aic3262 register space. +/** + * 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, * - *---------------------------------------------------------------------------- - */ -int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) + * Return: On success return 0. +*/ +static int aic3262_set_interface_fmt(struct snd_soc_dai *dai, unsigned int fmt, + unsigned int channel) { - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 data[2]; - u8 page; - int ret = 0; - - page = reg / 128; - data[AIC3262_REG_OFFSET_INDEX] = reg % 128; - if (aic3262->page_no != page) - aic3262_change_page(codec, page); + int aif_interface_reg; + int aif_bclk_offset_reg; + struct snd_soc_codec *codec = dai->codec; + u8 iface_val = 0; + u8 dsp_a_val = 0; - /* data is - * D15..D8 aic3262 register offset - * D7...D0 register data - */ - data[AIC3262_REG_DATA_INDEX] = value & AIC3262_8BITS_MASK; -#if defined(EN_REG_CACHE) - if ((page >= 0) & (page <= 4)) - aic3262_write_reg_cache(codec, reg, value); + 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; -#endif - if (!data[AIC3262_REG_OFFSET_INDEX]) { - /* if the write is to reg0 update aic3262->page_no */ - aic3262->page_no = value; } + /* 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 back + to following case */ + case SND_SOC_DAIFMT_DSP_B: + switch (channel) { + case 1: + iface_val = 0x80; /* Choose mono PCM */ + break; + case 2: + iface_val = 0x20; + break; + default: + 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_update_bits(codec, aif_interface_reg, + AIC3262_ASI_INTERFACE_MASK, iface_val); + snd_soc_update_bits(codec, aif_bclk_offset_reg, + AIC3262_BCLK_OFFSET_MASK, dsp_a_val); + return 0; - /*DBG("w %2x %02x\r\n", - data[AIC3262_REG_OFFSET_INDEX], data[AIC3262_REG_DATA_INDEX]);*/ - -#if defined(LOCAL_REG_ACCESS) - if (codec->hw_write(codec->control_data, data, 2) != 2) - ret = -EIO; -#else - ret = snd_soc_write(codec, data[AIC3262_REG_OFFSET_INDEX], - data[AIC3262_REG_DATA_INDEX]); -#endif - if (ret) - printk(KERN_ERR "Error in i2c write\n"); - - return ret; } -/* - *------------------------------------------------------------------------------ - * Function : aic3262_write__ - * Purpose : This function is to write to the aic3262 register space. - * (low level). - *------------------------------------------------------------------------------ +/** + * 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_write__(struct i2c_client *client, const char *buf, int count) +int aic3262_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - u8 data[3]; - int ret; - data[0] = *buf; - data[1] = *(buf+1); - data[2] = *(buf+2); - /*DBG("w %2x %02x\r\n", - data[AIC3262_REG_OFFSET_INDEX], data[AIC3262_REG_DATA_INDEX]);*/ - ret = i2c_master_send(client, data, 2); - if (ret < 2) { - printk( - KERN_ERR "I2C write Error : bytes written = %d\n\n", ret); - return -EIO; - } + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + int asi_reg; + u8 data = 0; - return ret; -} -/* - *---------------------------------------------------------------------------- - * Function : aic3262_reset_cache - * Purpose : This function is to reset the cache. - *---------------------------------------------------------------------------- - */ -int aic3262_reset_cache(struct snd_soc_codec *codec) -{ -#if defined(EN_REG_CACHE) - if (codec->reg_cache) { - memcpy(codec->reg_cache, aic3262_reg, sizeof(aic3262_reg)); - return 0; + 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; } - codec->reg_cache = kmemdup(aic3262_reg, - sizeof(aic3262_reg), GFP_KERNEL); - if (!codec->reg_cache) { - printk(KERN_ERR "aic32x4: kmemdup failed\n"); - return -ENOMEM; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + data = data | 0x00; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (0x08); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (0x10); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (0x18); + break; } -#endif - return 0; + + /* configure the respective Registers for the above configuration */ + snd_soc_update_bits(codec, asi_reg, + AIC3262_ASI_DATA_WORD_LENGTH_MASK, data); + return aic3262_set_interface_fmt(dai, aic3262->asi_fmt[dai->id], + params_channels(params)); } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_get_divs - * Purpose : This function is to get required divisor from the "aic3262_divs" - * table. +/** + * 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 inline int aic3262_get_divs(int mclk, int rate) +static int aic3262_mute(struct snd_soc_dai *dai, int mute) { - int i; - - for (i = 0; i < ARRAY_SIZE(aic3262_divs); i++) { - if ((aic3262_divs[i].rate == rate) - && (aic3262_divs[i].mclk == mclk)) { - DBG(KERN_INFO "#%s: Found Entry %d in Clock_Array\n", - __func__, i); - return i; - } - } - printk(KERN_ERR "Master clock and sample rate is not supported\n"); - return -EINVAL; + struct snd_soc_codec *codec = dai->codec; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(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_update_bits_locked(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_update_bits_locked(codec, + AIC3262_DAC_MVOL_CONF, + AIC3262_DAC_LR_MUTE_MASK, + 0x0); + aic3262->mute_asi |= ((0x1) << dai->id); + } + dev_dbg(codec->dev, "codec : %s : ended\n", __func__); + return 0; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_add_widgets - * Purpose : This function is to add the dapm widgets - * The following are the main widgets supported - * # Left DAC to Left Outputs - * # Right DAC to Right Outputs - * # Left Inputs to Left ADC - * # Right Inputs to Right ADC +/** + * aic3262_set_dai_sysclk: This function is to set the DAI system clock + * @codec_dai: ponter to dai Holds runtime data for a DAI, + * @freq: system clock to be set, + * @dir: integer dir, * - *---------------------------------------------------------------------------- + * Return: return 0 on success. */ -static int aic3262_add_widgets(struct snd_soc_codec *codec) +static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) { - int ret; - struct snd_soc_dapm_context *dapm = &codec->dapm; -#ifndef AIC3262_MULTI_I2S - int i; - for (i = 0; i < ARRAY_SIZE(aic3262_dapm_widgets); i++) - ret = snd_soc_dapm_new_control(dapm, &aic3262_dapm_widgets[i]); -#else - ret = snd_soc_dapm_new_controls(dapm, aic3262_dapm_widgets, - ARRAY_SIZE(aic3262_dapm_widgets)); - if (ret != 0) { - printk(KERN_ERR "#%s: Unable to add DAPM Controls. Err %d\n", - __func__, ret); + struct aic3262_priv *aic3262; + struct snd_soc_codec *codec; + + codec = codec_dai->codec; + aic3262 = snd_soc_codec_get_drvdata(codec); + switch (freq) { + case AIC3262_FREQ_12000000: + aic3262->sysclk = freq; + return 0; + case AIC3262_FREQ_24000000: + aic3262->sysclk = freq; + return 0; + break; + case AIC3262_FREQ_19200000: + aic3262->sysclk = freq; + return 0; + break; + case AIC3262_FREQ_38400000: + aic3262->sysclk = freq; + dev_dbg(codec->dev, "codec: sysclk = %d\n", aic3262->sysclk); + return 0; + break; + case AIC3262_FREQ_12288000: + aic3262->sysclk = freq; + dev_dbg(codec->dev, "codec: sysclk = %d\n", aic3262->sysclk); + return 0; + break; + } -#endif - /* set up audio path interconnects */ - DBG("#Completed adding new dapm widget controls size=%d\n", - ARRAY_SIZE(aic3262_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, aic3262_dapm_routes, - ARRAY_SIZE(aic3262_dapm_routes)); - DBG("#Completed adding DAPM routes\n"); - snd_soc_dapm_new_widgets(dapm); - DBG("#Completed updating dapm\n"); - return 0; + dev_err(codec->dev, "Invalid frequency to set DAI system clock\n"); + + return -EINVAL; } -/* - *---------------------------------------------------------------------------- - * Function : reg_def_conf - * Purpose : This function is to reset the codec book 0 registers + +/** + * 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. */ -int reg_def_conf(struct snd_soc_codec *codec) +static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { - int i = 0, ret; - DBG(KERN_INFO "#%s: Invoked..\n", __func__); + struct aic3262_priv *aic3262; + struct snd_soc_codec *codec; + u8 iface_val, master; + int aif_bclk_wclk_reg; - ret = aic3262_change_page(codec, 0); - if (ret != 0) - return ret; + codec = codec_dai->codec; + aic3262 = snd_soc_codec_get_drvdata(codec); + iface_val = 0x00; + master = 0x0; - ret = aic3262_change_book(codec, 0); - if (ret != 0) - return ret; + 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..just for debugging */ + 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"); - /* Configure the Codec with the default Initialization Values */ - for (i = 0; i < reg_init_size; i++) { - ret = snd_soc_write(codec, aic3262_reg_init[i].reg_offset, - aic3262_reg_init[i].reg_val); - if (ret) + 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; } - DBG(KERN_INFO "#%s: Done..\n", __func__); - return ret; + snd_soc_update_bits(codec, aif_bclk_wclk_reg, + AIC3262_WCLK_BCLK_MASTER_MASK, master); + return 0; } -/* - * i2c_verify_book0 +/** + * 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 + * @fin: frequency in, + * @fout: Frequency out, * - * This function is used to dump the values of the Book 0 Pages. - */ -int i2c_verify_book0(struct snd_soc_codec *codec) + * 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) { - int i, j, k = 0; - u8 val1; - - DBG("starting i2c_verify\n"); - DBG("Resetting page to 0\n"); - aic3262_change_book(codec, 0); - for (j = 0; j < 3; j++) { - if (j == 0) { - aic3262_change_page(codec, 0); - k = 0; - } - if (j == 1) { - aic3262_change_page(codec, 1); - k = 1; - } - /* - if (j == 2) { - aic3262_change_page(codec, 4); - k = 4; - }*/ - for (i = 0; i <= 127; i++) { -#if defined(LOCAL_REG_ACCESS) - val1 = i2c_smbus_read_byte_data(codec->control_data, i); -#else - val1 = snd_soc_read(codec, i); -#endif - /* printk("[%d][%d]=[0x%2x]\n",k,i,val1); */ - } - } + struct snd_soc_codec *codec = dai->codec; + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "In aic3262: dai_set_pll\n"); + dev_dbg(codec->dev, "%d, %s, dai->id = %d\n", __LINE__, + __func__, dai->id); + /* select the PLL_CLKIN */ + snd_soc_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); return 0; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_set_bias_level - * Purpose : This function is to get triggered when dapm events occurs. +/** * - *---------------------------------------------------------------------------- + * 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_codec *codec, - enum snd_soc_bias_level level) + enum snd_soc_bias_level level) { + switch (level) { /* full On */ case SND_SOC_BIAS_ON: - /* all power is driven by DAPM system */ dev_dbg(codec->dev, "set_bias_on\n"); break; /* partial On */ case SND_SOC_BIAS_PREPARE: - dev_dbg(codec->dev, "set_bias_prepare\n"); - break; - /* Off, with power */ + /* 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_dbg(codec->dev, "set_bias_stby\n"); + dev_dbg(codec->dev, "set_bias_stby\n"); + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, AIC3262_POWER_CONF, + (AIC3262_AVDD_TO_DVDD_MASK | + AIC3262_EXT_ANALOG_SUPPLY_MASK), + 0x0); + snd_soc_update_bits(codec, AIC3262_REF_PWR_DLY, + AIC3262_CHIP_REF_PWR_ON_MASK, + AIC3262_CHIP_REF_PWR_ON); + mdelay(40); + } break; - /* Off, without power */ - case SND_SOC_BIAS_OFF: - dev_dbg(codec->dev, "set_bias_off\n"); + /* Off, without power */ + case SND_SOC_BIAS_OFF: + dev_dbg(codec->dev, "set_bias_off\n"); + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + snd_soc_update_bits(codec, AIC3262_REF_PWR_DLY, + AIC3262_CHIP_REF_PWR_ON_MASK, 0x0); + snd_soc_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)); + } break; } - codec->dapm.bias_level=level; + codec->dapm.bias_level = level; return 0; } - /* *---------------------------------------------------------------------------- * Function : aic3262_suspend @@ -3897,13 +2178,7 @@ static int aic3262_set_bias_level(struct snd_soc_codec *codec, */ static int aic3262_suspend(struct snd_soc_codec *codec, pm_message_t state) { - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - DBG(KERN_INFO "#%s: Invoked..\n", __func__); - if (aic3262) - disable_irq(aic3262->irq); - - aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - + aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -3916,207 +2191,10 @@ static int aic3262_suspend(struct snd_soc_codec *codec, pm_message_t state) */ static int aic3262_resume(struct snd_soc_codec *codec) { - int i; - u8 data[2]; - int ret = 0; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u8 *cache = codec->reg_cache; - DBG(KERN_INFO "#%s: Invoked..\n", __func__); - - ret = aic3262_change_page(codec, 0); - if (ret) - return ret; -#if defined(EN_REG_CACHE) - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(aic3262_reg); i++) { - data[0] = i % 128; - data[1] = cache[i]; -#if defined(LOCAL_REG_ACCESS) - codec->hw_write(codec->control_data, data, 2); -#else - ret = snd_soc_write(codec, data[0], data[1]); - if (ret) - break; -#endif - } -#endif - if (!ret) { - aic3262_change_page(codec, 0); - aic3262_set_bias_level(codec, SND_SOC_BIAS_ON); - - if (aic3262) - enable_irq(aic3262->irq); - } - return ret; -} -/* - *---------------------------------------------------------------------------- - * Function : aic3262_hw_read - * Purpose : This is a low level harware read function. - * - *---------------------------------------------------------------------------- - */ -unsigned int aic3262_hw_read(struct snd_soc_codec *codec, unsigned int count) -{ - struct i2c_client *client = codec->control_data; - unsigned int buf; - - if (count > (sizeof(unsigned int))) - return 0; - - i2c_master_recv(client, (char *)&buf, count); - return buf; -} - -/* -* aic3262_jack_handler -* -* This function is called from the Interrupt Handler -* to check the status of the AIC3262 Registers related to Headset Detection -*/ -static irqreturn_t aic3262_jack_handler(int irq, void *data) -{ - struct snd_soc_codec *codec = data; - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - unsigned int value; - unsigned int micbits, hsbits = 0; - DBG(KERN_INFO "%s++\n", __func__); - - aic3262_change_page(codec, 0); - - /* Read the Jack Status Register*/ - value = snd_soc_read(codec, STICKY_FLAG2); - DBG(KERN_INFO "reg44 0x%x\n", value); - - - value = snd_soc_read(codec, INT_FLAG2); - DBG(KERN_INFO "reg46 0x%x\n", value); - - value = snd_soc_read(codec, DAC_FLAG_R1); - DBG(KERN_INFO "reg37 0x%x\n", value); - - micbits = value & DAC_FLAG_MIC_MASKBITS; - DBG(KERN_INFO "micbits 0x%x\n", micbits); - - hsbits = value & DAC_FLAG_HS_MASKBITS; - DBG(KERN_INFO "hsbits 0x%x\n", hsbits); - - - /* No Headphone or Headset*/ - if (!micbits && !hsbits) { - DBG(KERN_INFO "no headset/headphone\n"); - snd_soc_jack_report(aic3262->headset_jack, - 0, SND_JACK_HEADSET); - } - - /* Headphone Detected */ - if ((micbits == DAC_FLAG_R1_NOMIC) || (hsbits)) { - DBG(KERN_INFO "headphone\n"); - snd_soc_jack_report(aic3262->headset_jack, - SND_JACK_HEADPHONE, SND_JACK_HEADSET); - } - - /* Headset Detected - only with capless */ - if (micbits == DAC_FLAG_R1_MIC) { - DBG(KERN_INFO "headset\n"); - snd_soc_jack_report(aic3262->headset_jack, - SND_JACK_HEADSET, SND_JACK_HEADSET); - } - DBG(KERN_INFO "%s--\n", __func__); - return IRQ_HANDLED; -} - -/* -* aic326x_headset_detect -* -* Call-back function called to check the status of Headset Pin. -*/ -int aic326x_headset_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, int jack_type) -{ - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - aic3262->headset_jack = jack; - /*Enable the Headset Interrupts*/ - snd_soc_write(codec, INT1_CNTL, 0x80); - - return 0; -} -EXPORT_SYMBOL_GPL(aic326x_headset_detect); - -int aic326x_headset_button_init(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, int jack_type) -{ - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - - aic3262->button_dev = input_allocate_device(); - aic3262->button_dev->name = "aic326x_headset_button"; - aic3262->button_dev->phys = "codec/input0"; - aic3262->button_dev->dev.parent = snd_card_get_device_link(codec->card->snd_card); - input_set_capability(aic3262->button_dev, EV_KEY, KEY_MEDIA); - - if (input_register_device(aic3262->button_dev)) - { - printk( "Unable to register input device headset button"); - } + aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - aic3262_jack_handler(aic3262->irq, codec); return 0; } -#ifdef AIC3262_MULTI_I2S -/* -* aic3262_asi_default_config -* -* This function is used to perform the default pin configurations for -* the functionalities which are specific to each ASI Port of the AIC3262 -* Audio Codec Chipset. The user is encouraged to change these values -* if required on their platforms. -*/ -static void aic3262_asi_default_config(struct snd_soc_codec *codec) -{ - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); - u16 counter; - - DBG(KERN_INFO - "#%s: Invoked. Will Config ASI Registers to Defaults..\n", - __func__); - for (counter = 0; counter < MAX_ASI_COUNT; counter++) { - aic3262->asiCtxt[counter].asi_active = 0; - aic3262->asiCtxt[counter].bclk_div = 1; - aic3262->asiCtxt[counter].wclk_div = 1; - aic3262->asiCtxt[counter].port_muted = 1; - aic3262->asiCtxt[counter].bclk_div_option = - BDIV_CLKIN_DAC_MOD_CLK; - aic3262->asiCtxt[counter].offset1 = 0; - aic3262->asiCtxt[counter].offset2 = 0; - } - /* ASI1 Defaults */ - aic3262->asiCtxt[0].bclk_output = ASI1_BCLK_DIVIDER_OUTPUT; - aic3262->asiCtxt[0].wclk_output = GENERATED_DAC_FS; - aic3262->asiCtxt[0].left_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[0].right_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[0].adc_input = ADC_PATH_MINIDSP_1; - aic3262->asiCtxt[0].dout_option = ASI_OUTPUT; - - /* ASI2 Defaults */ - aic3262->asiCtxt[1].bclk_output = ASI2_BCLK_DIVIDER_OUTPUT; - aic3262->asiCtxt[1].wclk_output = GENERATED_DAC_FS; - aic3262->asiCtxt[1].left_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[1].right_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[1].adc_input = ADC_PATH_MINIDSP_2; - aic3262->asiCtxt[1].dout_option = ASI_OUTPUT; - - /* ASI3 Defaults */ - aic3262->asiCtxt[2].bclk_output = ASI3_BCLK_DIVIDER_OUTPUT; - aic3262->asiCtxt[2].wclk_output = GENERATED_DAC_FS; - aic3262->asiCtxt[2].left_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[2].right_dac_output = DAC_PATH_LEFT; - aic3262->asiCtxt[2].adc_input = ADC_PATH_MINIDSP_3; - aic3262->asiCtxt[2].dout_option = ASI2_INPUT; - return; -} - -#endif /* #ifdef AIC3262_MULTI_I2S */ /* *---------------------------------------------------------------------------- @@ -4125,402 +2203,223 @@ static void aic3262_asi_default_config(struct snd_soc_codec *codec) * *---------------------------------------------------------------------------- */ - -static int aic3262_probe(struct snd_soc_codec *codec) +static int aic3262_codec_probe(struct snd_soc_codec *codec) { - struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); int ret = 0; + int ret_btn = 0; + struct aic3262 *control; + struct aic3262_priv *aic3262; + struct aic3262_jack_data *jack; - DBG(KERN_INFO "#%s: Invoked..\n", __func__); - -#if defined(EN_REG_CACHE) - codec->reg_cache = - kmemdup(aic3262_reg, sizeof(aic3262_reg), GFP_KERNEL); + if (codec == NULL) + dev_err(codec->dev, "codec pointer is NULL.\n"); - if (!codec->reg_cache) { - printk(KERN_ERR "aic3262: kmemdup failed\n"); + codec->control_data = dev_get_drvdata(codec->dev->parent); + control = codec->control_data; + aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL); + if (aic3262 == NULL) return -ENOMEM; - } -#else - /* Setting cache bypass - not to overwrite the cache registers, - Codec registers have 4 pages which is not handled in the common - cache code properly - bypass it in write value and save it - using separate call*/ - codec->cache_bypass = 1; -#endif -#if defined(LOCAL_REG_ACCESS) - codec->control_data = aic3262->control_data; - codec->hw_write = (hw_write_t) aic3262_write__; - codec->hw_read = aic3262_hw_read; -#else - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); - if (ret != 0) { - dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } -#endif - ret = reg_def_conf(codec); - if (ret != 0) { - printk(KERN_ERR "Failed to init TI codec: %d\n", ret); - return ret; + snd_soc_codec_set_drvdata(codec, aic3262); + aic3262->pdata = dev_get_platdata(codec->dev->parent); + aic3262->codec = codec; + aic3262->cur_fw = NULL; + aic3262->isdefault_fw = 0; + aic3262->cfw_p = &(aic3262->cfw_ps); + aic3xxx_cfw_init(aic3262->cfw_p, &aic3262_cfw_codec_ops, aic3262); + aic3262->workqueue = create_singlethread_workqueue("aic3262-codec"); + if (!aic3262->workqueue) { + ret = -ENOMEM; + goto work_err; + } + ret = device_create_file(codec->dev, &dev_attr_debug_level); + if (ret) + dev_info(codec->dev, "Failed to add debug_level sysfs\n"); + INIT_DELAYED_WORK(&aic3262->delayed_work, aic3262_accessory_work); + mutex_init(&aic3262->mutex); + mutex_init(&codec->mutex); + mutex_init(&aic3262->cfw_mutex); + aic3262->dsp_runstate = 0; + /* use switch-class based headset reporting if platform requires it */ + jack = &aic3262->hs_jack; + aic3262->idev = input_allocate_device(); + if (aic3262->idev <= 0) + printk(KERN_ERR, "Allocate failed\n"); + + input_set_capability(aic3262->idev, EV_KEY, KEY_MEDIA); + ret = input_register_device(aic3262->idev); + if (ret < 0) { + dev_err(codec->dev, "register input dev fail\n"); + goto input_dev_err; } - if (aic3262->irq) { - /* audio interrupt */ - ret = request_threaded_irq(aic3262->irq, NULL, - aic3262_jack_handler, - IRQF_TRIGGER_FALLING, - "tlv320aic3262", codec); + if (control->irq) { + ret = aic3262_request_irq(codec->control_data, + AIC3262_IRQ_HEADSET_DETECT, + aic3262_audio_handler, 0, + "aic3262_irq_headset", codec); + if (ret) { - printk(KERN_INFO "#%s: IRQ Registration failed..[%d]", - __func__, ret); - dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); - return ret; - } else - DBG(KERN_INFO - "#%s: irq Registration for IRQ %d done..\n", - __func__, aic3262->irq); - } else { - DBG(KERN_INFO "#%s: I2C IRQ Configuration is Wrong. \ - Please check it..\n", __func__); - } + dev_err(codec->dev, "HEADSET detect irq request" + "failed: %d\n", ret); + goto irq_err; + } - aic3262_asi_default_config(codec); + ret = aic3262_request_irq(codec->control_data, + AIC3262_IRQ_BUTTON_PRESS, + aic3262_button_handler, 0, "aic3262_irq_button", + codec); + + if (ret) { + dev_err(codec->dev, "button press irq request" + "failed: %d\n", ret); + goto irq_err; + } + } + /* Keep the reference voltage ON while in$ + STANDBY mode for fast power up */ - /* off, with power on */ + snd_soc_update_bits(codec, AIC3262_REF_PWR_DLY, + AIC3262_CHIP_REF_PWR_ON_MASK, + AIC3262_CHIP_REF_PWR_ON); + mdelay(40); aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_add_controls(codec, aic3262_snd_controls, - ARRAY_SIZE(aic3262_snd_controls)); - if(ret) - { - printk(KERN_INFO "%s failed\n", __func__); - } + aic3262->mute_asi = 0; + + snd_soc_add_controls(codec, aic3262_snd_controls, + ARRAY_SIZE(aic3262_snd_controls)); aic3262_add_widgets(codec); - /*TODO*/ - snd_soc_write(codec, MIC_BIAS_CNTL, 0x66); #ifdef AIC3262_TiLoad ret = aic3262_driver_init(codec); if (ret < 0) - printk(KERN_ERR - "\nAIC3262 CODEC: aic3262_probe :TiLoad Initialization failed\n"); -#endif - - -#ifdef CONFIG_MINI_DSP - /* Program MINI DSP for ADC and DAC */ - aic3262_minidsp_program(codec); - aic3262_add_minidsp_controls(codec); - aic3262_change_book(codec, 0x0); + dev_err(codec->dev, "\nTiLoad Initialization failed\n"); #endif + /* force loading the default firmware */ + aic3262_firmware_load(NULL, codec); + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "tlv320aic3262_fw_v1.bin", codec->dev, + GFP_KERNEL, codec, aic3262_firmware_load); -#ifdef MULTIBYTE_CONFIG_SUPPORT - aic3262_add_multiconfig_controls(codec); -#endif - - DBG(KERN_INFO "#%s: done..\n", __func__); - return ret; + return 0; +irq_err: + input_unregister_device(aic3262->idev); + input_free_device(aic3262->idev); +input_dev_err: +reg_err: +work_err: + kfree(aic3262); + return 0; } - - /* - *---------------------------------------------------------------------------- - * Function : aic3262_remove - * Purpose : to remove aic3262 soc device - * - *---------------------------------------------------------------------------- - */ -static int aic3262_remove(struct snd_soc_codec *codec) +* 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 int aic3262_codec_remove(struct snd_soc_codec *codec) { - /* power down chip */ + struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec); + struct aic3262 *control = codec->control_data; + struct aic3262_jack_data *jack = &aic3262->hs_jack; + aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF); + /* free_irq if any */ + switch (control->type) { + case TLV320AIC3262: + if (control->irq) { + aic3262_free_irq(control, AIC3262_IRQ_HEADSET_DETECT, + codec); + aic3262_free_irq(control, AIC3262_IRQ_BUTTON_PRESS, + codec); + } + break; + } + /* release firmware if any */ + if (aic3262->cur_fw != NULL) + release_firmware(aic3262->cur_fw); + /* destroy workqueue for jac dev */ + destroy_workqueue(aic3262->workqueue); + input_unregister_device(aic3262->idev); + input_free_device(aic3262->idev); + + kfree(aic3262); + return 0; } - -/* - *---------------------------------------------------------------------------- - * @struct snd_soc_codec_device | - * This structure is soc audio codec device sturecute which pointer - * to basic functions aic3262_probe(), aic3262_remove(), - * aic3262_suspend() and aic3262_resume() - *---------------------------------------------------------------------------- - */ -static struct snd_soc_codec_driver soc_codec_dev_aic3262 = { - .probe = aic3262_probe, - .remove = aic3262_remove, +static struct snd_soc_codec_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, .set_bias_level = aic3262_set_bias_level, -#if defined(LOCAL_REG_ACCESS) - .read = aic3262_read, - .write = aic3262_write, -#endif -#if !defined(EN_REG_CACHE) - .reg_cache_size = ARRAY_SIZE(aic3262_reg), + .reg_cache_size = 0, .reg_word_size = sizeof(u8), - .reg_cache_default = aic3262_reg, -#endif + .reg_cache_default = NULL, }; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -/* - *---------------------------------------------------------------------------- - * Function : aic3262_codec_probe - * Purpose : This function attaches the i2c client and initializes - * AIC3262 CODEC. - * NOTE: - * This function is called from i2c core when the I2C address is - * valid. - * If the i2c layer weren't so broken, we could pass this kind of - * data around - * - *---------------------------------------------------------------------------- - */ -static __devinit int aic3262_codec_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int aic326x_probe(struct platform_device *pdev) { - int ret; - - struct aic3262_priv *aic3262; + return snd_soc_register_codec(&pdev->dev, &soc_codec_driver_aic326x, + aic326x_dai_driver, + ARRAY_SIZE(aic326x_dai_driver)); - DBG(KERN_INFO "#%s: Entered\n", __func__); - - aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL); - - if (!aic3262) { - printk(KERN_ERR "#%s: Unable to Allocate Priv struct..\n", - __func__); - return -ENOMEM; - } - - i2c_set_clientdata(i2c, aic3262); -#if defined(LOCAL_REG_ACCESS) - aic3262->control_data = i2c; -#endif - aic3262->control_type = SND_SOC_I2C; - aic3262->irq = i2c->irq; - aic3262->pdata = i2c->dev.platform_data; - - /* The Configuration Support will be by default to 3 which - * holds the MAIN Patch Configuration. - */ - aic3262->current_dac_config[0] = -1; - aic3262->current_dac_config[1] = -1; - aic3262->current_adc_config[0] = -1; - aic3262->current_adc_config[1] = -1; - - aic3262->mute_codec = 1; - - aic3262->page_no = 0; - aic3262->book_no = 0; - aic3262->active_count = 0; - aic3262->dac_clkin_option = 3; - aic3262->adc_clkin_option = 3; - - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_aic3262, - tlv320aic3262_dai, ARRAY_SIZE(tlv320aic3262_dai)); - - if (ret < 0) - kfree(aic3262); - DBG(KERN_INFO "#%s: Done ret %d\n", __func__, ret); - return ret; } -/* - *---------------------------------------------------------------------------- - * Function : aic3262_i2c_remove - * Purpose : This function removes the i2c client and uninitializes - * AIC3262 CODEC. - * NOTE: - * This function is called from i2c core - * If the i2c layer weren't so broken, we could pass this kind of - * data around - * - *---------------------------------------------------------------------------- - */ -static __devexit int aic3262_i2c_remove(struct i2c_client *i2c) +static int aic326x_remove(struct platform_device *pdev) { - snd_soc_unregister_codec(&i2c->dev); - kfree(i2c_get_clientdata(i2c)); + snd_soc_unregister_codec(&pdev->dev); return 0; } -static const struct i2c_device_id tlv320aic3262_id[] = { - {"aic3262-codec", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, tlv320aic3262_id); - -static struct i2c_driver tlv320aic3262_i2c_driver = { +static struct platform_driver aic326x_codec_driver = { .driver = { - .name = "aic3262-codec", - .owner = THIS_MODULE, - }, - .probe = aic3262_codec_probe, - .remove = __devexit_p(aic3262_i2c_remove), - .id_table = tlv320aic3262_id, + .name = "tlv320aic3262-codec", + .owner = THIS_MODULE, + }, + .probe = aic326x_probe, + .remove = __devexit_p(aic326x_remove), }; -#endif /*#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)*/ - -#if defined(CONFIG_SPI_MASTER) -static int aic3262_spi_write(struct spi_device *spi, const char *data, int len) -{ - struct spi_transfer t; - struct spi_message m; - u8 msg[2]; - - if (len <= 0) - return 0; - - msg[0] = data[0]; - msg[1] = data[1]; - - spi_message_init(&m); - memset(&t, 0, (sizeof t)); - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; -} - -#ifdef RUN_DELAYED_WORK /* - * This function forces any delayed work to be queued and run. - */ -static int run_delayed_work(struct delayed_work *dwork) -{ - int ret; - - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(dwork); - - /* if there was any work waiting then we run it now and - * wait for it's completion */ - if (ret) { - schedule_delayed_work(dwork, 0); - flush_scheduled_work(); - } - return ret; -} -#endif - -static int __devinit aic3262_spi_probe(struct spi_device *spi) -{ - int ret; - struct snd_soc_codec *codec; - struct aic3262_priv *aic3262; - printk(KERN_INFO "%s entering\n",__func__); - aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL); - - if (!aic3262) { - printk(KERN_ERR "#%s: Unable to Allocate Priv struct..\n", - __func__); - return -ENOMEM; - } - codec = &aic3262->codec; - codec->control_data = spi; - aic3262->control_type = SND_SOC_SPI; - codec->hw_write = (hw_write_t)aic3262_spi_write; - codec->dev = &spi->dev; - - aic3262->pdata = spi->dev.platform_data; - - /* The Configuration Support will be by default to 3 which - * holds the MAIN Patch Configuration. - */ - aic3262->current_dac_config[0] = -1; - aic3262->current_dac_config[1] = -1; - aic3262->current_adc_config[0] = -1; - aic3262->current_adc_config[1] = -1; - - aic3262->mute_codec = 1; - - aic3262->page_no = 0; - aic3262->book_no = 0; - aic3262->active_count = 0; - aic3262->dac_clkin_option = 3; - aic3262->adc_clkin_option = 3; - dev_set_drvdata(&spi->dev, aic3262); - spi_set_drvdata(spi, aic3262); - ret = snd_soc_register_codec(&spi->dev, - &soc_codec_dev_aic3262, - tlv320aic3262_dai, ARRAY_SIZE(tlv320aic3262_dai)); - - if (ret < 0) { - printk(KERN_INFO "%s codec registeration failed\n",__func__); - kfree(aic3262); - } - else { - printk(KERN_INFO "%s registered\n",__func__); - } - printk(KERN_INFO "#%s: Done ret %d\n", __func__, ret); - return ret; -} - -static int __devexit aic3262_spi_remove(struct spi_device *spi) -{ - struct aic3262_priv *aic3262 = dev_get_drvdata(&spi->dev); - aic3262_set_bias_level(&aic3262->codec, SND_SOC_BIAS_OFF); - snd_soc_unregister_codec(&spi->dev); - kfree(aic3262); - aic3262_codec = NULL; - return 0; - -} - -static struct spi_driver aic3262_spi_driver = { - .driver = { - .name = "aic3262-codec", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = aic3262_spi_probe, - .remove = __devexit_p(aic3262_spi_remove), -}; -#endif +*---------------------------------------------------------------------------- +* Function : tlv320aic3262_modinit +* Purpose : module init function. First function to run. +* +*---------------------------------------------------------------------------- +*/ static int __init tlv320aic3262_modinit(void) { - int ret = 0; - printk(KERN_INFO "In %s\n",__func__); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&tlv320aic3262_i2c_driver); - if (ret != 0) - printk(KERN_ERR "Failed to register aic326x i2c driver %d\n", - ret); -#endif -#if defined(CONFIG_SPI_MASTER) - printk(KERN_INFO "Inside config_spi_master\n"); - ret = spi_register_driver(&aic3262_spi_driver); - if (ret != 0) - printk(KERN_ERR "Failed to register aic3262 SPI driver: %d\n", ret); -#endif - return ret; - + return platform_driver_register(&aic326x_codec_driver); } module_init(tlv320aic3262_modinit); +/* +*---------------------------------------------------------------------------- +* Function : tlv320aic3262_exit +* Purpose : module init function. First function to run. +* +*---------------------------------------------------------------------------- +*/ static void __exit tlv320aic3262_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&tlv320aic3262_i2c_driver); -#endif + platform_driver_unregister(&aic326x_codec_driver); + } -module_exit(tlv320aic3262_exit); +module_exit(tlv320aic3262_exit); +MODULE_ALIAS("platform:tlv320aic3262-codec"); MODULE_DESCRIPTION("ASoC TLV320AIC3262 codec driver"); -MODULE_AUTHOR("Barani Prashanth"); -MODULE_AUTHOR("Ravindra"); +MODULE_AUTHOR("Y Preetam Sashank Reddy "); +MODULE_AUTHOR("Barani Prashanth "); +MODULE_AUTHOR("Mukund Navada K "); +MODULE_AUTHOR("Naren Vasanad "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic326x.h b/sound/soc/codecs/tlv320aic326x.h index bfcbefc5c079..4a7d909cea74 100644 --- a/sound/soc/codecs/tlv320aic326x.h +++ b/sound/soc/codecs/tlv320aic326x.h @@ -1,8 +1,9 @@ /* - * linux/sound/soc/codecs/tlv320aic3262.h + * linux/sound/soc/codecs/tlv320aic326x.h * + * Copyright (C) 2011 TI Solutions Pvt Ltd. * - * Copyright (C) 2012 Texas Instruments, Inc. + * Based on sound/soc/codecs/tlv320aic3262.c * * 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 @@ -12,49 +13,44 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * History: - * Rev 0.1 ASoC driver support 20-01-2011 + * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio + * codec with digital microphone inputs and programmable outputs. * - * The AIC3262 ASoC driver is ported for the codec AIC3262. + * 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 */ #ifndef _TLV320AIC3262_H #define _TLV320AIC3262_H -#include +#include "aic3xxx_cfw.h" +#include "aic3xxx_cfw_ops.h" +#include + #define AUDIO_NAME "aic3262" #define AIC3262_VERSION "1.1" - -//#define AIC3262_ASI2_MASTER 1 +/* Macro to enable the inclusion of tiload kernel driver */ +#define AIC3262_TiLoad +#undef AIC3262_SYNC_MODE + +/* #define AIC3262_ASI1_MASTER */ +#undef AIC3262_ASI1_MASTER +/* #define AIC3262_ASI2_MASTER */ +#undef AIC3262_ASI2_MASTER +/* #define AIC3262_ASI3_MASTER */ +#undef AIC3262_ASI3_MASTER +/* Macro for McBsp master / slave configuration */ +#define AIC3262_MCBSP_SLAVE /*3262 master */ /* Enable this macro allow for different ASI formats */ -/*#define ASI_MULTI_FMT*/ #undef ASI_MULTI_FMT -#define INT_FLAG2_BUTTN_PRESSBIT 0x20 - -/* Enable register caching on write */ -#define EN_REG_CACHE 1 - -//#define MULTIBYTE_CONFIG_SUPPORT - -/*Setting all codec reg/write locally*/ -/* This definition is added as the snd_ direct call are -result some issue with cache. Common code doesnot support -page, so fix that before commenting this line*/ -#define LOCAL_REG_ACCESS 1 - -/* Macro to enable the inclusion of tiload kernel driver */ -//#define AIC3262_TiLoad - - -/* Macro enables or disables support for miniDSP in the driver */ -/* Enable the AIC3262_TiLoad macro first before enabling these macros */ -//#define CONFIG_MINI_DSP -/*#undef CONFIG_MINI_DSP*/ - /* Enable or disable controls to have Input routing*/ -/*#define FULL_IN_CNTL */ #undef FULL_IN_CNTL /* AIC3262 supported sample rate are 8k to 192k */ #define AIC3262_RATES SNDRV_PCM_RATE_8000_192000 @@ -64,27 +60,10 @@ page, so fix that before commenting this line*/ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) #define AIC3262_FREQ_12000000 12000000 -#define AIC3262_FREQ_12288000 12288000 +#define AIC3262_FREQ_19200000 19200000 #define AIC3262_FREQ_24000000 24000000 - -/* Macro for enabling the Multi_I2S Support in Driver */ -#define AIC3262_MULTI_I2S 1 - -/* Driver Debug Messages Enabled */ -//#define DEBUG - -#ifdef DEBUG - #define DBG(x...) printk(x) -#else - #define DBG(x...) -#endif - -/*Select the below macro to decide on the DAC master volume controls. - *2 independent or one combined - */ -/*#define DAC_INDEPENDENT_VOL*/ -#undef DAC_INDEPENDENT_VOL - +#define AIC3262_FREQ_38400000 38400000 +#define AIC3262_FREQ_12288000 12288000 /* Audio data word length = 16-bits (default setting) */ #define AIC3262_WORD_LEN_16BITS 0x00 #define AIC3262_WORD_LEN_20BITS 0x01 @@ -92,510 +71,79 @@ page, so fix that before commenting this line*/ #define AIC3262_WORD_LEN_32BITS 0x03 /* sink: name of target widget */ -#define AIC3262_WIDGET_NAME 0 +#define AIC3262_WIDGET_NAME 0 /* control: mixer control name */ -#define AIC3262_CONTROL_NAME +#define AIC3262_CONTROL_NAME 1 /* source: name of source name */ -#define AIC3262_SOURCE_NAME 2 +#define AIC3262_SOURCE_NAME 2 /* D15..D8 aic3262 register offset */ -#define AIC3262_REG_OFFSET_INDEX 0 +#define AIC3262_REG_OFFSET_INDEX 0 /* D7...D0 register data */ -#define AIC3262_REG_DATA_INDEX 1 +#define AIC3262_REG_DATA_INDEX 1 /* Serial data bus uses I2S mode (Default mode) */ -#define AIC3262_I2S_MODE 0x00 -#define AIC3262_DSP_MODE 0x01 -#define AIC3262_RIGHT_JUSTIFIED_MODE 0x02 -#define AIC3262_LEFT_JUSTIFIED_MODE 0x03 +#define AIC3262_I2S_MODE 0x00 +#define AIC3262_DSP_MODE 0x01 +#define AIC3262_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC3262_LEFT_JUSTIFIED_MODE 0x03 /* 8 bit mask value */ -#define AIC3262_8BITS_MASK 0xFF +#define AIC3262_8BITS_MASK 0xFF /* shift value for CLK_REG_3 register */ -#define CLK_REG_3_SHIFT 6 +#define CLK_REG_3_SHIFT 6 /* shift value for DAC_OSR_MSB register */ -#define DAC_OSR_MSB_SHIFT 4 +#define DAC_OSR_MSB_SHIFT 4 /* number of codec specific register for configuration */ -#define NO_FEATURE_REGS 2 - -/* Total number of ASI Ports */ -#define MAX_ASI_COUNT 3 - +#define NO_FEATURE_REGS 2 /* AIC3262 register space */ /* Updated from 256 to support Page 3 registers */ -#define AIC3262_CACHEREGNUM 1024 -#define BIT7 (0x01 << 7) -#define BIT6 (0x01 << 6) -#define BIT5 (0x01 << 5) -#define BIT4 (0x01 << 4) -#define BIT3 (0x01 << 3) -#define BIT2 (0x01 << 2) -#define BIT1 (0x01 << 1) -#define BIT0 (0x01 << 0) - -#define DAC_FLAG_MIC_MASKBITS 0x30 -#define DAC_FLAG_HS_MASKBITS 0x03 -#define DAC_FLAG_R1_NOJACK 0 -#define DAC_FLAG_R1_NOMIC (0x1 << 4) -#define DAC_FLAG_R1_MIC (0x3 << 4) -#define DAC_FLAG_R1_NOHS 0 -#define DAC_FLAG_R1_MONOHS 1 -#define DAC_FLAG_R1_STEREOHS 2 - -/*mask patterns for DAC and ADC polling logic*/ -#define LDAC_POW_FLAG_MASK 0x80 -#define RDAC_POW_FLAG_MASK 0x08 -#define LADC_POW_FLAG_MASK 0x40 -#define RADC_POW_FLAG_MASK 0x04 - -/* ****************** Book 0 Registers **************************************/ - -/* ****************** Page 0 Registers **************************************/ - -#define PAGE_SEL_REG 0 -#define RESET_REG 1 -#define DAC_ADC_CLKIN_REG 4 -#define PLL_CLKIN_REG 5 -#define PLL_CLK_RANGE_REG 5 -#define PLL_PR_POW_REG 6 -#define PLL_J_REG 7 -#define PLL_D_MSB 8 -#define PLL_D_LSB 9 -#define PLL_CKIN_DIV 10 - -#define NDAC_DIV_POW_REG 11 -#define MDAC_DIV_POW_REG 12 -#define DOSR_MSB_REG 13 -#define DOSR_LSB_REG 14 - -#define NADC_DIV_POW_REG 18 -#define MADC_DIV_POW_REG 19 -#define AOSR_REG 20 -#define CLKOUT_MUX 21 -#define CLKOUT_MDIV_VAL 22 -#define TIMER_REG 23 - -#define LF_CLK_CNTL 24 -#define HF_CLK_CNTL_R1 25 -#define HF_CLK_CNTL_R2 26 -#define HF_CLK_CNTL_R3 27 -#define HF_CLK_CNTL_R4 28 -#define HF_CLK_TRIM_R1 29 -#define HF_CLK_TRIM_R2 30 -#define HF_CLK_TRIM_R3 31 -#define HF_CLK_TRIM_R4 32 -#define ADC_FLAG_R1 36 -#define DAC_FLAG_R1 37 -#define DAC_FLAG_R2 38 - -#define STICKY_FLAG1 42 -#define INT_FLAG1 43 -#define STICKY_FLAG2 44 -#define STICKY_FLAG3 45 -#define INT_FLAG2 46 -#define INT1_CNTL 48 -#define INT2_CNTL 49 -#define INT_FMT 51 - -#define DAC_PRB 60 -#define ADC_PRB 61 -#define PASI_DAC_DP_SETUP 63 -#define DAC_MVOL_CONF 64 -#define DAC_LVOL 65 -#define DAC_RVOL 66 -#define HP_DETECT 67 -#define DRC_CNTL_R1 68 -#define DRC_CNTL_R2 69 -#define DRC_CNTL_R3 70 -#define BEEP_CNTL_R1 71 -#define BEEP_CNTL_R2 72 - -#define ADC_CHANNEL_POW 81 -#define ADC_FINE_GAIN 82 -#define LADC_VOL 83 -#define RADC_VOL 84 -#define ADC_PHASE 85 +#define AIC3262_CACHEREGNUM 1024 -#define LAGC_CNTL 86 -#define LAGC_CNTL_R2 87 -#define LAGC_CNTL_R3 88 -#define LAGC_CNTL_R4 89 -#define LAGC_CNTL_R5 90 -#define LAGC_CNTL_R6 91 -#define LAGC_CNTL_R7 92 -#define LAGC_CNTL_R8 93 +#define DSP_NON_SYNC_MODE(state) (!((state & 0x03) && (state & 0x30))) -#define RAGC_CNTL 94 -#define RAGC_CNTL_R2 95 -#define RAGC_CNTL_R3 96 -#define RAGC_CNTL_R4 97 -#define RAGC_CNTL_R5 98 -#define RAGC_CNTL_R6 99 -#define RAGC_CNTL_R7 100 -#define RAGC_CNTL_R8 101 -#define MINIDSP_ACCESS_CTRL 121 -/* ****************** Page 1 Registers **************************************/ -#define PAGE_1 128 - -#define POWER_CONF (PAGE_1 + 1) -#define LDAC_PTM (PAGE_1 + 3) -#define RDAC_PTM (PAGE_1 + 4) -#define CM_REG (PAGE_1 + 8) -#define HP_CTL (PAGE_1 + 9) -#define HP_DEPOP (PAGE_1 + 11) -#define RECV_DEPOP (PAGE_1 + 12) -#define MA_CNTL (PAGE_1 + 17) -#define LADC_PGA_MAL_VOL (PAGE_1 + 18) -#define RADC_PGA_MAR_VOL (PAGE_1 + 19) - - -#define LINE_AMP_CNTL_R1 (PAGE_1 + 22) -#define LINE_AMP_CNTL_R2 (PAGE_1 + 23) - -#define HP_AMP_CNTL_R1 (PAGE_1 + 27) -#define HP_AMP_CNTL_R2 (PAGE_1 + 28) -#define HP_AMP_CNTL_R3 (PAGE_1 + 29) - -#define HPL_VOL (PAGE_1 + 31) -#define HPR_VOL (PAGE_1 + 32) -#define INT1_SEL_L (PAGE_1 + 34) -#define RAMP_CNTL_R1 (PAGE_1 + 36) -#define RAMP_CNTL_R2 (PAGE_1 + 37) -//#define INT1_SEL_RM (PAGE_1 + 39) -#define IN1L_SEL_RM (PAGE_1 + 39) -#define IN1R_SEL_RM (PAGE_1 + 39) - -#define REC_AMP_CNTL_R5 (PAGE_1 + 40) -#define RAMPR_VOL (PAGE_1 + 41) -#define RAMP_TIME_CNTL (PAGE_1 + 42) -#define SPK_AMP_CNTL_R1 (PAGE_1 + 45) -#define SPK_AMP_CNTL_R2 (PAGE_1 + 46) -#define SPK_AMP_CNTL_R3 (PAGE_1 + 47) -#define SPK_AMP_CNTL_R4 (PAGE_1 + 48) -#define MIC_BIAS_CNTL (PAGE_1 + 51) - -#define LMIC_PGA_PIN (PAGE_1 + 52) -#define LMIC_PGA_PM_IN4 (PAGE_1 + 53) -#define LMIC_PGA_MIN (PAGE_1 + 54) -#define RMIC_PGA_PIN (PAGE_1 + 55) -#define RMIC_PGA_PM_IN4 (PAGE_1 + 56) -#define RMIC_PGA_MIN (PAGE_1 + 57) -/* MIC PGA Gain Registers */ -#define MICL_PGA (PAGE_1 + 59) -#define MICR_PGA (PAGE_1 + 60) -#define HEADSET_TUNING1_REG (PAGE_1 + 119) -#define HEADSET_TUNING2_REG (PAGE_1 + 120) -#define MIC_PWR_DLY (PAGE_1 + 121) -#define REF_PWR_DLY (PAGE_1 + 122) - -/* ****************** Page 4 Registers **************************************/ -#define PAGE_4 512 -#define ASI1_BUS_FMT (PAGE_4 + 1) -#define ASI1_LCH_OFFSET (PAGE_4 + 2) -#define ASI1_RCH_OFFSET (PAGE_4 + 3) -#define ASI1_CHNL_SETUP (PAGE_4 + 4) -#define ASI1_MULTI_CH_SETUP_R1 (PAGE_4 + 5) -#define ASI1_MULTI_CH_SETUP_R2 (PAGE_4 + 6) -#define ASI1_ADC_INPUT_CNTL (PAGE_4 + 7) -#define ASI1_DAC_OUT_CNTL (PAGE_4 + 8) -#define ASI1_ADC_OUT_TRISTATE (PAGE_4 + 9) -#define ASI1_BWCLK_CNTL_REG (PAGE_4 + 10) -#define ASI1_BCLK_N_CNTL (PAGE_4 + 11) -#define ASI1_BCLK_N (PAGE_4 + 12) -#define ASI1_WCLK_N (PAGE_4 + 13) -#define ASI1_BWCLK_OUT_CNTL (PAGE_4 + 14) -#define ASI1_DATA_OUT (PAGE_4 + 15) -#define ASI2_BUS_FMT (PAGE_4 + 17) -#define ASI2_LCH_OFFSET (PAGE_4 + 18) -#define ASI2_RCH_OFFSET (PAGE_4 + 19) -#define ASI2_ADC_INPUT_CNTL (PAGE_4 + 23) -#define ASI2_DAC_OUT_CNTL (PAGE_4 + 24) -#define ASI2_BWCLK_CNTL_REG (PAGE_4 + 26) -#define ASI2_BCLK_N_CNTL (PAGE_4 + 27) -#define ASI2_BCLK_N (PAGE_4 + 28) -#define ASI2_WCLK_N (PAGE_4 + 29) -#define ASI2_BWCLK_OUT_CNTL (PAGE_4 + 30) -#define ASI2_DATA_OUT (PAGE_4 + 31) -#define ASI3_BUS_FMT (PAGE_4 + 33) -#define ASI3_LCH_OFFSET (PAGE_4 + 34) -#define ASI3_RCH_OFFSET (PAGE_4 + 35) -#define ASI3_ADC_INPUT_CNTL (PAGE_4 + 39) -#define ASI3_DAC_OUT_CNTL (PAGE_4 + 40) -#define ASI3_BWCLK_CNTL_REG (PAGE_4 + 42) -#define ASI3_BCLK_N_CNTL (PAGE_4 + 43) -#define ASI3_BCLK_N (PAGE_4 + 44) -#define ASI3_WCLK_N (PAGE_4 + 45) -#define ASI3_BWCLK_OUT_CNTL (PAGE_4 + 46) -#define ASI3_DATA_OUT (PAGE_4 + 47) -#define WCLK1_PIN_CNTL_REG (PAGE_4 + 65) -#define DOUT1_PIN_CNTL_REG (PAGE_4 + 67) -#define DIN1_PIN_CNTL_REG (PAGE_4 + 68) -#define WCLK2_PIN_CNTL_REG (PAGE_4 + 69) -#define BCLK2_PIN_CNTL_REG (PAGE_4 + 70) -#define DOUT2_PIN_CNTL_REG (PAGE_4 + 71) -#define DIN2_PIN_CNTL_REG (PAGE_4 + 72) -#define WCLK3_PIN_CNTL_REG (PAGE_4 + 73) -#define BCLK3_PIN_CNTL_REG (PAGE_4 + 74) -#define DOUT3_PIN_CNTL_REG (PAGE_4 + 75) -#define DIN3_PIN_CNTL_REG (PAGE_4 + 76) -#define MCLK2_PIN_CNTL_REG (PAGE_4 + 82) -#define GPIO1_IO_CNTL (PAGE_4 + 86) -#define GPIO2_IO_CNTL (PAGE_4 + 87) -#define GPI1_EN (PAGE_4 + 91) -#define GPO2_EN (PAGE_4 + 92) -#define GPO1_PIN_CNTL (PAGE_4 + 96) -#define MINIDSP_PORT_CNTL_REG (PAGE_4 + 118) - -/**************************************************************************** -* Mixer control related #defines -*************************************************************************** -*/ -#define WCLK1_ENUM 0 -#define DOUT1_ENUM 1 -#define DIN1_ENUM 2 -#define WCLK2_ENUM 3 -#define BCLK2_ENUM 4 -#define DOUT2_ENUM 5 -#define DIN2_ENUM 6 -#define WCLK3_ENUM 7 -#define BCLK3_ENUM 8 -#define DOUT3_ENUM 9 -#define DIN3_ENUM 10 -#define CLKIN_ENUM 11 -/* -***************************************************************************** -* Enumeration Definitions -***************************************************************************** -*/ -/* The below enumeration lists down all the possible inputs to the -* the PLL of the AIC3262. The Private structure will hold a member -* of this Enumeration Type. -*/ -enum AIC3262_PLL_OPTION { - PLL_CLKIN_MCLK1 = 0, /* 0000: (Device Pin) */ - PLL_CLKIN_BLKC1, /* 0001: (Device Pin) */ - PLL_CLKIN_GPIO1, /* 0010: (Device Pin)*/ - PLL_CLKIN_DIN1, /* 0011: (Device Pin)*/ - PLL_CLKIN_BCLK2, /* 0100: (Device Pin)*/ - PLL_CLKIN_GPI1, /* 0101: (Device Pin)*/ - PLL_CLKIN_HF_REF_CLK, /* 0110: (Device Pin)*/ - PLL_CLKIN_GPIO2, /* 0111: (Device Pin)*/ - PLL_CLKIN_GPI2, /* 1000: (Device Pin)*/ - PLL_CLKIN_MCLK2 /* 1001: (Device Pin)*/ -}; +#define TIME_DELAY 5 +#define DELAY_COUNTER 100 -/* ASI Specific Bit Clock Divider Input Options. -* Please refer to Page 4 Reg 11, Reg 27 and Reg 43 -*/ -enum ASI_BDIV_CLKIN_OPTION { - BDIV_CLKIN_DAC_CLK = 0, /* 00 DAC_CLK */ - BDIV_CLKIN_DAC_MOD_CLK, /* 01 DAC_MOD_CLK */ - BDIV_CLKIN_ADC_CLK, /* 02 ADC_CLK */ - BDIV_CLKIN_ADC_MOD_CLK /* 03 ADC_MOD_CLK */ -}; - -/* ASI Specific Bit Clock Output Mux Options. -* Please refer to Page 4 Reg 14, Reg 30 and Reg 46 -* Please note that we are not handling the Reserved -* cases here. -*/ -enum ASI_BCLK_OPTION { - ASI1_BCLK_DIVIDER_OUTPUT = 0, /* 00 ASI1 Bit Clock Divider Output */ - ASI1_BCLK_INPUT, /* 01 ASI1 Bit Clock Input */ - ASI2_BCLK_DIVIDER_OUTPUT, /* 02 ASI2 Bit Clock Divider Output */ - ASI2_BCLK_INPUT, /* 03 ASI2 Bit Clock Input */ - ASI3_BCLK_DIVIDER_OUTPUT, /* 04 ASI3 Bit Clock Divider Output */ - ASI3_BBCLK_INPUT /* 05 ASi3 Bit Clock Input */ -}; - -/* Above bits are to be configured after Shifting 4 bits */ -#define AIC3262_ASI_BCLK_MUX_SHIFT 4 -#define AIC3262_ASI_BCLK_MUX_MASK (BIT6 | BIT5 | BIT4) -#define AIC3262_ASI_WCLK_MUX_MASK (BIT2 | BIT1 | BIT0) - -/* ASI Specific Word Clock Output Mux Options */ -enum ASI_WCLK_OPTION { - GENERATED_DAC_FS = 0, /* 00 WCLK = DAC_FS */ - GENERATED_ADC_FS = 1, /* 01 WCLK = ADC_FS */ - ASI1_WCLK_DIV_OUTPUT = 2, /* 02 WCLK = ASI1 WCLK_DIV_OUT */ - ASI1_WCLK_INPUT = 3, /* 03 WCLK = ASI1 WCLK Input */ - ASI2_WCLK_DIV_OUTPUT = 4, /* 04 WCLK = ASI2 WCLK_DIV_OUT */ - ASI2_WCLK_INPUT = 5, /* 05 WCLK = ASI2 WCLK Input */ - ASI3_WCLK_DIV_OUTPUT = 6, /* 06 WCLK = ASI3 WCLK_DIV_OUT */ - ASI3_WCLK_INPUT = 7 /* 07 WCLK = ASI3 WCLK Input */ -}; - -/* ASI DAC Output Control Options */ -enum ASI_DAC_OUTPUT_OPTION { - DAC_PATH_OFF = 0, /* 00 DAC Datapath Off */ - DAC_PATH_LEFT, /* 01 DAC Datapath left Data */ - DAC_PATH_RIGHT, /* 02 DAC Datapath Right Data */ -}; - -#define AIC3262_READ_COMMAND_WORD(addr) ((1 << 15) | (addr << 5)) -#define AIC3262_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5)) - -/* Shift the above options by so many bits */ -#define AIC3262_ASI_LDAC_PATH_SHIFT 6 -#define AIC3262_ASI_LDAC_PATH_MASK (BIT5 | BIT4) -#define AIC3262_ASI_RDAC_PATH_SHIFT 4 -#define AIC3262_ASI_RDAC_PATH_MASK (BIT7 | BIT6) - - -#define DAC_LR_MUTE_MASK 0xc -#define DAC_LR_MUTE 0xc -#define ENABLE_CLK_MASK 0x80 -#define ENABLE_CLK 0x80 - -/* ASI specific ADC Input Control Options */ -enum ASI_ADC_INPUT_OPTION { - ADC_PATH_OFF = 0, /* 00 ASI Digital Output Disabled */ - ADC_PATH_MINIDSP_1, /* 01 ASI Digital O/P from miniDSP_A(L1,R1) */ - ADC_PATH_ASI1, /* 02 ASI Digital Output from ASI1 */ - ADC_PATH_ASI2, /* 03 ASI Digital Output from ASI2 */ - ADC_PATH_ASI3, /* 04 ASI Digital Output from ASI3 */ - ADC_PATH_MINIDSP_2, /* 05 ASI Digital O/P from miniDSP_A(L2,R2) */ - ADC_PATH_MINIDSP_3 /* 05 ASI Digital O/P from miniDSP_A(L3,R3) */ -}; - -/* ASI Specific DOUT Pin Options */ -enum ASI_DOUT_OPTION { - ASI_OUTPUT = 0, /* 00 Default ASI Output */ - ASI1_INPUT, /* 01 ASI1 Data Input */ - ASI2_INPUT, /* 02 ASI2 Data Input */ - ASI3_INPUT /* 03 ASI3 Data Input */ -}; - -#define AIC3262_ASI_DOUT_MASK (BIT1 | BIT0) - -/* - ***************************************************************************** - * Structures Definitions - ***************************************************************************** - */ -#define AIC3262_MULTI_ASI_ACTIVE(x) (((x)->asiCtxt[0].asi_active) || \ - ((x)->asiCtxt[1].asi_active) || \ - ((x)->asiCtxt[2].asi_active)) - -/* -*---------------------------------------------------------------------------- -* @struct aic3262_setup_data | -* i2c specific data setup for AIC3262. -* @field unsigned short |i2c_address | -* Unsigned short for i2c address. -*---------------------------------------------------------------------------- -*/ - struct aic3262_setup_data { - unsigned short i2c_address; -}; +#ifdef AIC3262_TiLoad +int aic3262_driver_init(struct snd_soc_codec *codec); +#endif -/* -*---------------------------------------------------------------------------- -* @struct aic3262_asi_data -* ASI specific data stored for each ASI Interface -* -* -*--------------------------------------------------------------------------- -*/ -struct aic3262_asi_data { - u8 asi_active; /* ASI Active Flag */ - u8 master; /* Frame Master */ - u32 sampling_rate; /* Sampling Rate */ - enum ASI_BDIV_CLKIN_OPTION bclk_div_option; /* BCLK DIV Mux Option*/ - enum ASI_BCLK_OPTION bclk_output; /* BCLK Output Option*/ - enum ASI_WCLK_OPTION wclk_output; /* WCLK Output Option*/ - u8 bclk_div; /* BCLK Divider */ - u8 wclk_div; /* WCLK Divider */ - enum ASI_DAC_OUTPUT_OPTION left_dac_output; /* LDAC Path */ - enum ASI_DAC_OUTPUT_OPTION right_dac_output; /* RDAC Path */ - enum ASI_ADC_INPUT_OPTION adc_input; /* ADC Input Control */ - enum ASI_DOUT_OPTION dout_option; /* DOUT Option */ - u8 playback_mode; /* Playback Selected */ - u8 capture_mode; /* Record Selected */ - u8 port_muted; /* ASI Muted */ - u8 pcm_format; /* PCM Format */ - u8 word_len; /* Word Length */ - u8 offset1; /* Left Ch offset */ - u8 offset2; /* Right Ch Offset */ +struct aic3262_jack_data { + struct snd_soc_jack *jack; + int report; + struct switch_dev sdev; }; -/* -*---------------------------------------------------------------------------- -* @struct aic3262_priv | -* AIC3262 priviate data structure to set the system clock, mode and -* page number. -* @field u32 | sysclk | -* system clock -* @field s32 | master | -* master/slave mode setting for AIC3262 -* @field u8 | book_no | -* book number. -* @field u8 | page_no | -* page number. Here, page 0 and page 1 are used. -*---------------------------------------------------------------------------- -*/ struct aic3262_priv { - enum snd_soc_control_type control_type; - struct aic326x_pdata *pdata; - struct snd_soc_codec codec; u32 sysclk; s32 master; - u8 book_no; - u8 page_no; - u8 process_flow; - u8 mute_codec; u8 stream_status; - u32 active_count; - int current_dac_config[MAX_ASI_COUNT]; - int current_adc_config[MAX_ASI_COUNT]; - int current_config; - struct aic3262_asi_data asiCtxt[MAX_ASI_COUNT]; - enum AIC3262_PLL_OPTION aic3262_pllclkin_option; - u8 dac_clkin_option; - u8 adc_clkin_option; - int irq; - u8 dac_reg; - u8 adc_gain; - u8 hpl; - u8 hpr; - u8 rec_amp; - u8 rampr; - u8 spk_amp; - struct spi_device *spi; - struct snd_soc_jack *headset_jack; - struct input_dev *button_dev; - int codec_audio_mode; -#if defined(LOCAL_REG_ACCESS) - void *control_data; -#endif -}; - -/* - *---------------------------------------------------------------------------- - * @struct aic3262_configs | - * AIC3262 initialization data which has register offset and register - * value. - * @field u8 | book_no | - * AIC3262 Book Number Offsets required for initialization.. - * @field u16 | reg_offset | - * AIC3262 Register offsets required for initialization.. - * @field u8 | reg_val | - * value to set the AIC3262 register to initialize the AIC3262. - *---------------------------------------------------------------------------- - */ -struct aic3262_configs { - u8 book_no; - u16 reg_offset; - u8 reg_val; + struct aic3262_jack_data hs_jack; + struct workqueue_struct *workqueue; + struct delayed_work delayed_work; + struct input_dev *idev; + struct snd_soc_codec *codec; + struct mutex mutex; + struct mutex cfw_mutex; + struct cfw_state cfw_ps; + struct cfw_state *cfw_p; + struct aic3262_pdata *pdata; + int mute_asi; /* Bit 0 -> ASI1, Bit 1-> ASI2, Bit 2 -> ASI3 */ + int asi_fmt[2]; + int dsp_runstate; + struct firmware *cur_fw; + int isdefault_fw; }; /* *---------------------------------------------------------------------------- * @struct aic3262_rate_divs | * Setting up the values to get different freqencies + * * @field u32 | mclk | * Master clock * @field u32 | rate | @@ -620,8 +168,6 @@ struct aic3262_configs { * value for madc * @field u32 | blck_N | * value for block N - * @field u32 | aic3262_configs | - * configurations for aic3262 register value *---------------------------------------------------------------------------- */ struct aic3262_rate_divs { @@ -637,49 +183,16 @@ struct aic3262_rate_divs { u8 nadc; u8 madc; u8 blck_N; - struct aic3262_configs codec_specific_regs[NO_FEATURE_REGS]; }; -/* -***************************************************************************** -* EXTERN DECLARATIONS -***************************************************************************** -*/ -/* - *---------------------------------------------------------------------------- - * @func aic326x_headset_detect - * This function help to setup the needed registers to - * enable the headset detection - * - */ -extern int aic326x_headset_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, int jack_type); -extern int aic326x_headset_button_init(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, int jack_type); - -extern unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg); -extern u16 aic3262_read_2byte(struct snd_soc_codec *codec, u16 reg); -extern int aic3262_reset_cache(struct snd_soc_codec *codec); -extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page); -extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value); -extern void aic3262_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, u8 value); -extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book); -extern int reg_def_conf(struct snd_soc_codec *codec); -extern int i2c_verify_book0(struct snd_soc_codec *codec); -extern int poll_dac(struct snd_soc_codec *codec, int left_right, int on_off); -extern int poll_adc(struct snd_soc_codec *codec, int left_right, int on_off); - -#ifdef CONFIG_MINI_DSP -extern int aic3262_minidsp_program(struct snd_soc_codec *codec); -extern int aic3262_add_minidsp_controls(struct snd_soc_codec *codec); -#endif - - -#ifdef MULTIBYTE_CONFIG_SUPPORT -extern int aic3262_add_multiconfig_controls(struct snd_soc_codec *codec); -#endif +extern struct snd_soc_dai tlv320aic3262_dai; +extern struct snd_soc_codec_device soc_codec_dev_aic3262; +extern const struct aic3xxx_codec_ops aic3262_cfw_codec_ops; +void aic3262_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report); +unsigned int aic3262_read(struct snd_soc_codec *codec, unsigned int reg); +int aic3262_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value); #endif /* _TLV320AIC3262_H */ -- cgit v1.2.3 From 554ff140ba7b0c4d7ba5b548689776cf284d0c8a Mon Sep 17 00:00:00 2001 From: Manoj Gangwal Date: Fri, 24 Aug 2012 12:38:38 +0530 Subject: asoc: aic326x machine: Add supp. for new TI driver Add support for latest TI codec aic326x driver. Bug 1034241 Change-Id: I387693ed2de466d1704c6f7ff0a1b15c7cbff32f Signed-off-by: Manoj Gangwal Reviewed-on: http://git-master/r/125180 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Lokesh Pathak --- sound/soc/tegra/tegra_aic326x.c | 48 ++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c index a809d4c74145..1b5598dc278c 100644 --- a/sound/soc/tegra/tegra_aic326x.c +++ b/sound/soc/tegra/tegra_aic326x.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "../codecs/tlv320aic326x.h" @@ -354,6 +355,9 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, return err; } + err = snd_soc_dai_set_pll(codec_dai, 0, AIC3262_PLL_CLKIN_MCLK1 , rate, + params_rate(params)); + #ifdef CONFIG_ARCH_TEGRA_2x_SOC err = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAP_SEL_DAC1, TEGRA20_DAS_DAP_ID_1); @@ -737,7 +741,15 @@ static int tegra_aic326x_voice_call_hw_params( return err; } - if(!machine_is_tegra_enterprise()) { + err = snd_soc_dai_set_pll(codec_dai, 0, AIC3262_PLL_CLKIN_MCLK1 , rate, + params_rate(params)); + + if (err < 0) { + dev_err(card->dev, "codec_dai PLL clock not set\n"); + return err; + } + + if (!machine_is_tegra_enterprise()) { if (params_rate(params) == 8000) { /* Change these Settings for 8KHz*/ pcmdiv = 1; @@ -889,7 +901,7 @@ enum headset_state { static int aic326x_headset_switch_notify(struct notifier_block *self, unsigned long action, void *dev) { - int state = 0; + int state = BIT_NO_HEADSET; switch (action) { case SND_JACK_HEADPHONE: @@ -975,10 +987,11 @@ static const struct snd_soc_dapm_route aic326x_audio_map[] = { {"IN2L", NULL, "Mic Bias Int"}, {"Mic Bias Int" ,NULL, "Int Mic"}, {"IN2R", NULL, "Mic Bias Int"}, - {"Mic Bias Ext" ,NULL, "Mic Jack"}, - {"CM1L" ,NULL, "Mic Jack"}, {"IN1L", NULL, "Mic Bias Ext"}, - {"IN1L", NULL, "CM1L"}, + {"Mic Bias Ext" ,NULL, "Mic Jack"}, +/* {"CM1L" ,NULL, "Mic Jack"}, */ +/* {"IN1L", NULL, "Mic Bias Ext"}, */ +/* {"IN1L", NULL, "CM1L"}, */ }; static const struct snd_kcontrol_new tegra_aic326x_controls[] = { @@ -1105,7 +1118,8 @@ static int tegra_aic326x_init(struct snd_soc_pcm_runtime *rtd) tegra_aic326x_hp_jack_pins); #endif - aic326x_headset_detect(codec, &tegra_aic326x_hp_jack, + /* update jack status during boot */ + aic3262_hs_jack_detect(codec, &tegra_aic326x_hp_jack, SND_JACK_HEADSET); /* Add call mode switch control */ @@ -1119,9 +1133,9 @@ static int tegra_aic326x_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS_EXT ON"); - snd_soc_dapm_force_enable_pin(dapm,"MICBIAS_INT ON"); - snd_soc_dapm_sync(dapm); +/* snd_soc_dapm_force_enable_pin(dapm, "Mic Bias Ext"); + snd_soc_dapm_force_enable_pin(dapm,"Mic Bias Int"); + snd_soc_dapm_sync(dapm); */ return 0; } @@ -1130,14 +1144,14 @@ static struct snd_soc_dai_link tegra_aic326x_dai[] = { [DAI_LINK_HIFI] = { .name = "AIC3262", .stream_name = "AIC3262 PCM HIFI", - .codec_name = "aic3262-codec.4-0018", + .codec_name = "tlv320aic3262-codec", .platform_name = "tegra-pcm-audio", #ifdef CONFIG_ARCH_TEGRA_2x_SOC .cpu_dai_name = "tegra20-i2s.0", #else .cpu_dai_name = "tegra30-i2s.0", #endif - .codec_dai_name = "aic3262-asi1", + .codec_dai_name = "aic326x-asi1", .init = tegra_aic326x_init, .ops = &tegra_aic326x_hifi_ops, }, @@ -1171,10 +1185,10 @@ static struct snd_soc_dai_link tegra_aic326x_dai[] = { [DAI_LINK_VOICE_CALL] = { .name = "VOICE CALL", .stream_name = "VOICE CALL PCM", - .codec_name = "aic3262-codec.4-0018", + .codec_name = "tlv320aic3262-codec", .platform_name = "tegra-pcm-audio", .cpu_dai_name = "dit-hifi", - .codec_dai_name = "aic3262-asi2", + .codec_dai_name = "aic326x-asi2", .ops = &tegra_aic326x_voice_call_ops, }, [DAI_LINK_BT_VOICE_CALL] = { @@ -1261,10 +1275,10 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev) 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"; + if (machine_is_tegra_enterprise()) { + tegra_aic326x_dai[DAI_LINK_HIFI].codec_name = "tlv320aic3262-codec"; + tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_name = "tlv320aic3262-codec"; + tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_dai_name = "aic326x-asi1"; } ret = snd_soc_register_card(card); -- cgit v1.2.3 From b46de34ef8ed03c0bf57f3448a3187941b37cd65 Mon Sep 17 00:00:00 2001 From: Manoj Gangwal Date: Wed, 22 Aug 2012 21:57:08 +0530 Subject: asoc: codecs: TI aic326x: Add Digital Mic support Bug 1034241 Change-Id: Id0037f18afa58be82cbeeb43e97b606614fb8ba8 Signed-off-by: Manoj Gangwal Reviewed-on: http://git-master/r/125273 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Scott Peterson --- sound/soc/codecs/tlv320aic3262_default_fw.h | 318 +++++++++++++++++++++++++--- 1 file changed, 285 insertions(+), 33 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/tlv320aic3262_default_fw.h b/sound/soc/codecs/tlv320aic3262_default_fw.h index 55606d76f854..7c1842f0f65f 100644 --- a/sound/soc/codecs/tlv320aic3262_default_fw.h +++ b/sound/soc/codecs/tlv320aic3262_default_fw.h @@ -1,6 +1,6 @@ unsigned char default_firmware[] = { - 237, 241, 209, 192, 179, 0, 1, 0, 123, 20, 0, 0, 238, 201, 253, 6, - 0, 0, 0, 0, 12, 220, 206, 79, 70, 105, 114, 109, 119, 97, 114, 101, + 237, 241, 209, 192, 179, 0, 1, 0, 58, 36, 0, 0, 37, 131, 58, 238, + 0, 0, 0, 0, 8, 207, 52, 80, 70, 105, 114, 109, 119, 97, 114, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -45,11 +45,11 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 0, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 0, 16, 6, 0, 0, 251, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 151, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 29, 0, 0, 54, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -57,16 +57,16 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 231, 19, 0, 0, 105, 110, 105, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 134, 34, 0, 0, 105, 110, 105, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 110, 105, 116, 105, 97, 108, 105, - 122, 97, 116, 105, 111, 110, 32, 115, 101, 113, 117, 101, 110, - 99, 101, 32, 40, 97, 112, 112, 108, 105, 101, 100, 32, 111, 110, - 108, 121, 32, 111, 110, 32, 115, 121, 115, 116, 101, 109, 32, 98, - 111, 111, 116, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 122, 97, 116, 105, 111, 110, 32, 115, 101, 113, 117, 101, 110, 99, 101, + 32, 40, 97, 112, 112, 108, 105, 101, 100, 32, 111, 110, 108, 121, + 32, 111, 110, 32, 115, 121, 115, 116, 101, 109, 32, 98, 111, 111, + 116, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -96,7 +96,7 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 236, 5, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 100, 0, 128, 0, 0, 1, 1, 0, 0, 1, 122, 1, 0, 0, 4, 51, 0, 0, 82, 0, 0, 0, 67, 151, 0, 1, 119, 127, - 100, 101, 102, 97, 117, 108, 116, 0, 98, 97, 115, 101, 95, 109, 97, + 100, 101, 102, 97, 117, 108, 116, 77, 117, 115, 105, 99, 0, 109, 97, 105, 110, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -133,8 +133,8 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 53, 12, 0, 0, 172, 9, 0, 0, 133, 12, 0, 0, - 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 12, 0, 0, 172, 9, 0, 0, 233, 12, 0, 0, + 114, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -194,13 +194,100 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, 44, 12, 0, 0, 2, 0, 0, 0, - 0, 0, 60, 1, 1, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 129, - 112, 0, 0, 6, 16, 0, 0, 129, 15, 0, 0, 6, 1, 0, 0, 7, - 8, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 129, 127, 0, 0, 11, - 2, 0, 0, 129, 127, 0, 0, 12, 8, 0, 0, 129, 127, 0, 0, 18, - 2, 0, 0, 129, 127, 0, 0, 19, 16, 0, 0, 20, 64, 0, 0, 13, - 0, 0, 0, 14, 128, 98, 97, 115, 101, 95, 109, 97, 105, 110, 0, 116, - 99, 104, 95, 98, 97, 115, 101, 95, 109, 97, 105, 110, 95, 82, 97, 116, + 0, 0, 60, 1, 1, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 129, + 127, 0, 0, 6, 17, 0, 0, 129, 63, 0, 0, 7, 8, 0, 0, 8, + 0, 0, 0, 9, 0, 0, 0, 129, 127, 0, 0, 10, 1, 0, 0, 129, + 127, 0, 0, 11, 2, 0, 0, 129, 127, 0, 0, 12, 8, 0, 0, 13, + 0, 0, 0, 14, 128, 0, 0, 129, 127, 0, 0, 18, 2, 0, 0, 129, + 127, 0, 0, 19, 16, 0, 0, 20, 64, 0, 0, 129, 3, 0, 4, 11, + 1, 0, 0, 129, 127, 0, 4, 12, 4, 0, 0, 129, 127, 0, 4, 13, + 32, 0, 0, 129, 119, 0, 4, 14, 2, 0, 0, 129, 3, 0, 4, 27, + 1, 0, 0, 129, 127, 0, 4, 28, 4, 0, 0, 129, 127, 0, 4, 29, + 32, 0, 0, 129, 119, 0, 4, 30, 32, 0, 0, 129, 3, 0, 4, 43, + 1, 0, 0, 129, 127, 0, 4, 44, 4, 0, 0, 129, 127, 0, 4, 45, + 32, 0, 0, 129, 119, 0, 4, 46, 64, 98, 97, 115, 101, 95, 109, 97, + 105, 110, 0, 116, 99, 104, 95, 98, 97, 115, 101, 95, 109, 97, 105, 110, + 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, + 0, 1, 0, 0, 0, 105, 15, 0, 0, 2, 0, 0, 0, 0, 0, 60, + 1, 1, 98, 97, 115, 101, 95, 115, 112, 107, 0, 97, 116, 99, 104, 95, + 98, 97, 115, 101, 95, 115, 112, 107, 95, 82, 97, 116, 101, 52, 56, 46, + 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 230, 17, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, 242, 17, + 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, 100, 101, 102, 97, 117, + 108, 116, 86, 111, 105, 99, 101, 0, 109, 97, 105, 110, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -235,13 +322,168 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 32, + 24, 0, 0, 151, 21, 0, 0, 212, 24, 0, 0, 93, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 97, 117, 108, 116, 47, 98, + 97, 115, 101, 95, 109, 97, 105, 110, 95, 82, 97, 116, 101, 52, 56, 46, + 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, + 0, 0, 0, 23, 24, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, + 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 129, 127, 0, 0, 6, 17, + 0, 0, 129, 63, 0, 0, 7, 8, 0, 0, 8, 0, 0, 0, 9, 0, + 0, 0, 129, 127, 0, 0, 10, 1, 0, 0, 129, 127, 0, 0, 11, 8, + 0, 0, 129, 127, 0, 0, 12, 2, 0, 0, 13, 3, 0, 0, 14, 0, + 0, 0, 129, 127, 0, 0, 18, 8, 0, 0, 129, 127, 0, 0, 19, 12, + 0, 0, 20, 128, 0, 0, 129, 3, 0, 4, 11, 1, 0, 0, 129, 127, + 0, 4, 12, 4, 0, 0, 129, 127, 0, 4, 13, 32, 0, 0, 129, 119, + 0, 4, 14, 2, 0, 0, 129, 3, 0, 4, 27, 1, 0, 0, 129, 127, + 0, 4, 28, 24, 0, 0, 129, 127, 0, 4, 29, 32, 0, 0, 129, 119, + 0, 4, 30, 36, 0, 0, 129, 3, 0, 4, 43, 1, 0, 0, 129, 127, + 0, 4, 44, 4, 0, 0, 129, 127, 0, 4, 45, 32, 0, 0, 129, 119, + 0, 4, 46, 64, 98, 97, 115, 101, 95, 109, 97, 105, 110, 0, 116, 99, + 104, 95, 98, 97, 115, 101, 95, 109, 97, 105, 110, 95, 82, 97, 116, 101, + 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 72, 27, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, + 84, 27, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, 98, 97, 115, + 101, 95, 115, 112, 107, 0, 97, 116, 99, 104, 95, 98, 97, 115, 101, 95, + 115, 112, 107, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 209, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 1, 0, 0, 0, 221, 29, 0, 0, 2, 0, 0, + 0, 0, 0, 60, 1, 1, 100, 101, 102, 97, 117, 108, 116, 77, 117, 115, + 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, - 0, 5, 15, 0, 0, 2, 0, 0, 0, 0, 0, 60, 1, 1, 98, 97, - 115, 101, 95, 115, 112, 107, 0, 97, 116, 99, 104, 95, 98, 97, 115, 101, - 95, 115, 112, 107, 95, 82, 97, 116, 101, 52, 56, 46, 99, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -272,15 +514,14 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 101, 102, 97, 117, 108, 116, 86, 111, 105, + 99, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 130, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, 142, 17, 0, 0, 2, 0, - 0, 0, 0, 0, 60, 1, 1, 100, 101, 102, 97, 117, 108, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -309,22 +550,33 @@ unsigned char default_firmware[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 100, 101, 102, 97, 117, 108, + 116, 77, 117, 115, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 100, 101, 102, 97, 117, 108, 116, 77, 117, 115, 105, 99, 32, 98, + 97, 115, 101, 95, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 97, 117, 108, + 116, 77, 117, 115, 105, 99, 32, 98, 97, 115, 101, 95, 115, 112, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 100, 101, 102, 97, 117, 108, 116, 86, 111, 105, 99, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 100, 101, 102, 97, 117, - 108, 116, 32, 98, 97, 115, 101, 95, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 97, 117, 108, + 116, 86, 111, 105, 99, 101, 32, 98, 97, 115, 101, 95, 109, 97, 105, + 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 100, 101, 102, 97, 117, 108, 116, 32, 98, 97, 115, 101, 95, - 115, 112, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 100, 101, 102, 97, 117, 108, 116, 86, 111, 105, 99, 101, 32, 98, + 97, 115, 101, 95, 115, 112, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, }; -- cgit v1.2.3 From 3745839f5f25ac8de101827813bf305476033d35 Mon Sep 17 00:00:00 2001 From: Manoj Gangwal Date: Wed, 22 Aug 2012 22:18:44 +0530 Subject: asoc: aic326x machine: Add support for D-Mic Bug 1034241 Change-Id: I205a74a8f27237470a3cd1266bb925971654459c Signed-off-by: Manoj Gangwal Reviewed-on: http://git-master/r/125280 Reviewed-by: Simone Willett Tested-by: Simone Willett --- sound/soc/tegra/tegra_aic326x.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c index 1b5598dc278c..de4a1457a7ae 100644 --- a/sound/soc/tegra/tegra_aic326x.c +++ b/sound/soc/tegra/tegra_aic326x.c @@ -973,6 +973,7 @@ static const struct snd_soc_dapm_widget tegra_aic326x_dapm_widgets[] = { SND_SOC_DAPM_INPUT("Ext Mic"), SND_SOC_DAPM_LINE("Linein", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), }; static const struct snd_soc_dapm_route aic326x_audio_map[] = { @@ -989,6 +990,10 @@ static const struct snd_soc_dapm_route aic326x_audio_map[] = { {"IN2R", NULL, "Mic Bias Int"}, {"IN1L", NULL, "Mic Bias Ext"}, {"Mic Bias Ext" ,NULL, "Mic Jack"}, + /* Connect LDMIC and RDMIC to DMIC widget*/ + {"Left DMIC", NULL, "Mic Bias Int"}, + {"Right DMIC", NULL, "Mic Bias Int"}, + {"Mic Bias Int", NULL, "Int Mic"}, /* {"CM1L" ,NULL, "Mic Jack"}, */ /* {"IN1L", NULL, "Mic Bias Ext"}, */ /* {"IN1L", NULL, "CM1L"}, */ -- cgit v1.2.3 From bad016421fc94642de2ad70a78d2917549d81541 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 7 Mar 2012 10:38:26 +0000 Subject: ASoC: dapm: Use DAPM mutex for DAPM ops instead of codec mutex It has now become necessary to use a DAPM mutex instead of the codec mutex to lock the DAPM operations. This is due to the recent multi component support and forth coming Dynamic PCM updates. Currently we lock DAPM operations with the codec mutex of the calling RTD context. However, DAPM operations can span the whole card context and all components. This patch updates the DAPM operations that use the codec mutex to now use the DAPM mutex PCM subclass for all DAPM ops. We also add a mutex subclass for DAPM init and PCM operations. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit a73fb2df01866b772a48fab93401fe3edbe0b38d) Updated call sequences Bug 1039523 Change-Id: If079bbe5032971251d6b5426c752cbcc6ad6bcf5 Signed-off-by: Sang-Hun Lee Reviewed-on: http://git-master/r/128937 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani --- sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 60 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 18 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ef69f5a02709..db2e0f0c00e8 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2897,6 +2897,7 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->list); card->instantiated = 0; mutex_init(&card->mutex); + mutex_init(&card->dapm_mutex); mutex_lock(&client_mutex); list_add(&card->list, &card_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d67c637557a7..ffb6af574c7f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1719,7 +1719,12 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, */ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { - return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); @@ -1882,15 +1887,18 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, { int i, ret; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { ret = snd_soc_dapm_add_route(dapm, route); if (ret < 0) { dev_err(dapm->dev, "Failed to add route %s->%s\n", route->source, route->sink); + mutex_unlock(&dapm->card->dapm_mutex); return ret; } route++; } + mutex_unlock(&dapm->card->dapm_mutex); return 0; } @@ -1963,12 +1971,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int i, err; int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { err = snd_soc_dapm_weak_route(dapm, route); if (err) ret = err; route++; } + mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -1987,6 +1997,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w; unsigned int val; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) @@ -1996,8 +2008,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) w->kcontrols = kzalloc(w->num_kcontrols * sizeof(struct snd_kcontrol *), GFP_KERNEL); - if (!w->kcontrols) + if (!w->kcontrols) { + mutex_unlock(&dapm->card->dapm_mutex); return -ENOMEM; + } } switch(w->id) { @@ -2060,6 +2074,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) } dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2119,6 +2134,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2145,7 +2161,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, /* old connection must be powered down */ connect = invert ? 1 : 0; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { @@ -2167,7 +2183,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2216,6 +2232,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; @@ -2236,7 +2253,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2258,7 +2275,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2295,6 +2312,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2304,7 +2322,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { @@ -2318,7 +2336,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2383,6 +2401,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; @@ -2401,7 +2420,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2423,7 +2442,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2458,14 +2477,15 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(&codec->dapm, pin); - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } @@ -2481,19 +2501,19 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&codec->dapm, pin); else snd_soc_dapm_disable_pin(&codec->dapm, pin); - snd_soc_dapm_sync(&codec->dapm); - - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_sync(&codec->dapm); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); @@ -2561,16 +2581,19 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, { int i, ret; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { ret = snd_soc_dapm_new_control(dapm, widget); if (ret < 0) { dev_err(dapm->dev, "ASoC: Failed to create DAPM control %s: %d\n", widget->name, ret); + mutex_unlock(&dapm->card->dapm_mutex); return ret; } widget++; } + mutex_unlock(&dapm->card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); @@ -2621,13 +2644,14 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, const char *stream, int event) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = rtd->card; if (stream == NULL) return 0; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); soc_dapm_stream_event(&codec->dapm, stream, event); - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } -- cgit v1.2.3 From 49553e9c6dd0ef9dac304adc69c676b0b000d31f Mon Sep 17 00:00:00 2001 From: Sumit Bhattacharya Date: Mon, 27 Aug 2012 16:07:39 +0800 Subject: ASoC: Tegra: Add ALSA ctl to set DMA address Add support for setting of DMA address through alsa control for AVP rendering. This is required to directly do DMA from IRAM to I2S. Bug 1024403 Change-Id: I6b79ae6e9a562160a19d238b817e1a8b407ac208 Signed-off-by: Sumit Bhattacharya Reviewed-on: http://git-master/r/127436 (cherry picked from commit f270f46bef98a56f44432e608041adb617c22559) Signed-off-by: Chandrakanth Gorantla Change-Id: I195aae9043f967273283579f44d5367fe73d542f Reviewed-on: http://git-master/r/129159 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Scott Peterson --- sound/soc/tegra/tegra_asoc_utils.c | 41 +++++++++++++++++++++++++++++++++++--- sound/soc/tegra/tegra_asoc_utils.h | 1 + sound/soc/tegra/tegra_pcm.c | 6 +++++- sound/soc/tegra/tegra_pcm.h | 1 + 4 files changed, 45 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 2c18a71b8b52..80d22d203fb6 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -86,8 +86,11 @@ static int tegra_set_avp_device(struct snd_kcontrol *kcontrol, prtd = substream->runtime->private_data; if (prtd->running) return -EBUSY; - if (prtd) + if (prtd) { prtd->disable_intr = true; + if (data->avp_dma_addr || prtd->avp_dma_addr) + prtd->avp_dma_addr = data->avp_dma_addr; + } } } data->avp_device_id = id; @@ -121,6 +124,30 @@ static int tegra_get_dma_ch_id(struct snd_kcontrol *kcontrol, return 0; } +static int tegra_set_dma_addr(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_asoc_utils_data *data = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = data->card; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + struct tegra_runtime_data *prtd; + + data->avp_dma_addr = ucontrol->value.integer.value[0]; + + rtd = &card->rtd[data->avp_device_id]; + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream || !substream->runtime) + return 0; + + prtd = substream->runtime->private_data; + if (!prtd) + return 0; + + prtd->avp_dma_addr = data->avp_dma_addr; + return 1; +} + static int tegra_get_dma_addr(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -128,6 +155,7 @@ static int tegra_get_dma_addr(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = data->card; struct snd_soc_pcm_runtime *rtd; struct snd_pcm_substream *substream; + struct tegra_runtime_data *prtd; ucontrol->value.integer.value[0] = 0; if (data->avp_device_id < 0) @@ -138,7 +166,14 @@ static int tegra_get_dma_addr(struct snd_kcontrol *kcontrol, if (!substream || !substream->runtime) return 0; - ucontrol->value.integer.value[0] = substream->runtime->dma_addr; + prtd = substream->runtime->private_data; + if (!prtd || !prtd->dma_chan) + return 0; + + ucontrol->value.integer.value[0] = prtd->avp_dma_addr ? + prtd->avp_dma_addr : + substream->runtime->dma_addr; + return 0; } @@ -148,7 +183,7 @@ struct snd_kcontrol_new tegra_avp_controls[] = { SOC_SINGLE_EXT("AVP DMA channel id", 0, 0, TEGRA_DMA_MAX_CHANNELS, \ 0, tegra_get_dma_ch_id, NULL), SOC_SINGLE_EXT("AVP DMA address", 0, 0, 0xFFFFFFFF, \ - 0, tegra_get_dma_addr, NULL), + 0, tegra_get_dma_addr, tegra_set_dma_addr), }; int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index ce379e3e513d..cd137967fcb0 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -43,6 +43,7 @@ struct tegra_asoc_utils_data { int set_mclk; int lock_count; int avp_device_id; + unsigned int avp_dma_addr; }; int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index f471fafff2ac..a7c9c0a110f9 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -70,7 +70,11 @@ static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd) if (++prtd->dma_req_idx >= prtd->dma_req_count) prtd->dma_req_idx -= prtd->dma_req_count; - addr = buf->addr + prtd->dma_pos; + if (prtd->avp_dma_addr) + addr = prtd->avp_dma_addr + prtd->dma_pos; + else + addr = buf->addr + prtd->dma_pos; + prtd->dma_pos += dma_req->size; if (prtd->dma_pos >= prtd->dma_pos_end) prtd->dma_pos = 0; diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index b63de32023e8..5a9531b9fbf7 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -54,6 +54,7 @@ struct tegra_runtime_data { struct tegra_dma_channel *dma_chan; int dma_req_count; int disable_intr; + unsigned int avp_dma_addr; }; int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd); -- cgit v1.2.3 From b02a1cb7dbb6b0815ddd8e104ad8819e847947a0 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 28 Sep 2012 16:29:24 +0530 Subject: asoc: tegra: WAR: Check AVP id before setting DMA WAR to check souncard data's avp_id before trying to set DMA address. An invalid avp_id can cause invlaid rtd, causing panic in control ioctl calls. Bug 1046249 Bug 1044761 Bug 1049940 Bug 1050152 Change-Id: Ib536220fdb92f8256c777d6fa14b66e47bc6b7be Signed-off-by: Shashank Sharma Reviewed-on: http://git-master/r/134426 (cherry picked from commit 007fd7b1ae101c5be2a3d35ad11e7190862c04ca) Reviewed-on: http://git-master/r/142493 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Tested-by: Matthew Pedro Reviewed-by: Winnie Hsu --- sound/soc/tegra/tegra_asoc_utils.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 80d22d203fb6..99ba90d9fc7b 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -133,6 +133,9 @@ static int tegra_set_dma_addr(struct snd_kcontrol *kcontrol, struct snd_pcm_substream *substream; struct tegra_runtime_data *prtd; + if (data->avp_device_id < 0) + return 0; + data->avp_dma_addr = ucontrol->value.integer.value[0]; rtd = &card->rtd[data->avp_device_id]; -- cgit v1.2.3