xref: /OK3568_Linux_fs/kernel/sound/soc/fsl/mx27vis-aic32x4.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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