18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * fireworks_command.c - a part of driver for Fireworks based devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "./fireworks.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * This driver uses transaction version 1 or later to use extended hardware
128c2ecf20Sopenharmony_ci * information. Then too old devices are not available.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Each commands are not required to have continuous sequence numbers. This
158c2ecf20Sopenharmony_ci * number is just used to match command and response.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * This module support a part of commands. Please see FFADO if you want to see
188c2ecf20Sopenharmony_ci * whole commands. But there are some commands which FFADO don't implement.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Fireworks also supports AV/C general commands and AV/C Stream Format
218c2ecf20Sopenharmony_ci * Information commands. But this module don't use them.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define KERNEL_SEQNUM_MIN	(SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
258c2ecf20Sopenharmony_ci#define KERNEL_SEQNUM_MAX	((u32)~0)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* for clock source and sampling rate */
288c2ecf20Sopenharmony_cistruct efc_clock {
298c2ecf20Sopenharmony_ci	u32 source;
308c2ecf20Sopenharmony_ci	u32 sampling_rate;
318c2ecf20Sopenharmony_ci	u32 index;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* command categories */
358c2ecf20Sopenharmony_cienum efc_category {
368c2ecf20Sopenharmony_ci	EFC_CAT_HWINFO		= 0,
378c2ecf20Sopenharmony_ci	EFC_CAT_TRANSPORT	= 2,
388c2ecf20Sopenharmony_ci	EFC_CAT_HWCTL		= 3,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* hardware info category commands */
428c2ecf20Sopenharmony_cienum efc_cmd_hwinfo {
438c2ecf20Sopenharmony_ci	EFC_CMD_HWINFO_GET_CAPS		= 0,
448c2ecf20Sopenharmony_ci	EFC_CMD_HWINFO_GET_POLLED	= 1,
458c2ecf20Sopenharmony_ci	EFC_CMD_HWINFO_SET_RESP_ADDR	= 2
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cienum efc_cmd_transport {
498c2ecf20Sopenharmony_ci	EFC_CMD_TRANSPORT_SET_TX_MODE	= 0
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* hardware control category commands */
538c2ecf20Sopenharmony_cienum efc_cmd_hwctl {
548c2ecf20Sopenharmony_ci	EFC_CMD_HWCTL_SET_CLOCK		= 0,
558c2ecf20Sopenharmony_ci	EFC_CMD_HWCTL_GET_CLOCK		= 1,
568c2ecf20Sopenharmony_ci	EFC_CMD_HWCTL_IDENTIFY		= 5
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* return values in response */
608c2ecf20Sopenharmony_cienum efr_status {
618c2ecf20Sopenharmony_ci	EFR_STATUS_OK			= 0,
628c2ecf20Sopenharmony_ci	EFR_STATUS_BAD			= 1,
638c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_COMMAND		= 2,
648c2ecf20Sopenharmony_ci	EFR_STATUS_COMM_ERR		= 3,
658c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_QUAD_COUNT	= 4,
668c2ecf20Sopenharmony_ci	EFR_STATUS_UNSUPPORTED		= 5,
678c2ecf20Sopenharmony_ci	EFR_STATUS_1394_TIMEOUT		= 6,
688c2ecf20Sopenharmony_ci	EFR_STATUS_DSP_TIMEOUT		= 7,
698c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_RATE		= 8,
708c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_CLOCK		= 9,
718c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_CHANNEL		= 10,
728c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_PAN		= 11,
738c2ecf20Sopenharmony_ci	EFR_STATUS_FLASH_BUSY		= 12,
748c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_MIRROR		= 13,
758c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_LED		= 14,
768c2ecf20Sopenharmony_ci	EFR_STATUS_BAD_PARAMETER	= 15,
778c2ecf20Sopenharmony_ci	EFR_STATUS_INCOMPLETE		= 0x80000000
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const char *const efr_status_names[] = {
818c2ecf20Sopenharmony_ci	[EFR_STATUS_OK]			= "OK",
828c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD]		= "bad",
838c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_COMMAND]	= "bad command",
848c2ecf20Sopenharmony_ci	[EFR_STATUS_COMM_ERR]		= "comm err",
858c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_QUAD_COUNT]	= "bad quad count",
868c2ecf20Sopenharmony_ci	[EFR_STATUS_UNSUPPORTED]	= "unsupported",
878c2ecf20Sopenharmony_ci	[EFR_STATUS_1394_TIMEOUT]	= "1394 timeout",
888c2ecf20Sopenharmony_ci	[EFR_STATUS_DSP_TIMEOUT]	= "DSP timeout",
898c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_RATE]		= "bad rate",
908c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_CLOCK]		= "bad clock",
918c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_CHANNEL]	= "bad channel",
928c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_PAN]		= "bad pan",
938c2ecf20Sopenharmony_ci	[EFR_STATUS_FLASH_BUSY]		= "flash busy",
948c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_MIRROR]		= "bad mirror",
958c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_LED]		= "bad LED",
968c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_PARAMETER]	= "bad parameter",
978c2ecf20Sopenharmony_ci	[EFR_STATUS_BAD_PARAMETER + 1]	= "incomplete"
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int
1018c2ecf20Sopenharmony_ciefw_transaction(struct snd_efw *efw, unsigned int category,
1028c2ecf20Sopenharmony_ci		unsigned int command,
1038c2ecf20Sopenharmony_ci		const __be32 *params, unsigned int param_bytes,
1048c2ecf20Sopenharmony_ci		const __be32 *resp, unsigned int resp_bytes)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct snd_efw_transaction *header;
1078c2ecf20Sopenharmony_ci	__be32 *buf;
1088c2ecf20Sopenharmony_ci	u32 seqnum;
1098c2ecf20Sopenharmony_ci	unsigned int buf_bytes, cmd_bytes;
1108c2ecf20Sopenharmony_ci	int err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* calculate buffer size*/
1138c2ecf20Sopenharmony_ci	buf_bytes = sizeof(struct snd_efw_transaction) +
1148c2ecf20Sopenharmony_ci		    max(param_bytes, resp_bytes);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* keep buffer */
1178c2ecf20Sopenharmony_ci	buf = kzalloc(buf_bytes, GFP_KERNEL);
1188c2ecf20Sopenharmony_ci	if (buf == NULL)
1198c2ecf20Sopenharmony_ci		return -ENOMEM;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* to keep consistency of sequence number */
1228c2ecf20Sopenharmony_ci	spin_lock(&efw->lock);
1238c2ecf20Sopenharmony_ci	if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
1248c2ecf20Sopenharmony_ci	    (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
1258c2ecf20Sopenharmony_ci		efw->seqnum = KERNEL_SEQNUM_MIN;
1268c2ecf20Sopenharmony_ci	else
1278c2ecf20Sopenharmony_ci		efw->seqnum += 2;
1288c2ecf20Sopenharmony_ci	seqnum = efw->seqnum;
1298c2ecf20Sopenharmony_ci	spin_unlock(&efw->lock);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* fill transaction header fields */
1328c2ecf20Sopenharmony_ci	cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
1338c2ecf20Sopenharmony_ci	header = (struct snd_efw_transaction *)buf;
1348c2ecf20Sopenharmony_ci	header->length	 = cpu_to_be32(cmd_bytes / sizeof(__be32));
1358c2ecf20Sopenharmony_ci	header->version	 = cpu_to_be32(1);
1368c2ecf20Sopenharmony_ci	header->seqnum	 = cpu_to_be32(seqnum);
1378c2ecf20Sopenharmony_ci	header->category = cpu_to_be32(category);
1388c2ecf20Sopenharmony_ci	header->command	 = cpu_to_be32(command);
1398c2ecf20Sopenharmony_ci	header->status	 = 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* fill transaction command parameters */
1428c2ecf20Sopenharmony_ci	memcpy(header->params, params, param_bytes);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
1458c2ecf20Sopenharmony_ci				      buf, buf_bytes);
1468c2ecf20Sopenharmony_ci	if (err < 0)
1478c2ecf20Sopenharmony_ci		goto end;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* check transaction header fields */
1508c2ecf20Sopenharmony_ci	if ((be32_to_cpu(header->version) < 1) ||
1518c2ecf20Sopenharmony_ci	    (be32_to_cpu(header->category) != category) ||
1528c2ecf20Sopenharmony_ci	    (be32_to_cpu(header->command) != command) ||
1538c2ecf20Sopenharmony_ci	    (be32_to_cpu(header->status) != EFR_STATUS_OK)) {
1548c2ecf20Sopenharmony_ci		dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
1558c2ecf20Sopenharmony_ci			be32_to_cpu(header->category),
1568c2ecf20Sopenharmony_ci			be32_to_cpu(header->command),
1578c2ecf20Sopenharmony_ci			efr_status_names[be32_to_cpu(header->status)]);
1588c2ecf20Sopenharmony_ci		err = -EIO;
1598c2ecf20Sopenharmony_ci		goto end;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (resp == NULL)
1638c2ecf20Sopenharmony_ci		goto end;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* fill transaction response parameters */
1668c2ecf20Sopenharmony_ci	memset((void *)resp, 0, resp_bytes);
1678c2ecf20Sopenharmony_ci	resp_bytes = min_t(unsigned int, resp_bytes,
1688c2ecf20Sopenharmony_ci			   be32_to_cpu(header->length) * sizeof(__be32) -
1698c2ecf20Sopenharmony_ci				sizeof(struct snd_efw_transaction));
1708c2ecf20Sopenharmony_ci	memcpy((void *)resp, &buf[6], resp_bytes);
1718c2ecf20Sopenharmony_ciend:
1728c2ecf20Sopenharmony_ci	kfree(buf);
1738c2ecf20Sopenharmony_ci	return err;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/*
1778c2ecf20Sopenharmony_ci * The address in host system for transaction response is changable when the
1788c2ecf20Sopenharmony_ci * device supports. struct hwinfo.flags includes its flag. The default is
1798c2ecf20Sopenharmony_ci * MEMORY_SPACE_EFW_RESPONSE.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_ciint snd_efw_command_set_resp_addr(struct snd_efw *efw,
1828c2ecf20Sopenharmony_ci				  u16 addr_high, u32 addr_low)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	__be32 addr[2];
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	addr[0] = cpu_to_be32(addr_high);
1878c2ecf20Sopenharmony_ci	addr[1] = cpu_to_be32(addr_low);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (!efw->resp_addr_changable)
1908c2ecf20Sopenharmony_ci		return -ENOSYS;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return efw_transaction(efw, EFC_CAT_HWCTL,
1938c2ecf20Sopenharmony_ci			       EFC_CMD_HWINFO_SET_RESP_ADDR,
1948c2ecf20Sopenharmony_ci			       addr, sizeof(addr), NULL, 0);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * This is for timestamp processing. In Windows mode, all 32bit fields of second
1998c2ecf20Sopenharmony_ci * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
2008c2ecf20Sopenharmony_ci * 'no data' packet the value of this field is 0x90ffffff.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_ciint snd_efw_command_set_tx_mode(struct snd_efw *efw,
2038c2ecf20Sopenharmony_ci				enum snd_efw_transport_mode mode)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	__be32 param = cpu_to_be32(mode);
2068c2ecf20Sopenharmony_ci	return efw_transaction(efw, EFC_CAT_TRANSPORT,
2078c2ecf20Sopenharmony_ci			       EFC_CMD_TRANSPORT_SET_TX_MODE,
2088c2ecf20Sopenharmony_ci			       &param, sizeof(param), NULL, 0);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ciint snd_efw_command_get_hwinfo(struct snd_efw *efw,
2128c2ecf20Sopenharmony_ci			       struct snd_efw_hwinfo *hwinfo)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int err;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	err  = efw_transaction(efw, EFC_CAT_HWINFO,
2178c2ecf20Sopenharmony_ci			       EFC_CMD_HWINFO_GET_CAPS,
2188c2ecf20Sopenharmony_ci			       NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
2198c2ecf20Sopenharmony_ci	if (err < 0)
2208c2ecf20Sopenharmony_ci		goto end;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->flags);
2238c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->guid_hi);
2248c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->guid_lo);
2258c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->type);
2268c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->version);
2278c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->supported_clocks);
2288c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
2298c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
2308c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->phys_out);
2318c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->phys_in);
2328c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->phys_out_grp_count);
2338c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->phys_in_grp_count);
2348c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->midi_out_ports);
2358c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->midi_in_ports);
2368c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->max_sample_rate);
2378c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->min_sample_rate);
2388c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->dsp_version);
2398c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->arm_version);
2408c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->mixer_playback_channels);
2418c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->mixer_capture_channels);
2428c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->fpga_version);
2438c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
2448c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
2458c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
2468c2ecf20Sopenharmony_ci	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* ensure terminated */
2498c2ecf20Sopenharmony_ci	hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
2508c2ecf20Sopenharmony_ci	hwinfo->model_name[HWINFO_NAME_SIZE_BYTES  - 1] = '\0';
2518c2ecf20Sopenharmony_ciend:
2528c2ecf20Sopenharmony_ci	return err;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciint snd_efw_command_get_phys_meters(struct snd_efw *efw,
2568c2ecf20Sopenharmony_ci				    struct snd_efw_phys_meters *meters,
2578c2ecf20Sopenharmony_ci				    unsigned int len)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	u32 *buf = (u32 *)meters;
2608c2ecf20Sopenharmony_ci	unsigned int i;
2618c2ecf20Sopenharmony_ci	int err;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	err = efw_transaction(efw, EFC_CAT_HWINFO,
2648c2ecf20Sopenharmony_ci			      EFC_CMD_HWINFO_GET_POLLED,
2658c2ecf20Sopenharmony_ci			      NULL, 0, (__be32 *)meters, len);
2668c2ecf20Sopenharmony_ci	if (err >= 0)
2678c2ecf20Sopenharmony_ci		for (i = 0; i < len / sizeof(u32); i++)
2688c2ecf20Sopenharmony_ci			be32_to_cpus(&buf[i]);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return err;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int
2748c2ecf20Sopenharmony_cicommand_get_clock(struct snd_efw *efw, struct efc_clock *clock)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int err;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	err = efw_transaction(efw, EFC_CAT_HWCTL,
2798c2ecf20Sopenharmony_ci			      EFC_CMD_HWCTL_GET_CLOCK,
2808c2ecf20Sopenharmony_ci			      NULL, 0,
2818c2ecf20Sopenharmony_ci			      (__be32 *)clock, sizeof(struct efc_clock));
2828c2ecf20Sopenharmony_ci	if (err >= 0) {
2838c2ecf20Sopenharmony_ci		be32_to_cpus(&clock->source);
2848c2ecf20Sopenharmony_ci		be32_to_cpus(&clock->sampling_rate);
2858c2ecf20Sopenharmony_ci		be32_to_cpus(&clock->index);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return err;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/* give UINT_MAX if set nothing */
2928c2ecf20Sopenharmony_cistatic int
2938c2ecf20Sopenharmony_cicommand_set_clock(struct snd_efw *efw,
2948c2ecf20Sopenharmony_ci		  unsigned int source, unsigned int rate)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct efc_clock clock = {0};
2978c2ecf20Sopenharmony_ci	int err;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* check arguments */
3008c2ecf20Sopenharmony_ci	if ((source == UINT_MAX) && (rate == UINT_MAX)) {
3018c2ecf20Sopenharmony_ci		err = -EINVAL;
3028c2ecf20Sopenharmony_ci		goto end;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* get current status */
3068c2ecf20Sopenharmony_ci	err = command_get_clock(efw, &clock);
3078c2ecf20Sopenharmony_ci	if (err < 0)
3088c2ecf20Sopenharmony_ci		goto end;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* no need */
3118c2ecf20Sopenharmony_ci	if ((clock.source == source) && (clock.sampling_rate == rate))
3128c2ecf20Sopenharmony_ci		goto end;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* set params */
3158c2ecf20Sopenharmony_ci	if ((source != UINT_MAX) && (clock.source != source))
3168c2ecf20Sopenharmony_ci		clock.source = source;
3178c2ecf20Sopenharmony_ci	if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
3188c2ecf20Sopenharmony_ci		clock.sampling_rate = rate;
3198c2ecf20Sopenharmony_ci	clock.index = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	cpu_to_be32s(&clock.source);
3228c2ecf20Sopenharmony_ci	cpu_to_be32s(&clock.sampling_rate);
3238c2ecf20Sopenharmony_ci	cpu_to_be32s(&clock.index);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	err = efw_transaction(efw, EFC_CAT_HWCTL,
3268c2ecf20Sopenharmony_ci			      EFC_CMD_HWCTL_SET_CLOCK,
3278c2ecf20Sopenharmony_ci			      (__be32 *)&clock, sizeof(struct efc_clock),
3288c2ecf20Sopenharmony_ci			      NULL, 0);
3298c2ecf20Sopenharmony_ci	if (err < 0)
3308c2ecf20Sopenharmony_ci		goto end;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/*
3338c2ecf20Sopenharmony_ci	 * With firmware version 5.8, just after changing clock state, these
3348c2ecf20Sopenharmony_ci	 * parameters are not immediately retrieved by get command. In my
3358c2ecf20Sopenharmony_ci	 * trial, there needs to be 100msec to get changed parameters.
3368c2ecf20Sopenharmony_ci	 */
3378c2ecf20Sopenharmony_ci	msleep(150);
3388c2ecf20Sopenharmony_ciend:
3398c2ecf20Sopenharmony_ci	return err;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ciint snd_efw_command_get_clock_source(struct snd_efw *efw,
3438c2ecf20Sopenharmony_ci				     enum snd_efw_clock_source *source)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	int err;
3468c2ecf20Sopenharmony_ci	struct efc_clock clock = {0};
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	err = command_get_clock(efw, &clock);
3498c2ecf20Sopenharmony_ci	if (err >= 0)
3508c2ecf20Sopenharmony_ci		*source = clock.source;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return err;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciint snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	int err;
3588c2ecf20Sopenharmony_ci	struct efc_clock clock = {0};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	err = command_get_clock(efw, &clock);
3618c2ecf20Sopenharmony_ci	if (err >= 0)
3628c2ecf20Sopenharmony_ci		*rate = clock.sampling_rate;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return err;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ciint snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	return command_set_clock(efw, UINT_MAX, rate);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
372