Commit | Line | Data |
---|---|---|
73cfbfa9 ST |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // | |
3 | // HDA audio driver for Cirrus Logic CS35L56 smart amp | |
4 | // | |
5 | // Copyright (C) 2023 Cirrus Logic, Inc. and | |
6 | // Cirrus Logic International Semiconductor Ltd. | |
7 | // | |
8 | ||
9 | #include <linux/acpi.h> | |
10 | #include <linux/debugfs.h> | |
11 | #include <linux/gpio/consumer.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/pm_runtime.h> | |
14 | #include <linux/regmap.h> | |
15 | #include <linux/slab.h> | |
16 | #include <sound/core.h> | |
cfa43aaa | 17 | #include <sound/cs-amp-lib.h> |
73cfbfa9 ST |
18 | #include <sound/hda_codec.h> |
19 | #include <sound/tlv.h> | |
6f03b446 | 20 | #include "cirrus_scodec.h" |
73cfbfa9 ST |
21 | #include "cs35l56_hda.h" |
22 | #include "hda_component.h" | |
23 | #include "hda_cs_dsp_ctl.h" | |
24 | #include "hda_generic.h" | |
25 | ||
26 | /* | |
27 | * The cs35l56_hda_dai_config[] reg sequence configures the device as | |
28 | * ASP1_BCLK_FREQ = 3.072 MHz | |
29 | * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S | |
30 | * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots | |
31 | * ASP1_RX_WL = 24 bits per sample | |
32 | * ASP1_TX_WL = 24 bits per sample | |
33 | * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled | |
856ce898 RF |
34 | * |
35 | * Override any Windows-specific mixer settings applied by the firmware. | |
73cfbfa9 ST |
36 | */ |
37 | static const struct reg_sequence cs35l56_hda_dai_config[] = { | |
38 | { CS35L56_ASP1_CONTROL1, 0x00000021 }, | |
39 | { CS35L56_ASP1_CONTROL2, 0x20200200 }, | |
40 | { CS35L56_ASP1_CONTROL3, 0x00000003 }, | |
856ce898 RF |
41 | { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, |
42 | { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, | |
73cfbfa9 ST |
43 | { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, |
44 | { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, | |
45 | { CS35L56_ASP1_ENABLES1, 0x00000000 }, | |
856ce898 RF |
46 | { CS35L56_ASP1TX1_INPUT, 0x00000018 }, |
47 | { CS35L56_ASP1TX2_INPUT, 0x00000019 }, | |
48 | { CS35L56_ASP1TX3_INPUT, 0x00000020 }, | |
49 | { CS35L56_ASP1TX4_INPUT, 0x00000028 }, | |
50 | ||
73cfbfa9 ST |
51 | }; |
52 | ||
53 | static void cs35l56_hda_play(struct cs35l56_hda *cs35l56) | |
54 | { | |
55 | unsigned int val; | |
56 | int ret; | |
57 | ||
58 | pm_runtime_get_sync(cs35l56->base.dev); | |
59 | ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY); | |
60 | if (ret == 0) { | |
61 | /* Wait for firmware to enter PS0 power state */ | |
62 | ret = regmap_read_poll_timeout(cs35l56->base.regmap, | |
63 | CS35L56_TRANSDUCER_ACTUAL_PS, | |
64 | val, (val == CS35L56_PS0), | |
65 | CS35L56_PS0_POLL_US, | |
66 | CS35L56_PS0_TIMEOUT_US); | |
67 | if (ret) | |
68 | dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); | |
69 | } | |
70 | regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, | |
71 | BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | | |
72 | cs35l56->asp_tx_mask); | |
73 | cs35l56->playing = true; | |
74 | } | |
75 | ||
76 | static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56) | |
77 | { | |
78 | cs35l56->playing = false; | |
79 | cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); | |
80 | regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, | |
81 | BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | | |
82 | BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) | | |
83 | BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT)); | |
84 | ||
85 | pm_runtime_mark_last_busy(cs35l56->base.dev); | |
86 | pm_runtime_put_autosuspend(cs35l56->base.dev); | |
87 | } | |
88 | ||
89 | static void cs35l56_hda_playback_hook(struct device *dev, int action) | |
90 | { | |
91 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
92 | ||
93 | dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action); | |
94 | ||
95 | switch (action) { | |
96 | case HDA_GEN_PCM_ACT_PREPARE: | |
97 | if (cs35l56->playing) | |
98 | break; | |
99 | ||
100 | /* If we're suspended: flag that resume should start playback */ | |
101 | if (cs35l56->suspended) { | |
102 | cs35l56->playing = true; | |
103 | break; | |
104 | } | |
105 | ||
106 | cs35l56_hda_play(cs35l56); | |
107 | break; | |
108 | case HDA_GEN_PCM_ACT_CLEANUP: | |
109 | if (!cs35l56->playing) | |
110 | break; | |
111 | ||
112 | cs35l56_hda_pause(cs35l56); | |
113 | break; | |
114 | default: | |
115 | break; | |
116 | } | |
117 | } | |
118 | ||
deff8486 | 119 | static int cs35l56_hda_runtime_suspend(struct device *dev) |
73cfbfa9 ST |
120 | { |
121 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
122 | ||
123 | if (cs35l56->cs_dsp.booted) | |
124 | cs_dsp_stop(&cs35l56->cs_dsp); | |
125 | ||
126 | return cs35l56_runtime_suspend_common(&cs35l56->base); | |
127 | } | |
128 | ||
deff8486 | 129 | static int cs35l56_hda_runtime_resume(struct device *dev) |
73cfbfa9 ST |
130 | { |
131 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
132 | int ret; | |
133 | ||
134 | ret = cs35l56_runtime_resume_common(&cs35l56->base, false); | |
135 | if (ret < 0) | |
136 | return ret; | |
137 | ||
138 | if (cs35l56->cs_dsp.booted) { | |
139 | ret = cs_dsp_run(&cs35l56->cs_dsp); | |
140 | if (ret) { | |
141 | dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); | |
142 | goto err; | |
143 | } | |
144 | } | |
145 | ||
856ce898 RF |
146 | ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); |
147 | if (ret) | |
148 | goto err; | |
149 | ||
73cfbfa9 ST |
150 | return 0; |
151 | ||
152 | err: | |
153 | cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); | |
154 | regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, | |
155 | CS35L56_MBOX_CMD_HIBERNATE_NOW); | |
156 | ||
157 | regcache_cache_only(cs35l56->base.regmap, true); | |
158 | ||
159 | return ret; | |
160 | } | |
161 | ||
162 | static int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol, | |
163 | struct snd_ctl_elem_info *uinfo) | |
164 | { | |
165 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | |
166 | uinfo->count = 1; | |
167 | uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC; | |
168 | if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC) | |
169 | uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1; | |
170 | strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item], | |
171 | sizeof(uinfo->value.enumerated.name)); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol, | |
177 | struct snd_ctl_elem_value *ucontrol) | |
178 | { | |
179 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
180 | unsigned int reg_val; | |
181 | int i; | |
182 | ||
183 | regmap_read(cs35l56->base.regmap, kcontrol->private_value, ®_val); | |
184 | reg_val &= CS35L56_ASP_TXn_SRC_MASK; | |
185 | ||
186 | for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) { | |
187 | if (cs35l56_tx_input_values[i] == reg_val) { | |
188 | ucontrol->value.enumerated.item[0] = i; | |
189 | break; | |
190 | } | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol, | |
197 | struct snd_ctl_elem_value *ucontrol) | |
198 | { | |
199 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
200 | unsigned int item = ucontrol->value.enumerated.item[0]; | |
201 | bool changed; | |
202 | ||
203 | if (item >= CS35L56_NUM_INPUT_SRC) | |
204 | return -EINVAL; | |
205 | ||
206 | regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value, | |
207 | CS35L56_INPUT_MASK, cs35l56_tx_input_values[item], | |
208 | &changed); | |
209 | ||
210 | return changed; | |
211 | } | |
212 | ||
213 | static int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol, | |
214 | struct snd_ctl_elem_info *uinfo) | |
215 | { | |
216 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
217 | uinfo->count = 1; | |
218 | uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN; | |
219 | uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX; | |
220 | return 0; | |
221 | } | |
222 | ||
223 | static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol, | |
224 | struct snd_ctl_elem_value *ucontrol) | |
225 | { | |
226 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
227 | unsigned int pos; | |
228 | int ret; | |
229 | ||
230 | ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos); | |
231 | if (ret) | |
232 | return ret; | |
233 | ||
234 | ucontrol->value.integer.value[0] = pos; | |
235 | ||
72ca5666 | 236 | return 0; |
73cfbfa9 ST |
237 | } |
238 | ||
239 | static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol, | |
240 | struct snd_ctl_elem_value *ucontrol) | |
241 | { | |
242 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
243 | unsigned long pos = ucontrol->value.integer.value[0]; | |
244 | bool changed; | |
245 | int ret; | |
246 | ||
247 | if ((pos < CS35L56_MAIN_POSTURE_MIN) || | |
248 | (pos > CS35L56_MAIN_POSTURE_MAX)) | |
249 | return -EINVAL; | |
250 | ||
251 | ret = regmap_update_bits_check(cs35l56->base.regmap, | |
252 | CS35L56_MAIN_POSTURE_NUMBER, | |
253 | CS35L56_MAIN_POSTURE_MASK, | |
254 | pos, &changed); | |
255 | if (ret) | |
256 | return ret; | |
257 | ||
258 | return changed; | |
259 | } | |
260 | ||
261 | static const struct { | |
262 | const char *name; | |
263 | unsigned int reg; | |
264 | } cs35l56_hda_mixer_controls[] = { | |
265 | { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT }, | |
266 | { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT }, | |
267 | { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT }, | |
268 | { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT }, | |
269 | }; | |
270 | ||
271 | static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0); | |
272 | ||
273 | static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol, | |
274 | struct snd_ctl_elem_info *uinfo) | |
275 | { | |
276 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
277 | uinfo->count = 1; | |
278 | uinfo->value.integer.step = 1; | |
279 | uinfo->value.integer.min = 0; | |
280 | uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX - | |
281 | CS35L56_MAIN_RENDER_USER_VOLUME_MIN; | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol, | |
287 | struct snd_ctl_elem_value *ucontrol) | |
288 | { | |
289 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
290 | unsigned int raw_vol; | |
291 | int vol; | |
292 | int ret; | |
293 | ||
294 | ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol); | |
295 | ||
296 | if (ret) | |
297 | return ret; | |
298 | ||
299 | vol = (s16)(raw_vol & 0xFFFF); | |
300 | vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; | |
301 | ||
302 | if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT)) | |
303 | vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1)); | |
304 | ||
305 | ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN; | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol, | |
311 | struct snd_ctl_elem_value *ucontrol) | |
312 | { | |
313 | struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; | |
314 | long vol = ucontrol->value.integer.value[0]; | |
315 | unsigned int raw_vol; | |
316 | bool changed; | |
317 | int ret; | |
318 | ||
319 | if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX - | |
320 | CS35L56_MAIN_RENDER_USER_VOLUME_MIN))) | |
321 | return -EINVAL; | |
322 | ||
323 | raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) << | |
324 | CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; | |
325 | ||
326 | ret = regmap_update_bits_check(cs35l56->base.regmap, | |
327 | CS35L56_MAIN_RENDER_USER_VOLUME, | |
328 | CS35L56_MAIN_RENDER_USER_VOLUME_MASK, | |
329 | raw_vol, &changed); | |
330 | if (ret) | |
331 | return ret; | |
332 | ||
333 | return changed; | |
334 | } | |
335 | ||
336 | static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56) | |
337 | { | |
338 | struct snd_kcontrol_new ctl_template = { | |
339 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
340 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
341 | .info = cs35l56_hda_posture_info, | |
342 | .get = cs35l56_hda_posture_get, | |
343 | .put = cs35l56_hda_posture_put, | |
344 | }; | |
345 | char name[64]; | |
346 | int i; | |
347 | ||
348 | snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name); | |
349 | ctl_template.name = name; | |
350 | cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56); | |
351 | if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl)) | |
352 | dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); | |
353 | ||
354 | /* Mixer controls */ | |
355 | ctl_template.info = cs35l56_hda_mixer_info; | |
356 | ctl_template.get = cs35l56_hda_mixer_get; | |
357 | ctl_template.put = cs35l56_hda_mixer_put; | |
358 | ||
359 | BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls)); | |
360 | ||
361 | for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) { | |
362 | snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name, | |
363 | cs35l56_hda_mixer_controls[i].name); | |
364 | ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg; | |
365 | cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56); | |
366 | if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) { | |
367 | dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", | |
368 | ctl_template.name); | |
369 | } | |
370 | } | |
371 | ||
372 | ctl_template.info = cs35l56_hda_vol_info; | |
373 | ctl_template.get = cs35l56_hda_vol_get; | |
374 | ctl_template.put = cs35l56_hda_vol_put; | |
375 | ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ); | |
376 | ctl_template.tlv.p = cs35l56_hda_vol_tlv; | |
377 | snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name); | |
378 | ctl_template.name = name; | |
379 | cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56); | |
380 | if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl)) | |
381 | dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); | |
382 | } | |
383 | ||
384 | static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56) | |
385 | { | |
386 | int i; | |
387 | ||
388 | for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--) | |
389 | snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]); | |
390 | ||
391 | snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl); | |
392 | snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl); | |
393 | } | |
394 | ||
395 | static const struct cs_dsp_client_ops cs35l56_hda_client_ops = { | |
396 | .control_remove = hda_cs_dsp_control_remove, | |
397 | }; | |
398 | ||
399 | static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, | |
400 | const struct firmware **firmware, char **filename, | |
6f8ad048 | 401 | const char *base_name, const char *system_name, |
73cfbfa9 ST |
402 | const char *amp_name, |
403 | const char *filetype) | |
404 | { | |
405 | char *s, c; | |
406 | int ret = 0; | |
407 | ||
408 | if (system_name && amp_name) | |
6f8ad048 | 409 | *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name, |
73cfbfa9 ST |
410 | system_name, amp_name, filetype); |
411 | else if (system_name) | |
6f8ad048 | 412 | *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name, |
73cfbfa9 ST |
413 | system_name, filetype); |
414 | else | |
6f8ad048 | 415 | *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype); |
73cfbfa9 ST |
416 | |
417 | if (!*filename) | |
418 | return -ENOMEM; | |
419 | ||
420 | /* | |
421 | * Make sure that filename is lower-case and any non alpha-numeric | |
422 | * characters except full stop and forward slash are replaced with | |
423 | * hyphens. | |
424 | */ | |
425 | s = *filename; | |
426 | while (*s) { | |
427 | c = *s; | |
428 | if (isalnum(c)) | |
429 | *s = tolower(c); | |
430 | else if (c != '.' && c != '/') | |
431 | *s = '-'; | |
432 | s++; | |
433 | } | |
434 | ||
435 | ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev); | |
436 | if (ret) { | |
437 | dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename); | |
438 | kfree(*filename); | |
439 | *filename = NULL; | |
440 | return ret; | |
441 | } | |
442 | ||
443 | dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename); | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
73cfbfa9 | 448 | static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, |
6f8ad048 | 449 | unsigned int preloaded_fw_ver, |
73cfbfa9 ST |
450 | const struct firmware **wmfw_firmware, |
451 | char **wmfw_filename, | |
452 | const struct firmware **coeff_firmware, | |
453 | char **coeff_filename) | |
454 | { | |
455 | const char *system_name = cs35l56->system_name; | |
456 | const char *amp_name = cs35l56->amp_name; | |
6f8ad048 | 457 | char base_name[37]; |
73cfbfa9 ST |
458 | int ret; |
459 | ||
6f8ad048 RF |
460 | if (preloaded_fw_ver) { |
461 | snprintf(base_name, sizeof(base_name), | |
769dca23 ST |
462 | "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc", |
463 | cs35l56->base.type, | |
6f8ad048 RF |
464 | cs35l56->base.rev, |
465 | cs35l56->base.secured ? "-s" : "", | |
466 | preloaded_fw_ver & 0xffffff); | |
467 | } else { | |
468 | snprintf(base_name, sizeof(base_name), | |
769dca23 ST |
469 | "cirrus/cs35l%02x-%02x%s-dsp1-misc", |
470 | cs35l56->base.type, | |
6f8ad048 RF |
471 | cs35l56->base.rev, |
472 | cs35l56->base.secured ? "-s" : ""); | |
473 | } | |
474 | ||
73cfbfa9 ST |
475 | if (system_name && amp_name) { |
476 | if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, | |
6f8ad048 | 477 | base_name, system_name, amp_name, "wmfw")) { |
73cfbfa9 | 478 | cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, |
6f8ad048 | 479 | base_name, system_name, amp_name, "bin"); |
73cfbfa9 ST |
480 | return; |
481 | } | |
482 | } | |
483 | ||
484 | if (system_name) { | |
485 | if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, | |
6f8ad048 | 486 | base_name, system_name, NULL, "wmfw")) { |
73cfbfa9 ST |
487 | if (amp_name) |
488 | cs35l56_hda_request_firmware_file(cs35l56, | |
489 | coeff_firmware, coeff_filename, | |
6f8ad048 | 490 | base_name, system_name, |
73cfbfa9 ST |
491 | amp_name, "bin"); |
492 | if (!*coeff_firmware) | |
493 | cs35l56_hda_request_firmware_file(cs35l56, | |
494 | coeff_firmware, coeff_filename, | |
6f8ad048 | 495 | base_name, system_name, |
73cfbfa9 ST |
496 | NULL, "bin"); |
497 | return; | |
498 | } | |
77c60722 RF |
499 | |
500 | /* | |
501 | * Check for system-specific bin files without wmfw before | |
502 | * falling back to generic firmware | |
503 | */ | |
504 | if (amp_name) | |
505 | cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, | |
6f8ad048 | 506 | base_name, system_name, amp_name, "bin"); |
77c60722 RF |
507 | if (!*coeff_firmware) |
508 | cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, | |
6f8ad048 | 509 | base_name, system_name, NULL, "bin"); |
77c60722 RF |
510 | |
511 | if (*coeff_firmware) | |
512 | return; | |
73cfbfa9 ST |
513 | } |
514 | ||
515 | ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, | |
6f8ad048 | 516 | base_name, NULL, NULL, "wmfw"); |
73cfbfa9 ST |
517 | if (!ret) { |
518 | cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, | |
6f8ad048 | 519 | base_name, NULL, NULL, "bin"); |
73cfbfa9 ST |
520 | return; |
521 | } | |
522 | ||
73cfbfa9 ST |
523 | if (!*coeff_firmware) |
524 | cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, | |
6f8ad048 | 525 | base_name, NULL, NULL, "bin"); |
73cfbfa9 ST |
526 | } |
527 | ||
528 | static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, | |
529 | char *wmfw_filename, | |
530 | const struct firmware *coeff_firmware, | |
531 | char *coeff_filename) | |
532 | { | |
533 | if (wmfw_firmware) | |
534 | release_firmware(wmfw_firmware); | |
535 | kfree(wmfw_filename); | |
536 | ||
537 | if (coeff_firmware) | |
538 | release_firmware(coeff_firmware); | |
539 | kfree(coeff_filename); | |
540 | } | |
541 | ||
542 | static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56) | |
543 | { | |
544 | struct hda_cs_dsp_ctl_info info; | |
545 | ||
546 | info.device_name = cs35l56->amp_name; | |
547 | info.fw_type = HDA_CS_DSP_FW_MISC; | |
548 | info.card = cs35l56->codec->card; | |
549 | ||
550 | hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info); | |
551 | } | |
552 | ||
cfa43aaa RF |
553 | static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) |
554 | { | |
555 | int ret; | |
556 | ||
557 | if (!cs35l56->base.cal_data_valid || cs35l56->base.secured) | |
558 | return; | |
559 | ||
560 | ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp, | |
561 | &cs35l56_calibration_controls, | |
562 | &cs35l56->base.cal_data); | |
563 | if (ret < 0) | |
564 | dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret); | |
565 | else | |
566 | dev_info(cs35l56->base.dev, "Calibration applied\n"); | |
567 | } | |
568 | ||
73cfbfa9 ST |
569 | static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) |
570 | { | |
571 | const struct firmware *coeff_firmware = NULL; | |
572 | const struct firmware *wmfw_firmware = NULL; | |
573 | char *coeff_filename = NULL; | |
574 | char *wmfw_filename = NULL; | |
6f8ad048 RF |
575 | unsigned int preloaded_fw_ver; |
576 | bool firmware_missing; | |
73cfbfa9 ST |
577 | int ret = 0; |
578 | ||
15c378d6 RF |
579 | /* Prepare for a new DSP power-up */ |
580 | if (cs35l56->base.fw_patched) | |
581 | cs_dsp_power_down(&cs35l56->cs_dsp); | |
582 | ||
583 | cs35l56->base.fw_patched = false; | |
584 | ||
2f860dd8 RF |
585 | pm_runtime_get_sync(cs35l56->base.dev); |
586 | ||
6f8ad048 RF |
587 | /* |
588 | * The firmware can only be upgraded if it is currently running | |
589 | * from the built-in ROM. If not, the wmfw/bin must be for the | |
590 | * version of firmware that is running on the chip. | |
591 | */ | |
592 | ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver); | |
593 | if (ret) | |
2f860dd8 | 594 | goto err_pm_put; |
2f860dd8 | 595 | |
6f8ad048 RF |
596 | if (firmware_missing) |
597 | preloaded_fw_ver = 0; | |
2f860dd8 | 598 | |
6f8ad048 RF |
599 | cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver, |
600 | &wmfw_firmware, &wmfw_filename, | |
601 | &coeff_firmware, &coeff_filename); | |
73cfbfa9 | 602 | |
3106797d RF |
603 | /* |
604 | * If the BIOS didn't patch the firmware a bin file is mandatory to | |
605 | * enable the ASP· | |
606 | */ | |
607 | if (!coeff_firmware && firmware_missing) { | |
608 | dev_err(cs35l56->base.dev, ".bin file required but not found\n"); | |
609 | ret = -ENOENT; | |
610 | goto err_fw_release; | |
611 | } | |
612 | ||
73cfbfa9 | 613 | mutex_lock(&cs35l56->base.irq_lock); |
73cfbfa9 ST |
614 | |
615 | /* | |
6f8ad048 RF |
616 | * If the firmware hasn't been patched it must be shutdown before |
617 | * doing a full patch and reset afterwards. If it is already | |
618 | * running a patched version the firmware files only contain | |
619 | * tunings and we can use the lower cost reinit sequence instead. | |
73cfbfa9 | 620 | */ |
6f8ad048 | 621 | if (firmware_missing && (wmfw_firmware || coeff_firmware)) { |
73cfbfa9 ST |
622 | ret = cs35l56_firmware_shutdown(&cs35l56->base); |
623 | if (ret) | |
624 | goto err; | |
625 | } | |
626 | ||
627 | ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename, | |
628 | coeff_firmware, coeff_filename, "misc"); | |
629 | if (ret) { | |
630 | dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); | |
631 | goto err; | |
632 | } | |
633 | ||
634 | if (wmfw_filename) | |
635 | dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename); | |
636 | ||
637 | if (coeff_filename) | |
638 | dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); | |
639 | ||
cfa43aaa RF |
640 | /* If we downloaded firmware, reset the device and wait for it to boot */ |
641 | if (firmware_missing && (wmfw_firmware || coeff_firmware)) { | |
73cfbfa9 | 642 | cs35l56_system_reset(&cs35l56->base, false); |
c3657097 | 643 | regcache_mark_dirty(cs35l56->base.regmap); |
73cfbfa9 ST |
644 | ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); |
645 | if (ret) | |
0ba0dfd9 | 646 | goto err_powered_up; |
73580ec6 RF |
647 | |
648 | regcache_cache_only(cs35l56->base.regmap, false); | |
73cfbfa9 ST |
649 | } |
650 | ||
651 | /* Disable auto-hibernate so that runtime_pm has control */ | |
652 | ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); | |
653 | if (ret) | |
0ba0dfd9 | 654 | goto err_powered_up; |
73cfbfa9 | 655 | |
73cfbfa9 ST |
656 | regcache_sync(cs35l56->base.regmap); |
657 | ||
658 | regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, | |
659 | CS35L56_FIRMWARE_MISSING); | |
660 | cs35l56->base.fw_patched = true; | |
7b6466ad RF |
661 | |
662 | ret = cs_dsp_run(&cs35l56->cs_dsp); | |
663 | if (ret) | |
664 | dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); | |
665 | ||
cfa43aaa RF |
666 | cs35l56_hda_apply_calibration(cs35l56); |
667 | ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); | |
668 | if (ret) | |
669 | cs_dsp_stop(&cs35l56->cs_dsp); | |
670 | ||
0ba0dfd9 RF |
671 | err_powered_up: |
672 | if (!cs35l56->base.fw_patched) | |
673 | cs_dsp_power_down(&cs35l56->cs_dsp); | |
73cfbfa9 | 674 | err: |
73cfbfa9 | 675 | mutex_unlock(&cs35l56->base.irq_lock); |
3106797d | 676 | err_fw_release: |
73cfbfa9 ST |
677 | cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, |
678 | coeff_firmware, coeff_filename); | |
2f860dd8 RF |
679 | err_pm_put: |
680 | pm_runtime_put(cs35l56->base.dev); | |
73cfbfa9 ST |
681 | |
682 | return ret; | |
683 | } | |
684 | ||
685 | static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data) | |
686 | { | |
687 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
688 | struct hda_component *comps = master_data; | |
689 | int ret; | |
690 | ||
691 | if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS) | |
692 | return -EINVAL; | |
693 | ||
694 | comps = &comps[cs35l56->index]; | |
695 | if (comps->dev) | |
696 | return -EBUSY; | |
697 | ||
698 | comps->dev = dev; | |
699 | cs35l56->codec = comps->codec; | |
700 | strscpy(comps->name, dev_name(dev), sizeof(comps->name)); | |
701 | comps->playback_hook = cs35l56_hda_playback_hook; | |
702 | ||
703 | ret = cs35l56_hda_fw_load(cs35l56); | |
704 | if (ret) | |
705 | return ret; | |
706 | ||
707 | cs35l56_hda_create_controls(cs35l56); | |
708 | cs35l56_hda_add_dsp_controls(cs35l56); | |
709 | ||
710 | #if IS_ENABLED(CONFIG_SND_DEBUG) | |
711 | cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root); | |
712 | cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root); | |
713 | #endif | |
714 | ||
715 | dev_dbg(cs35l56->base.dev, "Bound\n"); | |
716 | ||
717 | return 0; | |
718 | } | |
719 | ||
720 | static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data) | |
721 | { | |
722 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
723 | struct hda_component *comps = master_data; | |
724 | ||
725 | cs35l56_hda_remove_controls(cs35l56); | |
726 | ||
727 | #if IS_ENABLED(CONFIG_SND_DEBUG) | |
728 | cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp); | |
729 | debugfs_remove_recursive(cs35l56->debugfs_root); | |
730 | #endif | |
731 | ||
fb78d73d RF |
732 | if (cs35l56->base.fw_patched) |
733 | cs_dsp_power_down(&cs35l56->cs_dsp); | |
734 | ||
73cfbfa9 ST |
735 | cs_dsp_remove(&cs35l56->cs_dsp); |
736 | ||
737 | if (comps[cs35l56->index].dev == dev) | |
738 | memset(&comps[cs35l56->index], 0, sizeof(*comps)); | |
739 | ||
740 | dev_dbg(cs35l56->base.dev, "Unbound\n"); | |
741 | } | |
742 | ||
743 | static const struct component_ops cs35l56_hda_comp_ops = { | |
744 | .bind = cs35l56_hda_bind, | |
745 | .unbind = cs35l56_hda_unbind, | |
746 | }; | |
747 | ||
748 | static int cs35l56_hda_system_suspend(struct device *dev) | |
749 | { | |
750 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
751 | ||
752 | if (cs35l56->playing) | |
753 | cs35l56_hda_pause(cs35l56); | |
754 | ||
755 | cs35l56->suspended = true; | |
756 | ||
757 | /* | |
758 | * The interrupt line is normally shared, but after we start suspending | |
759 | * we can't check if our device is the source of an interrupt, and can't | |
760 | * clear it. Prevent this race by temporarily disabling the parent irq | |
761 | * until we reach _no_irq. | |
762 | */ | |
763 | if (cs35l56->base.irq) | |
764 | disable_irq(cs35l56->base.irq); | |
765 | ||
766 | return pm_runtime_force_suspend(dev); | |
767 | } | |
768 | ||
769 | static int cs35l56_hda_system_suspend_late(struct device *dev) | |
770 | { | |
771 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
772 | ||
773 | /* | |
774 | * RESET is usually shared by all amps so it must not be asserted until | |
775 | * all driver instances have done their suspend() stage. | |
776 | */ | |
777 | if (cs35l56->base.reset_gpio) { | |
778 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); | |
779 | cs35l56_wait_min_reset_pulse(); | |
780 | } | |
781 | ||
782 | return 0; | |
783 | } | |
784 | ||
785 | static int cs35l56_hda_system_suspend_no_irq(struct device *dev) | |
786 | { | |
787 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
788 | ||
789 | /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ | |
790 | if (cs35l56->base.irq) | |
791 | enable_irq(cs35l56->base.irq); | |
792 | ||
793 | return 0; | |
794 | } | |
795 | ||
796 | static int cs35l56_hda_system_resume_no_irq(struct device *dev) | |
797 | { | |
798 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
799 | ||
800 | /* | |
801 | * WAKE interrupts unmask if the CS35L56 hibernates, which can cause | |
802 | * spurious interrupts, and the interrupt line is normally shared. | |
803 | * We can't check if our device is the source of an interrupt, and can't | |
804 | * clear it, until it has fully resumed. Prevent this race by temporarily | |
805 | * disabling the parent irq until we complete resume(). | |
806 | */ | |
807 | if (cs35l56->base.irq) | |
808 | disable_irq(cs35l56->base.irq); | |
809 | ||
810 | return 0; | |
811 | } | |
812 | ||
813 | static int cs35l56_hda_system_resume_early(struct device *dev) | |
814 | { | |
815 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
816 | ||
817 | /* Ensure a spec-compliant RESET pulse. */ | |
818 | if (cs35l56->base.reset_gpio) { | |
819 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); | |
820 | cs35l56_wait_min_reset_pulse(); | |
821 | ||
822 | /* Release shared RESET before drivers start resume(). */ | |
823 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); | |
824 | cs35l56_wait_control_port_ready(); | |
825 | } | |
826 | ||
827 | return 0; | |
828 | } | |
829 | ||
830 | static int cs35l56_hda_system_resume(struct device *dev) | |
831 | { | |
832 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
833 | int ret; | |
834 | ||
835 | /* Undo pm_runtime_force_suspend() before re-enabling the irq */ | |
836 | ret = pm_runtime_force_resume(dev); | |
837 | if (cs35l56->base.irq) | |
838 | enable_irq(cs35l56->base.irq); | |
839 | ||
840 | if (ret) | |
841 | return ret; | |
842 | ||
843 | cs35l56->suspended = false; | |
844 | ||
845 | ret = cs35l56_is_fw_reload_needed(&cs35l56->base); | |
846 | dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); | |
847 | if (ret > 0) { | |
848 | ret = cs35l56_hda_fw_load(cs35l56); | |
849 | if (ret) | |
850 | return ret; | |
851 | } | |
852 | ||
853 | if (cs35l56->playing) | |
854 | cs35l56_hda_play(cs35l56); | |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
769dca23 | 859 | static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) |
73cfbfa9 ST |
860 | { |
861 | u32 values[HDA_MAX_COMPONENTS]; | |
769dca23 | 862 | char hid_string[8]; |
73cfbfa9 ST |
863 | struct acpi_device *adev; |
864 | const char *property, *sub; | |
865 | size_t nval; | |
866 | int i, ret; | |
867 | ||
868 | /* | |
869 | * ACPI_COMPANION isn't available when this driver was instantiated by | |
870 | * the serial-multi-instantiate driver, so lookup the node by HID | |
871 | */ | |
872 | if (!ACPI_COMPANION(cs35l56->base.dev)) { | |
769dca23 ST |
873 | snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid); |
874 | adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1); | |
73cfbfa9 ST |
875 | if (!adev) { |
876 | dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n", | |
877 | dev_name(cs35l56->base.dev)); | |
878 | return -ENODEV; | |
879 | } | |
880 | ACPI_COMPANION_SET(cs35l56->base.dev, adev); | |
881 | } | |
882 | ||
883 | property = "cirrus,dev-index"; | |
884 | ret = device_property_count_u32(cs35l56->base.dev, property); | |
885 | if (ret <= 0) | |
886 | goto err; | |
887 | ||
888 | if (ret > ARRAY_SIZE(values)) { | |
889 | ret = -EINVAL; | |
890 | goto err; | |
891 | } | |
892 | nval = ret; | |
893 | ||
894 | ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); | |
895 | if (ret) | |
896 | goto err; | |
897 | ||
898 | cs35l56->index = -1; | |
899 | for (i = 0; i < nval; i++) { | |
900 | if (values[i] == id) { | |
901 | cs35l56->index = i; | |
902 | break; | |
903 | } | |
904 | } | |
8ca3ee6f RF |
905 | /* |
906 | * It's not an error for the ID to be missing: for I2C there can be | |
907 | * an alias address that is not a real device. So reject silently. | |
908 | */ | |
73cfbfa9 | 909 | if (cs35l56->index == -1) { |
8ca3ee6f | 910 | dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); |
73cfbfa9 ST |
911 | ret = -ENODEV; |
912 | goto err; | |
913 | } | |
914 | ||
915 | sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); | |
916 | ||
917 | if (IS_ERR(sub)) { | |
21484e43 RF |
918 | dev_info(cs35l56->base.dev, |
919 | "Read ACPI _SUB failed(%ld): fallback to generic firmware\n", | |
920 | PTR_ERR(sub)); | |
921 | } else { | |
6f03b446 RF |
922 | ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1); |
923 | if (ret == -ENOENT) { | |
924 | cs35l56->system_name = sub; | |
925 | } else if (ret >= 0) { | |
926 | cs35l56->system_name = kasprintf(GFP_KERNEL, "%s-spkid%d", sub, ret); | |
927 | kfree(sub); | |
928 | if (!cs35l56->system_name) | |
929 | return -ENOMEM; | |
930 | } else { | |
931 | return ret; | |
932 | } | |
73cfbfa9 ST |
933 | } |
934 | ||
73cfbfa9 ST |
935 | cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev, |
936 | "reset", | |
937 | cs35l56->index, | |
938 | GPIOD_OUT_LOW); | |
939 | if (IS_ERR(cs35l56->base.reset_gpio)) { | |
940 | ret = PTR_ERR(cs35l56->base.reset_gpio); | |
941 | ||
942 | /* | |
943 | * If RESET is shared the first amp to probe will grab the reset | |
944 | * line and reset all the amps | |
945 | */ | |
946 | if (ret != -EBUSY) | |
947 | return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n"); | |
948 | ||
949 | dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n"); | |
950 | cs35l56->base.reset_gpio = NULL; | |
951 | } | |
952 | ||
953 | return 0; | |
954 | ||
955 | err: | |
8ca3ee6f RF |
956 | if (ret != -ENODEV) |
957 | dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret); | |
73cfbfa9 ST |
958 | |
959 | return ret; | |
960 | } | |
961 | ||
769dca23 | 962 | int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) |
73cfbfa9 ST |
963 | { |
964 | int ret; | |
965 | ||
966 | mutex_init(&cs35l56->base.irq_lock); | |
967 | dev_set_drvdata(cs35l56->base.dev, cs35l56); | |
968 | ||
769dca23 | 969 | ret = cs35l56_hda_read_acpi(cs35l56, hid, id); |
8ca3ee6f | 970 | if (ret) |
73cfbfa9 | 971 | goto err; |
73cfbfa9 ST |
972 | |
973 | cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d", | |
974 | cs35l56->index + 1); | |
975 | if (!cs35l56->amp_name) { | |
976 | ret = -ENOMEM; | |
977 | goto err; | |
978 | } | |
979 | ||
cfa43aaa RF |
980 | cs35l56->base.cal_index = cs35l56->index; |
981 | ||
73cfbfa9 ST |
982 | cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); |
983 | cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; | |
984 | ||
985 | if (cs35l56->base.reset_gpio) { | |
986 | dev_dbg(cs35l56->base.dev, "Hard reset\n"); | |
987 | ||
988 | /* | |
989 | * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the | |
990 | * ACPI defines a different default state. So explicitly set low. | |
991 | */ | |
992 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); | |
993 | cs35l56_wait_min_reset_pulse(); | |
994 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); | |
995 | } | |
996 | ||
997 | ret = cs35l56_hw_init(&cs35l56->base); | |
998 | if (ret < 0) | |
999 | goto err; | |
1000 | ||
1001 | /* Reset the device and wait for it to boot */ | |
1002 | cs35l56_system_reset(&cs35l56->base, false); | |
1003 | ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); | |
1004 | if (ret) | |
1005 | goto err; | |
1006 | ||
73580ec6 RF |
1007 | regcache_cache_only(cs35l56->base.regmap, false); |
1008 | ||
73cfbfa9 ST |
1009 | ret = cs35l56_set_patch(&cs35l56->base); |
1010 | if (ret) | |
367ef1e1 | 1011 | goto err; |
73cfbfa9 ST |
1012 | |
1013 | regcache_mark_dirty(cs35l56->base.regmap); | |
1014 | regcache_sync(cs35l56->base.regmap); | |
1015 | ||
1016 | /* Disable auto-hibernate so that runtime_pm has control */ | |
1017 | ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); | |
1018 | if (ret) | |
1019 | goto err; | |
1020 | ||
cfa43aaa RF |
1021 | ret = cs35l56_get_calibration(&cs35l56->base); |
1022 | if (ret) | |
1023 | goto err; | |
1024 | ||
73cfbfa9 ST |
1025 | ret = cs_dsp_halo_init(&cs35l56->cs_dsp); |
1026 | if (ret) { | |
1027 | dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); | |
1028 | goto err; | |
1029 | } | |
1030 | ||
3c953163 ST |
1031 | dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n", |
1032 | cs35l56->system_name, cs35l56->amp_name); | |
73cfbfa9 ST |
1033 | |
1034 | regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config, | |
1035 | ARRAY_SIZE(cs35l56_hda_dai_config)); | |
856ce898 RF |
1036 | ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); |
1037 | if (ret) | |
1038 | goto err; | |
73cfbfa9 ST |
1039 | |
1040 | /* | |
1041 | * By default only enable one ASP1TXn, where n=amplifier index, | |
1042 | * This prevents multiple amps trying to drive the same slot. | |
1043 | */ | |
1044 | cs35l56->asp_tx_mask = BIT(cs35l56->index); | |
1045 | ||
1046 | pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000); | |
1047 | pm_runtime_use_autosuspend(cs35l56->base.dev); | |
1048 | pm_runtime_set_active(cs35l56->base.dev); | |
1049 | pm_runtime_mark_last_busy(cs35l56->base.dev); | |
1050 | pm_runtime_enable(cs35l56->base.dev); | |
1051 | ||
cafe9c6a ST |
1052 | cs35l56->base.init_done = true; |
1053 | ||
73cfbfa9 ST |
1054 | ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops); |
1055 | if (ret) { | |
1056 | dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret); | |
1057 | goto pm_err; | |
1058 | } | |
1059 | ||
73cfbfa9 ST |
1060 | return 0; |
1061 | ||
1062 | pm_err: | |
1063 | pm_runtime_disable(cs35l56->base.dev); | |
1064 | err: | |
1065 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); | |
1066 | ||
1067 | return ret; | |
1068 | } | |
1069 | EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, SND_HDA_SCODEC_CS35L56); | |
1070 | ||
1071 | void cs35l56_hda_remove(struct device *dev) | |
1072 | { | |
1073 | struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); | |
1074 | ||
caaaa34e | 1075 | pm_runtime_dont_use_autosuspend(cs35l56->base.dev); |
73cfbfa9 ST |
1076 | pm_runtime_get_sync(cs35l56->base.dev); |
1077 | pm_runtime_disable(cs35l56->base.dev); | |
1078 | ||
1079 | component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops); | |
1080 | ||
1081 | kfree(cs35l56->system_name); | |
1082 | pm_runtime_put_noidle(cs35l56->base.dev); | |
1083 | ||
1084 | gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); | |
1085 | } | |
1086 | EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56); | |
1087 | ||
1088 | const struct dev_pm_ops cs35l56_hda_pm_ops = { | |
deff8486 | 1089 | RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) |
73cfbfa9 ST |
1090 | SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume) |
1091 | LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late, | |
1092 | cs35l56_hda_system_resume_early) | |
1093 | NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq, | |
1094 | cs35l56_hda_system_resume_no_irq) | |
1095 | }; | |
1096 | EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56); | |
1097 | ||
1098 | MODULE_DESCRIPTION("CS35L56 HDA Driver"); | |
cfa43aaa | 1099 | MODULE_IMPORT_NS(FW_CS_DSP); |
6f03b446 | 1100 | MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC); |
73cfbfa9 ST |
1101 | MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); |
1102 | MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); | |
cfa43aaa | 1103 | MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); |
73cfbfa9 ST |
1104 | MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); |
1105 | MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); | |
1106 | MODULE_LICENSE("GPL"); |