xref: /linux/sound/soc/sof/intel/hda-dsp.c (revision 9a6b55ac)
1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license.  When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2018 Intel Corporation. All rights reserved.
7 //
8 // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9 //	    Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
10 //	    Rander Wang <rander.wang@intel.com>
11 //          Keyon Jie <yang.jie@linux.intel.com>
12 //
13 
14 /*
15  * Hardware interface for generic Intel audio DSP HDA IP
16  */
17 
18 #include <sound/hdaudio_ext.h>
19 #include <sound/hda_register.h>
20 #include "../ops.h"
21 #include "hda.h"
22 #include "hda-ipc.h"
23 
24 /*
25  * DSP Core control.
26  */
27 
28 int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask)
29 {
30 	u32 adspcs;
31 	u32 reset;
32 	int ret;
33 
34 	/* set reset bits for cores */
35 	reset = HDA_DSP_ADSPCS_CRST_MASK(core_mask);
36 	snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
37 					 HDA_DSP_REG_ADSPCS,
38 					 reset, reset),
39 
40 	/* poll with timeout to check if operation successful */
41 	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
42 					HDA_DSP_REG_ADSPCS, adspcs,
43 					((adspcs & reset) == reset),
44 					HDA_DSP_REG_POLL_INTERVAL_US,
45 					HDA_DSP_RESET_TIMEOUT_US);
46 	if (ret < 0) {
47 		dev_err(sdev->dev,
48 			"error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
49 			__func__);
50 		return ret;
51 	}
52 
53 	/* has core entered reset ? */
54 	adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
55 				  HDA_DSP_REG_ADSPCS);
56 	if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) !=
57 		HDA_DSP_ADSPCS_CRST_MASK(core_mask)) {
58 		dev_err(sdev->dev,
59 			"error: reset enter failed: core_mask %x adspcs 0x%x\n",
60 			core_mask, adspcs);
61 		ret = -EIO;
62 	}
63 
64 	return ret;
65 }
66 
67 int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask)
68 {
69 	unsigned int crst;
70 	u32 adspcs;
71 	int ret;
72 
73 	/* clear reset bits for cores */
74 	snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
75 					 HDA_DSP_REG_ADSPCS,
76 					 HDA_DSP_ADSPCS_CRST_MASK(core_mask),
77 					 0);
78 
79 	/* poll with timeout to check if operation successful */
80 	crst = HDA_DSP_ADSPCS_CRST_MASK(core_mask);
81 	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
82 					    HDA_DSP_REG_ADSPCS, adspcs,
83 					    !(adspcs & crst),
84 					    HDA_DSP_REG_POLL_INTERVAL_US,
85 					    HDA_DSP_RESET_TIMEOUT_US);
86 
87 	if (ret < 0) {
88 		dev_err(sdev->dev,
89 			"error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
90 			__func__);
91 		return ret;
92 	}
93 
94 	/* has core left reset ? */
95 	adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
96 				  HDA_DSP_REG_ADSPCS);
97 	if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) {
98 		dev_err(sdev->dev,
99 			"error: reset leave failed: core_mask %x adspcs 0x%x\n",
100 			core_mask, adspcs);
101 		ret = -EIO;
102 	}
103 
104 	return ret;
105 }
106 
107 int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask)
108 {
109 	/* stall core */
110 	snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
111 					 HDA_DSP_REG_ADSPCS,
112 					 HDA_DSP_ADSPCS_CSTALL_MASK(core_mask),
113 					 HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
114 
115 	/* set reset state */
116 	return hda_dsp_core_reset_enter(sdev, core_mask);
117 }
118 
119 int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask)
120 {
121 	int ret;
122 
123 	/* leave reset state */
124 	ret = hda_dsp_core_reset_leave(sdev, core_mask);
125 	if (ret < 0)
126 		return ret;
127 
128 	/* run core */
129 	dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask);
130 	snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
131 					 HDA_DSP_REG_ADSPCS,
132 					 HDA_DSP_ADSPCS_CSTALL_MASK(core_mask),
133 					 0);
134 
135 	/* is core now running ? */
136 	if (!hda_dsp_core_is_enabled(sdev, core_mask)) {
137 		hda_dsp_core_stall_reset(sdev, core_mask);
138 		dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n",
139 			core_mask);
140 		ret = -EIO;
141 	}
142 
143 	return ret;
144 }
145 
146 /*
147  * Power Management.
148  */
149 
150 int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask)
151 {
152 	unsigned int cpa;
153 	u32 adspcs;
154 	int ret;
155 
156 	/* update bits */
157 	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS,
158 				HDA_DSP_ADSPCS_SPA_MASK(core_mask),
159 				HDA_DSP_ADSPCS_SPA_MASK(core_mask));
160 
161 	/* poll with timeout to check if operation successful */
162 	cpa = HDA_DSP_ADSPCS_CPA_MASK(core_mask);
163 	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
164 					    HDA_DSP_REG_ADSPCS, adspcs,
165 					    (adspcs & cpa) == cpa,
166 					    HDA_DSP_REG_POLL_INTERVAL_US,
167 					    HDA_DSP_RESET_TIMEOUT_US);
168 	if (ret < 0) {
169 		dev_err(sdev->dev,
170 			"error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
171 			__func__);
172 		return ret;
173 	}
174 
175 	/* did core power up ? */
176 	adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
177 				  HDA_DSP_REG_ADSPCS);
178 	if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) !=
179 		HDA_DSP_ADSPCS_CPA_MASK(core_mask)) {
180 		dev_err(sdev->dev,
181 			"error: power up core failed core_mask %xadspcs 0x%x\n",
182 			core_mask, adspcs);
183 		ret = -EIO;
184 	}
185 
186 	return ret;
187 }
188 
189 int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask)
190 {
191 	u32 adspcs;
192 	int ret;
193 
194 	/* update bits */
195 	snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
196 					 HDA_DSP_REG_ADSPCS,
197 					 HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0);
198 
199 	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
200 				HDA_DSP_REG_ADSPCS, adspcs,
201 				!(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)),
202 				HDA_DSP_REG_POLL_INTERVAL_US,
203 				HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC);
204 	if (ret < 0)
205 		dev_err(sdev->dev,
206 			"error: %s: timeout on HDA_DSP_REG_ADSPCS read\n",
207 			__func__);
208 
209 	return ret;
210 }
211 
212 bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
213 			     unsigned int core_mask)
214 {
215 	int val;
216 	bool is_enable;
217 
218 	val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
219 
220 	is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) &&
221 			(val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) &&
222 			!(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
223 			!(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)));
224 
225 	dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
226 		is_enable, core_mask);
227 
228 	return is_enable;
229 }
230 
231 int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask)
232 {
233 	int ret;
234 
235 	/* return if core is already enabled */
236 	if (hda_dsp_core_is_enabled(sdev, core_mask))
237 		return 0;
238 
239 	/* power up */
240 	ret = hda_dsp_core_power_up(sdev, core_mask);
241 	if (ret < 0) {
242 		dev_err(sdev->dev, "error: dsp core power up failed: core_mask %x\n",
243 			core_mask);
244 		return ret;
245 	}
246 
247 	return hda_dsp_core_run(sdev, core_mask);
248 }
249 
250 int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
251 				  unsigned int core_mask)
252 {
253 	int ret;
254 
255 	/* place core in reset prior to power down */
256 	ret = hda_dsp_core_stall_reset(sdev, core_mask);
257 	if (ret < 0) {
258 		dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n",
259 			core_mask);
260 		return ret;
261 	}
262 
263 	/* power down core */
264 	ret = hda_dsp_core_power_down(sdev, core_mask);
265 	if (ret < 0) {
266 		dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n",
267 			core_mask, ret);
268 		return ret;
269 	}
270 
271 	/* make sure we are in OFF state */
272 	if (hda_dsp_core_is_enabled(sdev, core_mask)) {
273 		dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n",
274 			core_mask, ret);
275 		ret = -EIO;
276 	}
277 
278 	return ret;
279 }
280 
281 void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
282 {
283 	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
284 	const struct sof_intel_dsp_desc *chip = hda->desc;
285 
286 	/* enable IPC DONE and BUSY interrupts */
287 	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
288 			HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY,
289 			HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY);
290 
291 	/* enable IPC interrupt */
292 	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
293 				HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
294 }
295 
296 void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
297 {
298 	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
299 	const struct sof_intel_dsp_desc *chip = hda->desc;
300 
301 	/* disable IPC interrupt */
302 	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
303 				HDA_DSP_ADSPIC_IPC, 0);
304 
305 	/* disable IPC BUSY and DONE interrupt */
306 	snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
307 			HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0);
308 }
309 
310 static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
311 {
312 	struct hdac_bus *bus = sof_to_bus(sdev);
313 	int retry = HDA_DSP_REG_POLL_RETRY_COUNT;
314 
315 	while (snd_hdac_chip_readb(bus, VS_D0I3C) & SOF_HDA_VS_D0I3C_CIP) {
316 		if (!retry--)
317 			return -ETIMEDOUT;
318 		usleep_range(10, 15);
319 	}
320 
321 	return 0;
322 }
323 
324 static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
325 {
326 	struct sof_ipc_pm_gate pm_gate;
327 	struct sof_ipc_reply reply;
328 
329 	memset(&pm_gate, 0, sizeof(pm_gate));
330 
331 	/* configure pm_gate ipc message */
332 	pm_gate.hdr.size = sizeof(pm_gate);
333 	pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
334 	pm_gate.flags = flags;
335 
336 	/* send pm_gate ipc to dsp */
337 	return sof_ipc_tx_message(sdev->ipc, pm_gate.hdr.cmd, &pm_gate,
338 				  sizeof(pm_gate), &reply, sizeof(reply));
339 }
340 
341 int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
342 			    enum sof_d0_substate d0_substate)
343 {
344 	struct hdac_bus *bus = sof_to_bus(sdev);
345 	u32 flags;
346 	int ret;
347 	u8 value;
348 
349 	/* Write to D0I3C after Command-In-Progress bit is cleared */
350 	ret = hda_dsp_wait_d0i3c_done(sdev);
351 	if (ret < 0) {
352 		dev_err(bus->dev, "CIP timeout before D0I3C update!\n");
353 		return ret;
354 	}
355 
356 	/* Update D0I3C register */
357 	value = d0_substate == SOF_DSP_D0I3 ? SOF_HDA_VS_D0I3C_I3 : 0;
358 	snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value);
359 
360 	/* Wait for cmd in progress to be cleared before exiting the function */
361 	ret = hda_dsp_wait_d0i3c_done(sdev);
362 	if (ret < 0) {
363 		dev_err(bus->dev, "CIP timeout after D0I3C update!\n");
364 		return ret;
365 	}
366 
367 	dev_vdbg(bus->dev, "D0I3C updated, register = 0x%x\n",
368 		 snd_hdac_chip_readb(bus, VS_D0I3C));
369 
370 	if (d0_substate == SOF_DSP_D0I0)
371 		flags = HDA_PM_PPG;/* prevent power gating in D0 */
372 	else
373 		flags = HDA_PM_NO_DMA_TRACE;/* disable DMA trace in D0I3*/
374 
375 	/* sending pm_gate IPC */
376 	ret = hda_dsp_send_pm_gate_ipc(sdev, flags);
377 	if (ret < 0)
378 		dev_err(sdev->dev,
379 			"error: PM_GATE ipc error %d\n", ret);
380 
381 	return ret;
382 }
383 
384 static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
385 {
386 	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
387 	const struct sof_intel_dsp_desc *chip = hda->desc;
388 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
389 	struct hdac_bus *bus = sof_to_bus(sdev);
390 #endif
391 	int ret;
392 
393 	/* disable IPC interrupts */
394 	hda_dsp_ipc_int_disable(sdev);
395 
396 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
397 	if (runtime_suspend)
398 		hda_codec_jack_wake_enable(sdev);
399 
400 	/* power down all hda link */
401 	snd_hdac_ext_bus_link_power_down_all(bus);
402 #endif
403 
404 	/* power down DSP */
405 	ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask);
406 	if (ret < 0) {
407 		dev_err(sdev->dev,
408 			"error: failed to power down core during suspend\n");
409 		return ret;
410 	}
411 
412 	/* disable ppcap interrupt */
413 	hda_dsp_ctrl_ppcap_enable(sdev, false);
414 	hda_dsp_ctrl_ppcap_int_enable(sdev, false);
415 
416 	/* disable hda bus irq and streams */
417 	hda_dsp_ctrl_stop_chip(sdev);
418 
419 	/* disable LP retention mode */
420 	snd_sof_pci_update_bits(sdev, PCI_PGCTL,
421 				PCI_PGCTL_LSRMD_MASK, PCI_PGCTL_LSRMD_MASK);
422 
423 	/* reset controller */
424 	ret = hda_dsp_ctrl_link_reset(sdev, true);
425 	if (ret < 0) {
426 		dev_err(sdev->dev,
427 			"error: failed to reset controller during suspend\n");
428 		return ret;
429 	}
430 
431 	return 0;
432 }
433 
434 static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
435 {
436 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
437 	struct hdac_bus *bus = sof_to_bus(sdev);
438 	struct hdac_ext_link *hlink = NULL;
439 #endif
440 	int ret;
441 
442 	/*
443 	 * clear TCSEL to clear playback on some HD Audio
444 	 * codecs. PCI TCSEL is defined in the Intel manuals.
445 	 */
446 	snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0);
447 
448 	/* reset and start hda controller */
449 	ret = hda_dsp_ctrl_init_chip(sdev, true);
450 	if (ret < 0) {
451 		dev_err(sdev->dev,
452 			"error: failed to start controller after resume\n");
453 		return ret;
454 	}
455 
456 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
457 	/* check jack status */
458 	if (runtime_resume)
459 		hda_codec_jack_check(sdev);
460 
461 	/* turn off the links that were off before suspend */
462 	list_for_each_entry(hlink, &bus->hlink_list, list) {
463 		if (!hlink->ref_count)
464 			snd_hdac_ext_bus_link_power_down(hlink);
465 	}
466 
467 	/* check dma status and clean up CORB/RIRB buffers */
468 	if (!bus->cmd_dma_state)
469 		snd_hdac_bus_stop_cmd_io(bus);
470 #endif
471 
472 	/* enable ppcap interrupt */
473 	hda_dsp_ctrl_ppcap_enable(sdev, true);
474 	hda_dsp_ctrl_ppcap_int_enable(sdev, true);
475 
476 	return 0;
477 }
478 
479 int hda_dsp_resume(struct snd_sof_dev *sdev)
480 {
481 	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
482 	struct pci_dev *pci = to_pci_dev(sdev->dev);
483 
484 	if (sdev->s0_suspend) {
485 		/* restore L1SEN bit */
486 		if (hda->l1_support_changed)
487 			snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
488 						HDA_VS_INTEL_EM2,
489 						HDA_VS_INTEL_EM2_L1SEN, 0);
490 
491 		/* restore and disable the system wakeup */
492 		pci_restore_state(pci);
493 		disable_irq_wake(pci->irq);
494 		return 0;
495 	}
496 
497 	/* init hda controller. DSP cores will be powered up during fw boot */
498 	return hda_resume(sdev, false);
499 }
500 
501 int hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
502 {
503 	/* init hda controller. DSP cores will be powered up during fw boot */
504 	return hda_resume(sdev, true);
505 }
506 
507 int hda_dsp_runtime_idle(struct snd_sof_dev *sdev)
508 {
509 	struct hdac_bus *hbus = sof_to_bus(sdev);
510 
511 	if (hbus->codec_powered) {
512 		dev_dbg(sdev->dev, "some codecs still powered (%08X), not idle\n",
513 			(unsigned int)hbus->codec_powered);
514 		return -EBUSY;
515 	}
516 
517 	return 0;
518 }
519 
520 int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
521 {
522 	/* stop hda controller and power dsp off */
523 	return hda_suspend(sdev, true);
524 }
525 
526 int hda_dsp_suspend(struct snd_sof_dev *sdev)
527 {
528 	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
529 	struct hdac_bus *bus = sof_to_bus(sdev);
530 	struct pci_dev *pci = to_pci_dev(sdev->dev);
531 	int ret;
532 
533 	if (sdev->s0_suspend) {
534 		/* enable L1SEN to make sure the system can enter S0Ix */
535 		hda->l1_support_changed =
536 			snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
537 						HDA_VS_INTEL_EM2,
538 						HDA_VS_INTEL_EM2_L1SEN,
539 						HDA_VS_INTEL_EM2_L1SEN);
540 
541 		/* enable the system waking up via IPC IRQ */
542 		enable_irq_wake(pci->irq);
543 		pci_save_state(pci);
544 		return 0;
545 	}
546 
547 	/* stop hda controller and power dsp off */
548 	ret = hda_suspend(sdev, false);
549 	if (ret < 0) {
550 		dev_err(bus->dev, "error: suspending dsp\n");
551 		return ret;
552 	}
553 
554 	return 0;
555 }
556 
557 int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
558 {
559 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
560 	struct hdac_bus *bus = sof_to_bus(sdev);
561 	struct snd_soc_pcm_runtime *rtd;
562 	struct hdac_ext_stream *stream;
563 	struct hdac_ext_link *link;
564 	struct hdac_stream *s;
565 	const char *name;
566 	int stream_tag;
567 
568 	/* set internal flag for BE */
569 	list_for_each_entry(s, &bus->stream_list, list) {
570 		stream = stream_to_hdac_ext_stream(s);
571 
572 		/*
573 		 * clear stream. This should already be taken care for running
574 		 * streams when the SUSPEND trigger is called. But paused
575 		 * streams do not get suspended, so this needs to be done
576 		 * explicitly during suspend.
577 		 */
578 		if (stream->link_substream) {
579 			rtd = snd_pcm_substream_chip(stream->link_substream);
580 			name = rtd->codec_dai->component->name;
581 			link = snd_hdac_ext_bus_get_link(bus, name);
582 			if (!link)
583 				return -EINVAL;
584 
585 			stream->link_prepared = 0;
586 
587 			if (hdac_stream(stream)->direction ==
588 				SNDRV_PCM_STREAM_CAPTURE)
589 				continue;
590 
591 			stream_tag = hdac_stream(stream)->stream_tag;
592 			snd_hdac_ext_link_clear_stream_id(link, stream_tag);
593 		}
594 	}
595 #endif
596 	return 0;
597 }
598