1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ALSA driver for ICEnsemble VT1724 (Envy24HT) |
4 | * |
5 | * Lowlevel functions for Philips PSC724 Ultimate Edge |
6 | * |
7 | * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> |
8 | */ |
9 | |
10 | #include <linux/delay.h> |
11 | #include <linux/init.h> |
12 | #include <linux/slab.h> |
13 | #include <sound/core.h> |
14 | |
15 | #include "ice1712.h" |
16 | #include "envy24ht.h" |
17 | #include "psc724.h" |
18 | #include "wm8766.h" |
19 | #include "wm8776.h" |
20 | |
21 | struct psc724_spec { |
22 | struct snd_wm8766 wm8766; |
23 | struct snd_wm8776 wm8776; |
24 | bool mute_all, jack_detect; |
25 | struct snd_ice1712 *ice; |
26 | struct delayed_work hp_work; |
27 | bool hp_connected; |
28 | }; |
29 | |
30 | /****************************************************************************/ |
31 | /* PHILIPS PSC724 ULTIMATE EDGE */ |
32 | /****************************************************************************/ |
33 | /* |
34 | * VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz |
35 | * |
36 | * system configuration ICE_EEP2_SYSCONF=0x42 |
37 | * XIN1 49.152MHz |
38 | * no MPU401 |
39 | * one stereo ADC, no S/PDIF receiver |
40 | * three stereo DACs (FRONT, REAR, CENTER+LFE) |
41 | * |
42 | * AC-Link configuration ICE_EEP2_ACLINK=0x80 |
43 | * use I2S, not AC97 |
44 | * |
45 | * I2S converters feature ICE_EEP2_I2S=0x30 |
46 | * I2S codec has no volume/mute control feature (bug!) |
47 | * I2S codec does not support 96KHz or 192KHz (bug!) |
48 | * I2S codec 24bits |
49 | * |
50 | * S/PDIF configuration ICE_EEP2_SPDIF=0xc1 |
51 | * Enable integrated S/PDIF transmitter |
52 | * internal S/PDIF out implemented |
53 | * No S/PDIF input |
54 | * External S/PDIF out implemented |
55 | * |
56 | * |
57 | * ** connected chips ** |
58 | * |
59 | * WM8776 |
60 | * 2-channel DAC used for main output and stereo ADC (with 10-channel MUX) |
61 | * AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC |
62 | * Controlled by I2C using VT1722 I2C interface: |
63 | * MODE (pin16) -- GND |
64 | * CE (pin17) -- GND I2C mode (address=0x34) |
65 | * DI (pin18) -- SDA (VT1722 pin70) |
66 | * CL (pin19) -- SCLK (VT1722 pin71) |
67 | * |
68 | * WM8766 |
69 | * 6-channel DAC used for rear & center/LFE outputs (only 4 channels used) |
70 | * Controlled by SPI using VT1722 GPIO pins: |
71 | * MODE (pin 1) -- GPIO19 (VT1722 pin99) |
72 | * ML/I2S (pin11) -- GPIO18 (VT1722 pin98) |
73 | * MC/IWL (pin12) -- GPIO17 (VT1722 pin97) |
74 | * MD/DM (pin13) -- GPIO16 (VT1722 pin96) |
75 | * MUTE (pin14) -- GPIO20 (VT1722 pin101) |
76 | * |
77 | * GPIO14 is used as input for headphone jack detection (1 = connected) |
78 | * GPIO22 is used as MUTE ALL output, grounding all 6 channels |
79 | * |
80 | * ** output pins and device names ** |
81 | * |
82 | * 5.1ch name -- output connector color -- device (-D option) |
83 | * |
84 | * FRONT 2ch -- green -- plughw:0,0 |
85 | * CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0 |
86 | * REAR 2ch -- black -- plughw:0,2,1 |
87 | */ |
88 | |
89 | /* codec access low-level functions */ |
90 | |
91 | #define GPIO_HP_JACK (1 << 14) |
92 | #define GPIO_MUTE_SUR (1 << 20) |
93 | #define GPIO_MUTE_ALL (1 << 22) |
94 | |
95 | #define JACK_INTERVAL 1000 |
96 | |
97 | #define PSC724_SPI_DELAY 1 |
98 | |
99 | #define PSC724_SPI_DATA (1 << 16) |
100 | #define PSC724_SPI_CLK (1 << 17) |
101 | #define PSC724_SPI_LOAD (1 << 18) |
102 | #define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD) |
103 | |
104 | static void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) |
105 | { |
106 | struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766); |
107 | struct snd_ice1712 *ice = spec->ice; |
108 | u32 st, bits; |
109 | int i; |
110 | |
111 | snd_ice1712_save_gpio_status(ice); |
112 | |
113 | st = ((addr & 0x7f) << 9) | (data & 0x1ff); |
114 | snd_ice1712_gpio_set_dir(ice, bits: ice->gpio.direction | PSC724_SPI_MASK); |
115 | snd_ice1712_gpio_set_mask(ice, bits: ice->gpio.write_mask & ~PSC724_SPI_MASK); |
116 | bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK; |
117 | snd_ice1712_gpio_write(ice, val: bits); |
118 | |
119 | for (i = 0; i < 16; i++) { |
120 | udelay(PSC724_SPI_DELAY); |
121 | bits &= ~PSC724_SPI_CLK; |
122 | /* MSB first */ |
123 | st <<= 1; |
124 | if (st & 0x10000) |
125 | bits |= PSC724_SPI_DATA; |
126 | else |
127 | bits &= ~PSC724_SPI_DATA; |
128 | snd_ice1712_gpio_write(ice, val: bits); |
129 | /* CLOCK high */ |
130 | udelay(PSC724_SPI_DELAY); |
131 | bits |= PSC724_SPI_CLK; |
132 | snd_ice1712_gpio_write(ice, val: bits); |
133 | } |
134 | /* LOAD high */ |
135 | udelay(PSC724_SPI_DELAY); |
136 | bits |= PSC724_SPI_LOAD; |
137 | snd_ice1712_gpio_write(ice, val: bits); |
138 | /* LOAD low, DATA and CLOCK high */ |
139 | udelay(PSC724_SPI_DELAY); |
140 | bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK); |
141 | snd_ice1712_gpio_write(ice, val: bits); |
142 | |
143 | snd_ice1712_restore_gpio_status(ice); |
144 | } |
145 | |
146 | static void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data) |
147 | { |
148 | struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776); |
149 | |
150 | snd_vt1724_write_i2c(ice: spec->ice, dev: 0x34, addr, data); |
151 | } |
152 | |
153 | /* mute all */ |
154 | |
155 | static void psc724_set_master_switch(struct snd_ice1712 *ice, bool on) |
156 | { |
157 | unsigned int bits = snd_ice1712_gpio_read(ice); |
158 | struct psc724_spec *spec = ice->spec; |
159 | |
160 | spec->mute_all = !on; |
161 | if (on) |
162 | bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR); |
163 | else |
164 | bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR; |
165 | snd_ice1712_gpio_write(ice, val: bits); |
166 | } |
167 | |
168 | static bool psc724_get_master_switch(struct snd_ice1712 *ice) |
169 | { |
170 | struct psc724_spec *spec = ice->spec; |
171 | |
172 | return !spec->mute_all; |
173 | } |
174 | |
175 | /* jack detection */ |
176 | |
177 | static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) |
178 | { |
179 | struct psc724_spec *spec = ice->spec; |
180 | struct snd_kcontrol *kctl; |
181 | u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD; |
182 | |
183 | psc724_set_master_switch(ice, on: !hp_connected); |
184 | if (!hp_connected) |
185 | power |= WM8776_PWR_HPPD; |
186 | snd_wm8776_set_power(wm: &spec->wm8776, power); |
187 | spec->hp_connected = hp_connected; |
188 | /* notify about master speaker mute change */ |
189 | kctl = snd_ctl_find_id_mixer(card: ice->card, |
190 | name: "Master Speakers Playback Switch" ); |
191 | if (kctl) |
192 | snd_ctl_notify(card: ice->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &kctl->id); |
193 | /* and headphone mute change */ |
194 | kctl = snd_ctl_find_id_mixer(card: ice->card, |
195 | name: spec->wm8776.ctl[WM8776_CTL_HP_SW].name); |
196 | if (kctl) |
197 | snd_ctl_notify(card: ice->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &kctl->id); |
198 | } |
199 | |
200 | static void psc724_update_hp_jack_state(struct work_struct *work) |
201 | { |
202 | struct psc724_spec *spec = container_of(work, struct psc724_spec, |
203 | hp_work.work); |
204 | struct snd_ice1712 *ice = spec->ice; |
205 | bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; |
206 | |
207 | schedule_delayed_work(dwork: &spec->hp_work, delay: msecs_to_jiffies(JACK_INTERVAL)); |
208 | if (hp_connected == spec->hp_connected) |
209 | return; |
210 | psc724_set_jack_state(ice, hp_connected); |
211 | } |
212 | |
213 | static void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on) |
214 | { |
215 | struct psc724_spec *spec = ice->spec; |
216 | |
217 | if (spec->jack_detect == on) |
218 | return; |
219 | |
220 | spec->jack_detect = on; |
221 | if (on) { |
222 | bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; |
223 | psc724_set_jack_state(ice, hp_connected); |
224 | schedule_delayed_work(dwork: &spec->hp_work, |
225 | delay: msecs_to_jiffies(JACK_INTERVAL)); |
226 | } else |
227 | cancel_delayed_work_sync(dwork: &spec->hp_work); |
228 | } |
229 | |
230 | static bool psc724_get_jack_detection(struct snd_ice1712 *ice) |
231 | { |
232 | struct psc724_spec *spec = ice->spec; |
233 | |
234 | return spec->jack_detect; |
235 | } |
236 | |
237 | /* mixer controls */ |
238 | |
239 | struct psc724_control { |
240 | const char *name; |
241 | void (*set)(struct snd_ice1712 *ice, bool on); |
242 | bool (*get)(struct snd_ice1712 *ice); |
243 | }; |
244 | |
245 | static const struct psc724_control psc724_cont[] = { |
246 | { |
247 | .name = "Master Speakers Playback Switch" , |
248 | .set = psc724_set_master_switch, |
249 | .get = psc724_get_master_switch, |
250 | }, |
251 | { |
252 | .name = "Headphone Jack Detection Playback Switch" , |
253 | .set = psc724_set_jack_detection, |
254 | .get = psc724_get_jack_detection, |
255 | }, |
256 | }; |
257 | |
258 | static int psc724_ctl_get(struct snd_kcontrol *kcontrol, |
259 | struct snd_ctl_elem_value *ucontrol) |
260 | { |
261 | struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); |
262 | int n = kcontrol->private_value; |
263 | |
264 | ucontrol->value.integer.value[0] = psc724_cont[n].get(ice); |
265 | |
266 | return 0; |
267 | } |
268 | |
269 | static int psc724_ctl_put(struct snd_kcontrol *kcontrol, |
270 | struct snd_ctl_elem_value *ucontrol) |
271 | { |
272 | struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); |
273 | int n = kcontrol->private_value; |
274 | |
275 | psc724_cont[n].set(ice, ucontrol->value.integer.value[0]); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | static const char *front_volume = "Front Playback Volume" ; |
281 | static const char *front_switch = "Front Playback Switch" ; |
282 | static const char *front_zc = "Front Zero Cross Detect Playback Switch" ; |
283 | static const char *front_izd = "Front Infinite Zero Detect Playback Switch" ; |
284 | static const char *front_phase = "Front Phase Invert Playback Switch" ; |
285 | static const char *front_deemph = "Front Deemphasis Playback Switch" ; |
286 | static const char *ain1_switch = "Line Capture Switch" ; |
287 | static const char *ain2_switch = "CD Capture Switch" ; |
288 | static const char *ain3_switch = "AUX Capture Switch" ; |
289 | static const char *ain4_switch = "Front Mic Capture Switch" ; |
290 | static const char *ain5_switch = "Rear Mic Capture Switch" ; |
291 | static const char *rear_volume = "Surround Playback Volume" ; |
292 | static const char *clfe_volume = "CLFE Playback Volume" ; |
293 | static const char *rear_switch = "Surround Playback Switch" ; |
294 | static const char *clfe_switch = "CLFE Playback Switch" ; |
295 | static const char *rear_phase = "Surround Phase Invert Playback Switch" ; |
296 | static const char *clfe_phase = "CLFE Phase Invert Playback Switch" ; |
297 | static const char *rear_deemph = "Surround Deemphasis Playback Switch" ; |
298 | static const char *clfe_deemph = "CLFE Deemphasis Playback Switch" ; |
299 | static const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch" ; |
300 | static const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch" ; |
301 | |
302 | static int psc724_add_controls(struct snd_ice1712 *ice) |
303 | { |
304 | struct snd_kcontrol_new cont; |
305 | struct snd_kcontrol *ctl; |
306 | int err, i; |
307 | struct psc724_spec *spec = ice->spec; |
308 | |
309 | spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume; |
310 | spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch; |
311 | spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc; |
312 | spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL; |
313 | spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd; |
314 | spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase; |
315 | spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph; |
316 | spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch; |
317 | spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch; |
318 | spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch; |
319 | spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch; |
320 | spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch; |
321 | snd_wm8776_build_controls(wm: &spec->wm8776); |
322 | spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume; |
323 | spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume; |
324 | spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL; |
325 | spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch; |
326 | spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch; |
327 | spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL; |
328 | spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase; |
329 | spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase; |
330 | spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL; |
331 | spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph; |
332 | spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph; |
333 | spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL; |
334 | spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd; |
335 | spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc; |
336 | snd_wm8766_build_controls(wm: &spec->wm8766); |
337 | |
338 | memset(&cont, 0, sizeof(cont)); |
339 | cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
340 | for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) { |
341 | cont.private_value = i; |
342 | cont.name = psc724_cont[i].name; |
343 | cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; |
344 | cont.info = snd_ctl_boolean_mono_info; |
345 | cont.get = psc724_ctl_get; |
346 | cont.put = psc724_ctl_put; |
347 | ctl = snd_ctl_new1(kcontrolnew: &cont, private_data: ice); |
348 | if (!ctl) |
349 | return -ENOMEM; |
350 | err = snd_ctl_add(card: ice->card, kcontrol: ctl); |
351 | if (err < 0) |
352 | return err; |
353 | } |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate) |
359 | { |
360 | struct psc724_spec *spec = ice->spec; |
361 | /* restore codec volume settings after rate change (PMCLK stop) */ |
362 | snd_wm8776_volume_restore(wm: &spec->wm8776); |
363 | snd_wm8766_volume_restore(wm: &spec->wm8766); |
364 | } |
365 | |
366 | /* power management */ |
367 | |
368 | #ifdef CONFIG_PM_SLEEP |
369 | static int psc724_resume(struct snd_ice1712 *ice) |
370 | { |
371 | struct psc724_spec *spec = ice->spec; |
372 | |
373 | snd_wm8776_resume(wm: &spec->wm8776); |
374 | snd_wm8766_resume(wm: &spec->wm8766); |
375 | |
376 | return 0; |
377 | } |
378 | #endif |
379 | |
380 | /* init */ |
381 | |
382 | static int psc724_init(struct snd_ice1712 *ice) |
383 | { |
384 | struct psc724_spec *spec; |
385 | |
386 | spec = kzalloc(size: sizeof(*spec), GFP_KERNEL); |
387 | if (!spec) |
388 | return -ENOMEM; |
389 | ice->spec = spec; |
390 | spec->ice = ice; |
391 | |
392 | ice->num_total_dacs = 6; |
393 | ice->num_total_adcs = 2; |
394 | spec->wm8776.ops.write = psc724_wm8776_write; |
395 | spec->wm8776.card = ice->card; |
396 | snd_wm8776_init(wm: &spec->wm8776); |
397 | spec->wm8766.ops.write = psc724_wm8766_write; |
398 | spec->wm8766.card = ice->card; |
399 | #ifdef CONFIG_PM_SLEEP |
400 | ice->pm_resume = psc724_resume; |
401 | ice->pm_suspend_enabled = 1; |
402 | #endif |
403 | snd_wm8766_init(wm: &spec->wm8766); |
404 | snd_wm8766_set_if(wm: &spec->wm8766, |
405 | WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT); |
406 | ice->gpio.set_pro_rate = psc724_set_pro_rate; |
407 | INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state); |
408 | psc724_set_jack_detection(ice, on: true); |
409 | return 0; |
410 | } |
411 | |
412 | static void psc724_exit(struct snd_ice1712 *ice) |
413 | { |
414 | struct psc724_spec *spec = ice->spec; |
415 | |
416 | cancel_delayed_work_sync(dwork: &spec->hp_work); |
417 | } |
418 | |
419 | /* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */ |
420 | static const unsigned char psc724_eeprom[] = { |
421 | [ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */ |
422 | [ICE_EEP2_ACLINK] = 0x80, /* I2S */ |
423 | [ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */ |
424 | [ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */ |
425 | /* GPIO outputs */ |
426 | [ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ |
427 | /* GPIO write enable */ |
428 | [ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */ |
429 | [ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */ |
430 | [ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ |
431 | /* GPIO initial state */ |
432 | [ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */ |
433 | }; |
434 | |
435 | struct snd_ice1712_card_info snd_vt1724_psc724_cards[] = { |
436 | { |
437 | .subvendor = VT1724_SUBDEVICE_PSC724, |
438 | .name = "Philips PSC724 Ultimate Edge" , |
439 | .model = "psc724" , |
440 | .chip_init = psc724_init, |
441 | .chip_exit = psc724_exit, |
442 | .build_controls = psc724_add_controls, |
443 | .eeprom_size = sizeof(psc724_eeprom), |
444 | .eeprom_data = psc724_eeprom, |
445 | }, |
446 | {} /*terminator*/ |
447 | }; |
448 | |