18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Asihpi soundcard
48c2ecf20Sopenharmony_ci *  Copyright (c) by AudioScience Inc <support@audioscience.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  The following is not a condition of use, merely a request:
78c2ecf20Sopenharmony_ci *  If you modify this program, particularly if you fix errors, AudioScience Inc
88c2ecf20Sopenharmony_ci *  would appreciate it if you grant us the right to use those modifications
98c2ecf20Sopenharmony_ci *  for any purpose including commercial applications.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "hpi_internal.h"
138c2ecf20Sopenharmony_ci#include "hpi_version.h"
148c2ecf20Sopenharmony_ci#include "hpimsginit.h"
158c2ecf20Sopenharmony_ci#include "hpioctl.h"
168c2ecf20Sopenharmony_ci#include "hpicmn.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/time.h>
238c2ecf20Sopenharmony_ci#include <linux/wait.h>
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <sound/core.h>
268c2ecf20Sopenharmony_ci#include <sound/control.h>
278c2ecf20Sopenharmony_ci#include <sound/pcm.h>
288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
298c2ecf20Sopenharmony_ci#include <sound/info.h>
308c2ecf20Sopenharmony_ci#include <sound/initval.h>
318c2ecf20Sopenharmony_ci#include <sound/tlv.h>
328c2ecf20Sopenharmony_ci#include <sound/hwdep.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
358c2ecf20Sopenharmony_ciMODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx "
378c2ecf20Sopenharmony_ci			HPI_VER_STRING);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#if defined CONFIG_SND_DEBUG_VERBOSE
408c2ecf20Sopenharmony_ci/**
418c2ecf20Sopenharmony_ci * snd_printddd - very verbose debug printk
428c2ecf20Sopenharmony_ci * @format: format string
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Works like snd_printk() for debugging purposes.
458c2ecf20Sopenharmony_ci * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
468c2ecf20Sopenharmony_ci * Must set snd module debug parameter to 3 to enable at runtime.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci#define snd_printddd(format, args...) \
498c2ecf20Sopenharmony_ci	__snd_printk(3, __FILE__, __LINE__, format, ##args)
508c2ecf20Sopenharmony_ci#else
518c2ecf20Sopenharmony_ci#define snd_printddd(format, args...) do { } while (0)
528c2ecf20Sopenharmony_ci#endif
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* index 0-MAX */
558c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
568c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
578c2ecf20Sopenharmony_cistatic bool enable_hpi_hwdep = 1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cimodule_param(enable_hpi_hwdep, bool, 0644);
698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_hpi_hwdep,
708c2ecf20Sopenharmony_ci		"ALSA enable HPI hwdep for AudioScience soundcard ");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* identify driver */
738c2ecf20Sopenharmony_ci#ifdef KERNEL_ALSA_BUILD
748c2ecf20Sopenharmony_cistatic char *build_info = "Built using headers from kernel source";
758c2ecf20Sopenharmony_cimodule_param(build_info, charp, 0444);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built using headers from kernel source");
778c2ecf20Sopenharmony_ci#else
788c2ecf20Sopenharmony_cistatic char *build_info = "Built within ALSA source";
798c2ecf20Sopenharmony_cimodule_param(build_info, charp, 0444);
808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built within ALSA source");
818c2ecf20Sopenharmony_ci#endif
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* set to 1 to dump every control from adapter to log */
848c2ecf20Sopenharmony_cistatic const int mixer_dump;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define DEFAULT_SAMPLERATE 44100
878c2ecf20Sopenharmony_cistatic int adapter_fs = DEFAULT_SAMPLERATE;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* defaults */
908c2ecf20Sopenharmony_ci#define PERIODS_MIN 2
918c2ecf20Sopenharmony_ci#define PERIOD_BYTES_MIN  2048
928c2ecf20Sopenharmony_ci#define BUFFER_BYTES_MAX (512 * 1024)
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct clk_source {
978c2ecf20Sopenharmony_ci	int source;
988c2ecf20Sopenharmony_ci	int index;
998c2ecf20Sopenharmony_ci	const char *name;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct clk_cache {
1038c2ecf20Sopenharmony_ci	int count;
1048c2ecf20Sopenharmony_ci	int has_local;
1058c2ecf20Sopenharmony_ci	struct clk_source s[MAX_CLOCKSOURCES];
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* Per card data */
1098c2ecf20Sopenharmony_cistruct snd_card_asihpi {
1108c2ecf20Sopenharmony_ci	struct snd_card *card;
1118c2ecf20Sopenharmony_ci	struct pci_dev *pci;
1128c2ecf20Sopenharmony_ci	struct hpi_adapter *hpi;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* In low latency mode there is only one stream, a pointer to its
1158c2ecf20Sopenharmony_ci	 * private data is stored here on trigger and cleared on stop.
1168c2ecf20Sopenharmony_ci	 * The interrupt handler uses it as a parameter when calling
1178c2ecf20Sopenharmony_ci	 * snd_card_asihpi_timer_function().
1188c2ecf20Sopenharmony_ci	 */
1198c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *llmode_streampriv;
1208c2ecf20Sopenharmony_ci	void (*pcm_start)(struct snd_pcm_substream *substream);
1218c2ecf20Sopenharmony_ci	void (*pcm_stop)(struct snd_pcm_substream *substream);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	u32 h_mixer;
1248c2ecf20Sopenharmony_ci	struct clk_cache cc;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	u16 can_dma;
1278c2ecf20Sopenharmony_ci	u16 support_grouping;
1288c2ecf20Sopenharmony_ci	u16 support_mrx;
1298c2ecf20Sopenharmony_ci	u16 update_interval_frames;
1308c2ecf20Sopenharmony_ci	u16 in_max_chans;
1318c2ecf20Sopenharmony_ci	u16 out_max_chans;
1328c2ecf20Sopenharmony_ci	u16 in_min_chans;
1338c2ecf20Sopenharmony_ci	u16 out_min_chans;
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* Per stream data */
1378c2ecf20Sopenharmony_cistruct snd_card_asihpi_pcm {
1388c2ecf20Sopenharmony_ci	struct timer_list timer;
1398c2ecf20Sopenharmony_ci	unsigned int respawn_timer;
1408c2ecf20Sopenharmony_ci	unsigned int hpi_buffer_attached;
1418c2ecf20Sopenharmony_ci	unsigned int buffer_bytes;
1428c2ecf20Sopenharmony_ci	unsigned int period_bytes;
1438c2ecf20Sopenharmony_ci	unsigned int bytes_per_sec;
1448c2ecf20Sopenharmony_ci	unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
1458c2ecf20Sopenharmony_ci	unsigned int pcm_buf_dma_ofs;	/* DMA R/W offset in buffer */
1468c2ecf20Sopenharmony_ci	unsigned int pcm_buf_elapsed_dma_ofs;	/* DMA R/W offset in buffer */
1478c2ecf20Sopenharmony_ci	unsigned int drained_count;
1488c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
1498c2ecf20Sopenharmony_ci	u32 h_stream;
1508c2ecf20Sopenharmony_ci	struct hpi_format format;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* universal stream verbs work with out or in stream handles */
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Functions to allow driver to give a buffer to HPI for busmastering */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic u16 hpi_stream_host_buffer_attach(
1588c2ecf20Sopenharmony_ci	u32 h_stream,   /* handle to outstream. */
1598c2ecf20Sopenharmony_ci	u32 size_in_bytes, /* size in bytes of bus mastering buffer */
1608c2ecf20Sopenharmony_ci	u32 pci_address
1618c2ecf20Sopenharmony_ci)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct hpi_message hm;
1648c2ecf20Sopenharmony_ci	struct hpi_response hr;
1658c2ecf20Sopenharmony_ci	unsigned int obj = hpi_handle_object(h_stream);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (!h_stream)
1688c2ecf20Sopenharmony_ci		return HPI_ERROR_INVALID_OBJ;
1698c2ecf20Sopenharmony_ci	hpi_init_message_response(&hm, &hr, obj,
1708c2ecf20Sopenharmony_ci			obj == HPI_OBJ_OSTREAM ?
1718c2ecf20Sopenharmony_ci				HPI_OSTREAM_HOSTBUFFER_ALLOC :
1728c2ecf20Sopenharmony_ci				HPI_ISTREAM_HOSTBUFFER_ALLOC);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	hpi_handle_to_indexes(h_stream, &hm.adapter_index,
1758c2ecf20Sopenharmony_ci				&hm.obj_index);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	hm.u.d.u.buffer.buffer_size = size_in_bytes;
1788c2ecf20Sopenharmony_ci	hm.u.d.u.buffer.pci_address = pci_address;
1798c2ecf20Sopenharmony_ci	hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER;
1808c2ecf20Sopenharmony_ci	hpi_send_recv(&hm, &hr);
1818c2ecf20Sopenharmony_ci	return hr.error;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic u16 hpi_stream_host_buffer_detach(u32  h_stream)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct hpi_message hm;
1878c2ecf20Sopenharmony_ci	struct hpi_response hr;
1888c2ecf20Sopenharmony_ci	unsigned int obj = hpi_handle_object(h_stream);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!h_stream)
1918c2ecf20Sopenharmony_ci		return HPI_ERROR_INVALID_OBJ;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	hpi_init_message_response(&hm, &hr,  obj,
1948c2ecf20Sopenharmony_ci			obj == HPI_OBJ_OSTREAM ?
1958c2ecf20Sopenharmony_ci				HPI_OSTREAM_HOSTBUFFER_FREE :
1968c2ecf20Sopenharmony_ci				HPI_ISTREAM_HOSTBUFFER_FREE);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	hpi_handle_to_indexes(h_stream, &hm.adapter_index,
1998c2ecf20Sopenharmony_ci				&hm.obj_index);
2008c2ecf20Sopenharmony_ci	hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER;
2018c2ecf20Sopenharmony_ci	hpi_send_recv(&hm, &hr);
2028c2ecf20Sopenharmony_ci	return hr.error;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_start(u32 h_stream)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
2088c2ecf20Sopenharmony_ci		return hpi_outstream_start(h_stream);
2098c2ecf20Sopenharmony_ci	else
2108c2ecf20Sopenharmony_ci		return hpi_instream_start(h_stream);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_stop(u32 h_stream)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
2168c2ecf20Sopenharmony_ci		return hpi_outstream_stop(h_stream);
2178c2ecf20Sopenharmony_ci	else
2188c2ecf20Sopenharmony_ci		return hpi_instream_stop(h_stream);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_get_info_ex(
2228c2ecf20Sopenharmony_ci    u32 h_stream,
2238c2ecf20Sopenharmony_ci    u16        *pw_state,
2248c2ecf20Sopenharmony_ci    u32        *pbuffer_size,
2258c2ecf20Sopenharmony_ci    u32        *pdata_in_buffer,
2268c2ecf20Sopenharmony_ci    u32        *psample_count,
2278c2ecf20Sopenharmony_ci    u32        *pauxiliary_data
2288c2ecf20Sopenharmony_ci)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u16 e;
2318c2ecf20Sopenharmony_ci	if (hpi_handle_object(h_stream)  ==  HPI_OBJ_OSTREAM)
2328c2ecf20Sopenharmony_ci		e = hpi_outstream_get_info_ex(h_stream, pw_state,
2338c2ecf20Sopenharmony_ci					pbuffer_size, pdata_in_buffer,
2348c2ecf20Sopenharmony_ci					psample_count, pauxiliary_data);
2358c2ecf20Sopenharmony_ci	else
2368c2ecf20Sopenharmony_ci		e = hpi_instream_get_info_ex(h_stream, pw_state,
2378c2ecf20Sopenharmony_ci					pbuffer_size, pdata_in_buffer,
2388c2ecf20Sopenharmony_ci					psample_count, pauxiliary_data);
2398c2ecf20Sopenharmony_ci	return e;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_group_add(
2438c2ecf20Sopenharmony_ci					u32 h_master,
2448c2ecf20Sopenharmony_ci					u32 h_stream)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (hpi_handle_object(h_master) ==  HPI_OBJ_OSTREAM)
2478c2ecf20Sopenharmony_ci		return hpi_outstream_group_add(h_master, h_stream);
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		return hpi_instream_group_add(h_master, h_stream);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_group_reset(u32 h_stream)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
2558c2ecf20Sopenharmony_ci		return hpi_outstream_group_reset(h_stream);
2568c2ecf20Sopenharmony_ci	else
2578c2ecf20Sopenharmony_ci		return hpi_instream_group_reset(h_stream);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic u16 handle_error(u16 err, int line, char *filename)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	if (err)
2638c2ecf20Sopenharmony_ci		printk(KERN_WARNING
2648c2ecf20Sopenharmony_ci			"in file %s, line %d: HPI error %d\n",
2658c2ecf20Sopenharmony_ci			filename, line, err);
2668c2ecf20Sopenharmony_ci	return err;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci#define hpi_handle_error(x)  handle_error(x, __LINE__, __FILE__)
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/***************************** GENERAL PCM ****************/
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void print_hwparams(struct snd_pcm_substream *substream,
2748c2ecf20Sopenharmony_ci				struct snd_pcm_hw_params *p)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	char name[16];
2778c2ecf20Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
2788c2ecf20Sopenharmony_ci	snd_printdd("%s HWPARAMS\n", name);
2798c2ecf20Sopenharmony_ci	snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n",
2808c2ecf20Sopenharmony_ci		params_rate(p), params_channels(p),
2818c2ecf20Sopenharmony_ci		params_format(p), params_subformat(p));
2828c2ecf20Sopenharmony_ci	snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n",
2838c2ecf20Sopenharmony_ci		params_buffer_bytes(p), params_period_bytes(p),
2848c2ecf20Sopenharmony_ci		params_period_size(p), params_periods(p));
2858c2ecf20Sopenharmony_ci	snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n",
2868c2ecf20Sopenharmony_ci		params_buffer_size(p), params_access(p),
2878c2ecf20Sopenharmony_ci		params_rate(p) * params_channels(p) *
2888c2ecf20Sopenharmony_ci		snd_pcm_format_width(params_format(p)) / 8);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci#define INVALID_FORMAT	(__force snd_pcm_format_t)(-1)
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic const snd_pcm_format_t hpi_to_alsa_formats[] = {
2948c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* INVALID */
2958c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_U8,	/* HPI_FORMAT_PCM8_UNSIGNED        1 */
2968c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_S16,	/* HPI_FORMAT_PCM16_SIGNED         2 */
2978c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_MPEG_L1              3 */
2988c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L2              4 */
2998c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L3              5 */
3008c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_DOLBY_AC2            6 */
3018c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_DOLBY_AC3            7 */
3028c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN      8 */
3038c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_HITS       9 */
3048c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_INSERTS   10 */
3058c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_S32,	/* HPI_FORMAT_PCM32_SIGNED        11 */
3068c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_RAW_BITSTREAM       12 */
3078c2ecf20Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_HITS_EX1  13 */
3088c2ecf20Sopenharmony_ci	SNDRV_PCM_FORMAT_FLOAT,	/* HPI_FORMAT_PCM32_FLOAT         14 */
3098c2ecf20Sopenharmony_ci#if 1
3108c2ecf20Sopenharmony_ci	/* ALSA can't handle 3 byte sample size together with power-of-2
3118c2ecf20Sopenharmony_ci	 *  constraint on buffer_bytes, so disable this format
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	INVALID_FORMAT
3148c2ecf20Sopenharmony_ci#else
3158c2ecf20Sopenharmony_ci	/* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */
3168c2ecf20Sopenharmony_ci#endif
3178c2ecf20Sopenharmony_ci};
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
3218c2ecf20Sopenharmony_ci					   u16 *hpi_format)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	u16 format;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
3268c2ecf20Sopenharmony_ci	     format <= HPI_FORMAT_PCM24_SIGNED; format++) {
3278c2ecf20Sopenharmony_ci		if (hpi_to_alsa_formats[format] == alsa_format) {
3288c2ecf20Sopenharmony_ci			*hpi_format = format;
3298c2ecf20Sopenharmony_ci			return 0;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	snd_printd(KERN_WARNING "failed match for alsa format %d\n",
3348c2ecf20Sopenharmony_ci		   alsa_format);
3358c2ecf20Sopenharmony_ci	*hpi_format = 0;
3368c2ecf20Sopenharmony_ci	return -EINVAL;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
3408c2ecf20Sopenharmony_ci					 struct snd_pcm_hardware *pcmhw)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	u16 err;
3438c2ecf20Sopenharmony_ci	u32 h_control;
3448c2ecf20Sopenharmony_ci	u32 sample_rate;
3458c2ecf20Sopenharmony_ci	int idx;
3468c2ecf20Sopenharmony_ci	unsigned int rate_min = 200000;
3478c2ecf20Sopenharmony_ci	unsigned int rate_max = 0;
3488c2ecf20Sopenharmony_ci	unsigned int rates = 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (asihpi->support_mrx) {
3518c2ecf20Sopenharmony_ci		rates |= SNDRV_PCM_RATE_CONTINUOUS;
3528c2ecf20Sopenharmony_ci		rates |= SNDRV_PCM_RATE_8000_96000;
3538c2ecf20Sopenharmony_ci		rate_min = 8000;
3548c2ecf20Sopenharmony_ci		rate_max = 100000;
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		/* on cards without SRC,
3578c2ecf20Sopenharmony_ci		   valid rates are determined by sampleclock */
3588c2ecf20Sopenharmony_ci		err = hpi_mixer_get_control(asihpi->h_mixer,
3598c2ecf20Sopenharmony_ci					  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
3608c2ecf20Sopenharmony_ci					  HPI_CONTROL_SAMPLECLOCK, &h_control);
3618c2ecf20Sopenharmony_ci		if (err) {
3628c2ecf20Sopenharmony_ci			dev_err(&asihpi->pci->dev,
3638c2ecf20Sopenharmony_ci				"No local sampleclock, err %d\n", err);
3648c2ecf20Sopenharmony_ci		}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		for (idx = -1; idx < 100; idx++) {
3678c2ecf20Sopenharmony_ci			if (idx == -1) {
3688c2ecf20Sopenharmony_ci				if (hpi_sample_clock_get_sample_rate(h_control,
3698c2ecf20Sopenharmony_ci								&sample_rate))
3708c2ecf20Sopenharmony_ci					continue;
3718c2ecf20Sopenharmony_ci			} else if (hpi_sample_clock_query_local_rate(h_control,
3728c2ecf20Sopenharmony_ci							idx, &sample_rate)) {
3738c2ecf20Sopenharmony_ci				break;
3748c2ecf20Sopenharmony_ci			}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci			rate_min = min(rate_min, sample_rate);
3778c2ecf20Sopenharmony_ci			rate_max = max(rate_max, sample_rate);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci			switch (sample_rate) {
3808c2ecf20Sopenharmony_ci			case 5512:
3818c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_5512;
3828c2ecf20Sopenharmony_ci				break;
3838c2ecf20Sopenharmony_ci			case 8000:
3848c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_8000;
3858c2ecf20Sopenharmony_ci				break;
3868c2ecf20Sopenharmony_ci			case 11025:
3878c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_11025;
3888c2ecf20Sopenharmony_ci				break;
3898c2ecf20Sopenharmony_ci			case 16000:
3908c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_16000;
3918c2ecf20Sopenharmony_ci				break;
3928c2ecf20Sopenharmony_ci			case 22050:
3938c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_22050;
3948c2ecf20Sopenharmony_ci				break;
3958c2ecf20Sopenharmony_ci			case 32000:
3968c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_32000;
3978c2ecf20Sopenharmony_ci				break;
3988c2ecf20Sopenharmony_ci			case 44100:
3998c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_44100;
4008c2ecf20Sopenharmony_ci				break;
4018c2ecf20Sopenharmony_ci			case 48000:
4028c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_48000;
4038c2ecf20Sopenharmony_ci				break;
4048c2ecf20Sopenharmony_ci			case 64000:
4058c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_64000;
4068c2ecf20Sopenharmony_ci				break;
4078c2ecf20Sopenharmony_ci			case 88200:
4088c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_88200;
4098c2ecf20Sopenharmony_ci				break;
4108c2ecf20Sopenharmony_ci			case 96000:
4118c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_96000;
4128c2ecf20Sopenharmony_ci				break;
4138c2ecf20Sopenharmony_ci			case 176400:
4148c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_176400;
4158c2ecf20Sopenharmony_ci				break;
4168c2ecf20Sopenharmony_ci			case 192000:
4178c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_192000;
4188c2ecf20Sopenharmony_ci				break;
4198c2ecf20Sopenharmony_ci			default: /* some other rate */
4208c2ecf20Sopenharmony_ci				rates |= SNDRV_PCM_RATE_KNOT;
4218c2ecf20Sopenharmony_ci			}
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	pcmhw->rates = rates;
4268c2ecf20Sopenharmony_ci	pcmhw->rate_min = rate_min;
4278c2ecf20Sopenharmony_ci	pcmhw->rate_max = rate_max;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
4318c2ecf20Sopenharmony_ci					 struct snd_pcm_hw_params *params)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4348c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
4358c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
4368c2ecf20Sopenharmony_ci	int err;
4378c2ecf20Sopenharmony_ci	u16 format;
4388c2ecf20Sopenharmony_ci	int width;
4398c2ecf20Sopenharmony_ci	unsigned int bytes_per_sec;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	print_hwparams(substream, params);
4428c2ecf20Sopenharmony_ci	err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
4438c2ecf20Sopenharmony_ci	if (err)
4448c2ecf20Sopenharmony_ci		return err;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_format_create(&dpcm->format,
4478c2ecf20Sopenharmony_ci			params_channels(params),
4488c2ecf20Sopenharmony_ci			format, params_rate(params), 0, 0));
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
4518c2ecf20Sopenharmony_ci		if (hpi_instream_reset(dpcm->h_stream) != 0)
4528c2ecf20Sopenharmony_ci			return -EINVAL;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		if (hpi_instream_set_format(
4558c2ecf20Sopenharmony_ci			dpcm->h_stream, &dpcm->format) != 0)
4568c2ecf20Sopenharmony_ci			return -EINVAL;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	dpcm->hpi_buffer_attached = 0;
4608c2ecf20Sopenharmony_ci	if (card->can_dma) {
4618c2ecf20Sopenharmony_ci		err = hpi_stream_host_buffer_attach(dpcm->h_stream,
4628c2ecf20Sopenharmony_ci			params_buffer_bytes(params),  runtime->dma_addr);
4638c2ecf20Sopenharmony_ci		if (err == 0) {
4648c2ecf20Sopenharmony_ci			snd_printdd(
4658c2ecf20Sopenharmony_ci				"stream_host_buffer_attach success %u %lu\n",
4668c2ecf20Sopenharmony_ci				params_buffer_bytes(params),
4678c2ecf20Sopenharmony_ci				(unsigned long)runtime->dma_addr);
4688c2ecf20Sopenharmony_ci		} else {
4698c2ecf20Sopenharmony_ci			snd_printd("stream_host_buffer_attach error %d\n",
4708c2ecf20Sopenharmony_ci					err);
4718c2ecf20Sopenharmony_ci			return -ENOMEM;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
4758c2ecf20Sopenharmony_ci				&dpcm->hpi_buffer_attached, NULL, NULL, NULL);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	bytes_per_sec = params_rate(params) * params_channels(params);
4788c2ecf20Sopenharmony_ci	width = snd_pcm_format_width(params_format(params));
4798c2ecf20Sopenharmony_ci	bytes_per_sec *= width;
4808c2ecf20Sopenharmony_ci	bytes_per_sec /= 8;
4818c2ecf20Sopenharmony_ci	if (width < 0 || bytes_per_sec == 0)
4828c2ecf20Sopenharmony_ci		return -EINVAL;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	dpcm->bytes_per_sec = bytes_per_sec;
4858c2ecf20Sopenharmony_ci	dpcm->buffer_bytes = params_buffer_bytes(params);
4868c2ecf20Sopenharmony_ci	dpcm->period_bytes = params_period_bytes(params);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int
4928c2ecf20Sopenharmony_cisnd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4958c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
4968c2ecf20Sopenharmony_ci	if (dpcm->hpi_buffer_attached)
4978c2ecf20Sopenharmony_ci		hpi_stream_host_buffer_detach(dpcm->h_stream);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
5058c2ecf20Sopenharmony_ci	kfree(dpcm);
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
5098c2ecf20Sopenharmony_ci					    substream)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5128c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
5138c2ecf20Sopenharmony_ci	int expiry;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	expiry = HZ / 200;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	expiry = max(expiry, 1); /* don't let it be zero! */
5188c2ecf20Sopenharmony_ci	mod_timer(&dpcm->timer, jiffies + expiry);
5198c2ecf20Sopenharmony_ci	dpcm->respawn_timer = 1;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5258c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	dpcm->respawn_timer = 0;
5288c2ecf20Sopenharmony_ci	del_timer(&dpcm->timer);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
5348c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
5378c2ecf20Sopenharmony_ci	card = snd_pcm_substream_chip(substream);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	WARN_ON(in_interrupt());
5408c2ecf20Sopenharmony_ci	card->llmode_streampriv = dpcm;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
5438c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_IRQ_RATE,
5448c2ecf20Sopenharmony_ci		card->update_interval_frames, 0));
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	card = snd_pcm_substream_chip(substream);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
5548c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	card->llmode_streampriv = NULL;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
5608c2ecf20Sopenharmony_ci					   int cmd)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
5638c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
5648c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
5658c2ecf20Sopenharmony_ci	u16 e;
5668c2ecf20Sopenharmony_ci	char name[16];
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	switch (cmd) {
5718c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
5728c2ecf20Sopenharmony_ci		snd_printdd("%s trigger start\n", name);
5738c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
5748c2ecf20Sopenharmony_ci			struct snd_pcm_runtime *runtime = s->runtime;
5758c2ecf20Sopenharmony_ci			struct snd_card_asihpi_pcm *ds = runtime->private_data;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci			if (snd_pcm_substream_chip(s) != card)
5788c2ecf20Sopenharmony_ci				continue;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci			/* don't link Cap and Play */
5818c2ecf20Sopenharmony_ci			if (substream->stream != s->stream)
5828c2ecf20Sopenharmony_ci				continue;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci			ds->drained_count = 0;
5858c2ecf20Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5868c2ecf20Sopenharmony_ci				/* How do I know how much valid data is present
5878c2ecf20Sopenharmony_ci				* in buffer? Must be at least one period!
5888c2ecf20Sopenharmony_ci				* Guessing 2 periods, but if
5898c2ecf20Sopenharmony_ci				* buffer is bigger it may contain even more
5908c2ecf20Sopenharmony_ci				* data??
5918c2ecf20Sopenharmony_ci				*/
5928c2ecf20Sopenharmony_ci				unsigned int preload = ds->period_bytes * 1;
5938c2ecf20Sopenharmony_ci				snd_printddd("%d preload %d\n", s->number, preload);
5948c2ecf20Sopenharmony_ci				hpi_handle_error(hpi_outstream_write_buf(
5958c2ecf20Sopenharmony_ci						ds->h_stream,
5968c2ecf20Sopenharmony_ci						&runtime->dma_area[0],
5978c2ecf20Sopenharmony_ci						preload,
5988c2ecf20Sopenharmony_ci						&ds->format));
5998c2ecf20Sopenharmony_ci				ds->pcm_buf_host_rw_ofs = preload;
6008c2ecf20Sopenharmony_ci			}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci			if (card->support_grouping) {
6038c2ecf20Sopenharmony_ci				snd_printdd("%d group\n", s->number);
6048c2ecf20Sopenharmony_ci				e = hpi_stream_group_add(
6058c2ecf20Sopenharmony_ci					dpcm->h_stream,
6068c2ecf20Sopenharmony_ci					ds->h_stream);
6078c2ecf20Sopenharmony_ci				if (!e) {
6088c2ecf20Sopenharmony_ci					snd_pcm_trigger_done(s, substream);
6098c2ecf20Sopenharmony_ci				} else {
6108c2ecf20Sopenharmony_ci					hpi_handle_error(e);
6118c2ecf20Sopenharmony_ci					break;
6128c2ecf20Sopenharmony_ci				}
6138c2ecf20Sopenharmony_ci			} else
6148c2ecf20Sopenharmony_ci				break;
6158c2ecf20Sopenharmony_ci		}
6168c2ecf20Sopenharmony_ci		/* start the master stream */
6178c2ecf20Sopenharmony_ci		card->pcm_start(substream);
6188c2ecf20Sopenharmony_ci		if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
6198c2ecf20Sopenharmony_ci			!card->can_dma)
6208c2ecf20Sopenharmony_ci			hpi_handle_error(hpi_stream_start(dpcm->h_stream));
6218c2ecf20Sopenharmony_ci		break;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
6248c2ecf20Sopenharmony_ci		snd_printdd("%s trigger stop\n", name);
6258c2ecf20Sopenharmony_ci		card->pcm_stop(substream);
6268c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
6278c2ecf20Sopenharmony_ci			if (snd_pcm_substream_chip(s) != card)
6288c2ecf20Sopenharmony_ci				continue;
6298c2ecf20Sopenharmony_ci			/* don't link Cap and Play */
6308c2ecf20Sopenharmony_ci			if (substream->stream != s->stream)
6318c2ecf20Sopenharmony_ci				continue;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci			/*? workaround linked streams don't
6348c2ecf20Sopenharmony_ci			transition to SETUP 20070706*/
6358c2ecf20Sopenharmony_ci			s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci			if (card->support_grouping) {
6388c2ecf20Sopenharmony_ci				snd_printdd("%d group\n", s->number);
6398c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
6408c2ecf20Sopenharmony_ci			} else
6418c2ecf20Sopenharmony_ci				break;
6428c2ecf20Sopenharmony_ci		}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci		/* _prepare and _hwparams reset the stream */
6458c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
6468c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6478c2ecf20Sopenharmony_ci			hpi_handle_error(
6488c2ecf20Sopenharmony_ci				hpi_outstream_reset(dpcm->h_stream));
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		if (card->support_grouping)
6518c2ecf20Sopenharmony_ci			hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream));
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
6558c2ecf20Sopenharmony_ci		snd_printdd("%s trigger pause release\n", name);
6568c2ecf20Sopenharmony_ci		card->pcm_start(substream);
6578c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_stream_start(dpcm->h_stream));
6588c2ecf20Sopenharmony_ci		break;
6598c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
6608c2ecf20Sopenharmony_ci		snd_printdd("%s trigger pause push\n", name);
6618c2ecf20Sopenharmony_ci		card->pcm_stop(substream);
6628c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
6638c2ecf20Sopenharmony_ci		break;
6648c2ecf20Sopenharmony_ci	default:
6658c2ecf20Sopenharmony_ci		snd_printd(KERN_ERR "\tINVALID\n");
6668c2ecf20Sopenharmony_ci		return -EINVAL;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return 0;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci/*algorithm outline
6738c2ecf20Sopenharmony_ci Without linking degenerates to getting single stream pos etc
6748c2ecf20Sopenharmony_ci Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
6758c2ecf20Sopenharmony_ci*/
6768c2ecf20Sopenharmony_ci/*
6778c2ecf20Sopenharmony_cipcm_buf_dma_ofs=get_buf_pos(s);
6788c2ecf20Sopenharmony_cifor_each_linked_stream(s) {
6798c2ecf20Sopenharmony_ci	pcm_buf_dma_ofs=get_buf_pos(s);
6808c2ecf20Sopenharmony_ci	min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes)
6818c2ecf20Sopenharmony_ci	new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos)
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_citimer.expires = jiffies + predict_next_period_ready(min_buf_pos);
6848c2ecf20Sopenharmony_cifor_each_linked_stream(s) {
6858c2ecf20Sopenharmony_ci	s->pcm_buf_dma_ofs = min_buf_pos;
6868c2ecf20Sopenharmony_ci	if (new_data > period_bytes) {
6878c2ecf20Sopenharmony_ci		if (mmap) {
6888c2ecf20Sopenharmony_ci			irq_pos = (irq_pos + period_bytes) % buffer_bytes;
6898c2ecf20Sopenharmony_ci			if (playback) {
6908c2ecf20Sopenharmony_ci				write(period_bytes);
6918c2ecf20Sopenharmony_ci			} else {
6928c2ecf20Sopenharmony_ci				read(period_bytes);
6938c2ecf20Sopenharmony_ci			}
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(s);
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci*/
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/** Minimum of 2 modulo values.  Works correctly when the difference between
7018c2ecf20Sopenharmony_ci* the values is less than half the modulus
7028c2ecf20Sopenharmony_ci*/
7038c2ecf20Sopenharmony_cistatic inline unsigned int modulo_min(unsigned int a, unsigned int b,
7048c2ecf20Sopenharmony_ci					unsigned long int modulus)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	unsigned int result;
7078c2ecf20Sopenharmony_ci	if (((a-b) % modulus) < (modulus/2))
7088c2ecf20Sopenharmony_ci		result = b;
7098c2ecf20Sopenharmony_ci	else
7108c2ecf20Sopenharmony_ci		result = a;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	return result;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci/** Timer function, equivalent to interrupt service routine for cards
7168c2ecf20Sopenharmony_ci*/
7178c2ecf20Sopenharmony_cistatic void snd_card_asihpi_timer_function(struct timer_list *t)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer);
7208c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = dpcm->substream;
7218c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
7228c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
7238c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
7248c2ecf20Sopenharmony_ci	unsigned int newdata = 0;
7258c2ecf20Sopenharmony_ci	unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
7268c2ecf20Sopenharmony_ci	unsigned int remdata, xfercount, next_jiffies;
7278c2ecf20Sopenharmony_ci	int first = 1;
7288c2ecf20Sopenharmony_ci	int loops = 0;
7298c2ecf20Sopenharmony_ci	u16 state;
7308c2ecf20Sopenharmony_ci	u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
7318c2ecf20Sopenharmony_ci	char name[16];
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/* find minimum newdata and buffer pos in group */
7378c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
7388c2ecf20Sopenharmony_ci		struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
7398c2ecf20Sopenharmony_ci		runtime = s->runtime;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		if (snd_pcm_substream_chip(s) != card)
7428c2ecf20Sopenharmony_ci			continue;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		/* don't link Cap and Play */
7458c2ecf20Sopenharmony_ci		if (substream->stream != s->stream)
7468c2ecf20Sopenharmony_ci			continue;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_stream_get_info_ex(
7498c2ecf20Sopenharmony_ci					ds->h_stream, &state,
7508c2ecf20Sopenharmony_ci					&buffer_size, &bytes_avail,
7518c2ecf20Sopenharmony_ci					&samples_played, &on_card_bytes));
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		/* number of bytes in on-card buffer */
7548c2ecf20Sopenharmony_ci		runtime->delay = on_card_bytes;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		if (!card->can_dma)
7578c2ecf20Sopenharmony_ci			on_card_bytes = bytes_avail;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci		if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
7608c2ecf20Sopenharmony_ci			pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
7618c2ecf20Sopenharmony_ci			if (state == HPI_STATE_STOPPED) {
7628c2ecf20Sopenharmony_ci				if (bytes_avail == 0) {
7638c2ecf20Sopenharmony_ci					hpi_handle_error(hpi_stream_start(ds->h_stream));
7648c2ecf20Sopenharmony_ci					snd_printdd("P%d start\n", s->number);
7658c2ecf20Sopenharmony_ci					ds->drained_count = 0;
7668c2ecf20Sopenharmony_ci				}
7678c2ecf20Sopenharmony_ci			} else if (state == HPI_STATE_DRAINED) {
7688c2ecf20Sopenharmony_ci				snd_printd(KERN_WARNING "P%d drained\n",
7698c2ecf20Sopenharmony_ci						s->number);
7708c2ecf20Sopenharmony_ci				ds->drained_count++;
7718c2ecf20Sopenharmony_ci				if (ds->drained_count > 20) {
7728c2ecf20Sopenharmony_ci					snd_pcm_stop_xrun(s);
7738c2ecf20Sopenharmony_ci					continue;
7748c2ecf20Sopenharmony_ci				}
7758c2ecf20Sopenharmony_ci			} else {
7768c2ecf20Sopenharmony_ci				ds->drained_count = 0;
7778c2ecf20Sopenharmony_ci			}
7788c2ecf20Sopenharmony_ci		} else
7798c2ecf20Sopenharmony_ci			pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		if (first) {
7828c2ecf20Sopenharmony_ci			/* can't statically init min when wrap is involved */
7838c2ecf20Sopenharmony_ci			min_buf_pos = pcm_buf_dma_ofs;
7848c2ecf20Sopenharmony_ci			newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes;
7858c2ecf20Sopenharmony_ci			first = 0;
7868c2ecf20Sopenharmony_ci		} else {
7878c2ecf20Sopenharmony_ci			min_buf_pos =
7888c2ecf20Sopenharmony_ci				modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L);
7898c2ecf20Sopenharmony_ci			newdata = min(
7908c2ecf20Sopenharmony_ci				(pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes,
7918c2ecf20Sopenharmony_ci				newdata);
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci		snd_printddd(
7958c2ecf20Sopenharmony_ci			"timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
7968c2ecf20Sopenharmony_ci			name, s->number, state,
7978c2ecf20Sopenharmony_ci			ds->pcm_buf_elapsed_dma_ofs,
7988c2ecf20Sopenharmony_ci			ds->pcm_buf_host_rw_ofs,
7998c2ecf20Sopenharmony_ci			pcm_buf_dma_ofs,
8008c2ecf20Sopenharmony_ci			(int)bytes_avail,
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci			(int)on_card_bytes,
8038c2ecf20Sopenharmony_ci			buffer_size-bytes_avail,
8048c2ecf20Sopenharmony_ci			(unsigned long)frames_to_bytes(runtime,
8058c2ecf20Sopenharmony_ci						runtime->status->hw_ptr),
8068c2ecf20Sopenharmony_ci			(unsigned long)frames_to_bytes(runtime,
8078c2ecf20Sopenharmony_ci						runtime->control->appl_ptr)
8088c2ecf20Sopenharmony_ci		);
8098c2ecf20Sopenharmony_ci		loops++;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci	pcm_buf_dma_ofs = min_buf_pos;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	remdata = newdata % dpcm->period_bytes;
8148c2ecf20Sopenharmony_ci	xfercount = newdata - remdata; /* a multiple of period_bytes */
8158c2ecf20Sopenharmony_ci	/* come back when on_card_bytes has decreased enough to allow
8168c2ecf20Sopenharmony_ci	   write to happen, or when data has been consumed to make another
8178c2ecf20Sopenharmony_ci	   period
8188c2ecf20Sopenharmony_ci	*/
8198c2ecf20Sopenharmony_ci	if (xfercount && (on_card_bytes  > dpcm->period_bytes))
8208c2ecf20Sopenharmony_ci		next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec);
8218c2ecf20Sopenharmony_ci	else
8228c2ecf20Sopenharmony_ci		next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	next_jiffies = max(next_jiffies, 1U);
8258c2ecf20Sopenharmony_ci	dpcm->timer.expires = jiffies + next_jiffies;
8268c2ecf20Sopenharmony_ci	snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
8278c2ecf20Sopenharmony_ci			next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
8308c2ecf20Sopenharmony_ci		struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci		/* don't link Cap and Play */
8338c2ecf20Sopenharmony_ci		if (substream->stream != s->stream)
8348c2ecf20Sopenharmony_ci			continue;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci		/* Store dma offset for use by pointer callback */
8378c2ecf20Sopenharmony_ci		ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		if (xfercount &&
8408c2ecf20Sopenharmony_ci			/* Limit use of on card fifo for playback */
8418c2ecf20Sopenharmony_ci			((on_card_bytes <= ds->period_bytes) ||
8428c2ecf20Sopenharmony_ci			(s->stream == SNDRV_PCM_STREAM_CAPTURE)))
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		{
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci			unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
8478c2ecf20Sopenharmony_ci			unsigned int xfer1, xfer2;
8488c2ecf20Sopenharmony_ci			char *pd = &s->runtime->dma_area[buf_ofs];
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci			if (card->can_dma) { /* buffer wrap is handled at lower level */
8518c2ecf20Sopenharmony_ci				xfer1 = xfercount;
8528c2ecf20Sopenharmony_ci				xfer2 = 0;
8538c2ecf20Sopenharmony_ci			} else {
8548c2ecf20Sopenharmony_ci				xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
8558c2ecf20Sopenharmony_ci				xfer2 = xfercount - xfer1;
8568c2ecf20Sopenharmony_ci			}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
8598c2ecf20Sopenharmony_ci				snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n",
8608c2ecf20Sopenharmony_ci					s->number, xfer1, buf_ofs);
8618c2ecf20Sopenharmony_ci				hpi_handle_error(
8628c2ecf20Sopenharmony_ci					hpi_outstream_write_buf(
8638c2ecf20Sopenharmony_ci						ds->h_stream, pd, xfer1,
8648c2ecf20Sopenharmony_ci						&ds->format));
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci				if (xfer2) {
8678c2ecf20Sopenharmony_ci					pd = s->runtime->dma_area;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci					snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n",
8708c2ecf20Sopenharmony_ci							s->number,
8718c2ecf20Sopenharmony_ci							xfercount - xfer1, buf_ofs);
8728c2ecf20Sopenharmony_ci					hpi_handle_error(
8738c2ecf20Sopenharmony_ci						hpi_outstream_write_buf(
8748c2ecf20Sopenharmony_ci							ds->h_stream, pd,
8758c2ecf20Sopenharmony_ci							xfercount - xfer1,
8768c2ecf20Sopenharmony_ci							&ds->format));
8778c2ecf20Sopenharmony_ci				}
8788c2ecf20Sopenharmony_ci			} else {
8798c2ecf20Sopenharmony_ci				snd_printddd("read1, C=%d, xfer=%d\n",
8808c2ecf20Sopenharmony_ci					s->number, xfer1);
8818c2ecf20Sopenharmony_ci				hpi_handle_error(
8828c2ecf20Sopenharmony_ci					hpi_instream_read_buf(
8838c2ecf20Sopenharmony_ci						ds->h_stream,
8848c2ecf20Sopenharmony_ci						pd, xfer1));
8858c2ecf20Sopenharmony_ci				if (xfer2) {
8868c2ecf20Sopenharmony_ci					pd = s->runtime->dma_area;
8878c2ecf20Sopenharmony_ci					snd_printddd("read2, C=%d, xfer=%d\n",
8888c2ecf20Sopenharmony_ci						s->number, xfer2);
8898c2ecf20Sopenharmony_ci					hpi_handle_error(
8908c2ecf20Sopenharmony_ci						hpi_instream_read_buf(
8918c2ecf20Sopenharmony_ci							ds->h_stream,
8928c2ecf20Sopenharmony_ci							pd, xfer2));
8938c2ecf20Sopenharmony_ci				}
8948c2ecf20Sopenharmony_ci			}
8958c2ecf20Sopenharmony_ci			/* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
8968c2ecf20Sopenharmony_ci			ds->pcm_buf_host_rw_ofs += xfercount;
8978c2ecf20Sopenharmony_ci			ds->pcm_buf_elapsed_dma_ofs += xfercount;
8988c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(s);
8998c2ecf20Sopenharmony_ci		}
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
9038c2ecf20Sopenharmony_ci		add_timer(&dpcm->timer);
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic void snd_card_asihpi_isr(struct hpi_adapter *a)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
9118c2ecf20Sopenharmony_ci	asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
9128c2ecf20Sopenharmony_ci	if (asihpi->llmode_streampriv)
9138c2ecf20Sopenharmony_ci		snd_card_asihpi_timer_function(
9148c2ecf20Sopenharmony_ci			&asihpi->llmode_streampriv->timer);
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci/***************************** PLAYBACK OPS ****************/
9188c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
9198c2ecf20Sopenharmony_ci					    substream)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9228c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	snd_printdd("P%d prepare\n", substream->number);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
9278c2ecf20Sopenharmony_ci	dpcm->pcm_buf_host_rw_ofs = 0;
9288c2ecf20Sopenharmony_ci	dpcm->pcm_buf_dma_ofs = 0;
9298c2ecf20Sopenharmony_ci	dpcm->pcm_buf_elapsed_dma_ofs = 0;
9308c2ecf20Sopenharmony_ci	return 0;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
9348c2ecf20Sopenharmony_cisnd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9378c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
9388c2ecf20Sopenharmony_ci	snd_pcm_uframes_t ptr;
9398c2ecf20Sopenharmony_ci	char name[16];
9408c2ecf20Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs  % dpcm->buffer_bytes);
9438c2ecf20Sopenharmony_ci	snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr);
9448c2ecf20Sopenharmony_ci	return ptr;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi,
9488c2ecf20Sopenharmony_ci						u32 h_stream)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct hpi_format hpi_format;
9518c2ecf20Sopenharmony_ci	u16 format;
9528c2ecf20Sopenharmony_ci	u16 err;
9538c2ecf20Sopenharmony_ci	u32 h_control;
9548c2ecf20Sopenharmony_ci	u32 sample_rate = 48000;
9558c2ecf20Sopenharmony_ci	u64 formats = 0;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* on cards without SRC, must query at valid rate,
9588c2ecf20Sopenharmony_ci	* maybe set by external sync
9598c2ecf20Sopenharmony_ci	*/
9608c2ecf20Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
9618c2ecf20Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
9628c2ecf20Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (!err)
9658c2ecf20Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control,
9668c2ecf20Sopenharmony_ci				&sample_rate);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
9698c2ecf20Sopenharmony_ci	     format <= HPI_FORMAT_PCM24_SIGNED; format++) {
9708c2ecf20Sopenharmony_ci		err = hpi_format_create(&hpi_format, asihpi->out_max_chans,
9718c2ecf20Sopenharmony_ci					format, sample_rate, 128000, 0);
9728c2ecf20Sopenharmony_ci		if (!err)
9738c2ecf20Sopenharmony_ci			err = hpi_outstream_query_format(h_stream, &hpi_format);
9748c2ecf20Sopenharmony_ci		if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
9758c2ecf20Sopenharmony_ci			formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci	return formats;
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9838c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
9848c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
9858c2ecf20Sopenharmony_ci	struct snd_pcm_hardware snd_card_asihpi_playback;
9868c2ecf20Sopenharmony_ci	int err;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
9898c2ecf20Sopenharmony_ci	if (dpcm == NULL)
9908c2ecf20Sopenharmony_ci		return -ENOMEM;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	err = hpi_outstream_open(card->hpi->adapter->index,
9938c2ecf20Sopenharmony_ci			      substream->number, &dpcm->h_stream);
9948c2ecf20Sopenharmony_ci	hpi_handle_error(err);
9958c2ecf20Sopenharmony_ci	if (err)
9968c2ecf20Sopenharmony_ci		kfree(dpcm);
9978c2ecf20Sopenharmony_ci	if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
9988c2ecf20Sopenharmony_ci		return -EBUSY;
9998c2ecf20Sopenharmony_ci	if (err)
10008c2ecf20Sopenharmony_ci		return -EIO;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	/*? also check ASI5000 samplerate source
10038c2ecf20Sopenharmony_ci	    If external, only support external rate.
10048c2ecf20Sopenharmony_ci	    If internal and other stream playing, can't switch
10058c2ecf20Sopenharmony_ci	*/
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
10088c2ecf20Sopenharmony_ci	dpcm->substream = substream;
10098c2ecf20Sopenharmony_ci	runtime->private_data = dpcm;
10108c2ecf20Sopenharmony_ci	runtime->private_free = snd_card_asihpi_runtime_free;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
10138c2ecf20Sopenharmony_ci	if (!card->hpi->interrupt_mode) {
10148c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
10158c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
10168c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
10178c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.periods_min = PERIODS_MIN;
10188c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
10198c2ecf20Sopenharmony_ci	} else {
10208c2ecf20Sopenharmony_ci		size_t pbmin = card->update_interval_frames *
10218c2ecf20Sopenharmony_ci			card->out_max_chans;
10228c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
10238c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_min = pbmin;
10248c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
10258c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.periods_min = PERIODS_MIN;
10268c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	/* snd_card_asihpi_playback.fifo_size = 0; */
10308c2ecf20Sopenharmony_ci	snd_card_asihpi_playback.channels_max = card->out_max_chans;
10318c2ecf20Sopenharmony_ci	snd_card_asihpi_playback.channels_min = card->out_min_chans;
10328c2ecf20Sopenharmony_ci	snd_card_asihpi_playback.formats =
10338c2ecf20Sopenharmony_ci			snd_card_asihpi_playback_formats(card, dpcm->h_stream);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_playback);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED |
10388c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_DOUBLE |
10398c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_BATCH |
10408c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_BLOCK_TRANSFER |
10418c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_PAUSE |
10428c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_MMAP |
10438c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_MMAP_VALID;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (card->support_grouping) {
10468c2ecf20Sopenharmony_ci		snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
10478c2ecf20Sopenharmony_ci		snd_pcm_set_sync(substream);
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	/* struct is copied, so can create initializer dynamically */
10518c2ecf20Sopenharmony_ci	runtime->hw = snd_card_asihpi_playback;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (card->can_dma)
10548c2ecf20Sopenharmony_ci		err = snd_pcm_hw_constraint_pow2(runtime, 0,
10558c2ecf20Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
10568c2ecf20Sopenharmony_ci	if (err < 0)
10578c2ecf20Sopenharmony_ci		return err;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
10608c2ecf20Sopenharmony_ci		card->update_interval_frames);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
10638c2ecf20Sopenharmony_ci		card->update_interval_frames, UINT_MAX);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	snd_printdd("playback open\n");
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	return 0;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
10738c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
10768c2ecf20Sopenharmony_ci	snd_printdd("playback close\n");
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	return 0;
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
10828c2ecf20Sopenharmony_ci	.open = snd_card_asihpi_playback_open,
10838c2ecf20Sopenharmony_ci	.close = snd_card_asihpi_playback_close,
10848c2ecf20Sopenharmony_ci	.hw_params = snd_card_asihpi_pcm_hw_params,
10858c2ecf20Sopenharmony_ci	.hw_free = snd_card_asihpi_hw_free,
10868c2ecf20Sopenharmony_ci	.prepare = snd_card_asihpi_playback_prepare,
10878c2ecf20Sopenharmony_ci	.trigger = snd_card_asihpi_trigger,
10888c2ecf20Sopenharmony_ci	.pointer = snd_card_asihpi_playback_pointer,
10898c2ecf20Sopenharmony_ci};
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci/***************************** CAPTURE OPS ****************/
10928c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
10938c2ecf20Sopenharmony_cisnd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
10968c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
10978c2ecf20Sopenharmony_ci	char name[16];
10988c2ecf20Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
11018c2ecf20Sopenharmony_ci	/* NOTE Unlike playback can't use actual samples_played
11028c2ecf20Sopenharmony_ci		for the capture position, because those samples aren't yet in
11038c2ecf20Sopenharmony_ci		the local buffer available for reading.
11048c2ecf20Sopenharmony_ci	*/
11058c2ecf20Sopenharmony_ci	return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11118c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_instream_reset(dpcm->h_stream));
11148c2ecf20Sopenharmony_ci	dpcm->pcm_buf_host_rw_ofs = 0;
11158c2ecf20Sopenharmony_ci	dpcm->pcm_buf_dma_ofs = 0;
11168c2ecf20Sopenharmony_ci	dpcm->pcm_buf_elapsed_dma_ofs = 0;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	snd_printdd("Capture Prepare %d\n", substream->number);
11198c2ecf20Sopenharmony_ci	return 0;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
11238c2ecf20Sopenharmony_ci					u32 h_stream)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	struct hpi_format hpi_format;
11268c2ecf20Sopenharmony_ci	u16 format;
11278c2ecf20Sopenharmony_ci	u16 err;
11288c2ecf20Sopenharmony_ci	u32 h_control;
11298c2ecf20Sopenharmony_ci	u32 sample_rate = 48000;
11308c2ecf20Sopenharmony_ci	u64 formats = 0;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	/* on cards without SRC, must query at valid rate,
11338c2ecf20Sopenharmony_ci		maybe set by external sync */
11348c2ecf20Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
11358c2ecf20Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
11368c2ecf20Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	if (!err)
11398c2ecf20Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control,
11408c2ecf20Sopenharmony_ci			&sample_rate);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
11438c2ecf20Sopenharmony_ci		format <= HPI_FORMAT_PCM24_SIGNED; format++) {
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci		err = hpi_format_create(&hpi_format, asihpi->in_max_chans,
11468c2ecf20Sopenharmony_ci					format, sample_rate, 128000, 0);
11478c2ecf20Sopenharmony_ci		if (!err)
11488c2ecf20Sopenharmony_ci			err = hpi_instream_query_format(h_stream, &hpi_format);
11498c2ecf20Sopenharmony_ci		if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
11508c2ecf20Sopenharmony_ci			formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci	return formats;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11588c2ecf20Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
11598c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
11608c2ecf20Sopenharmony_ci	struct snd_pcm_hardware snd_card_asihpi_capture;
11618c2ecf20Sopenharmony_ci	int err;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
11648c2ecf20Sopenharmony_ci	if (dpcm == NULL)
11658c2ecf20Sopenharmony_ci		return -ENOMEM;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	snd_printdd("capture open adapter %d stream %d\n",
11688c2ecf20Sopenharmony_ci			card->hpi->adapter->index, substream->number);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	err = hpi_handle_error(
11718c2ecf20Sopenharmony_ci	    hpi_instream_open(card->hpi->adapter->index,
11728c2ecf20Sopenharmony_ci			     substream->number, &dpcm->h_stream));
11738c2ecf20Sopenharmony_ci	if (err)
11748c2ecf20Sopenharmony_ci		kfree(dpcm);
11758c2ecf20Sopenharmony_ci	if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
11768c2ecf20Sopenharmony_ci		return -EBUSY;
11778c2ecf20Sopenharmony_ci	if (err)
11788c2ecf20Sopenharmony_ci		return -EIO;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
11818c2ecf20Sopenharmony_ci	dpcm->substream = substream;
11828c2ecf20Sopenharmony_ci	runtime->private_data = dpcm;
11838c2ecf20Sopenharmony_ci	runtime->private_free = snd_card_asihpi_runtime_free;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
11868c2ecf20Sopenharmony_ci	if (!card->hpi->interrupt_mode) {
11878c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
11888c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
11898c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
11908c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.periods_min = PERIODS_MIN;
11918c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
11928c2ecf20Sopenharmony_ci	} else {
11938c2ecf20Sopenharmony_ci		size_t pbmin = card->update_interval_frames *
11948c2ecf20Sopenharmony_ci			card->out_max_chans;
11958c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
11968c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_min = pbmin;
11978c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
11988c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.periods_min = PERIODS_MIN;
11998c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci	/* snd_card_asihpi_capture.fifo_size = 0; */
12028c2ecf20Sopenharmony_ci	snd_card_asihpi_capture.channels_max = card->in_max_chans;
12038c2ecf20Sopenharmony_ci	snd_card_asihpi_capture.channels_min = card->in_min_chans;
12048c2ecf20Sopenharmony_ci	snd_card_asihpi_capture.formats =
12058c2ecf20Sopenharmony_ci		snd_card_asihpi_capture_formats(card, dpcm->h_stream);
12068c2ecf20Sopenharmony_ci	snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_capture);
12078c2ecf20Sopenharmony_ci	snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
12088c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_MMAP |
12098c2ecf20Sopenharmony_ci					SNDRV_PCM_INFO_MMAP_VALID;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	if (card->support_grouping)
12128c2ecf20Sopenharmony_ci		snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	runtime->hw = snd_card_asihpi_capture;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	if (card->can_dma)
12178c2ecf20Sopenharmony_ci		err = snd_pcm_hw_constraint_pow2(runtime, 0,
12188c2ecf20Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
12198c2ecf20Sopenharmony_ci	if (err < 0)
12208c2ecf20Sopenharmony_ci		return err;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
12238c2ecf20Sopenharmony_ci		card->update_interval_frames);
12248c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
12258c2ecf20Sopenharmony_ci		card->update_interval_frames, UINT_MAX);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return 0;
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_instream_close(dpcm->h_stream));
12378c2ecf20Sopenharmony_ci	return 0;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
12418c2ecf20Sopenharmony_ci	.open = snd_card_asihpi_capture_open,
12428c2ecf20Sopenharmony_ci	.close = snd_card_asihpi_capture_close,
12438c2ecf20Sopenharmony_ci	.hw_params = snd_card_asihpi_pcm_hw_params,
12448c2ecf20Sopenharmony_ci	.hw_free = snd_card_asihpi_hw_free,
12458c2ecf20Sopenharmony_ci	.prepare = snd_card_asihpi_capture_prepare,
12468c2ecf20Sopenharmony_ci	.trigger = snd_card_asihpi_trigger,
12478c2ecf20Sopenharmony_ci	.pointer = snd_card_asihpi_capture_pointer,
12488c2ecf20Sopenharmony_ci};
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
12538c2ecf20Sopenharmony_ci	int err;
12548c2ecf20Sopenharmony_ci	u16 num_instreams, num_outstreams, x16;
12558c2ecf20Sopenharmony_ci	u32 x32;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	err = hpi_adapter_get_info(asihpi->hpi->adapter->index,
12588c2ecf20Sopenharmony_ci			&num_outstreams, &num_instreams,
12598c2ecf20Sopenharmony_ci			&x16, &x32, &x16);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	err = snd_pcm_new(asihpi->card, "Asihpi PCM", device,
12628c2ecf20Sopenharmony_ci			num_outstreams,	num_instreams, &pcm);
12638c2ecf20Sopenharmony_ci	if (err < 0)
12648c2ecf20Sopenharmony_ci		return err;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	/* pointer to ops struct is stored, dont change ops afterwards! */
12678c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
12688c2ecf20Sopenharmony_ci			&snd_card_asihpi_playback_mmap_ops);
12698c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
12708c2ecf20Sopenharmony_ci			&snd_card_asihpi_capture_mmap_ops);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	pcm->private_data = asihpi;
12738c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
12748c2ecf20Sopenharmony_ci	strcpy(pcm->name, "Asihpi PCM");
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/*? do we want to emulate MMAP for non-BBM cards?
12778c2ecf20Sopenharmony_ci	Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
12788c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
12798c2ecf20Sopenharmony_ci				       &asihpi->pci->dev,
12808c2ecf20Sopenharmony_ci				       64*1024, BUFFER_BYTES_MAX);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	return 0;
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci/***************************** MIXER CONTROLS ****************/
12868c2ecf20Sopenharmony_cistruct hpi_control {
12878c2ecf20Sopenharmony_ci	u32 h_control;
12888c2ecf20Sopenharmony_ci	u16 control_type;
12898c2ecf20Sopenharmony_ci	u16 src_node_type;
12908c2ecf20Sopenharmony_ci	u16 src_node_index;
12918c2ecf20Sopenharmony_ci	u16 dst_node_type;
12928c2ecf20Sopenharmony_ci	u16 dst_node_index;
12938c2ecf20Sopenharmony_ci	u16 band;
12948c2ecf20Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */
12958c2ecf20Sopenharmony_ci};
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic const char * const asihpi_tuner_band_names[] = {
12988c2ecf20Sopenharmony_ci	"invalid",
12998c2ecf20Sopenharmony_ci	"AM",
13008c2ecf20Sopenharmony_ci	"FM mono",
13018c2ecf20Sopenharmony_ci	"TV NTSC-M",
13028c2ecf20Sopenharmony_ci	"FM stereo",
13038c2ecf20Sopenharmony_ci	"AUX",
13048c2ecf20Sopenharmony_ci	"TV PAL BG",
13058c2ecf20Sopenharmony_ci	"TV PAL I",
13068c2ecf20Sopenharmony_ci	"TV PAL DK",
13078c2ecf20Sopenharmony_ci	"TV SECAM",
13088c2ecf20Sopenharmony_ci	"TV DAB",
13098c2ecf20Sopenharmony_ci};
13108c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */
13118c2ecf20Sopenharmony_cicompile_time_assert(
13128c2ecf20Sopenharmony_ci	(ARRAY_SIZE(asihpi_tuner_band_names) ==
13138c2ecf20Sopenharmony_ci		(HPI_TUNER_BAND_LAST+1)),
13148c2ecf20Sopenharmony_ci	assert_tuner_band_names_size);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic const char * const asihpi_src_names[] = {
13178c2ecf20Sopenharmony_ci	"no source",
13188c2ecf20Sopenharmony_ci	"PCM",
13198c2ecf20Sopenharmony_ci	"Line",
13208c2ecf20Sopenharmony_ci	"Digital",
13218c2ecf20Sopenharmony_ci	"Tuner",
13228c2ecf20Sopenharmony_ci	"RF",
13238c2ecf20Sopenharmony_ci	"Clock",
13248c2ecf20Sopenharmony_ci	"Bitstream",
13258c2ecf20Sopenharmony_ci	"Mic",
13268c2ecf20Sopenharmony_ci	"Net",
13278c2ecf20Sopenharmony_ci	"Analog",
13288c2ecf20Sopenharmony_ci	"Adapter",
13298c2ecf20Sopenharmony_ci	"RTP",
13308c2ecf20Sopenharmony_ci	"Internal",
13318c2ecf20Sopenharmony_ci	"AVB",
13328c2ecf20Sopenharmony_ci	"BLU-Link"
13338c2ecf20Sopenharmony_ci};
13348c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */
13358c2ecf20Sopenharmony_cicompile_time_assert(
13368c2ecf20Sopenharmony_ci	(ARRAY_SIZE(asihpi_src_names) ==
13378c2ecf20Sopenharmony_ci		(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
13388c2ecf20Sopenharmony_ci	assert_src_names_size);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic const char * const asihpi_dst_names[] = {
13418c2ecf20Sopenharmony_ci	"no destination",
13428c2ecf20Sopenharmony_ci	"PCM",
13438c2ecf20Sopenharmony_ci	"Line",
13448c2ecf20Sopenharmony_ci	"Digital",
13458c2ecf20Sopenharmony_ci	"RF",
13468c2ecf20Sopenharmony_ci	"Speaker",
13478c2ecf20Sopenharmony_ci	"Net",
13488c2ecf20Sopenharmony_ci	"Analog",
13498c2ecf20Sopenharmony_ci	"RTP",
13508c2ecf20Sopenharmony_ci	"AVB",
13518c2ecf20Sopenharmony_ci	"Internal",
13528c2ecf20Sopenharmony_ci	"BLU-Link"
13538c2ecf20Sopenharmony_ci};
13548c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */
13558c2ecf20Sopenharmony_cicompile_time_assert(
13568c2ecf20Sopenharmony_ci	(ARRAY_SIZE(asihpi_dst_names) ==
13578c2ecf20Sopenharmony_ci		(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
13588c2ecf20Sopenharmony_ci	assert_dst_names_size);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
13618c2ecf20Sopenharmony_ci				struct snd_card_asihpi *asihpi)
13628c2ecf20Sopenharmony_ci{
13638c2ecf20Sopenharmony_ci	int err;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi));
13668c2ecf20Sopenharmony_ci	if (err < 0)
13678c2ecf20Sopenharmony_ci		return err;
13688c2ecf20Sopenharmony_ci	else if (mixer_dump)
13698c2ecf20Sopenharmony_ci		dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index);
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	return 0;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci/* Convert HPI control name and location into ALSA control name */
13758c2ecf20Sopenharmony_cistatic void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
13768c2ecf20Sopenharmony_ci				struct hpi_control *hpi_ctl,
13778c2ecf20Sopenharmony_ci				char *name)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	char *dir;
13808c2ecf20Sopenharmony_ci	memset(snd_control, 0, sizeof(*snd_control));
13818c2ecf20Sopenharmony_ci	snd_control->name = hpi_ctl->name;
13828c2ecf20Sopenharmony_ci	snd_control->private_value = hpi_ctl->h_control;
13838c2ecf20Sopenharmony_ci	snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
13848c2ecf20Sopenharmony_ci	snd_control->index = 0;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
13878c2ecf20Sopenharmony_ci		dir = ""; /* clock is neither capture nor playback */
13888c2ecf20Sopenharmony_ci	else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
13898c2ecf20Sopenharmony_ci		dir = "Capture ";  /* On or towards a PCM capture destination*/
13908c2ecf20Sopenharmony_ci	else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
13918c2ecf20Sopenharmony_ci		(!hpi_ctl->dst_node_type))
13928c2ecf20Sopenharmony_ci		dir = "Capture "; /* On a source node that is not PCM playback */
13938c2ecf20Sopenharmony_ci	else if (hpi_ctl->src_node_type &&
13948c2ecf20Sopenharmony_ci		(hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
13958c2ecf20Sopenharmony_ci		(hpi_ctl->dst_node_type))
13968c2ecf20Sopenharmony_ci		dir = "Monitor Playback "; /* Between an input and an output */
13978c2ecf20Sopenharmony_ci	else
13988c2ecf20Sopenharmony_ci		dir = "Playback "; /* PCM Playback source, or  output node */
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
14018c2ecf20Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
14028c2ecf20Sopenharmony_ci			asihpi_src_names[hpi_ctl->src_node_type],
14038c2ecf20Sopenharmony_ci			hpi_ctl->src_node_index,
14048c2ecf20Sopenharmony_ci			asihpi_dst_names[hpi_ctl->dst_node_type],
14058c2ecf20Sopenharmony_ci			hpi_ctl->dst_node_index,
14068c2ecf20Sopenharmony_ci			dir, name);
14078c2ecf20Sopenharmony_ci	else if (hpi_ctl->dst_node_type) {
14088c2ecf20Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s%s",
14098c2ecf20Sopenharmony_ci		asihpi_dst_names[hpi_ctl->dst_node_type],
14108c2ecf20Sopenharmony_ci		hpi_ctl->dst_node_index,
14118c2ecf20Sopenharmony_ci		dir, name);
14128c2ecf20Sopenharmony_ci	} else {
14138c2ecf20Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s%s",
14148c2ecf20Sopenharmony_ci		asihpi_src_names[hpi_ctl->src_node_type],
14158c2ecf20Sopenharmony_ci		hpi_ctl->src_node_index,
14168c2ecf20Sopenharmony_ci		dir, name);
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci	/* printk(KERN_INFO "Adding %s %d to %d ",  hpi_ctl->name,
14198c2ecf20Sopenharmony_ci		hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci/*------------------------------------------------------------
14238c2ecf20Sopenharmony_ci   Volume controls
14248c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
14258c2ecf20Sopenharmony_ci#define VOL_STEP_mB 1
14268c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
14278c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
14288c2ecf20Sopenharmony_ci{
14298c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
14308c2ecf20Sopenharmony_ci	u32 count;
14318c2ecf20Sopenharmony_ci	u16 err;
14328c2ecf20Sopenharmony_ci	/* native gains are in millibels */
14338c2ecf20Sopenharmony_ci	short min_gain_mB;
14348c2ecf20Sopenharmony_ci	short max_gain_mB;
14358c2ecf20Sopenharmony_ci	short step_gain_mB;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	err = hpi_volume_query_range(h_control,
14388c2ecf20Sopenharmony_ci			&min_gain_mB, &max_gain_mB, &step_gain_mB);
14398c2ecf20Sopenharmony_ci	if (err) {
14408c2ecf20Sopenharmony_ci		max_gain_mB = 0;
14418c2ecf20Sopenharmony_ci		min_gain_mB = -10000;
14428c2ecf20Sopenharmony_ci		step_gain_mB = VOL_STEP_mB;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	err = hpi_meter_query_channels(h_control, &count);
14468c2ecf20Sopenharmony_ci	if (err)
14478c2ecf20Sopenharmony_ci		count = HPI_MAX_CHANNELS;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
14508c2ecf20Sopenharmony_ci	uinfo->count = count;
14518c2ecf20Sopenharmony_ci	uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB;
14528c2ecf20Sopenharmony_ci	uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB;
14538c2ecf20Sopenharmony_ci	uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB;
14548c2ecf20Sopenharmony_ci	return 0;
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
14588c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
14598c2ecf20Sopenharmony_ci{
14608c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
14618c2ecf20Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB));
14648c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
14658c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	return 0;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
14718c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
14748c2ecf20Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	an_gain_mB[0] =
14778c2ecf20Sopenharmony_ci	    (ucontrol->value.integer.value[0]) * VOL_STEP_mB;
14788c2ecf20Sopenharmony_ci	an_gain_mB[1] =
14798c2ecf20Sopenharmony_ci	    (ucontrol->value.integer.value[1]) * VOL_STEP_mB;
14808c2ecf20Sopenharmony_ci	/*  change = asihpi->mixer_volume[addr][0] != left ||
14818c2ecf20Sopenharmony_ci	   asihpi->mixer_volume[addr][1] != right;
14828c2ecf20Sopenharmony_ci	 */
14838c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
14848c2ecf20Sopenharmony_ci	return 1;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci#define snd_asihpi_volume_mute_info	snd_ctl_boolean_mono_info
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol,
14928c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
14958c2ecf20Sopenharmony_ci	u32 mute;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_volume_get_mute(h_control, &mute));
14988c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = mute ? 0 : 1;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	return 0;
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
15048c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
15058c2ecf20Sopenharmony_ci{
15068c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
15078c2ecf20Sopenharmony_ci	/* HPI currently only supports all or none muting of multichannel volume
15088c2ecf20Sopenharmony_ci	ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
15098c2ecf20Sopenharmony_ci	*/
15108c2ecf20Sopenharmony_ci	int mute =  ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
15118c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_volume_set_mute(h_control, mute));
15128c2ecf20Sopenharmony_ci	return 1;
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
15168c2ecf20Sopenharmony_ci				 struct hpi_control *hpi_ctl)
15178c2ecf20Sopenharmony_ci{
15188c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
15198c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
15208c2ecf20Sopenharmony_ci	int err;
15218c2ecf20Sopenharmony_ci	u32 mute;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
15248c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
15258c2ecf20Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
15268c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_volume_info;
15278c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_volume_get;
15288c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_volume_put;
15298c2ecf20Sopenharmony_ci	snd_control.tlv.p = db_scale_100;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	err = ctl_add(card, &snd_control, asihpi);
15328c2ecf20Sopenharmony_ci	if (err)
15338c2ecf20Sopenharmony_ci		return err;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) {
15368c2ecf20Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Switch");
15378c2ecf20Sopenharmony_ci		snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
15388c2ecf20Sopenharmony_ci		snd_control.info = snd_asihpi_volume_mute_info;
15398c2ecf20Sopenharmony_ci		snd_control.get = snd_asihpi_volume_mute_get;
15408c2ecf20Sopenharmony_ci		snd_control.put = snd_asihpi_volume_mute_put;
15418c2ecf20Sopenharmony_ci		err = ctl_add(card, &snd_control, asihpi);
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci	return err;
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci/*------------------------------------------------------------
15478c2ecf20Sopenharmony_ci   Level controls
15488c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
15498c2ecf20Sopenharmony_cistatic int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
15508c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
15538c2ecf20Sopenharmony_ci	u16 err;
15548c2ecf20Sopenharmony_ci	short min_gain_mB;
15558c2ecf20Sopenharmony_ci	short max_gain_mB;
15568c2ecf20Sopenharmony_ci	short step_gain_mB;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	err =
15598c2ecf20Sopenharmony_ci	    hpi_level_query_range(h_control, &min_gain_mB,
15608c2ecf20Sopenharmony_ci			       &max_gain_mB, &step_gain_mB);
15618c2ecf20Sopenharmony_ci	if (err) {
15628c2ecf20Sopenharmony_ci		max_gain_mB = 2400;
15638c2ecf20Sopenharmony_ci		min_gain_mB = -1000;
15648c2ecf20Sopenharmony_ci		step_gain_mB = 100;
15658c2ecf20Sopenharmony_ci	}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
15688c2ecf20Sopenharmony_ci	uinfo->count = 2;
15698c2ecf20Sopenharmony_ci	uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB;
15708c2ecf20Sopenharmony_ci	uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB;
15718c2ecf20Sopenharmony_ci	uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB;
15728c2ecf20Sopenharmony_ci	return 0;
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_cistatic int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
15768c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
15798c2ecf20Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB));
15828c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
15838c2ecf20Sopenharmony_ci	    an_gain_mB[0] / HPI_UNITS_PER_dB;
15848c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] =
15858c2ecf20Sopenharmony_ci	    an_gain_mB[1] / HPI_UNITS_PER_dB;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	return 0;
15888c2ecf20Sopenharmony_ci}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_cistatic int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
15918c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
15928c2ecf20Sopenharmony_ci{
15938c2ecf20Sopenharmony_ci	int change;
15948c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
15958c2ecf20Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	an_gain_mB[0] =
15988c2ecf20Sopenharmony_ci	    (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
15998c2ecf20Sopenharmony_ci	an_gain_mB[1] =
16008c2ecf20Sopenharmony_ci	    (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB;
16018c2ecf20Sopenharmony_ci	/*  change = asihpi->mixer_level[addr][0] != left ||
16028c2ecf20Sopenharmony_ci	   asihpi->mixer_level[addr][1] != right;
16038c2ecf20Sopenharmony_ci	 */
16048c2ecf20Sopenharmony_ci	change = 1;
16058c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB));
16068c2ecf20Sopenharmony_ci	return change;
16078c2ecf20Sopenharmony_ci}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic int snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
16128c2ecf20Sopenharmony_ci				struct hpi_control *hpi_ctl)
16138c2ecf20Sopenharmony_ci{
16148c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
16158c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/* can't use 'volume' cos some nodes have volume as well */
16188c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Level");
16198c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
16208c2ecf20Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
16218c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_level_info;
16228c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_level_get;
16238c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_level_put;
16248c2ecf20Sopenharmony_ci	snd_control.tlv.p = db_scale_level;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci/*------------------------------------------------------------
16308c2ecf20Sopenharmony_ci   AESEBU controls
16318c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci/* AESEBU format */
16348c2ecf20Sopenharmony_cistatic const char * const asihpi_aesebu_format_names[] = {
16358c2ecf20Sopenharmony_ci	"N/A", "S/PDIF", "AES/EBU" };
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
16388c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names);
16418c2ecf20Sopenharmony_ci}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
16448c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol,
16458c2ecf20Sopenharmony_ci			u16 (*func)(u32, u16 *))
16468c2ecf20Sopenharmony_ci{
16478c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
16488c2ecf20Sopenharmony_ci	u16 source, err;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	err = func(h_control, &source);
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	/* default to N/A */
16538c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
16548c2ecf20Sopenharmony_ci	/* return success but set the control to N/A */
16558c2ecf20Sopenharmony_ci	if (err)
16568c2ecf20Sopenharmony_ci		return 0;
16578c2ecf20Sopenharmony_ci	if (source == HPI_AESEBU_FORMAT_SPDIF)
16588c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 1;
16598c2ecf20Sopenharmony_ci	if (source == HPI_AESEBU_FORMAT_AESEBU)
16608c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 2;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	return 0;
16638c2ecf20Sopenharmony_ci}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
16668c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol,
16678c2ecf20Sopenharmony_ci			 u16 (*func)(u32, u16))
16688c2ecf20Sopenharmony_ci{
16698c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	/* default to S/PDIF */
16728c2ecf20Sopenharmony_ci	u16 source = HPI_AESEBU_FORMAT_SPDIF;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 1)
16758c2ecf20Sopenharmony_ci		source = HPI_AESEBU_FORMAT_SPDIF;
16768c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 2)
16778c2ecf20Sopenharmony_ci		source = HPI_AESEBU_FORMAT_AESEBU;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (func(h_control, source) != 0)
16808c2ecf20Sopenharmony_ci		return -EINVAL;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return 1;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
16868c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
16878c2ecf20Sopenharmony_ci	return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
16888c2ecf20Sopenharmony_ci					hpi_aesebu_receiver_get_format);
16898c2ecf20Sopenharmony_ci}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
16928c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
16938c2ecf20Sopenharmony_ci	return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
16948c2ecf20Sopenharmony_ci					hpi_aesebu_receiver_set_format);
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
16988c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
17018c2ecf20Sopenharmony_ci	uinfo->count = 1;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
17048c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 0X1F;
17058c2ecf20Sopenharmony_ci	uinfo->value.integer.step = 1;
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	return 0;
17088c2ecf20Sopenharmony_ci}
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
17118c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
17148c2ecf20Sopenharmony_ci	u16 status;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_aesebu_receiver_get_error_status(
17178c2ecf20Sopenharmony_ci					 h_control, &status));
17188c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = status;
17198c2ecf20Sopenharmony_ci	return 0;
17208c2ecf20Sopenharmony_ci}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
17238c2ecf20Sopenharmony_ci				    struct hpi_control *hpi_ctl)
17248c2ecf20Sopenharmony_ci{
17258c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
17268c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
17298c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
17308c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_format_info;
17318c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_rx_format_get;
17328c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_aesebu_rx_format_put;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
17368c2ecf20Sopenharmony_ci		return -EINVAL;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Status");
17398c2ecf20Sopenharmony_ci	snd_control.access =
17408c2ecf20Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
17418c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_rxstatus_info;
17428c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_rxstatus_get;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
17458c2ecf20Sopenharmony_ci}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
17488c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
17498c2ecf20Sopenharmony_ci	return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
17508c2ecf20Sopenharmony_ci					hpi_aesebu_transmitter_get_format);
17518c2ecf20Sopenharmony_ci}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
17548c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
17558c2ecf20Sopenharmony_ci	return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
17568c2ecf20Sopenharmony_ci					hpi_aesebu_transmitter_set_format);
17578c2ecf20Sopenharmony_ci}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
17618c2ecf20Sopenharmony_ci				    struct hpi_control *hpi_ctl)
17628c2ecf20Sopenharmony_ci{
17638c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
17648c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
17678c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
17688c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_format_info;
17698c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_tx_format_get;
17708c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_aesebu_tx_format_put;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci/*------------------------------------------------------------
17768c2ecf20Sopenharmony_ci   Tuner controls
17778c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci/* Gain */
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
17828c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
17858c2ecf20Sopenharmony_ci	u16 err;
17868c2ecf20Sopenharmony_ci	short idx;
17878c2ecf20Sopenharmony_ci	u16 gain_range[3];
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	for (idx = 0; idx < 3; idx++) {
17908c2ecf20Sopenharmony_ci		err = hpi_tuner_query_gain(h_control,
17918c2ecf20Sopenharmony_ci					  idx, &gain_range[idx]);
17928c2ecf20Sopenharmony_ci		if (err != 0)
17938c2ecf20Sopenharmony_ci			return err;
17948c2ecf20Sopenharmony_ci	}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
17978c2ecf20Sopenharmony_ci	uinfo->count = 1;
17988c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB;
17998c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB;
18008c2ecf20Sopenharmony_ci	uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB;
18018c2ecf20Sopenharmony_ci	return 0;
18028c2ecf20Sopenharmony_ci}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
18058c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
18068c2ecf20Sopenharmony_ci{
18078c2ecf20Sopenharmony_ci	/*
18088c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
18098c2ecf20Sopenharmony_ci	*/
18108c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
18118c2ecf20Sopenharmony_ci	short gain;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_gain(h_control, &gain));
18148c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	return 0;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
18208c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
18218c2ecf20Sopenharmony_ci{
18228c2ecf20Sopenharmony_ci	/*
18238c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
18248c2ecf20Sopenharmony_ci	*/
18258c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
18268c2ecf20Sopenharmony_ci	short gain;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
18298c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_gain(h_control, gain));
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	return 1;
18328c2ecf20Sopenharmony_ci}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci/* Band  */
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
18378c2ecf20Sopenharmony_ci					u16 *band_list, u32 len) {
18388c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
18398c2ecf20Sopenharmony_ci	u16 err = 0;
18408c2ecf20Sopenharmony_ci	u32 i;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
18438c2ecf20Sopenharmony_ci		err = hpi_tuner_query_band(
18448c2ecf20Sopenharmony_ci				h_control, i, &band_list[i]);
18458c2ecf20Sopenharmony_ci		if (err != 0)
18468c2ecf20Sopenharmony_ci			break;
18478c2ecf20Sopenharmony_ci	}
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX))
18508c2ecf20Sopenharmony_ci		return -EIO;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	return i;
18538c2ecf20Sopenharmony_ci}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
18568c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
18578c2ecf20Sopenharmony_ci{
18588c2ecf20Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
18598c2ecf20Sopenharmony_ci	int num_bands = 0;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
18628c2ecf20Sopenharmony_ci				HPI_TUNER_BAND_LAST);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (num_bands < 0)
18658c2ecf20Sopenharmony_ci		return num_bands;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names);
18688c2ecf20Sopenharmony_ci}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
18718c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
18728c2ecf20Sopenharmony_ci{
18738c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
18748c2ecf20Sopenharmony_ci	/*
18758c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
18768c2ecf20Sopenharmony_ci	*/
18778c2ecf20Sopenharmony_ci	u16 band, idx;
18788c2ecf20Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
18798c2ecf20Sopenharmony_ci	__always_unused u32 num_bands;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
18828c2ecf20Sopenharmony_ci				HPI_TUNER_BAND_LAST);
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_band(h_control, &band));
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = -1;
18878c2ecf20Sopenharmony_ci	for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
18888c2ecf20Sopenharmony_ci		if (tuner_bands[idx] == band) {
18898c2ecf20Sopenharmony_ci			ucontrol->value.enumerated.item[0] = idx;
18908c2ecf20Sopenharmony_ci			break;
18918c2ecf20Sopenharmony_ci		}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	return 0;
18948c2ecf20Sopenharmony_ci}
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
18978c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	/*
19008c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
19018c2ecf20Sopenharmony_ci	*/
19028c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
19038c2ecf20Sopenharmony_ci	unsigned int idx;
19048c2ecf20Sopenharmony_ci	u16 band;
19058c2ecf20Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
19068c2ecf20Sopenharmony_ci	__always_unused u32 num_bands;
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
19098c2ecf20Sopenharmony_ci			HPI_TUNER_BAND_LAST);
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	idx = ucontrol->value.enumerated.item[0];
19128c2ecf20Sopenharmony_ci	if (idx >= ARRAY_SIZE(tuner_bands))
19138c2ecf20Sopenharmony_ci		idx = ARRAY_SIZE(tuner_bands) - 1;
19148c2ecf20Sopenharmony_ci	band = tuner_bands[idx];
19158c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_band(h_control, band));
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	return 1;
19188c2ecf20Sopenharmony_ci}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci/* Freq */
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
19238c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
19248c2ecf20Sopenharmony_ci{
19258c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
19268c2ecf20Sopenharmony_ci	u16 err;
19278c2ecf20Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
19288c2ecf20Sopenharmony_ci	u16 num_bands = 0, band_iter, idx;
19298c2ecf20Sopenharmony_ci	u32 freq_range[3], temp_freq_range[3];
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
19328c2ecf20Sopenharmony_ci			HPI_TUNER_BAND_LAST);
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	freq_range[0] = INT_MAX;
19358c2ecf20Sopenharmony_ci	freq_range[1] = 0;
19368c2ecf20Sopenharmony_ci	freq_range[2] = INT_MAX;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	for (band_iter = 0; band_iter < num_bands; band_iter++) {
19398c2ecf20Sopenharmony_ci		for (idx = 0; idx < 3; idx++) {
19408c2ecf20Sopenharmony_ci			err = hpi_tuner_query_frequency(h_control,
19418c2ecf20Sopenharmony_ci				idx, tuner_bands[band_iter],
19428c2ecf20Sopenharmony_ci				&temp_freq_range[idx]);
19438c2ecf20Sopenharmony_ci			if (err != 0)
19448c2ecf20Sopenharmony_ci				return err;
19458c2ecf20Sopenharmony_ci		}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci		/* skip band with bogus stepping */
19488c2ecf20Sopenharmony_ci		if (temp_freq_range[2] <= 0)
19498c2ecf20Sopenharmony_ci			continue;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci		if (temp_freq_range[0] < freq_range[0])
19528c2ecf20Sopenharmony_ci			freq_range[0] = temp_freq_range[0];
19538c2ecf20Sopenharmony_ci		if (temp_freq_range[1] > freq_range[1])
19548c2ecf20Sopenharmony_ci			freq_range[1] = temp_freq_range[1];
19558c2ecf20Sopenharmony_ci		if (temp_freq_range[2] < freq_range[2])
19568c2ecf20Sopenharmony_ci			freq_range[2] = temp_freq_range[2];
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
19608c2ecf20Sopenharmony_ci	uinfo->count = 1;
19618c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ((int)freq_range[0]);
19628c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ((int)freq_range[1]);
19638c2ecf20Sopenharmony_ci	uinfo->value.integer.step = ((int)freq_range[2]);
19648c2ecf20Sopenharmony_ci	return 0;
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
19688c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
19698c2ecf20Sopenharmony_ci{
19708c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
19718c2ecf20Sopenharmony_ci	u32 freq;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq));
19748c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = freq;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	return 0;
19778c2ecf20Sopenharmony_ci}
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
19808c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
19818c2ecf20Sopenharmony_ci{
19828c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
19838c2ecf20Sopenharmony_ci	u32 freq;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	freq = ucontrol->value.integer.value[0];
19868c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_frequency(h_control, freq));
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	return 1;
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci/* Tuner control group initializer  */
19928c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
19938c2ecf20Sopenharmony_ci				struct hpi_control *hpi_ctl)
19948c2ecf20Sopenharmony_ci{
19958c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
19968c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	snd_control.private_value = hpi_ctl->h_control;
19998c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) {
20028c2ecf20Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Gain");
20038c2ecf20Sopenharmony_ci		snd_control.info = snd_asihpi_tuner_gain_info;
20048c2ecf20Sopenharmony_ci		snd_control.get = snd_asihpi_tuner_gain_get;
20058c2ecf20Sopenharmony_ci		snd_control.put = snd_asihpi_tuner_gain_put;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci		if (ctl_add(card, &snd_control, asihpi) < 0)
20088c2ecf20Sopenharmony_ci			return -EINVAL;
20098c2ecf20Sopenharmony_ci	}
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Band");
20128c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_tuner_band_info;
20138c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_tuner_band_get;
20148c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_tuner_band_put;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
20178c2ecf20Sopenharmony_ci		return -EINVAL;
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Freq");
20208c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_tuner_freq_info;
20218c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_tuner_freq_get;
20228c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_tuner_freq_put;
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
20258c2ecf20Sopenharmony_ci}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci/*------------------------------------------------------------
20288c2ecf20Sopenharmony_ci   Meter controls
20298c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
20308c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
20318c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
20328c2ecf20Sopenharmony_ci{
20338c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
20348c2ecf20Sopenharmony_ci	u32 count;
20358c2ecf20Sopenharmony_ci	u16 err;
20368c2ecf20Sopenharmony_ci	err = hpi_meter_query_channels(h_control, &count);
20378c2ecf20Sopenharmony_ci	if (err)
20388c2ecf20Sopenharmony_ci		count = HPI_MAX_CHANNELS;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
20418c2ecf20Sopenharmony_ci	uinfo->count = count;
20428c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
20438c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 0x7FFFFFFF;
20448c2ecf20Sopenharmony_ci	return 0;
20458c2ecf20Sopenharmony_ci}
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci/* linear values for 10dB steps */
20488c2ecf20Sopenharmony_cistatic const int log2lin[] = {
20498c2ecf20Sopenharmony_ci	0x7FFFFFFF, /* 0dB */
20508c2ecf20Sopenharmony_ci	679093956,
20518c2ecf20Sopenharmony_ci	214748365,
20528c2ecf20Sopenharmony_ci	 67909396,
20538c2ecf20Sopenharmony_ci	 21474837,
20548c2ecf20Sopenharmony_ci	  6790940,
20558c2ecf20Sopenharmony_ci	  2147484, /* -60dB */
20568c2ecf20Sopenharmony_ci	   679094,
20578c2ecf20Sopenharmony_ci	   214748, /* -80 */
20588c2ecf20Sopenharmony_ci	    67909,
20598c2ecf20Sopenharmony_ci	    21475, /* -100 */
20608c2ecf20Sopenharmony_ci	     6791,
20618c2ecf20Sopenharmony_ci	     2147,
20628c2ecf20Sopenharmony_ci	      679,
20638c2ecf20Sopenharmony_ci	      214,
20648c2ecf20Sopenharmony_ci	       68,
20658c2ecf20Sopenharmony_ci	       21,
20668c2ecf20Sopenharmony_ci		7,
20678c2ecf20Sopenharmony_ci		2
20688c2ecf20Sopenharmony_ci};
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
20718c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
20728c2ecf20Sopenharmony_ci{
20738c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
20748c2ecf20Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS], i;
20758c2ecf20Sopenharmony_ci	u16 err;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	err = hpi_meter_get_peak(h_control, an_gain_mB);
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	for (i = 0; i < HPI_MAX_CHANNELS; i++) {
20808c2ecf20Sopenharmony_ci		if (err) {
20818c2ecf20Sopenharmony_ci			ucontrol->value.integer.value[i] = 0;
20828c2ecf20Sopenharmony_ci		} else if (an_gain_mB[i] >= 0) {
20838c2ecf20Sopenharmony_ci			ucontrol->value.integer.value[i] =
20848c2ecf20Sopenharmony_ci				an_gain_mB[i] << 16;
20858c2ecf20Sopenharmony_ci		} else {
20868c2ecf20Sopenharmony_ci			/* -ve is log value in millibels < -60dB,
20878c2ecf20Sopenharmony_ci			* convert to (roughly!) linear,
20888c2ecf20Sopenharmony_ci			*/
20898c2ecf20Sopenharmony_ci			ucontrol->value.integer.value[i] =
20908c2ecf20Sopenharmony_ci					log2lin[an_gain_mB[i] / -1000];
20918c2ecf20Sopenharmony_ci		}
20928c2ecf20Sopenharmony_ci	}
20938c2ecf20Sopenharmony_ci	return 0;
20948c2ecf20Sopenharmony_ci}
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
20978c2ecf20Sopenharmony_ci				struct hpi_control *hpi_ctl, int subidx)
20988c2ecf20Sopenharmony_ci{
20998c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
21008c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Meter");
21038c2ecf20Sopenharmony_ci	snd_control.access =
21048c2ecf20Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
21058c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_meter_info;
21068c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_meter_get;
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	snd_control.index = subidx;
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
21118c2ecf20Sopenharmony_ci}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci/*------------------------------------------------------------
21148c2ecf20Sopenharmony_ci   Multiplexer controls
21158c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
21168c2ecf20Sopenharmony_cistatic int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
21178c2ecf20Sopenharmony_ci{
21188c2ecf20Sopenharmony_ci	u32 h_control = snd_control->private_value;
21198c2ecf20Sopenharmony_ci	struct hpi_control hpi_ctl;
21208c2ecf20Sopenharmony_ci	int s, err;
21218c2ecf20Sopenharmony_ci	for (s = 0; s < 32; s++) {
21228c2ecf20Sopenharmony_ci		err = hpi_multiplexer_query_source(h_control, s,
21238c2ecf20Sopenharmony_ci						  &hpi_ctl.
21248c2ecf20Sopenharmony_ci						  src_node_type,
21258c2ecf20Sopenharmony_ci						  &hpi_ctl.
21268c2ecf20Sopenharmony_ci						  src_node_index);
21278c2ecf20Sopenharmony_ci		if (err)
21288c2ecf20Sopenharmony_ci			break;
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci	return s;
21318c2ecf20Sopenharmony_ci}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
21348c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
21358c2ecf20Sopenharmony_ci{
21368c2ecf20Sopenharmony_ci	u16 src_node_type, src_node_index;
21378c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
21408c2ecf20Sopenharmony_ci	uinfo->count = 1;
21418c2ecf20Sopenharmony_ci	uinfo->value.enumerated.items =
21428c2ecf20Sopenharmony_ci	    snd_card_asihpi_mux_count_sources(kcontrol);
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
21458c2ecf20Sopenharmony_ci		uinfo->value.enumerated.item =
21468c2ecf20Sopenharmony_ci		    uinfo->value.enumerated.items - 1;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	hpi_multiplexer_query_source(h_control,
21498c2ecf20Sopenharmony_ci				     uinfo->value.enumerated.item,
21508c2ecf20Sopenharmony_ci				     &src_node_type, &src_node_index);
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	sprintf(uinfo->value.enumerated.name, "%s %d",
21538c2ecf20Sopenharmony_ci		asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE],
21548c2ecf20Sopenharmony_ci		src_node_index);
21558c2ecf20Sopenharmony_ci	return 0;
21568c2ecf20Sopenharmony_ci}
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
21598c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
21608c2ecf20Sopenharmony_ci{
21618c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
21628c2ecf20Sopenharmony_ci	u16 source_type, source_index;
21638c2ecf20Sopenharmony_ci	u16 src_node_type, src_node_index;
21648c2ecf20Sopenharmony_ci	int s;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_multiplexer_get_source(h_control,
21678c2ecf20Sopenharmony_ci				&source_type, &source_index));
21688c2ecf20Sopenharmony_ci	/* Should cache this search result! */
21698c2ecf20Sopenharmony_ci	for (s = 0; s < 256; s++) {
21708c2ecf20Sopenharmony_ci		if (hpi_multiplexer_query_source(h_control, s,
21718c2ecf20Sopenharmony_ci					    &src_node_type, &src_node_index))
21728c2ecf20Sopenharmony_ci			break;
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci		if ((source_type == src_node_type)
21758c2ecf20Sopenharmony_ci		    && (source_index == src_node_index)) {
21768c2ecf20Sopenharmony_ci			ucontrol->value.enumerated.item[0] = s;
21778c2ecf20Sopenharmony_ci			return 0;
21788c2ecf20Sopenharmony_ci		}
21798c2ecf20Sopenharmony_ci	}
21808c2ecf20Sopenharmony_ci	snd_printd(KERN_WARNING
21818c2ecf20Sopenharmony_ci		"Control %x failed to match mux source %hu %hu\n",
21828c2ecf20Sopenharmony_ci		h_control, source_type, source_index);
21838c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
21848c2ecf20Sopenharmony_ci	return 0;
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
21888c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	int change;
21918c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
21928c2ecf20Sopenharmony_ci	u16 source_type, source_index;
21938c2ecf20Sopenharmony_ci	u16 e;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	change = 1;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	e = hpi_multiplexer_query_source(h_control,
21988c2ecf20Sopenharmony_ci				    ucontrol->value.enumerated.item[0],
21998c2ecf20Sopenharmony_ci				    &source_type, &source_index);
22008c2ecf20Sopenharmony_ci	if (!e)
22018c2ecf20Sopenharmony_ci		hpi_handle_error(
22028c2ecf20Sopenharmony_ci			hpi_multiplexer_set_source(h_control,
22038c2ecf20Sopenharmony_ci						source_type, source_index));
22048c2ecf20Sopenharmony_ci	return change;
22058c2ecf20Sopenharmony_ci}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_cistatic int  snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
22098c2ecf20Sopenharmony_ci			       struct hpi_control *hpi_ctl)
22108c2ecf20Sopenharmony_ci{
22118c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
22128c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Route");
22158c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
22168c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_mux_info;
22178c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_mux_get;
22188c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_mux_put;
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci}
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci/*------------------------------------------------------------
22258c2ecf20Sopenharmony_ci   Channel mode controls
22268c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
22278c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
22288c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
22298c2ecf20Sopenharmony_ci{
22308c2ecf20Sopenharmony_ci	static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = {
22318c2ecf20Sopenharmony_ci		"invalid",
22328c2ecf20Sopenharmony_ci		"Normal", "Swap",
22338c2ecf20Sopenharmony_ci		"From Left", "From Right",
22348c2ecf20Sopenharmony_ci		"To Left", "To Right"
22358c2ecf20Sopenharmony_ci	};
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
22388c2ecf20Sopenharmony_ci	u16 mode;
22398c2ecf20Sopenharmony_ci	int i;
22408c2ecf20Sopenharmony_ci	const char *mapped_names[6];
22418c2ecf20Sopenharmony_ci	int valid_modes = 0;
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	/* HPI channel mode values can be from 1 to 6
22448c2ecf20Sopenharmony_ci	Some adapters only support a contiguous subset
22458c2ecf20Sopenharmony_ci	*/
22468c2ecf20Sopenharmony_ci	for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
22478c2ecf20Sopenharmony_ci		if (!hpi_channel_mode_query_mode(
22488c2ecf20Sopenharmony_ci			h_control, i, &mode)) {
22498c2ecf20Sopenharmony_ci			mapped_names[valid_modes] = mode_names[mode];
22508c2ecf20Sopenharmony_ci			valid_modes++;
22518c2ecf20Sopenharmony_ci			}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	if (!valid_modes)
22548c2ecf20Sopenharmony_ci		return -EINVAL;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names);
22578c2ecf20Sopenharmony_ci}
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
22608c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
22618c2ecf20Sopenharmony_ci{
22628c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
22638c2ecf20Sopenharmony_ci	u16 mode;
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	if (hpi_channel_mode_get(h_control, &mode))
22668c2ecf20Sopenharmony_ci		mode = 1;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = mode - 1;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	return 0;
22718c2ecf20Sopenharmony_ci}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
22748c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
22758c2ecf20Sopenharmony_ci{
22768c2ecf20Sopenharmony_ci	int change;
22778c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	change = 1;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_channel_mode_set(h_control,
22828c2ecf20Sopenharmony_ci			   ucontrol->value.enumerated.item[0] + 1));
22838c2ecf20Sopenharmony_ci	return change;
22848c2ecf20Sopenharmony_ci}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
22888c2ecf20Sopenharmony_ci				struct hpi_control *hpi_ctl)
22898c2ecf20Sopenharmony_ci{
22908c2ecf20Sopenharmony_ci	struct snd_card *card = asihpi->card;
22918c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Mode");
22948c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
22958c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_cmode_info;
22968c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_cmode_get;
22978c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_cmode_put;
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
23008c2ecf20Sopenharmony_ci}
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci/*------------------------------------------------------------
23038c2ecf20Sopenharmony_ci   Sampleclock source  controls
23048c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
23058c2ecf20Sopenharmony_cistatic const char * const sampleclock_sources[] = {
23068c2ecf20Sopenharmony_ci	"N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
23078c2ecf20Sopenharmony_ci	"SMPTE", "Digital1", "Auto", "Network", "Invalid",
23088c2ecf20Sopenharmony_ci	"Prev Module", "BLU-Link",
23098c2ecf20Sopenharmony_ci	"Digital2", "Digital3", "Digital4", "Digital5",
23108c2ecf20Sopenharmony_ci	"Digital6", "Digital7", "Digital8"};
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	/* Number of strings must match expected enumerated values */
23138c2ecf20Sopenharmony_ci	compile_time_assert(
23148c2ecf20Sopenharmony_ci		(ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES),
23158c2ecf20Sopenharmony_ci		assert_sampleclock_sources_size);
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
23188c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
23198c2ecf20Sopenharmony_ci{
23208c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi =
23218c2ecf20Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
23228c2ecf20Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
23238c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
23248c2ecf20Sopenharmony_ci	uinfo->count = 1;
23258c2ecf20Sopenharmony_ci	uinfo->value.enumerated.items = clkcache->count;
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
23288c2ecf20Sopenharmony_ci		uinfo->value.enumerated.item =
23298c2ecf20Sopenharmony_ci				uinfo->value.enumerated.items - 1;
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	strcpy(uinfo->value.enumerated.name,
23328c2ecf20Sopenharmony_ci	       clkcache->s[uinfo->value.enumerated.item].name);
23338c2ecf20Sopenharmony_ci	return 0;
23348c2ecf20Sopenharmony_ci}
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
23378c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
23388c2ecf20Sopenharmony_ci{
23398c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi =
23408c2ecf20Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
23418c2ecf20Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
23428c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
23438c2ecf20Sopenharmony_ci	u16 source, srcindex = 0;
23448c2ecf20Sopenharmony_ci	int i;
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
23478c2ecf20Sopenharmony_ci	if (hpi_sample_clock_get_source(h_control, &source))
23488c2ecf20Sopenharmony_ci		source = 0;
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci	if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
23518c2ecf20Sopenharmony_ci		if (hpi_sample_clock_get_source_index(h_control, &srcindex))
23528c2ecf20Sopenharmony_ci			srcindex = 0;
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	for (i = 0; i < clkcache->count; i++)
23558c2ecf20Sopenharmony_ci		if ((clkcache->s[i].source == source) &&
23568c2ecf20Sopenharmony_ci			(clkcache->s[i].index == srcindex))
23578c2ecf20Sopenharmony_ci			break;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = i;
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	return 0;
23628c2ecf20Sopenharmony_ci}
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
23658c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
23668c2ecf20Sopenharmony_ci{
23678c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi =
23688c2ecf20Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
23698c2ecf20Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
23708c2ecf20Sopenharmony_ci	unsigned int item;
23718c2ecf20Sopenharmony_ci	int change;
23728c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	change = 1;
23758c2ecf20Sopenharmony_ci	item = ucontrol->value.enumerated.item[0];
23768c2ecf20Sopenharmony_ci	if (item >= clkcache->count)
23778c2ecf20Sopenharmony_ci		item = clkcache->count-1;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_sample_clock_set_source(
23808c2ecf20Sopenharmony_ci				h_control, clkcache->s[item].source));
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
23838c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_sample_clock_set_source_index(
23848c2ecf20Sopenharmony_ci				h_control, clkcache->s[item].index));
23858c2ecf20Sopenharmony_ci	return change;
23868c2ecf20Sopenharmony_ci}
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci/*------------------------------------------------------------
23898c2ecf20Sopenharmony_ci   Clkrate controls
23908c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
23918c2ecf20Sopenharmony_ci/* Need to change this to enumerated control with list of rates */
23928c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol,
23938c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
23948c2ecf20Sopenharmony_ci{
23958c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
23968c2ecf20Sopenharmony_ci	uinfo->count = 1;
23978c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 8000;
23988c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 192000;
23998c2ecf20Sopenharmony_ci	uinfo->value.integer.step = 100;
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	return 0;
24028c2ecf20Sopenharmony_ci}
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
24058c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
24068c2ecf20Sopenharmony_ci{
24078c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
24088c2ecf20Sopenharmony_ci	u32 rate;
24098c2ecf20Sopenharmony_ci	u16 e;
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	e = hpi_sample_clock_get_local_rate(h_control, &rate);
24128c2ecf20Sopenharmony_ci	if (!e)
24138c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = rate;
24148c2ecf20Sopenharmony_ci	else
24158c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
24168c2ecf20Sopenharmony_ci	return 0;
24178c2ecf20Sopenharmony_ci}
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
24208c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
24218c2ecf20Sopenharmony_ci{
24228c2ecf20Sopenharmony_ci	int change;
24238c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	/*  change = asihpi->mixer_clkrate[addr][0] != left ||
24268c2ecf20Sopenharmony_ci	   asihpi->mixer_clkrate[addr][1] != right;
24278c2ecf20Sopenharmony_ci	 */
24288c2ecf20Sopenharmony_ci	change = 1;
24298c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_sample_clock_set_local_rate(h_control,
24308c2ecf20Sopenharmony_ci				      ucontrol->value.integer.value[0]));
24318c2ecf20Sopenharmony_ci	return change;
24328c2ecf20Sopenharmony_ci}
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_cistatic int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol,
24358c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
24368c2ecf20Sopenharmony_ci{
24378c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
24388c2ecf20Sopenharmony_ci	uinfo->count = 1;
24398c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 8000;
24408c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 192000;
24418c2ecf20Sopenharmony_ci	uinfo->value.integer.step = 100;
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	return 0;
24448c2ecf20Sopenharmony_ci}
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_cistatic int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
24478c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
24488c2ecf20Sopenharmony_ci{
24498c2ecf20Sopenharmony_ci	u32 h_control = kcontrol->private_value;
24508c2ecf20Sopenharmony_ci	u32 rate;
24518c2ecf20Sopenharmony_ci	u16 e;
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	e = hpi_sample_clock_get_sample_rate(h_control, &rate);
24548c2ecf20Sopenharmony_ci	if (!e)
24558c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = rate;
24568c2ecf20Sopenharmony_ci	else
24578c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
24588c2ecf20Sopenharmony_ci	return 0;
24598c2ecf20Sopenharmony_ci}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_cistatic int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
24628c2ecf20Sopenharmony_ci				      struct hpi_control *hpi_ctl)
24638c2ecf20Sopenharmony_ci{
24648c2ecf20Sopenharmony_ci	struct snd_card *card;
24658c2ecf20Sopenharmony_ci	struct snd_kcontrol_new snd_control;
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	struct clk_cache *clkcache;
24688c2ecf20Sopenharmony_ci	u32 hSC =  hpi_ctl->h_control;
24698c2ecf20Sopenharmony_ci	int has_aes_in = 0;
24708c2ecf20Sopenharmony_ci	int i, j;
24718c2ecf20Sopenharmony_ci	u16 source;
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!asihpi))
24748c2ecf20Sopenharmony_ci		return -EINVAL;
24758c2ecf20Sopenharmony_ci	card = asihpi->card;
24768c2ecf20Sopenharmony_ci	clkcache = &asihpi->cc;
24778c2ecf20Sopenharmony_ci	snd_control.private_value = hpi_ctl->h_control;
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	clkcache->has_local = 0;
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
24828c2ecf20Sopenharmony_ci		if  (hpi_sample_clock_query_source(hSC,
24838c2ecf20Sopenharmony_ci				i, &source))
24848c2ecf20Sopenharmony_ci			break;
24858c2ecf20Sopenharmony_ci		clkcache->s[i].source = source;
24868c2ecf20Sopenharmony_ci		clkcache->s[i].index = 0;
24878c2ecf20Sopenharmony_ci		clkcache->s[i].name = sampleclock_sources[source];
24888c2ecf20Sopenharmony_ci		if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
24898c2ecf20Sopenharmony_ci			has_aes_in = 1;
24908c2ecf20Sopenharmony_ci		if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL)
24918c2ecf20Sopenharmony_ci			clkcache->has_local = 1;
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci	if (has_aes_in)
24948c2ecf20Sopenharmony_ci		/* already will have picked up index 0 above */
24958c2ecf20Sopenharmony_ci		for (j = 1; j < 8; j++) {
24968c2ecf20Sopenharmony_ci			if (hpi_sample_clock_query_source_index(hSC,
24978c2ecf20Sopenharmony_ci				j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
24988c2ecf20Sopenharmony_ci				&source))
24998c2ecf20Sopenharmony_ci				break;
25008c2ecf20Sopenharmony_ci			clkcache->s[i].source =
25018c2ecf20Sopenharmony_ci				HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT;
25028c2ecf20Sopenharmony_ci			clkcache->s[i].index = j;
25038c2ecf20Sopenharmony_ci			clkcache->s[i].name = sampleclock_sources[
25048c2ecf20Sopenharmony_ci					j+HPI_SAMPLECLOCK_SOURCE_LAST];
25058c2ecf20Sopenharmony_ci			i++;
25068c2ecf20Sopenharmony_ci		}
25078c2ecf20Sopenharmony_ci	clkcache->count = i;
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Source");
25108c2ecf20Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
25118c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_clksrc_info;
25128c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_clksrc_get;
25138c2ecf20Sopenharmony_ci	snd_control.put = snd_asihpi_clksrc_put;
25148c2ecf20Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
25158c2ecf20Sopenharmony_ci		return -EINVAL;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci
25188c2ecf20Sopenharmony_ci	if (clkcache->has_local) {
25198c2ecf20Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate");
25208c2ecf20Sopenharmony_ci		snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
25218c2ecf20Sopenharmony_ci		snd_control.info = snd_asihpi_clklocal_info;
25228c2ecf20Sopenharmony_ci		snd_control.get = snd_asihpi_clklocal_get;
25238c2ecf20Sopenharmony_ci		snd_control.put = snd_asihpi_clklocal_put;
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci		if (ctl_add(card, &snd_control, asihpi) < 0)
25278c2ecf20Sopenharmony_ci			return -EINVAL;
25288c2ecf20Sopenharmony_ci	}
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Rate");
25318c2ecf20Sopenharmony_ci	snd_control.access =
25328c2ecf20Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
25338c2ecf20Sopenharmony_ci	snd_control.info = snd_asihpi_clkrate_info;
25348c2ecf20Sopenharmony_ci	snd_control.get = snd_asihpi_clkrate_get;
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
25378c2ecf20Sopenharmony_ci}
25388c2ecf20Sopenharmony_ci/*------------------------------------------------------------
25398c2ecf20Sopenharmony_ci   Mixer
25408c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_cistatic int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
25438c2ecf20Sopenharmony_ci{
25448c2ecf20Sopenharmony_ci	struct snd_card *card;
25458c2ecf20Sopenharmony_ci	unsigned int idx = 0;
25468c2ecf20Sopenharmony_ci	unsigned int subindex = 0;
25478c2ecf20Sopenharmony_ci	int err;
25488c2ecf20Sopenharmony_ci	struct hpi_control hpi_ctl, prev_ctl;
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!asihpi))
25518c2ecf20Sopenharmony_ci		return -EINVAL;
25528c2ecf20Sopenharmony_ci	card = asihpi->card;
25538c2ecf20Sopenharmony_ci	strcpy(card->mixername, "Asihpi Mixer");
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	err =
25568c2ecf20Sopenharmony_ci	    hpi_mixer_open(asihpi->hpi->adapter->index,
25578c2ecf20Sopenharmony_ci			  &asihpi->h_mixer);
25588c2ecf20Sopenharmony_ci	hpi_handle_error(err);
25598c2ecf20Sopenharmony_ci	if (err)
25608c2ecf20Sopenharmony_ci		return -err;
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	memset(&prev_ctl, 0, sizeof(prev_ctl));
25638c2ecf20Sopenharmony_ci	prev_ctl.control_type = -1;
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci	for (idx = 0; idx < 2000; idx++) {
25668c2ecf20Sopenharmony_ci		err = hpi_mixer_get_control_by_index(
25678c2ecf20Sopenharmony_ci				asihpi->h_mixer,
25688c2ecf20Sopenharmony_ci				idx,
25698c2ecf20Sopenharmony_ci				&hpi_ctl.src_node_type,
25708c2ecf20Sopenharmony_ci				&hpi_ctl.src_node_index,
25718c2ecf20Sopenharmony_ci				&hpi_ctl.dst_node_type,
25728c2ecf20Sopenharmony_ci				&hpi_ctl.dst_node_index,
25738c2ecf20Sopenharmony_ci				&hpi_ctl.control_type,
25748c2ecf20Sopenharmony_ci				&hpi_ctl.h_control);
25758c2ecf20Sopenharmony_ci		if (err) {
25768c2ecf20Sopenharmony_ci			if (err == HPI_ERROR_CONTROL_DISABLED) {
25778c2ecf20Sopenharmony_ci				if (mixer_dump)
25788c2ecf20Sopenharmony_ci					dev_info(&asihpi->pci->dev,
25798c2ecf20Sopenharmony_ci						   "Disabled HPI Control(%d)\n",
25808c2ecf20Sopenharmony_ci						   idx);
25818c2ecf20Sopenharmony_ci				continue;
25828c2ecf20Sopenharmony_ci			} else
25838c2ecf20Sopenharmony_ci				break;
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci		}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci		hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE;
25888c2ecf20Sopenharmony_ci		hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci		/* ASI50xx in SSX mode has multiple meters on the same node.
25918c2ecf20Sopenharmony_ci		   Use subindex to create distinct ALSA controls
25928c2ecf20Sopenharmony_ci		   for any duplicated controls.
25938c2ecf20Sopenharmony_ci		*/
25948c2ecf20Sopenharmony_ci		if ((hpi_ctl.control_type == prev_ctl.control_type) &&
25958c2ecf20Sopenharmony_ci		    (hpi_ctl.src_node_type == prev_ctl.src_node_type) &&
25968c2ecf20Sopenharmony_ci		    (hpi_ctl.src_node_index == prev_ctl.src_node_index) &&
25978c2ecf20Sopenharmony_ci		    (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) &&
25988c2ecf20Sopenharmony_ci		    (hpi_ctl.dst_node_index == prev_ctl.dst_node_index))
25998c2ecf20Sopenharmony_ci			subindex++;
26008c2ecf20Sopenharmony_ci		else
26018c2ecf20Sopenharmony_ci			subindex = 0;
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci		prev_ctl = hpi_ctl;
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci		switch (hpi_ctl.control_type) {
26068c2ecf20Sopenharmony_ci		case HPI_CONTROL_VOLUME:
26078c2ecf20Sopenharmony_ci			err = snd_asihpi_volume_add(asihpi, &hpi_ctl);
26088c2ecf20Sopenharmony_ci			break;
26098c2ecf20Sopenharmony_ci		case HPI_CONTROL_LEVEL:
26108c2ecf20Sopenharmony_ci			err = snd_asihpi_level_add(asihpi, &hpi_ctl);
26118c2ecf20Sopenharmony_ci			break;
26128c2ecf20Sopenharmony_ci		case HPI_CONTROL_MULTIPLEXER:
26138c2ecf20Sopenharmony_ci			err = snd_asihpi_mux_add(asihpi, &hpi_ctl);
26148c2ecf20Sopenharmony_ci			break;
26158c2ecf20Sopenharmony_ci		case HPI_CONTROL_CHANNEL_MODE:
26168c2ecf20Sopenharmony_ci			err = snd_asihpi_cmode_add(asihpi, &hpi_ctl);
26178c2ecf20Sopenharmony_ci			break;
26188c2ecf20Sopenharmony_ci		case HPI_CONTROL_METER:
26198c2ecf20Sopenharmony_ci			err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex);
26208c2ecf20Sopenharmony_ci			break;
26218c2ecf20Sopenharmony_ci		case HPI_CONTROL_SAMPLECLOCK:
26228c2ecf20Sopenharmony_ci			err = snd_asihpi_sampleclock_add(
26238c2ecf20Sopenharmony_ci						asihpi, &hpi_ctl);
26248c2ecf20Sopenharmony_ci			break;
26258c2ecf20Sopenharmony_ci		case HPI_CONTROL_CONNECTION:	/* ignore these */
26268c2ecf20Sopenharmony_ci			continue;
26278c2ecf20Sopenharmony_ci		case HPI_CONTROL_TUNER:
26288c2ecf20Sopenharmony_ci			err = snd_asihpi_tuner_add(asihpi, &hpi_ctl);
26298c2ecf20Sopenharmony_ci			break;
26308c2ecf20Sopenharmony_ci		case HPI_CONTROL_AESEBU_TRANSMITTER:
26318c2ecf20Sopenharmony_ci			err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl);
26328c2ecf20Sopenharmony_ci			break;
26338c2ecf20Sopenharmony_ci		case HPI_CONTROL_AESEBU_RECEIVER:
26348c2ecf20Sopenharmony_ci			err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl);
26358c2ecf20Sopenharmony_ci			break;
26368c2ecf20Sopenharmony_ci		case HPI_CONTROL_VOX:
26378c2ecf20Sopenharmony_ci		case HPI_CONTROL_BITSTREAM:
26388c2ecf20Sopenharmony_ci		case HPI_CONTROL_MICROPHONE:
26398c2ecf20Sopenharmony_ci		case HPI_CONTROL_PARAMETRIC_EQ:
26408c2ecf20Sopenharmony_ci		case HPI_CONTROL_COMPANDER:
26418c2ecf20Sopenharmony_ci		default:
26428c2ecf20Sopenharmony_ci			if (mixer_dump)
26438c2ecf20Sopenharmony_ci				dev_info(&asihpi->pci->dev,
26448c2ecf20Sopenharmony_ci					"Untranslated HPI Control (%d) %d %d %d %d %d\n",
26458c2ecf20Sopenharmony_ci					idx,
26468c2ecf20Sopenharmony_ci					hpi_ctl.control_type,
26478c2ecf20Sopenharmony_ci					hpi_ctl.src_node_type,
26488c2ecf20Sopenharmony_ci					hpi_ctl.src_node_index,
26498c2ecf20Sopenharmony_ci					hpi_ctl.dst_node_type,
26508c2ecf20Sopenharmony_ci					hpi_ctl.dst_node_index);
26518c2ecf20Sopenharmony_ci			continue;
26528c2ecf20Sopenharmony_ci		}
26538c2ecf20Sopenharmony_ci		if (err < 0)
26548c2ecf20Sopenharmony_ci			return err;
26558c2ecf20Sopenharmony_ci	}
26568c2ecf20Sopenharmony_ci	if (HPI_ERROR_INVALID_OBJ_INDEX != err)
26578c2ecf20Sopenharmony_ci		hpi_handle_error(err);
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx);
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	return 0;
26628c2ecf20Sopenharmony_ci}
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci/*------------------------------------------------------------
26658c2ecf20Sopenharmony_ci   /proc interface
26668c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_cistatic void
26698c2ecf20Sopenharmony_cisnd_asihpi_proc_read(struct snd_info_entry *entry,
26708c2ecf20Sopenharmony_ci			struct snd_info_buffer *buffer)
26718c2ecf20Sopenharmony_ci{
26728c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi = entry->private_data;
26738c2ecf20Sopenharmony_ci	u32 h_control;
26748c2ecf20Sopenharmony_ci	u32 rate = 0;
26758c2ecf20Sopenharmony_ci	u16 source = 0;
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	u16 num_outstreams;
26788c2ecf20Sopenharmony_ci	u16 num_instreams;
26798c2ecf20Sopenharmony_ci	u16 version;
26808c2ecf20Sopenharmony_ci	u32 serial_number;
26818c2ecf20Sopenharmony_ci	u16 type;
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci	int err;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "ASIHPI driver proc file\n");
26868c2ecf20Sopenharmony_ci
26878c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index,
26888c2ecf20Sopenharmony_ci			&num_outstreams, &num_instreams,
26898c2ecf20Sopenharmony_ci			&version, &serial_number, &type));
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	snd_iprintf(buffer,
26928c2ecf20Sopenharmony_ci			"Adapter type ASI%4X\nHardware Index %d\n"
26938c2ecf20Sopenharmony_ci			"%d outstreams\n%d instreams\n",
26948c2ecf20Sopenharmony_ci			type, asihpi->hpi->adapter->index,
26958c2ecf20Sopenharmony_ci			num_outstreams, num_instreams);
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	snd_iprintf(buffer,
26988c2ecf20Sopenharmony_ci		"Serial#%d\nHardware version %c%d\nDSP code version %03d\n",
26998c2ecf20Sopenharmony_ci		serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7,
27008c2ecf20Sopenharmony_ci		((version >> 13) * 100) + ((version >> 7) & 0x3f));
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
27038c2ecf20Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
27048c2ecf20Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	if (!err) {
27078c2ecf20Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control, &rate);
27088c2ecf20Sopenharmony_ci		err += hpi_sample_clock_get_source(h_control, &source);
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ci		if (!err)
27118c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "Sample Clock %dHz, source %s\n",
27128c2ecf20Sopenharmony_ci			rate, sampleclock_sources[source]);
27138c2ecf20Sopenharmony_ci	}
27148c2ecf20Sopenharmony_ci}
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_cistatic void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
27178c2ecf20Sopenharmony_ci{
27188c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(asihpi->card, "info", asihpi,
27198c2ecf20Sopenharmony_ci			     snd_asihpi_proc_read);
27208c2ecf20Sopenharmony_ci}
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci/*------------------------------------------------------------
27238c2ecf20Sopenharmony_ci   HWDEP
27248c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file)
27278c2ecf20Sopenharmony_ci{
27288c2ecf20Sopenharmony_ci	if (enable_hpi_hwdep)
27298c2ecf20Sopenharmony_ci		return 0;
27308c2ecf20Sopenharmony_ci	else
27318c2ecf20Sopenharmony_ci		return -ENODEV;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci}
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file)
27368c2ecf20Sopenharmony_ci{
27378c2ecf20Sopenharmony_ci	if (enable_hpi_hwdep)
27388c2ecf20Sopenharmony_ci		return asihpi_hpi_release(file);
27398c2ecf20Sopenharmony_ci	else
27408c2ecf20Sopenharmony_ci		return -ENODEV;
27418c2ecf20Sopenharmony_ci}
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file,
27448c2ecf20Sopenharmony_ci				unsigned int cmd, unsigned long arg)
27458c2ecf20Sopenharmony_ci{
27468c2ecf20Sopenharmony_ci	if (enable_hpi_hwdep)
27478c2ecf20Sopenharmony_ci		return asihpi_hpi_ioctl(file, cmd, arg);
27488c2ecf20Sopenharmony_ci	else
27498c2ecf20Sopenharmony_ci		return -ENODEV;
27508c2ecf20Sopenharmony_ci}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci/* results in /dev/snd/hwC#D0 file for each card with index #
27548c2ecf20Sopenharmony_ci   also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card'
27558c2ecf20Sopenharmony_ci*/
27568c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device)
27578c2ecf20Sopenharmony_ci{
27588c2ecf20Sopenharmony_ci	struct snd_hwdep *hw;
27598c2ecf20Sopenharmony_ci	int err;
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
27628c2ecf20Sopenharmony_ci	if (err < 0)
27638c2ecf20Sopenharmony_ci		return err;
27648c2ecf20Sopenharmony_ci	strcpy(hw->name, "asihpi (HPI)");
27658c2ecf20Sopenharmony_ci	hw->iface = SNDRV_HWDEP_IFACE_LAST;
27668c2ecf20Sopenharmony_ci	hw->ops.open = snd_asihpi_hpi_open;
27678c2ecf20Sopenharmony_ci	hw->ops.ioctl = snd_asihpi_hpi_ioctl;
27688c2ecf20Sopenharmony_ci	hw->ops.release = snd_asihpi_hpi_release;
27698c2ecf20Sopenharmony_ci	hw->private_data = asihpi;
27708c2ecf20Sopenharmony_ci	return 0;
27718c2ecf20Sopenharmony_ci}
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci/*------------------------------------------------------------
27748c2ecf20Sopenharmony_ci   CARD
27758c2ecf20Sopenharmony_ci ------------------------------------------------------------*/
27768c2ecf20Sopenharmony_cistatic int snd_asihpi_probe(struct pci_dev *pci_dev,
27778c2ecf20Sopenharmony_ci			    const struct pci_device_id *pci_id)
27788c2ecf20Sopenharmony_ci{
27798c2ecf20Sopenharmony_ci	int err;
27808c2ecf20Sopenharmony_ci	struct hpi_adapter *hpi;
27818c2ecf20Sopenharmony_ci	struct snd_card *card;
27828c2ecf20Sopenharmony_ci	struct snd_card_asihpi *asihpi;
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	u32 h_control;
27858c2ecf20Sopenharmony_ci	u32 h_stream;
27868c2ecf20Sopenharmony_ci	u32 adapter_index;
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	static int dev;
27898c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
27908c2ecf20Sopenharmony_ci		return -ENODEV;
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	/* Should this be enable[hpi->index] ? */
27938c2ecf20Sopenharmony_ci	if (!enable[dev]) {
27948c2ecf20Sopenharmony_ci		dev++;
27958c2ecf20Sopenharmony_ci		return -ENOENT;
27968c2ecf20Sopenharmony_ci	}
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	/* Initialise low-level HPI driver */
27998c2ecf20Sopenharmony_ci	err = asihpi_adapter_probe(pci_dev, pci_id);
28008c2ecf20Sopenharmony_ci	if (err < 0)
28018c2ecf20Sopenharmony_ci		return err;
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	hpi = pci_get_drvdata(pci_dev);
28048c2ecf20Sopenharmony_ci	adapter_index = hpi->adapter->index;
28058c2ecf20Sopenharmony_ci	/* first try to give the card the same index as its hardware index */
28068c2ecf20Sopenharmony_ci	err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index],
28078c2ecf20Sopenharmony_ci			   THIS_MODULE, sizeof(struct snd_card_asihpi), &card);
28088c2ecf20Sopenharmony_ci	if (err < 0) {
28098c2ecf20Sopenharmony_ci		/* if that fails, try the default index==next available */
28108c2ecf20Sopenharmony_ci		err = snd_card_new(&pci_dev->dev, index[dev], id[dev],
28118c2ecf20Sopenharmony_ci				   THIS_MODULE, sizeof(struct snd_card_asihpi),
28128c2ecf20Sopenharmony_ci				   &card);
28138c2ecf20Sopenharmony_ci		if (err < 0)
28148c2ecf20Sopenharmony_ci			return err;
28158c2ecf20Sopenharmony_ci		dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n",
28168c2ecf20Sopenharmony_ci			adapter_index, card->number);
28178c2ecf20Sopenharmony_ci	}
28188c2ecf20Sopenharmony_ci
28198c2ecf20Sopenharmony_ci	asihpi = card->private_data;
28208c2ecf20Sopenharmony_ci	asihpi->card = card;
28218c2ecf20Sopenharmony_ci	asihpi->pci = pci_dev;
28228c2ecf20Sopenharmony_ci	asihpi->hpi = hpi;
28238c2ecf20Sopenharmony_ci	hpi->snd_card = card;
28248c2ecf20Sopenharmony_ci
28258c2ecf20Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
28268c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CAPS1,
28278c2ecf20Sopenharmony_ci		NULL, &asihpi->support_grouping);
28288c2ecf20Sopenharmony_ci	if (err)
28298c2ecf20Sopenharmony_ci		asihpi->support_grouping = 0;
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
28328c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CAPS2,
28338c2ecf20Sopenharmony_ci		&asihpi->support_mrx, NULL);
28348c2ecf20Sopenharmony_ci	if (err)
28358c2ecf20Sopenharmony_ci		asihpi->support_mrx = 0;
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
28388c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_INTERVAL,
28398c2ecf20Sopenharmony_ci		NULL, &asihpi->update_interval_frames);
28408c2ecf20Sopenharmony_ci	if (err)
28418c2ecf20Sopenharmony_ci		asihpi->update_interval_frames = 512;
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci	if (hpi->interrupt_mode) {
28448c2ecf20Sopenharmony_ci		asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
28458c2ecf20Sopenharmony_ci		asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
28468c2ecf20Sopenharmony_ci		hpi->interrupt_callback = snd_card_asihpi_isr;
28478c2ecf20Sopenharmony_ci	} else {
28488c2ecf20Sopenharmony_ci		asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
28498c2ecf20Sopenharmony_ci		asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
28508c2ecf20Sopenharmony_ci	}
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_instream_open(adapter_index,
28538c2ecf20Sopenharmony_ci			     0, &h_stream));
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	err = hpi_instream_host_buffer_free(h_stream);
28568c2ecf20Sopenharmony_ci	asihpi->can_dma = (!err);
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci	hpi_handle_error(hpi_instream_close(h_stream));
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_ci	if (!asihpi->can_dma)
28618c2ecf20Sopenharmony_ci		asihpi->update_interval_frames *= 2;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
28648c2ecf20Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CURCHANNELS,
28658c2ecf20Sopenharmony_ci		&asihpi->in_max_chans, &asihpi->out_max_chans);
28668c2ecf20Sopenharmony_ci	if (err) {
28678c2ecf20Sopenharmony_ci		asihpi->in_max_chans = 2;
28688c2ecf20Sopenharmony_ci		asihpi->out_max_chans = 2;
28698c2ecf20Sopenharmony_ci	}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	if (asihpi->out_max_chans > 2) { /* assume LL mode */
28728c2ecf20Sopenharmony_ci		asihpi->out_min_chans = asihpi->out_max_chans;
28738c2ecf20Sopenharmony_ci		asihpi->in_min_chans = asihpi->in_max_chans;
28748c2ecf20Sopenharmony_ci		asihpi->support_grouping = 0;
28758c2ecf20Sopenharmony_ci	} else {
28768c2ecf20Sopenharmony_ci		asihpi->out_min_chans = 1;
28778c2ecf20Sopenharmony_ci		asihpi->in_min_chans = 1;
28788c2ecf20Sopenharmony_ci	}
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n",
28818c2ecf20Sopenharmony_ci			asihpi->can_dma,
28828c2ecf20Sopenharmony_ci			asihpi->support_grouping,
28838c2ecf20Sopenharmony_ci			asihpi->support_mrx,
28848c2ecf20Sopenharmony_ci			asihpi->update_interval_frames
28858c2ecf20Sopenharmony_ci	      );
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	err = snd_card_asihpi_pcm_new(asihpi, 0);
28888c2ecf20Sopenharmony_ci	if (err < 0) {
28898c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "pcm_new failed\n");
28908c2ecf20Sopenharmony_ci		goto __nodev;
28918c2ecf20Sopenharmony_ci	}
28928c2ecf20Sopenharmony_ci	err = snd_card_asihpi_mixer_new(asihpi);
28938c2ecf20Sopenharmony_ci	if (err < 0) {
28948c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "mixer_new failed\n");
28958c2ecf20Sopenharmony_ci		goto __nodev;
28968c2ecf20Sopenharmony_ci	}
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
28998c2ecf20Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
29008c2ecf20Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	if (!err)
29038c2ecf20Sopenharmony_ci		err = hpi_sample_clock_set_local_rate(
29048c2ecf20Sopenharmony_ci			h_control, adapter_fs);
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci	snd_asihpi_proc_init(asihpi);
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_ci	/* always create, can be enabled or disabled dynamically
29098c2ecf20Sopenharmony_ci	    by enable_hwdep  module param*/
29108c2ecf20Sopenharmony_ci	snd_asihpi_hpi_new(asihpi, 0);
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	strcpy(card->driver, "ASIHPI");
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	sprintf(card->shortname, "AudioScience ASI%4X",
29158c2ecf20Sopenharmony_ci			asihpi->hpi->adapter->type);
29168c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s %i",
29178c2ecf20Sopenharmony_ci			card->shortname, adapter_index);
29188c2ecf20Sopenharmony_ci	err = snd_card_register(card);
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci	if (!err) {
29218c2ecf20Sopenharmony_ci		dev++;
29228c2ecf20Sopenharmony_ci		return 0;
29238c2ecf20Sopenharmony_ci	}
29248c2ecf20Sopenharmony_ci__nodev:
29258c2ecf20Sopenharmony_ci	snd_card_free(card);
29268c2ecf20Sopenharmony_ci	dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err);
29278c2ecf20Sopenharmony_ci	return err;
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci}
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_cistatic void snd_asihpi_remove(struct pci_dev *pci_dev)
29328c2ecf20Sopenharmony_ci{
29338c2ecf20Sopenharmony_ci	struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	/* Stop interrupts */
29368c2ecf20Sopenharmony_ci	if (hpi->interrupt_mode) {
29378c2ecf20Sopenharmony_ci		hpi->interrupt_callback = NULL;
29388c2ecf20Sopenharmony_ci		hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
29398c2ecf20Sopenharmony_ci			HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
29408c2ecf20Sopenharmony_ci	}
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci	snd_card_free(hpi->snd_card);
29438c2ecf20Sopenharmony_ci	hpi->snd_card = NULL;
29448c2ecf20Sopenharmony_ci	asihpi_adapter_remove(pci_dev);
29458c2ecf20Sopenharmony_ci}
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_cistatic const struct pci_device_id asihpi_pci_tbl[] = {
29488c2ecf20Sopenharmony_ci	{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
29498c2ecf20Sopenharmony_ci		HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
29508c2ecf20Sopenharmony_ci		(kernel_ulong_t)HPI_6205},
29518c2ecf20Sopenharmony_ci	{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
29528c2ecf20Sopenharmony_ci		HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
29538c2ecf20Sopenharmony_ci		(kernel_ulong_t)HPI_6000},
29548c2ecf20Sopenharmony_ci	{0,}
29558c2ecf20Sopenharmony_ci};
29568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_cistatic struct pci_driver driver = {
29598c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
29608c2ecf20Sopenharmony_ci	.id_table = asihpi_pci_tbl,
29618c2ecf20Sopenharmony_ci	.probe = snd_asihpi_probe,
29628c2ecf20Sopenharmony_ci	.remove = snd_asihpi_remove,
29638c2ecf20Sopenharmony_ci};
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_cistatic int __init snd_asihpi_init(void)
29668c2ecf20Sopenharmony_ci{
29678c2ecf20Sopenharmony_ci	asihpi_init();
29688c2ecf20Sopenharmony_ci	return pci_register_driver(&driver);
29698c2ecf20Sopenharmony_ci}
29708c2ecf20Sopenharmony_ci
29718c2ecf20Sopenharmony_cistatic void __exit snd_asihpi_exit(void)
29728c2ecf20Sopenharmony_ci{
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	pci_unregister_driver(&driver);
29758c2ecf20Sopenharmony_ci	asihpi_exit();
29768c2ecf20Sopenharmony_ci}
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_cimodule_init(snd_asihpi_init)
29798c2ecf20Sopenharmony_cimodule_exit(snd_asihpi_exit)
29808c2ecf20Sopenharmony_ci
2981