xref: /OK3568_Linux_fs/kernel/sound/soc/samsung/smdk_wm8580.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // Copyright (c) 2009 Samsung Electronics Co. Ltd
4*4882a593Smuzhiyun // Author: Jaswinder Singh <jassisinghbrar@gmail.com>
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <sound/soc.h>
8*4882a593Smuzhiyun #include <sound/pcm_params.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "../codecs/wm8580.h"
11*4882a593Smuzhiyun #include "i2s.h"
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun /*
14*4882a593Smuzhiyun  * Default CFG switch settings to use this driver:
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  *   SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On
17*4882a593Smuzhiyun  */
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* SMDK has a 12MHZ crystal attached to WM8580 */
20*4882a593Smuzhiyun #define SMDK_WM8580_FREQ 12000000
21*4882a593Smuzhiyun 
smdk_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)22*4882a593Smuzhiyun static int smdk_hw_params(struct snd_pcm_substream *substream,
23*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
26*4882a593Smuzhiyun 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
27*4882a593Smuzhiyun 	unsigned int pll_out;
28*4882a593Smuzhiyun 	int rfs, ret;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	switch (params_width(params)) {
31*4882a593Smuzhiyun 	case 8:
32*4882a593Smuzhiyun 	case 16:
33*4882a593Smuzhiyun 		break;
34*4882a593Smuzhiyun 	default:
35*4882a593Smuzhiyun 		return -EINVAL;
36*4882a593Smuzhiyun 	}
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	/* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
39*4882a593Smuzhiyun 	 * This criterion can't be met if we request PLL output
40*4882a593Smuzhiyun 	 * as {8000x256, 64000x256, 11025x256}Hz.
41*4882a593Smuzhiyun 	 * As a wayout, we rather change rfs to a minimum value that
42*4882a593Smuzhiyun 	 * results in (params_rate(params) * rfs), and itself, acceptable
43*4882a593Smuzhiyun 	 * to both - the CODEC and the CPU.
44*4882a593Smuzhiyun 	 */
45*4882a593Smuzhiyun 	switch (params_rate(params)) {
46*4882a593Smuzhiyun 	case 16000:
47*4882a593Smuzhiyun 	case 22050:
48*4882a593Smuzhiyun 	case 32000:
49*4882a593Smuzhiyun 	case 44100:
50*4882a593Smuzhiyun 	case 48000:
51*4882a593Smuzhiyun 	case 88200:
52*4882a593Smuzhiyun 	case 96000:
53*4882a593Smuzhiyun 		rfs = 256;
54*4882a593Smuzhiyun 		break;
55*4882a593Smuzhiyun 	case 64000:
56*4882a593Smuzhiyun 		rfs = 384;
57*4882a593Smuzhiyun 		break;
58*4882a593Smuzhiyun 	case 8000:
59*4882a593Smuzhiyun 	case 11025:
60*4882a593Smuzhiyun 		rfs = 512;
61*4882a593Smuzhiyun 		break;
62*4882a593Smuzhiyun 	default:
63*4882a593Smuzhiyun 		return -EINVAL;
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 	pll_out = params_rate(params) * rfs;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	/* Set WM8580 to drive MCLK from its PLLA */
68*4882a593Smuzhiyun 	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
69*4882a593Smuzhiyun 					WM8580_CLKSRC_PLLA);
70*4882a593Smuzhiyun 	if (ret < 0)
71*4882a593Smuzhiyun 		return ret;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
74*4882a593Smuzhiyun 					SMDK_WM8580_FREQ, pll_out);
75*4882a593Smuzhiyun 	if (ret < 0)
76*4882a593Smuzhiyun 		return ret;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
79*4882a593Smuzhiyun 				     pll_out, SND_SOC_CLOCK_IN);
80*4882a593Smuzhiyun 	if (ret < 0)
81*4882a593Smuzhiyun 		return ret;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	return 0;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun /*
87*4882a593Smuzhiyun  * SMDK WM8580 DAI operations.
88*4882a593Smuzhiyun  */
89*4882a593Smuzhiyun static struct snd_soc_ops smdk_ops = {
90*4882a593Smuzhiyun 	.hw_params = smdk_hw_params,
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun /* SMDK Playback widgets */
94*4882a593Smuzhiyun static const struct snd_soc_dapm_widget smdk_wm8580_dapm_widgets[] = {
95*4882a593Smuzhiyun 	SND_SOC_DAPM_HP("Front", NULL),
96*4882a593Smuzhiyun 	SND_SOC_DAPM_HP("Center+Sub", NULL),
97*4882a593Smuzhiyun 	SND_SOC_DAPM_HP("Rear", NULL),
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	SND_SOC_DAPM_MIC("MicIn", NULL),
100*4882a593Smuzhiyun 	SND_SOC_DAPM_LINE("LineIn", NULL),
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /* SMDK-PAIFTX connections */
104*4882a593Smuzhiyun static const struct snd_soc_dapm_route smdk_wm8580_audio_map[] = {
105*4882a593Smuzhiyun 	/* MicIn feeds AINL */
106*4882a593Smuzhiyun 	{"AINL", NULL, "MicIn"},
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	/* LineIn feeds AINL/R */
109*4882a593Smuzhiyun 	{"AINL", NULL, "LineIn"},
110*4882a593Smuzhiyun 	{"AINR", NULL, "LineIn"},
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	/* Front Left/Right are fed VOUT1L/R */
113*4882a593Smuzhiyun 	{"Front", NULL, "VOUT1L"},
114*4882a593Smuzhiyun 	{"Front", NULL, "VOUT1R"},
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	/* Center/Sub are fed VOUT2L/R */
117*4882a593Smuzhiyun 	{"Center+Sub", NULL, "VOUT2L"},
118*4882a593Smuzhiyun 	{"Center+Sub", NULL, "VOUT2R"},
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	/* Rear Left/Right are fed VOUT3L/R */
121*4882a593Smuzhiyun 	{"Rear", NULL, "VOUT3L"},
122*4882a593Smuzhiyun 	{"Rear", NULL, "VOUT3R"},
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun 
smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime * rtd)125*4882a593Smuzhiyun static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	/* Enabling the microphone requires the fitting of a 0R
128*4882a593Smuzhiyun 	 * resistor to connect the line from the microphone jack.
129*4882a593Smuzhiyun 	 */
130*4882a593Smuzhiyun 	snd_soc_dapm_disable_pin(&rtd->card->dapm, "MicIn");
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	return 0;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun enum {
136*4882a593Smuzhiyun 	PRI_PLAYBACK = 0,
137*4882a593Smuzhiyun 	PRI_CAPTURE,
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun #define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
141*4882a593Smuzhiyun 	SND_SOC_DAIFMT_CBM_CFM)
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun SND_SOC_DAILINK_DEFS(paif_rx,
144*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")),
145*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-playback")),
146*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun SND_SOC_DAILINK_DEFS(paif_tx,
149*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")),
150*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-capture")),
151*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun static struct snd_soc_dai_link smdk_dai[] = {
154*4882a593Smuzhiyun 	[PRI_PLAYBACK] = { /* Primary Playback i/f */
155*4882a593Smuzhiyun 		.name = "WM8580 PAIF RX",
156*4882a593Smuzhiyun 		.stream_name = "Playback",
157*4882a593Smuzhiyun 		.dai_fmt = SMDK_DAI_FMT,
158*4882a593Smuzhiyun 		.ops = &smdk_ops,
159*4882a593Smuzhiyun 		SND_SOC_DAILINK_REG(paif_rx),
160*4882a593Smuzhiyun 	},
161*4882a593Smuzhiyun 	[PRI_CAPTURE] = { /* Primary Capture i/f */
162*4882a593Smuzhiyun 		.name = "WM8580 PAIF TX",
163*4882a593Smuzhiyun 		.stream_name = "Capture",
164*4882a593Smuzhiyun 		.dai_fmt = SMDK_DAI_FMT,
165*4882a593Smuzhiyun 		.init = smdk_wm8580_init_paiftx,
166*4882a593Smuzhiyun 		.ops = &smdk_ops,
167*4882a593Smuzhiyun 		SND_SOC_DAILINK_REG(paif_tx),
168*4882a593Smuzhiyun 	},
169*4882a593Smuzhiyun };
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun static struct snd_soc_card smdk = {
172*4882a593Smuzhiyun 	.name = "SMDK-I2S",
173*4882a593Smuzhiyun 	.owner = THIS_MODULE,
174*4882a593Smuzhiyun 	.dai_link = smdk_dai,
175*4882a593Smuzhiyun 	.num_links = ARRAY_SIZE(smdk_dai),
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	.dapm_widgets = smdk_wm8580_dapm_widgets,
178*4882a593Smuzhiyun 	.num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets),
179*4882a593Smuzhiyun 	.dapm_routes = smdk_wm8580_audio_map,
180*4882a593Smuzhiyun 	.num_dapm_routes = ARRAY_SIZE(smdk_wm8580_audio_map),
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun static struct platform_device *smdk_snd_device;
184*4882a593Smuzhiyun 
smdk_audio_init(void)185*4882a593Smuzhiyun static int __init smdk_audio_init(void)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	int ret;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	smdk_snd_device = platform_device_alloc("soc-audio", -1);
190*4882a593Smuzhiyun 	if (!smdk_snd_device)
191*4882a593Smuzhiyun 		return -ENOMEM;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	platform_set_drvdata(smdk_snd_device, &smdk);
194*4882a593Smuzhiyun 	ret = platform_device_add(smdk_snd_device);
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	if (ret)
197*4882a593Smuzhiyun 		platform_device_put(smdk_snd_device);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	return ret;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun module_init(smdk_audio_init);
202*4882a593Smuzhiyun 
smdk_audio_exit(void)203*4882a593Smuzhiyun static void __exit smdk_audio_exit(void)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	platform_device_unregister(smdk_snd_device);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun module_exit(smdk_audio_exit);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun MODULE_AUTHOR("Jaswinder Singh, jassisinghbrar@gmail.com");
210*4882a593Smuzhiyun MODULE_DESCRIPTION("ALSA SoC SMDK WM8580");
211*4882a593Smuzhiyun MODULE_LICENSE("GPL");
212