18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// mx27vis-aic32x4.c 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2011 Vista Silicon S.L. 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Author: Javier Martin <javier.martin@vista-silicon.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-mx27vis.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h> 198c2ecf20Sopenharmony_ci#include <sound/tlv.h> 208c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "../codecs/tlv320aic32x4.h" 238c2ecf20Sopenharmony_ci#include "imx-ssi.h" 248c2ecf20Sopenharmony_ci#include "imx-audmux.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define MX27VIS_AMP_GAIN 0 278c2ecf20Sopenharmony_ci#define MX27VIS_AMP_MUTE 1 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int mx27vis_amp_gain; 308c2ecf20Sopenharmony_cistatic int mx27vis_amp_mute; 318c2ecf20Sopenharmony_cistatic int mx27vis_amp_gain0_gpio; 328c2ecf20Sopenharmony_cistatic int mx27vis_amp_gain1_gpio; 338c2ecf20Sopenharmony_cistatic int mx27vis_amp_mutel_gpio; 348c2ecf20Sopenharmony_cistatic int mx27vis_amp_muter_gpio; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, 378c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 408c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 418c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, 0, 458c2ecf20Sopenharmony_ci 25000000, SND_SOC_CLOCK_OUT); 468c2ecf20Sopenharmony_ci if (ret) { 478c2ecf20Sopenharmony_ci pr_err("%s: failed setting codec sysclk\n", __func__); 488c2ecf20Sopenharmony_ci return ret; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, 528c2ecf20Sopenharmony_ci SND_SOC_CLOCK_IN); 538c2ecf20Sopenharmony_ci if (ret) { 548c2ecf20Sopenharmony_ci pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const struct snd_soc_ops mx27vis_aic32x4_snd_ops = { 628c2ecf20Sopenharmony_ci .hw_params = mx27vis_aic32x4_hw_params, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int mx27vis_amp_set(struct snd_kcontrol *kcontrol, 668c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 698c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 708c2ecf20Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 718c2ecf20Sopenharmony_ci unsigned int reg = mc->reg; 728c2ecf20Sopenharmony_ci int max = mc->max; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (value > max) 758c2ecf20Sopenharmony_ci return -EINVAL; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci switch (reg) { 788c2ecf20Sopenharmony_ci case MX27VIS_AMP_GAIN: 798c2ecf20Sopenharmony_ci gpio_set_value(mx27vis_amp_gain0_gpio, value & 1); 808c2ecf20Sopenharmony_ci gpio_set_value(mx27vis_amp_gain1_gpio, value >> 1); 818c2ecf20Sopenharmony_ci mx27vis_amp_gain = value; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case MX27VIS_AMP_MUTE: 848c2ecf20Sopenharmony_ci gpio_set_value(mx27vis_amp_mutel_gpio, value & 1); 858c2ecf20Sopenharmony_ci gpio_set_value(mx27vis_amp_muter_gpio, value >> 1); 868c2ecf20Sopenharmony_ci mx27vis_amp_mute = value; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int mx27vis_amp_get(struct snd_kcontrol *kcontrol, 938c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 968c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 978c2ecf20Sopenharmony_ci unsigned int reg = mc->reg; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci switch (reg) { 1008c2ecf20Sopenharmony_ci case MX27VIS_AMP_GAIN: 1018c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mx27vis_amp_gain; 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case MX27VIS_AMP_MUTE: 1048c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mx27vis_amp_mute; 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* From 6dB to 24dB in steps of 6dB */ 1118c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { 1148c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("External Mic"), 1158c2ecf20Sopenharmony_ci SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, 1168c2ecf20Sopenharmony_ci mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), 1178c2ecf20Sopenharmony_ci SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, 1188c2ecf20Sopenharmony_ci mx27vis_amp_get, mx27vis_amp_set), 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { 1228c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("External Mic", NULL), 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { 1268c2ecf20Sopenharmony_ci {"Mic Bias", NULL, "External Mic"}, 1278c2ecf20Sopenharmony_ci {"IN1_R", NULL, "Mic Bias"}, 1288c2ecf20Sopenharmony_ci {"IN2_R", NULL, "Mic Bias"}, 1298c2ecf20Sopenharmony_ci {"IN3_R", NULL, "Mic Bias"}, 1308c2ecf20Sopenharmony_ci {"IN1_L", NULL, "Mic Bias"}, 1318c2ecf20Sopenharmony_ci {"IN2_L", NULL, "Mic Bias"}, 1328c2ecf20Sopenharmony_ci {"IN3_L", NULL, "Mic Bias"}, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(hifi, 1368c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), 1378c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic32x4.0-0018", 1388c2ecf20Sopenharmony_ci "tlv320aic32x4-hifi")), 1398c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link mx27vis_aic32x4_dai = { 1428c2ecf20Sopenharmony_ci .name = "tlv320aic32x4", 1438c2ecf20Sopenharmony_ci .stream_name = "TLV320AIC32X4", 1448c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | 1458c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 1468c2ecf20Sopenharmony_ci .ops = &mx27vis_aic32x4_snd_ops, 1478c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(hifi), 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct snd_soc_card mx27vis_aic32x4 = { 1518c2ecf20Sopenharmony_ci .name = "visstrim_m10-audio", 1528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1538c2ecf20Sopenharmony_ci .dai_link = &mx27vis_aic32x4_dai, 1548c2ecf20Sopenharmony_ci .num_links = 1, 1558c2ecf20Sopenharmony_ci .controls = mx27vis_aic32x4_controls, 1568c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), 1578c2ecf20Sopenharmony_ci .dapm_widgets = aic32x4_dapm_widgets, 1588c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), 1598c2ecf20Sopenharmony_ci .dapm_routes = aic32x4_dapm_routes, 1608c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int mx27vis_aic32x4_probe(struct platform_device *pdev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct snd_mx27vis_platform_data *pdata = pdev->dev.platform_data; 1668c2ecf20Sopenharmony_ci int ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!pdata) { 1698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data supplied\n"); 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mx27vis_amp_gain0_gpio = pdata->amp_gain0_gpio; 1748c2ecf20Sopenharmony_ci mx27vis_amp_gain1_gpio = pdata->amp_gain1_gpio; 1758c2ecf20Sopenharmony_ci mx27vis_amp_mutel_gpio = pdata->amp_mutel_gpio; 1768c2ecf20Sopenharmony_ci mx27vis_amp_muter_gpio = pdata->amp_muter_gpio; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci mx27vis_aic32x4.dev = &pdev->dev; 1798c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, &mx27vis_aic32x4); 1808c2ecf20Sopenharmony_ci if (ret) { 1818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 1828c2ecf20Sopenharmony_ci ret); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Connect SSI0 as clock slave to SSI1 external pins */ 1878c2ecf20Sopenharmony_ci imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, 1888c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_SYN | 1898c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_TFSDIR | 1908c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_TCLKDIR | 1918c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | 1928c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) 1938c2ecf20Sopenharmony_ci ); 1948c2ecf20Sopenharmony_ci imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, 1958c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_SYN | 1968c2ecf20Sopenharmony_ci IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) 1978c2ecf20Sopenharmony_ci ); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct platform_driver mx27vis_aic32x4_audio_driver = { 2038c2ecf20Sopenharmony_ci .driver = { 2048c2ecf20Sopenharmony_ci .name = "mx27vis", 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci .probe = mx27vis_aic32x4_probe, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cimodule_platform_driver(mx27vis_aic32x4_audio_driver); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); 2128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); 2138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2148c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mx27vis"); 215