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