Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/renesas/migor.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// ALSA SoC driver for Migo-R
4
//
5
// Copyright (C) 2009-2010 Guennadi Liakhovetski <[email protected]>
6
7
#include <linux/clkdev.h>
8
#include <linux/device.h>
9
#include <linux/firmware.h>
10
#include <linux/module.h>
11
12
#include <asm/clock.h>
13
14
#include <cpu/sh7722.h>
15
16
#include <sound/core.h>
17
#include <sound/pcm.h>
18
#include <sound/soc.h>
19
20
#include "../codecs/wm8978.h"
21
#include "siu.h"
22
23
/* Default 8000Hz sampling frequency */
24
static unsigned long codec_freq = 8000 * 512;
25
26
static unsigned int use_count;
27
28
/* External clock, sourced from the codec at the SIUMCKB pin */
29
static unsigned long siumckb_recalc(struct clk *clk)
30
{
31
return codec_freq;
32
}
33
34
static struct sh_clk_ops siumckb_clk_ops = {
35
.recalc = siumckb_recalc,
36
};
37
38
static struct clk siumckb_clk = {
39
.ops = &siumckb_clk_ops,
40
.rate = 0, /* initialised at run-time */
41
};
42
43
static struct clk_lookup *siumckb_lookup;
44
45
static int migor_hw_params(struct snd_pcm_substream *substream,
46
struct snd_pcm_hw_params *params)
47
{
48
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
49
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
50
int ret;
51
unsigned int rate = params_rate(params);
52
53
ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000,
54
SND_SOC_CLOCK_IN);
55
if (ret < 0)
56
return ret;
57
58
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512);
59
if (ret < 0)
60
return ret;
61
62
codec_freq = rate * 512;
63
/*
64
* This propagates the parent frequency change to children and
65
* recalculates the frequency table
66
*/
67
clk_set_rate(&siumckb_clk, codec_freq);
68
dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
69
70
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT,
71
codec_freq / 2, SND_SOC_CLOCK_IN);
72
73
if (!ret)
74
use_count++;
75
76
return ret;
77
}
78
79
static int migor_hw_free(struct snd_pcm_substream *substream)
80
{
81
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
82
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
83
84
if (use_count) {
85
use_count--;
86
87
if (!use_count)
88
snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0,
89
SND_SOC_CLOCK_IN);
90
} else {
91
dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n");
92
}
93
94
return 0;
95
}
96
97
static const struct snd_soc_ops migor_dai_ops = {
98
.hw_params = migor_hw_params,
99
.hw_free = migor_hw_free,
100
};
101
102
static const struct snd_soc_dapm_widget migor_dapm_widgets[] = {
103
SND_SOC_DAPM_HP("Headphone", NULL),
104
SND_SOC_DAPM_MIC("Onboard Microphone", NULL),
105
SND_SOC_DAPM_MIC("External Microphone", NULL),
106
};
107
108
static const struct snd_soc_dapm_route audio_map[] = {
109
/* Headphone output connected to LHP/RHP, enable OUT4 for VMID */
110
{ "Headphone", NULL, "OUT4 VMID" },
111
{ "OUT4 VMID", NULL, "LHP" },
112
{ "OUT4 VMID", NULL, "RHP" },
113
114
/* On-board microphone */
115
{ "RMICN", NULL, "Mic Bias" },
116
{ "RMICP", NULL, "Mic Bias" },
117
{ "Mic Bias", NULL, "Onboard Microphone" },
118
119
/* External microphone */
120
{ "LMICN", NULL, "Mic Bias" },
121
{ "LMICP", NULL, "Mic Bias" },
122
{ "Mic Bias", NULL, "External Microphone" },
123
};
124
125
/* migor digital audio interface glue - connects codec <--> CPU */
126
SND_SOC_DAILINK_DEFS(wm8978,
127
DAILINK_COMP_ARRAY(COMP_CPU("siu-pcm-audio")),
128
DAILINK_COMP_ARRAY(COMP_CODEC("wm8978.0-001a", "wm8978-hifi")),
129
DAILINK_COMP_ARRAY(COMP_PLATFORM("siu-pcm-audio")));
130
131
static struct snd_soc_dai_link migor_dai = {
132
.name = "wm8978",
133
.stream_name = "WM8978",
134
.dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S |
135
SND_SOC_DAIFMT_CBC_CFC,
136
.ops = &migor_dai_ops,
137
SND_SOC_DAILINK_REG(wm8978),
138
};
139
140
/* migor audio machine driver */
141
static struct snd_soc_card snd_soc_migor = {
142
.name = "Migo-R",
143
.owner = THIS_MODULE,
144
.dai_link = &migor_dai,
145
.num_links = 1,
146
147
.dapm_widgets = migor_dapm_widgets,
148
.num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets),
149
.dapm_routes = audio_map,
150
.num_dapm_routes = ARRAY_SIZE(audio_map),
151
};
152
153
static struct platform_device *migor_snd_device;
154
155
static int __init migor_init(void)
156
{
157
int ret;
158
159
ret = clk_register(&siumckb_clk);
160
if (ret < 0)
161
return ret;
162
163
siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
164
if (!siumckb_lookup) {
165
ret = -ENOMEM;
166
goto eclkdevalloc;
167
}
168
169
/* Port number used on this machine: port B */
170
migor_snd_device = platform_device_alloc("soc-audio", 1);
171
if (!migor_snd_device) {
172
ret = -ENOMEM;
173
goto epdevalloc;
174
}
175
176
platform_set_drvdata(migor_snd_device, &snd_soc_migor);
177
178
ret = platform_device_add(migor_snd_device);
179
if (ret)
180
goto epdevadd;
181
182
return 0;
183
184
epdevadd:
185
platform_device_put(migor_snd_device);
186
epdevalloc:
187
clkdev_drop(siumckb_lookup);
188
eclkdevalloc:
189
clk_unregister(&siumckb_clk);
190
return ret;
191
}
192
193
static void __exit migor_exit(void)
194
{
195
clkdev_drop(siumckb_lookup);
196
clk_unregister(&siumckb_clk);
197
platform_device_unregister(migor_snd_device);
198
}
199
200
module_init(migor_init);
201
module_exit(migor_exit);
202
203
MODULE_AUTHOR("Guennadi Liakhovetski <[email protected]>");
204
MODULE_DESCRIPTION("ALSA SoC Migor");
205
MODULE_LICENSE("GPL v2");
206
207