~hui.wang/alsa-driver/tiwai-trunk-fgit

« back to all changes in this revision

Viewing changes to soc/codecs/wm8524.c

  • Committer: Hui Wang
  • Date: 2018-06-07 01:04:04 UTC
  • Revision ID: git-v1:baf13208df10376d9e4588ad3524aeb3c9973bdf
sync the alsa hda driver from Takashi's tree

Signed-off-by: Hui Wang <hui.wang@canonical.com>

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * wm8524.c  --  WM8524 ALSA SoC Audio driver
 
3
 *
 
4
 * Copyright 2009 Wolfson Microelectronics plc
 
5
 * Copyright 2017 NXP
 
6
 *
 
7
 * Based on WM8523 ALSA SoC Audio driver written by Mark Brown
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License version 2 as
 
11
 * published by the Free Software Foundation.
 
12
 */
 
13
 
 
14
#include <linux/module.h>
 
15
#include <linux/moduleparam.h>
 
16
#include <linux/init.h>
 
17
#include <linux/delay.h>
 
18
#include <linux/slab.h>
 
19
#include <linux/gpio/consumer.h>
 
20
#include <linux/of_device.h>
 
21
#include <sound/core.h>
 
22
#include <sound/pcm.h>
 
23
#include <sound/pcm_params.h>
 
24
#include <sound/soc.h>
 
25
#include <sound/initval.h>
 
26
 
 
27
#define WM8524_NUM_RATES 7
 
28
 
 
29
/* codec private data */
 
30
struct wm8524_priv {
 
31
        struct gpio_desc *mute;
 
32
        unsigned int sysclk;
 
33
        unsigned int rate_constraint_list[WM8524_NUM_RATES];
 
34
        struct snd_pcm_hw_constraint_list rate_constraint;
 
35
};
 
36
 
 
37
 
 
38
static const struct snd_soc_dapm_widget wm8524_dapm_widgets[] = {
 
39
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
 
40
SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
 
41
SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
 
42
};
 
43
 
 
44
static const struct snd_soc_dapm_route wm8524_dapm_routes[] = {
 
45
        { "LINEVOUTL", NULL, "DAC" },
 
46
        { "LINEVOUTR", NULL, "DAC" },
 
47
};
 
48
 
 
49
static const struct {
 
50
        int value;
 
51
        int ratio;
 
52
} lrclk_ratios[WM8524_NUM_RATES] = {
 
53
        { 1, 128 },
 
54
        { 2, 192 },
 
55
        { 3, 256 },
 
56
        { 4, 384 },
 
57
        { 5, 512 },
 
58
        { 6, 768 },
 
59
        { 7, 1152 },
 
60
};
 
61
 
 
62
static int wm8524_startup(struct snd_pcm_substream *substream,
 
63
                          struct snd_soc_dai *dai)
 
64
{
 
65
        struct snd_soc_component *component = dai->component;
 
66
        struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
 
67
 
 
68
        /* The set of sample rates that can be supported depends on the
 
69
         * MCLK supplied to the CODEC - enforce this.
 
70
         */
 
71
        if (!wm8524->sysclk) {
 
72
                dev_err(component->dev,
 
73
                        "No MCLK configured, call set_sysclk() on init\n");
 
74
                return -EINVAL;
 
75
        }
 
76
 
 
77
        snd_pcm_hw_constraint_list(substream->runtime, 0,
 
78
                                   SNDRV_PCM_HW_PARAM_RATE,
 
79
                                   &wm8524->rate_constraint);
 
80
 
 
81
        gpiod_set_value_cansleep(wm8524->mute, 1);
 
82
 
 
83
        return 0;
 
84
}
 
85
 
 
86
static void wm8524_shutdown(struct snd_pcm_substream *substream,
 
87
                          struct snd_soc_dai *dai)
 
88
{
 
89
        struct snd_soc_component *component = dai->component;
 
90
        struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
 
91
 
 
92
        gpiod_set_value_cansleep(wm8524->mute, 0);
 
93
}
 
94
 
 
95
static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 
96
                int clk_id, unsigned int freq, int dir)
 
97
{
 
98
        struct snd_soc_component *component = codec_dai->component;
 
99
        struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
 
100
        unsigned int val;
 
101
        int i, j = 0;
 
102
 
 
103
        wm8524->sysclk = freq;
 
104
 
 
105
        wm8524->rate_constraint.count = 0;
 
106
        for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
 
107
                val = freq / lrclk_ratios[i].ratio;
 
108
                /* Check that it's a standard rate since core can't
 
109
                 * cope with others and having the odd rates confuses
 
110
                 * constraint matching.
 
111
                 */
 
112
                switch (val) {
 
113
                case 8000:
 
114
                case 32000:
 
115
                case 44100:
 
116
                case 48000:
 
117
                case 88200:
 
118
                case 96000:
 
119
                case 176400:
 
120
                case 192000:
 
121
                        dev_dbg(component->dev, "Supported sample rate: %dHz\n",
 
122
                                val);
 
123
                        wm8524->rate_constraint_list[j++] = val;
 
124
                        wm8524->rate_constraint.count++;
 
125
                        break;
 
126
                default:
 
127
                        dev_dbg(component->dev, "Skipping sample rate: %dHz\n",
 
128
                                val);
 
129
                }
 
130
        }
 
131
 
 
132
        /* Need at least one supported rate... */
 
133
        if (wm8524->rate_constraint.count == 0)
 
134
                return -EINVAL;
 
135
 
 
136
        return 0;
 
137
}
 
138
 
 
139
static int wm8524_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
 
140
{
 
141
        fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
 
142
                SND_SOC_DAIFMT_MASTER_MASK);
 
143
 
 
144
        if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 
145
                    SND_SOC_DAIFMT_CBS_CFS)) {
 
146
                dev_err(codec_dai->dev, "Invalid DAI format\n");
 
147
                return -EINVAL;
 
148
        }
 
149
 
 
150
        return 0;
 
151
}
 
152
 
 
153
static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
 
154
{
 
155
        struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(dai->component);
 
156
 
 
157
        if (wm8524->mute)
 
158
                gpiod_set_value_cansleep(wm8524->mute, mute);
 
159
 
 
160
        return 0;
 
161
}
 
162
 
 
163
#define WM8524_RATES SNDRV_PCM_RATE_8000_192000
 
164
 
 
165
#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
 
166
 
 
167
static const struct snd_soc_dai_ops wm8524_dai_ops = {
 
168
        .startup        = wm8524_startup,
 
169
        .shutdown       = wm8524_shutdown,
 
170
        .set_sysclk     = wm8524_set_dai_sysclk,
 
171
        .set_fmt        = wm8524_set_fmt,
 
172
        .mute_stream    = wm8524_mute_stream,
 
173
};
 
174
 
 
175
static struct snd_soc_dai_driver wm8524_dai = {
 
176
        .name = "wm8524-hifi",
 
177
        .playback = {
 
178
                .stream_name = "Playback",
 
179
                .channels_min = 2,
 
180
                .channels_max = 2,
 
181
                .rates = WM8524_RATES,
 
182
                .formats = WM8524_FORMATS,
 
183
        },
 
184
        .ops = &wm8524_dai_ops,
 
185
};
 
186
 
 
187
static int wm8524_probe(struct snd_soc_component *component)
 
188
{
 
189
        struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
 
190
 
 
191
        wm8524->rate_constraint.list = &wm8524->rate_constraint_list[0];
 
192
        wm8524->rate_constraint.count =
 
193
                ARRAY_SIZE(wm8524->rate_constraint_list);
 
194
 
 
195
        return 0;
 
196
}
 
197
 
 
198
static const struct snd_soc_component_driver soc_component_dev_wm8524 = {
 
199
        .probe                  = wm8524_probe,
 
200
        .dapm_widgets           = wm8524_dapm_widgets,
 
201
        .num_dapm_widgets       = ARRAY_SIZE(wm8524_dapm_widgets),
 
202
        .dapm_routes            = wm8524_dapm_routes,
 
203
        .num_dapm_routes        = ARRAY_SIZE(wm8524_dapm_routes),
 
204
        .idle_bias_on           = 1,
 
205
        .use_pmdown_time        = 1,
 
206
        .endianness             = 1,
 
207
        .non_legacy_dai_naming  = 1,
 
208
};
 
209
 
 
210
static const struct of_device_id wm8524_of_match[] = {
 
211
        { .compatible = "wlf,wm8524" },
 
212
        { /* sentinel*/ }
 
213
};
 
214
MODULE_DEVICE_TABLE(of, wm8524_of_match);
 
215
 
 
216
static int wm8524_codec_probe(struct platform_device *pdev)
 
217
{
 
218
        struct wm8524_priv *wm8524;
 
219
        int ret;
 
220
 
 
221
        wm8524 = devm_kzalloc(&pdev->dev, sizeof(struct wm8524_priv),
 
222
                                                  GFP_KERNEL);
 
223
        if (wm8524 == NULL)
 
224
                return -ENOMEM;
 
225
 
 
226
        platform_set_drvdata(pdev, wm8524);
 
227
 
 
228
        wm8524->mute = devm_gpiod_get(&pdev->dev, "wlf,mute", GPIOD_OUT_LOW);
 
229
        if (IS_ERR(wm8524->mute)) {
 
230
                ret = PTR_ERR(wm8524->mute);
 
231
                dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret);
 
232
                return ret;
 
233
        }
 
234
 
 
235
        ret = devm_snd_soc_register_component(&pdev->dev,
 
236
                        &soc_component_dev_wm8524, &wm8524_dai, 1);
 
237
        if (ret < 0)
 
238
                dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
 
239
 
 
240
        return ret;
 
241
}
 
242
 
 
243
static struct platform_driver wm8524_codec_driver = {
 
244
        .probe          = wm8524_codec_probe,
 
245
        .driver         = {
 
246
                .name   = "wm8524-codec",
 
247
                .of_match_table = wm8524_of_match,
 
248
        },
 
249
};
 
250
module_platform_driver(wm8524_codec_driver);
 
251
 
 
252
MODULE_DESCRIPTION("ASoC WM8524 driver");
 
253
MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
 
254
MODULE_ALIAS("platform:wm8524-codec");
 
255
MODULE_LICENSE("GPL");