162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
962306a36Sopenharmony_ci//	    Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1062306a36Sopenharmony_ci//	    Rander Wang <rander.wang@intel.com>
1162306a36Sopenharmony_ci//          Keyon Jie <yang.jie@linux.intel.com>
1262306a36Sopenharmony_ci//
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Hardware interface for HDA DSP code loader
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/firmware.h>
1962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h>
2062306a36Sopenharmony_ci#include <sound/hda_register.h>
2162306a36Sopenharmony_ci#include <sound/sof.h>
2262306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h>
2362306a36Sopenharmony_ci#include "ext_manifest.h"
2462306a36Sopenharmony_ci#include "../ipc4-priv.h"
2562306a36Sopenharmony_ci#include "../ops.h"
2662306a36Sopenharmony_ci#include "../sof-priv.h"
2762306a36Sopenharmony_ci#include "hda.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
3262306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip = hda->desc;
3362306a36Sopenharmony_ci	int i;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* DSP is powered up, set all SSPs to clock consumer/codec provider mode */
3662306a36Sopenharmony_ci	for (i = 0; i < chip->ssp_count; i++) {
3762306a36Sopenharmony_ci		snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
3862306a36Sopenharmony_ci						 chip->ssp_base_offset
3962306a36Sopenharmony_ci						 + i * SSP_DEV_MEM_SIZE
4062306a36Sopenharmony_ci						 + SSP_SSC1_OFFSET,
4162306a36Sopenharmony_ci						 SSP_SET_CBP_CFP,
4262306a36Sopenharmony_ci						 SSP_SET_CBP_CFP);
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
4762306a36Sopenharmony_ci					      unsigned int size, struct snd_dma_buffer *dmab,
4862306a36Sopenharmony_ci					      int direction)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream;
5162306a36Sopenharmony_ci	struct hdac_stream *hstream;
5262306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(sdev->dev);
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	hext_stream = hda_dsp_stream_get(sdev, direction, 0);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!hext_stream) {
5862306a36Sopenharmony_ci		dev_err(sdev->dev, "error: no stream available\n");
5962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	hstream = &hext_stream->hstream;
6262306a36Sopenharmony_ci	hstream->substream = NULL;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* allocate DMA buffer */
6562306a36Sopenharmony_ci	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab);
6662306a36Sopenharmony_ci	if (ret < 0) {
6762306a36Sopenharmony_ci		dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret);
6862306a36Sopenharmony_ci		goto out_put;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	hstream->period_bytes = 0;/* initialize period_bytes */
7262306a36Sopenharmony_ci	hstream->format_val = format;
7362306a36Sopenharmony_ci	hstream->bufsize = size;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (direction == SNDRV_PCM_STREAM_CAPTURE) {
7662306a36Sopenharmony_ci		ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
7762306a36Sopenharmony_ci		if (ret < 0) {
7862306a36Sopenharmony_ci			dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
7962306a36Sopenharmony_ci			goto out_free;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	} else {
8262306a36Sopenharmony_ci		ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
8362306a36Sopenharmony_ci		if (ret < 0) {
8462306a36Sopenharmony_ci			dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
8562306a36Sopenharmony_ci			goto out_free;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return hext_stream;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciout_free:
9362306a36Sopenharmony_ci	snd_dma_free_pages(dmab);
9462306a36Sopenharmony_ciout_put:
9562306a36Sopenharmony_ci	hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
9662306a36Sopenharmony_ci	return ERR_PTR(ret);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * first boot sequence has some extra steps.
10162306a36Sopenharmony_ci * power on all host managed cores and only unstall/run the boot core to boot the
10262306a36Sopenharmony_ci * DSP then turn off all non boot cores (if any) is powered on.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ciint cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
10762306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip = hda->desc;
10862306a36Sopenharmony_ci	unsigned int status, target_status;
10962306a36Sopenharmony_ci	u32 flags, ipc_hdr, j;
11062306a36Sopenharmony_ci	unsigned long mask;
11162306a36Sopenharmony_ci	char *dump_msg;
11262306a36Sopenharmony_ci	int ret;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* step 1: power up corex */
11562306a36Sopenharmony_ci	ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask);
11662306a36Sopenharmony_ci	if (ret < 0) {
11762306a36Sopenharmony_ci		if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
11862306a36Sopenharmony_ci			dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n");
11962306a36Sopenharmony_ci		goto err;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	hda_ssp_set_cbp_cfp(sdev);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */
12562306a36Sopenharmony_ci	ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL;
12662306a36Sopenharmony_ci	if (!imr_boot)
12762306a36Sopenharmony_ci		ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* step 3: unset core 0 reset state & unstall/run core 0 */
13262306a36Sopenharmony_ci	ret = hda_dsp_core_run(sdev, chip->init_core_mask);
13362306a36Sopenharmony_ci	if (ret < 0) {
13462306a36Sopenharmony_ci		if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
13562306a36Sopenharmony_ci			dev_err(sdev->dev,
13662306a36Sopenharmony_ci				"error: dsp core start failed %d\n", ret);
13762306a36Sopenharmony_ci		ret = -EIO;
13862306a36Sopenharmony_ci		goto err;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* step 4: wait for IPC DONE bit from ROM */
14262306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
14362306a36Sopenharmony_ci					    chip->ipc_ack, status,
14462306a36Sopenharmony_ci					    ((status & chip->ipc_ack_mask)
14562306a36Sopenharmony_ci						    == chip->ipc_ack_mask),
14662306a36Sopenharmony_ci					    HDA_DSP_REG_POLL_INTERVAL_US,
14762306a36Sopenharmony_ci					    HDA_DSP_INIT_TIMEOUT_US);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (ret < 0) {
15062306a36Sopenharmony_ci		if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
15162306a36Sopenharmony_ci			dev_err(sdev->dev,
15262306a36Sopenharmony_ci				"error: %s: timeout for HIPCIE done\n",
15362306a36Sopenharmony_ci				__func__);
15462306a36Sopenharmony_ci		goto err;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* set DONE bit to clear the reply IPC message */
15862306a36Sopenharmony_ci	snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
15962306a36Sopenharmony_ci				       chip->ipc_ack,
16062306a36Sopenharmony_ci				       chip->ipc_ack_mask,
16162306a36Sopenharmony_ci				       chip->ipc_ack_mask);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* step 5: power down cores that are no longer needed */
16462306a36Sopenharmony_ci	ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask &
16562306a36Sopenharmony_ci					   ~(chip->init_core_mask));
16662306a36Sopenharmony_ci	if (ret < 0) {
16762306a36Sopenharmony_ci		if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
16862306a36Sopenharmony_ci			dev_err(sdev->dev,
16962306a36Sopenharmony_ci				"error: dsp core x power down failed\n");
17062306a36Sopenharmony_ci		goto err;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* step 6: enable IPC interrupts */
17462306a36Sopenharmony_ci	hda_dsp_ipc_int_enable(sdev);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/*
17762306a36Sopenharmony_ci	 * step 7:
17862306a36Sopenharmony_ci	 * - Cold/Full boot: wait for ROM init to proceed to download the firmware
17962306a36Sopenharmony_ci	 * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if (imr_boot)
18262306a36Sopenharmony_ci		target_status = FSR_STATE_FW_ENTERED;
18362306a36Sopenharmony_ci	else
18462306a36Sopenharmony_ci		target_status = FSR_STATE_INIT_DONE;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
18762306a36Sopenharmony_ci					chip->rom_status_reg, status,
18862306a36Sopenharmony_ci					(FSR_TO_STATE_CODE(status) == target_status),
18962306a36Sopenharmony_ci					HDA_DSP_REG_POLL_INTERVAL_US,
19062306a36Sopenharmony_ci					chip->rom_init_timeout *
19162306a36Sopenharmony_ci					USEC_PER_MSEC);
19262306a36Sopenharmony_ci	if (!ret) {
19362306a36Sopenharmony_ci		/* set enabled cores mask and increment ref count for cores in init_core_mask */
19462306a36Sopenharmony_ci		sdev->enabled_cores_mask |= chip->init_core_mask;
19562306a36Sopenharmony_ci		mask = sdev->enabled_cores_mask;
19662306a36Sopenharmony_ci		for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
19762306a36Sopenharmony_ci			sdev->dsp_core_ref_count[j]++;
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
20262306a36Sopenharmony_ci		dev_err(sdev->dev,
20362306a36Sopenharmony_ci			"%s: timeout with rom_status_reg (%#x) read\n",
20462306a36Sopenharmony_ci			__func__, chip->rom_status_reg);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cierr:
20762306a36Sopenharmony_ci	flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* after max boot attempts make sure that the dump is printed */
21062306a36Sopenharmony_ci	if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
21162306a36Sopenharmony_ci		flags &= ~SOF_DBG_DUMP_OPTIONAL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
21462306a36Sopenharmony_ci			     hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
21562306a36Sopenharmony_ci	snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
21662306a36Sopenharmony_ci	hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	kfree(dump_msg);
21962306a36Sopenharmony_ci	return ret;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int cl_trigger(struct snd_sof_dev *sdev,
22362306a36Sopenharmony_ci		      struct hdac_ext_stream *hext_stream, int cmd)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct hdac_stream *hstream = &hext_stream->hstream;
22662306a36Sopenharmony_ci	int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* code loader is special case that reuses stream ops */
22962306a36Sopenharmony_ci	switch (cmd) {
23062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
23162306a36Sopenharmony_ci		snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
23262306a36Sopenharmony_ci					1 << hstream->index,
23362306a36Sopenharmony_ci					1 << hstream->index);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
23662306a36Sopenharmony_ci					sd_offset,
23762306a36Sopenharmony_ci					SOF_HDA_SD_CTL_DMA_START |
23862306a36Sopenharmony_ci					SOF_HDA_CL_DMA_SD_INT_MASK,
23962306a36Sopenharmony_ci					SOF_HDA_SD_CTL_DMA_START |
24062306a36Sopenharmony_ci					SOF_HDA_CL_DMA_SD_INT_MASK);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		hstream->running = true;
24362306a36Sopenharmony_ci		return 0;
24462306a36Sopenharmony_ci	default:
24562306a36Sopenharmony_ci		return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
25062306a36Sopenharmony_ci		   struct hdac_ext_stream *hext_stream)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct hdac_stream *hstream = &hext_stream->hstream;
25362306a36Sopenharmony_ci	int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
25462306a36Sopenharmony_ci	int ret = 0;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
25762306a36Sopenharmony_ci		ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
25862306a36Sopenharmony_ci	else
25962306a36Sopenharmony_ci		snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
26062306a36Sopenharmony_ci					SOF_HDA_SD_CTL_DMA_START, 0);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
26362306a36Sopenharmony_ci	hstream->running = 0;
26462306a36Sopenharmony_ci	hstream->substream = NULL;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* reset BDL address */
26762306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
26862306a36Sopenharmony_ci			  sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
26962306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
27062306a36Sopenharmony_ci			  sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
27362306a36Sopenharmony_ci	snd_dma_free_pages(dmab);
27462306a36Sopenharmony_ci	dmab->area = NULL;
27562306a36Sopenharmony_ci	hstream->bufsize = 0;
27662306a36Sopenharmony_ci	hstream->format_val = 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return ret;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciint hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
28462306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip = hda->desc;
28562306a36Sopenharmony_ci	unsigned int reg;
28662306a36Sopenharmony_ci	int ret, status;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START);
28962306a36Sopenharmony_ci	if (ret < 0) {
29062306a36Sopenharmony_ci		dev_err(sdev->dev, "error: DMA trigger start failed\n");
29162306a36Sopenharmony_ci		return ret;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
29562306a36Sopenharmony_ci					chip->rom_status_reg, reg,
29662306a36Sopenharmony_ci					(FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED),
29762306a36Sopenharmony_ci					HDA_DSP_REG_POLL_INTERVAL_US,
29862306a36Sopenharmony_ci					HDA_DSP_BASEFW_TIMEOUT_US);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/*
30162306a36Sopenharmony_ci	 * even in case of errors we still need to stop the DMAs,
30262306a36Sopenharmony_ci	 * but we return the initial error should the DMA stop also fail
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (status < 0) {
30662306a36Sopenharmony_ci		dev_err(sdev->dev,
30762306a36Sopenharmony_ci			"%s: timeout with rom_status_reg (%#x) read\n",
30862306a36Sopenharmony_ci			__func__, chip->rom_status_reg);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
31262306a36Sopenharmony_ci	if (ret < 0) {
31362306a36Sopenharmony_ci		dev_err(sdev->dev, "error: DMA trigger stop failed\n");
31462306a36Sopenharmony_ci		if (!status)
31562306a36Sopenharmony_ci			status = ret;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return status;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciint hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct hdac_ext_stream *iccmax_stream;
32462306a36Sopenharmony_ci	struct snd_dma_buffer dmab_bdl;
32562306a36Sopenharmony_ci	int ret, ret1;
32662306a36Sopenharmony_ci	u8 original_gb;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* save the original LTRP guardband value */
32962306a36Sopenharmony_ci	original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) &
33062306a36Sopenharmony_ci		HDA_VS_INTEL_LTRP_GB_MASK;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/*
33362306a36Sopenharmony_ci	 * Prepare capture stream for ICCMAX. We do not need to store
33462306a36Sopenharmony_ci	 * the data, so use a buffer of PAGE_SIZE for receiving.
33562306a36Sopenharmony_ci	 */
33662306a36Sopenharmony_ci	iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, PAGE_SIZE,
33762306a36Sopenharmony_ci					      &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE);
33862306a36Sopenharmony_ci	if (IS_ERR(iccmax_stream)) {
33962306a36Sopenharmony_ci		dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
34062306a36Sopenharmony_ci		return PTR_ERR(iccmax_stream);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = hda_dsp_cl_boot_firmware(sdev);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/*
34662306a36Sopenharmony_ci	 * Perform iccmax stream cleanup. This should be done even if firmware loading fails.
34762306a36Sopenharmony_ci	 * If the cleanup also fails, we return the initial error
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	ret1 = hda_cl_cleanup(sdev, &dmab_bdl, iccmax_stream);
35062306a36Sopenharmony_ci	if (ret1 < 0) {
35162306a36Sopenharmony_ci		dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		/* set return value to indicate cleanup failure */
35462306a36Sopenharmony_ci		if (!ret)
35562306a36Sopenharmony_ci			ret = ret1;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* restore the original guardband value after FW boot */
35962306a36Sopenharmony_ci	snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
36062306a36Sopenharmony_ci			    HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return ret;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int hda_dsp_boot_imr(struct snd_sof_dev *sdev)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip_info;
36862306a36Sopenharmony_ci	int ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	chip_info = get_chip_info(sdev->pdata);
37162306a36Sopenharmony_ci	if (chip_info->cl_init)
37262306a36Sopenharmony_ci		ret = chip_info->cl_init(sdev, 0, true);
37362306a36Sopenharmony_ci	else
37462306a36Sopenharmony_ci		ret = -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (!ret)
37762306a36Sopenharmony_ci		hda_sdw_process_wakeen(sdev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return ret;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciint hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
38562306a36Sopenharmony_ci	struct snd_sof_pdata *plat_data = sdev->pdata;
38662306a36Sopenharmony_ci	const struct sof_dev_desc *desc = plat_data->desc;
38762306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip_info;
38862306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream;
38962306a36Sopenharmony_ci	struct firmware stripped_firmware;
39062306a36Sopenharmony_ci	struct snd_dma_buffer dmab;
39162306a36Sopenharmony_ci	int ret, ret1, i;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) {
39462306a36Sopenharmony_ci		dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n");
39562306a36Sopenharmony_ci		hda->boot_iteration = 0;
39662306a36Sopenharmony_ci		ret = hda_dsp_boot_imr(sdev);
39762306a36Sopenharmony_ci		if (!ret) {
39862306a36Sopenharmony_ci			hda->booted_from_imr = true;
39962306a36Sopenharmony_ci			return 0;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n");
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	hda->booted_from_imr = false;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	chip_info = desc->chip_info;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) {
41062306a36Sopenharmony_ci		dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
41162306a36Sopenharmony_ci		return -EINVAL;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
41562306a36Sopenharmony_ci	stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* init for booting wait */
41862306a36Sopenharmony_ci	init_waitqueue_head(&sdev->boot_wait);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* prepare DMA for code loader stream */
42162306a36Sopenharmony_ci	hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT,
42262306a36Sopenharmony_ci					    stripped_firmware.size,
42362306a36Sopenharmony_ci					    &dmab, SNDRV_PCM_STREAM_PLAYBACK);
42462306a36Sopenharmony_ci	if (IS_ERR(hext_stream)) {
42562306a36Sopenharmony_ci		dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
42662306a36Sopenharmony_ci		return PTR_ERR(hext_stream);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	memcpy(dmab.area, stripped_firmware.data,
43062306a36Sopenharmony_ci	       stripped_firmware.size);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* try ROM init a few times before giving up */
43362306a36Sopenharmony_ci	for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) {
43462306a36Sopenharmony_ci		dev_dbg(sdev->dev,
43562306a36Sopenharmony_ci			"Attempting iteration %d of Core En/ROM load...\n", i);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		hda->boot_iteration = i + 1;
43862306a36Sopenharmony_ci		if (chip_info->cl_init)
43962306a36Sopenharmony_ci			ret = chip_info->cl_init(sdev, hext_stream->hstream.stream_tag, false);
44062306a36Sopenharmony_ci		else
44162306a36Sopenharmony_ci			ret = -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		/* don't retry anymore if successful */
44462306a36Sopenharmony_ci		if (!ret)
44562306a36Sopenharmony_ci			break;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (i == HDA_FW_BOOT_ATTEMPTS) {
44962306a36Sopenharmony_ci		dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n",
45062306a36Sopenharmony_ci			i, ret);
45162306a36Sopenharmony_ci		goto cleanup;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/*
45562306a36Sopenharmony_ci	 * When a SoundWire link is in clock stop state, a Slave
45662306a36Sopenharmony_ci	 * device may trigger in-band wakes for events such as jack
45762306a36Sopenharmony_ci	 * insertion or acoustic event detection. This event will lead
45862306a36Sopenharmony_ci	 * to a WAKEEN interrupt, handled by the PCI device and routed
45962306a36Sopenharmony_ci	 * to PME if the PCI device is in D3. The resume function in
46062306a36Sopenharmony_ci	 * audio PCI driver will be invoked by ACPI for PME event and
46162306a36Sopenharmony_ci	 * initialize the device and process WAKEEN interrupt.
46262306a36Sopenharmony_ci	 *
46362306a36Sopenharmony_ci	 * The WAKEEN interrupt should be processed ASAP to prevent an
46462306a36Sopenharmony_ci	 * interrupt flood, otherwise other interrupts, such IPC,
46562306a36Sopenharmony_ci	 * cannot work normally.  The WAKEEN is handled after the ROM
46662306a36Sopenharmony_ci	 * is initialized successfully, which ensures power rails are
46762306a36Sopenharmony_ci	 * enabled before accessing the SoundWire SHIM registers
46862306a36Sopenharmony_ci	 */
46962306a36Sopenharmony_ci	if (!sdev->first_boot)
47062306a36Sopenharmony_ci		hda_sdw_process_wakeen(sdev);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/*
47362306a36Sopenharmony_ci	 * Set the boot_iteration to the last attempt, indicating that the
47462306a36Sopenharmony_ci	 * DSP ROM has been initialized and from this point there will be no
47562306a36Sopenharmony_ci	 * retry done to boot.
47662306a36Sopenharmony_ci	 *
47762306a36Sopenharmony_ci	 * Continue with code loading and firmware boot
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
48062306a36Sopenharmony_ci	ret = hda_cl_copy_fw(sdev, hext_stream);
48162306a36Sopenharmony_ci	if (!ret) {
48262306a36Sopenharmony_ci		dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
48362306a36Sopenharmony_ci		hda->skip_imr_boot = false;
48462306a36Sopenharmony_ci	} else {
48562306a36Sopenharmony_ci		snd_sof_dsp_dbg_dump(sdev, "Firmware download failed",
48662306a36Sopenharmony_ci				     SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX);
48762306a36Sopenharmony_ci		hda->skip_imr_boot = true;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cicleanup:
49162306a36Sopenharmony_ci	/*
49262306a36Sopenharmony_ci	 * Perform codeloader stream cleanup.
49362306a36Sopenharmony_ci	 * This should be done even if firmware loading fails.
49462306a36Sopenharmony_ci	 * If the cleanup also fails, we return the initial error
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream);
49762306a36Sopenharmony_ci	if (ret1 < 0) {
49862306a36Sopenharmony_ci		dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		/* set return value to indicate cleanup failure */
50162306a36Sopenharmony_ci		if (!ret)
50262306a36Sopenharmony_ci			ret = ret1;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/*
50662306a36Sopenharmony_ci	 * return primary core id if both fw copy
50762306a36Sopenharmony_ci	 * and stream clean up are successful
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	if (!ret)
51062306a36Sopenharmony_ci		return chip_info->init_core_mask;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* disable DSP */
51362306a36Sopenharmony_ci	snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
51462306a36Sopenharmony_ci				SOF_HDA_REG_PP_PPCTL,
51562306a36Sopenharmony_ci				SOF_HDA_PPCTL_GPROCEN, 0);
51662306a36Sopenharmony_ci	return ret;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ciint hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
52062306a36Sopenharmony_ci			      struct sof_ipc4_fw_library *fw_lib, bool reload)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
52362306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream;
52462306a36Sopenharmony_ci	struct firmware stripped_firmware;
52562306a36Sopenharmony_ci	struct sof_ipc4_msg msg = {};
52662306a36Sopenharmony_ci	struct snd_dma_buffer dmab;
52762306a36Sopenharmony_ci	int ret, ret1;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* IMR booting will restore the libraries as well, skip the loading */
53062306a36Sopenharmony_ci	if (reload && hda->booted_from_imr)
53162306a36Sopenharmony_ci		return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* the fw_lib has been verified during loading, we can trust the validity here */
53462306a36Sopenharmony_ci	stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset;
53562306a36Sopenharmony_ci	stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* prepare DMA for code loader stream */
53862306a36Sopenharmony_ci	hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT,
53962306a36Sopenharmony_ci					    stripped_firmware.size,
54062306a36Sopenharmony_ci					    &dmab, SNDRV_PCM_STREAM_PLAYBACK);
54162306a36Sopenharmony_ci	if (IS_ERR(hext_stream)) {
54262306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__);
54362306a36Sopenharmony_ci		return PTR_ERR(hext_stream);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	msg.primary = hext_stream->hstream.stream_tag - 1;
54962306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY);
55062306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
55162306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
55262306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(fw_lib->id);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START);
55562306a36Sopenharmony_ci	if (ret < 0) {
55662306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__);
55762306a36Sopenharmony_ci		goto cleanup;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
56362306a36Sopenharmony_ci	if (ret1 < 0) {
56462306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: DMA trigger stop failed\n", __func__);
56562306a36Sopenharmony_ci		if (!ret)
56662306a36Sopenharmony_ci			ret = ret1;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cicleanup:
57062306a36Sopenharmony_ci	/* clean up even in case of error and return the first error */
57162306a36Sopenharmony_ci	ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream);
57262306a36Sopenharmony_ci	if (ret1 < 0) {
57362306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		/* set return value to indicate cleanup failure */
57662306a36Sopenharmony_ci		if (!ret)
57762306a36Sopenharmony_ci			ret = ret1;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/* pre fw run operations */
58462306a36Sopenharmony_ciint hda_dsp_pre_fw_run(struct snd_sof_dev *sdev)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	/* disable clock gating and power gating */
58762306a36Sopenharmony_ci	return hda_dsp_ctrl_clock_power_gating(sdev, false);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/* post fw run operations */
59162306a36Sopenharmony_ciint hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	int ret;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (sdev->first_boot) {
59662306a36Sopenharmony_ci		struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		ret = hda_sdw_startup(sdev);
59962306a36Sopenharmony_ci		if (ret < 0) {
60062306a36Sopenharmony_ci			dev_err(sdev->dev,
60162306a36Sopenharmony_ci				"error: could not startup SoundWire links\n");
60262306a36Sopenharmony_ci			return ret;
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		/* Check if IMR boot is usable */
60662306a36Sopenharmony_ci		if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
60762306a36Sopenharmony_ci		    (sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT ||
60862306a36Sopenharmony_ci		     sdev->pdata->ipc_type == SOF_INTEL_IPC4))
60962306a36Sopenharmony_ci			hdev->imrboot_supported = true;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	hda_sdw_int_enable(sdev, true);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* re-enable clock gating and power gating */
61562306a36Sopenharmony_ci	return hda_dsp_ctrl_clock_power_gating(sdev, true);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ciint hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev,
61962306a36Sopenharmony_ci					 const struct sof_ext_man_elem_header *hdr)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	const struct sof_ext_man_cavs_config_data *config_data =
62262306a36Sopenharmony_ci		container_of(hdr, struct sof_ext_man_cavs_config_data, hdr);
62362306a36Sopenharmony_ci	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
62462306a36Sopenharmony_ci	int i, elem_num;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* calculate total number of config data elements */
62762306a36Sopenharmony_ci	elem_num = (hdr->size - sizeof(struct sof_ext_man_elem_header))
62862306a36Sopenharmony_ci		   / sizeof(struct sof_config_elem);
62962306a36Sopenharmony_ci	if (elem_num <= 0) {
63062306a36Sopenharmony_ci		dev_err(sdev->dev, "cavs config data is inconsistent: %d\n", elem_num);
63162306a36Sopenharmony_ci		return -EINVAL;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	for (i = 0; i < elem_num; i++)
63562306a36Sopenharmony_ci		switch (config_data->elems[i].token) {
63662306a36Sopenharmony_ci		case SOF_EXT_MAN_CAVS_CONFIG_EMPTY:
63762306a36Sopenharmony_ci			/* skip empty token */
63862306a36Sopenharmony_ci			break;
63962306a36Sopenharmony_ci		case SOF_EXT_MAN_CAVS_CONFIG_CAVS_LPRO:
64062306a36Sopenharmony_ci			hda->clk_config_lpro = config_data->elems[i].value;
64162306a36Sopenharmony_ci			dev_dbg(sdev->dev, "FW clock config: %s\n",
64262306a36Sopenharmony_ci				hda->clk_config_lpro ? "LPRO" : "HPRO");
64362306a36Sopenharmony_ci			break;
64462306a36Sopenharmony_ci		case SOF_EXT_MAN_CAVS_CONFIG_OUTBOX_SIZE:
64562306a36Sopenharmony_ci		case SOF_EXT_MAN_CAVS_CONFIG_INBOX_SIZE:
64662306a36Sopenharmony_ci			/* These elements are defined but not being used yet. No warn is required */
64762306a36Sopenharmony_ci			break;
64862306a36Sopenharmony_ci		default:
64962306a36Sopenharmony_ci			dev_info(sdev->dev, "unsupported token type: %d\n",
65062306a36Sopenharmony_ci				 config_data->elems[i].token);
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return 0;
65462306a36Sopenharmony_ci}
655