1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for the 1250-EV1 audio I/O module |
4 | * |
5 | * Copyright 2011 Wolfson Microelectronics plc |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/module.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/gpio/consumer.h> |
13 | |
14 | #include <sound/soc.h> |
15 | #include <sound/soc-dapm.h> |
16 | |
17 | struct wm1250_priv { |
18 | struct gpio_desc *clk_ena; |
19 | struct gpio_desc *clk_sel0; |
20 | struct gpio_desc *clk_sel1; |
21 | struct gpio_desc *osr; |
22 | struct gpio_desc *master; |
23 | }; |
24 | |
25 | static int wm1250_ev1_set_bias_level(struct snd_soc_component *component, |
26 | enum snd_soc_bias_level level) |
27 | { |
28 | struct wm1250_priv *wm1250 = dev_get_drvdata(dev: component->dev); |
29 | |
30 | switch (level) { |
31 | case SND_SOC_BIAS_ON: |
32 | break; |
33 | |
34 | case SND_SOC_BIAS_PREPARE: |
35 | break; |
36 | |
37 | case SND_SOC_BIAS_STANDBY: |
38 | gpiod_set_value_cansleep(desc: wm1250->clk_ena, value: 1); |
39 | break; |
40 | |
41 | case SND_SOC_BIAS_OFF: |
42 | gpiod_set_value_cansleep(desc: wm1250->clk_ena, value: 0); |
43 | break; |
44 | } |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = { |
50 | SND_SOC_DAPM_ADC("ADC" , "wm1250-ev1 Capture" , SND_SOC_NOPM, 0, 0), |
51 | SND_SOC_DAPM_DAC("DAC" , "wm1250-ev1 Playback" , SND_SOC_NOPM, 0, 0), |
52 | |
53 | SND_SOC_DAPM_INPUT("WM1250 Input" ), |
54 | SND_SOC_DAPM_OUTPUT("WM1250 Output" ), |
55 | }; |
56 | |
57 | static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = { |
58 | { "ADC" , NULL, "WM1250 Input" }, |
59 | { "WM1250 Output" , NULL, "DAC" }, |
60 | }; |
61 | |
62 | static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream, |
63 | struct snd_pcm_hw_params *params, |
64 | struct snd_soc_dai *dai) |
65 | { |
66 | struct wm1250_priv *wm1250 = snd_soc_component_get_drvdata(c: dai->component); |
67 | |
68 | switch (params_rate(p: params)) { |
69 | case 8000: |
70 | gpiod_set_value(desc: wm1250->clk_sel0, value: 1); |
71 | gpiod_set_value(desc: wm1250->clk_sel1, value: 1); |
72 | break; |
73 | case 16000: |
74 | gpiod_set_value(desc: wm1250->clk_sel0, value: 0); |
75 | gpiod_set_value(desc: wm1250->clk_sel1, value: 1); |
76 | break; |
77 | case 32000: |
78 | gpiod_set_value(desc: wm1250->clk_sel0, value: 1); |
79 | gpiod_set_value(desc: wm1250->clk_sel1, value: 0); |
80 | break; |
81 | case 64000: |
82 | gpiod_set_value(desc: wm1250->clk_sel0, value: 0); |
83 | gpiod_set_value(desc: wm1250->clk_sel1, value: 0); |
84 | break; |
85 | default: |
86 | return -EINVAL; |
87 | } |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static const struct snd_soc_dai_ops wm1250_ev1_ops = { |
93 | .hw_params = wm1250_ev1_hw_params, |
94 | }; |
95 | |
96 | #define WM1250_EV1_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ |
97 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000) |
98 | |
99 | static struct snd_soc_dai_driver wm1250_ev1_dai = { |
100 | .name = "wm1250-ev1" , |
101 | .playback = { |
102 | .stream_name = "Playback" , |
103 | .channels_min = 1, |
104 | .channels_max = 2, |
105 | .rates = WM1250_EV1_RATES, |
106 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
107 | }, |
108 | .capture = { |
109 | .stream_name = "Capture" , |
110 | .channels_min = 1, |
111 | .channels_max = 2, |
112 | .rates = WM1250_EV1_RATES, |
113 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
114 | }, |
115 | .ops = &wm1250_ev1_ops, |
116 | }; |
117 | |
118 | static const struct snd_soc_component_driver soc_component_dev_wm1250_ev1 = { |
119 | .dapm_widgets = wm1250_ev1_dapm_widgets, |
120 | .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets), |
121 | .dapm_routes = wm1250_ev1_dapm_routes, |
122 | .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes), |
123 | .set_bias_level = wm1250_ev1_set_bias_level, |
124 | .use_pmdown_time = 1, |
125 | .endianness = 1, |
126 | }; |
127 | |
128 | static int wm1250_ev1_pdata(struct i2c_client *i2c) |
129 | { |
130 | struct wm1250_ev1_pdata *pdata = dev_get_platdata(dev: &i2c->dev); |
131 | struct wm1250_priv *wm1250; |
132 | |
133 | if (!pdata) |
134 | return 0; |
135 | |
136 | wm1250 = devm_kzalloc(dev: &i2c->dev, size: sizeof(*wm1250), GFP_KERNEL); |
137 | if (!wm1250) |
138 | return -ENOMEM; |
139 | |
140 | wm1250->clk_ena = devm_gpiod_get(dev: &i2c->dev, con_id: "clk-ena" , flags: GPIOD_OUT_LOW); |
141 | if (IS_ERR(ptr: wm1250->clk_ena)) |
142 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: wm1250->clk_ena), |
143 | fmt: "failed to get clock enable GPIO\n" ); |
144 | |
145 | wm1250->clk_sel0 = devm_gpiod_get(dev: &i2c->dev, con_id: "clk-sel0" , flags: GPIOD_OUT_HIGH); |
146 | if (IS_ERR(ptr: wm1250->clk_sel0)) |
147 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: wm1250->clk_sel0), |
148 | fmt: "failed to get clock sel0 GPIO\n" ); |
149 | |
150 | wm1250->clk_sel1 = devm_gpiod_get(dev: &i2c->dev, con_id: "clk-sel1" , flags: GPIOD_OUT_HIGH); |
151 | if (IS_ERR(ptr: wm1250->clk_sel1)) |
152 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: wm1250->clk_sel1), |
153 | fmt: "failed to get clock sel1 GPIO\n" ); |
154 | |
155 | wm1250->osr = devm_gpiod_get(dev: &i2c->dev, con_id: "osr" , flags: GPIOD_OUT_LOW); |
156 | if (IS_ERR(ptr: wm1250->osr)) |
157 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: wm1250->osr), |
158 | fmt: "failed to get OSR GPIO\n" ); |
159 | |
160 | wm1250->master = devm_gpiod_get(dev: &i2c->dev, con_id: "master" , flags: GPIOD_OUT_LOW); |
161 | if (IS_ERR(ptr: wm1250->master)) |
162 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: wm1250->master), |
163 | fmt: "failed to get MASTER GPIO\n" ); |
164 | |
165 | dev_set_drvdata(dev: &i2c->dev, data: wm1250); |
166 | |
167 | return 0; |
168 | } |
169 | |
170 | static int wm1250_ev1_probe(struct i2c_client *i2c) |
171 | { |
172 | int id, board, rev, ret; |
173 | |
174 | dev_set_drvdata(dev: &i2c->dev, NULL); |
175 | |
176 | board = i2c_smbus_read_byte_data(client: i2c, command: 0); |
177 | if (board < 0) { |
178 | dev_err(&i2c->dev, "Failed to read ID: %d\n" , board); |
179 | return board; |
180 | } |
181 | |
182 | id = (board & 0xfe) >> 2; |
183 | rev = board & 0x3; |
184 | |
185 | if (id != 1) { |
186 | dev_err(&i2c->dev, "Unknown board ID %d\n" , id); |
187 | return -ENODEV; |
188 | } |
189 | |
190 | dev_info(&i2c->dev, "revision %d\n" , rev + 1); |
191 | |
192 | ret = wm1250_ev1_pdata(i2c); |
193 | if (ret != 0) |
194 | return ret; |
195 | |
196 | ret = devm_snd_soc_register_component(dev: &i2c->dev, component_driver: &soc_component_dev_wm1250_ev1, |
197 | dai_drv: &wm1250_ev1_dai, num_dai: 1); |
198 | if (ret != 0) { |
199 | dev_err(&i2c->dev, "Failed to register CODEC: %d\n" , ret); |
200 | return ret; |
201 | } |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static const struct i2c_device_id wm1250_ev1_i2c_id[] = { |
207 | { "wm1250-ev1" , 0 }, |
208 | { } |
209 | }; |
210 | MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id); |
211 | |
212 | static struct i2c_driver wm1250_ev1_i2c_driver = { |
213 | .driver = { |
214 | .name = "wm1250-ev1" , |
215 | }, |
216 | .probe = wm1250_ev1_probe, |
217 | .id_table = wm1250_ev1_i2c_id, |
218 | }; |
219 | |
220 | module_i2c_driver(wm1250_ev1_i2c_driver); |
221 | |
222 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>" ); |
223 | MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver" ); |
224 | MODULE_LICENSE("GPL" ); |
225 | |