1// SPDX-License-Identifier: GPL-2.0+ 2// 3// mx27vis-aic32x4.c 4// 5// Copyright 2011 Vista Silicon S.L. 6// 7// Author: Javier Martin <javier.martin@vista-silicon.com> 8 9#include <linux/module.h> 10#include <linux/moduleparam.h> 11#include <linux/device.h> 12#include <linux/i2c.h> 13#include <linux/gpio.h> 14#include <linux/platform_data/asoc-mx27vis.h> 15#include <sound/core.h> 16#include <sound/pcm.h> 17#include <sound/soc.h> 18#include <sound/soc-dapm.h> 19#include <sound/tlv.h> 20#include <asm/mach-types.h> 21 22#include "../codecs/tlv320aic32x4.h" 23#include "imx-ssi.h" 24#include "imx-audmux.h" 25 26#define MX27VIS_AMP_GAIN 0 27#define MX27VIS_AMP_MUTE 1 28 29static int mx27vis_amp_gain; 30static int mx27vis_amp_mute; 31static int mx27vis_amp_gain0_gpio; 32static int mx27vis_amp_gain1_gpio; 33static int mx27vis_amp_mutel_gpio; 34static int mx27vis_amp_muter_gpio; 35 36static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, 37 struct snd_pcm_hw_params *params) 38{ 39 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 40 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 41 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 42 int ret; 43 44 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 45 25000000, SND_SOC_CLOCK_OUT); 46 if (ret) { 47 pr_err("%s: failed setting codec sysclk\n", __func__); 48 return ret; 49 } 50 51 ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, 52 SND_SOC_CLOCK_IN); 53 if (ret) { 54 pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); 55 return ret; 56 } 57 58 return 0; 59} 60 61static const struct snd_soc_ops mx27vis_aic32x4_snd_ops = { 62 .hw_params = mx27vis_aic32x4_hw_params, 63}; 64 65static int mx27vis_amp_set(struct snd_kcontrol *kcontrol, 66 struct snd_ctl_elem_value *ucontrol) 67{ 68 struct soc_mixer_control *mc = 69 (struct soc_mixer_control *)kcontrol->private_value; 70 int value = ucontrol->value.integer.value[0]; 71 unsigned int reg = mc->reg; 72 int max = mc->max; 73 74 if (value > max) 75 return -EINVAL; 76 77 switch (reg) { 78 case MX27VIS_AMP_GAIN: 79 gpio_set_value(mx27vis_amp_gain0_gpio, value & 1); 80 gpio_set_value(mx27vis_amp_gain1_gpio, value >> 1); 81 mx27vis_amp_gain = value; 82 break; 83 case MX27VIS_AMP_MUTE: 84 gpio_set_value(mx27vis_amp_mutel_gpio, value & 1); 85 gpio_set_value(mx27vis_amp_muter_gpio, value >> 1); 86 mx27vis_amp_mute = value; 87 break; 88 } 89 return 0; 90} 91 92static int mx27vis_amp_get(struct snd_kcontrol *kcontrol, 93 struct snd_ctl_elem_value *ucontrol) 94{ 95 struct soc_mixer_control *mc = 96 (struct soc_mixer_control *)kcontrol->private_value; 97 unsigned int reg = mc->reg; 98 99 switch (reg) { 100 case MX27VIS_AMP_GAIN: 101 ucontrol->value.integer.value[0] = mx27vis_amp_gain; 102 break; 103 case MX27VIS_AMP_MUTE: 104 ucontrol->value.integer.value[0] = mx27vis_amp_mute; 105 break; 106 } 107 return 0; 108} 109 110/* From 6dB to 24dB in steps of 6dB */ 111static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); 112 113static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { 114 SOC_DAPM_PIN_SWITCH("External Mic"), 115 SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, 116 mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), 117 SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, 118 mx27vis_amp_get, mx27vis_amp_set), 119}; 120 121static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { 122 SND_SOC_DAPM_MIC("External Mic", NULL), 123}; 124 125static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { 126 {"Mic Bias", NULL, "External Mic"}, 127 {"IN1_R", NULL, "Mic Bias"}, 128 {"IN2_R", NULL, "Mic Bias"}, 129 {"IN3_R", NULL, "Mic Bias"}, 130 {"IN1_L", NULL, "Mic Bias"}, 131 {"IN2_L", NULL, "Mic Bias"}, 132 {"IN3_L", NULL, "Mic Bias"}, 133}; 134 135SND_SOC_DAILINK_DEFS(hifi, 136 DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), 137 DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic32x4.0-0018", 138 "tlv320aic32x4-hifi")), 139 DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); 140 141static struct snd_soc_dai_link mx27vis_aic32x4_dai = { 142 .name = "tlv320aic32x4", 143 .stream_name = "TLV320AIC32X4", 144 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | 145 SND_SOC_DAIFMT_CBM_CFM, 146 .ops = &mx27vis_aic32x4_snd_ops, 147 SND_SOC_DAILINK_REG(hifi), 148}; 149 150static struct snd_soc_card mx27vis_aic32x4 = { 151 .name = "visstrim_m10-audio", 152 .owner = THIS_MODULE, 153 .dai_link = &mx27vis_aic32x4_dai, 154 .num_links = 1, 155 .controls = mx27vis_aic32x4_controls, 156 .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), 157 .dapm_widgets = aic32x4_dapm_widgets, 158 .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), 159 .dapm_routes = aic32x4_dapm_routes, 160 .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), 161}; 162 163static int mx27vis_aic32x4_probe(struct platform_device *pdev) 164{ 165 struct snd_mx27vis_platform_data *pdata = pdev->dev.platform_data; 166 int ret; 167 168 if (!pdata) { 169 dev_err(&pdev->dev, "No platform data supplied\n"); 170 return -EINVAL; 171 } 172 173 mx27vis_amp_gain0_gpio = pdata->amp_gain0_gpio; 174 mx27vis_amp_gain1_gpio = pdata->amp_gain1_gpio; 175 mx27vis_amp_mutel_gpio = pdata->amp_mutel_gpio; 176 mx27vis_amp_muter_gpio = pdata->amp_muter_gpio; 177 178 mx27vis_aic32x4.dev = &pdev->dev; 179 ret = devm_snd_soc_register_card(&pdev->dev, &mx27vis_aic32x4); 180 if (ret) { 181 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 182 ret); 183 return ret; 184 } 185 186 /* Connect SSI0 as clock slave to SSI1 external pins */ 187 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, 188 IMX_AUDMUX_V1_PCR_SYN | 189 IMX_AUDMUX_V1_PCR_TFSDIR | 190 IMX_AUDMUX_V1_PCR_TCLKDIR | 191 IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | 192 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) 193 ); 194 imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, 195 IMX_AUDMUX_V1_PCR_SYN | 196 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) 197 ); 198 199 return ret; 200} 201 202static struct platform_driver mx27vis_aic32x4_audio_driver = { 203 .driver = { 204 .name = "mx27vis", 205 }, 206 .probe = mx27vis_aic32x4_probe, 207}; 208 209module_platform_driver(mx27vis_aic32x4_audio_driver); 210 211MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); 212MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); 213MODULE_LICENSE("GPL"); 214MODULE_ALIAS("platform:mx27vis"); 215