162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Asihpi soundcard
462306a36Sopenharmony_ci *  Copyright (c) by AudioScience Inc <support@audioscience.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  The following is not a condition of use, merely a request:
762306a36Sopenharmony_ci *  If you modify this program, particularly if you fix errors, AudioScience Inc
862306a36Sopenharmony_ci *  would appreciate it if you grant us the right to use those modifications
962306a36Sopenharmony_ci *  for any purpose including commercial applications.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "hpi_internal.h"
1362306a36Sopenharmony_ci#include "hpi_version.h"
1462306a36Sopenharmony_ci#include "hpimsginit.h"
1562306a36Sopenharmony_ci#include "hpioctl.h"
1662306a36Sopenharmony_ci#include "hpicmn.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/jiffies.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/time.h>
2362306a36Sopenharmony_ci#include <linux/wait.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <sound/core.h>
2662306a36Sopenharmony_ci#include <sound/control.h>
2762306a36Sopenharmony_ci#include <sound/pcm.h>
2862306a36Sopenharmony_ci#include <sound/pcm_params.h>
2962306a36Sopenharmony_ci#include <sound/info.h>
3062306a36Sopenharmony_ci#include <sound/initval.h>
3162306a36Sopenharmony_ci#include <sound/tlv.h>
3262306a36Sopenharmony_ci#include <sound/hwdep.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3562306a36Sopenharmony_ciMODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
3662306a36Sopenharmony_ciMODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx "
3762306a36Sopenharmony_ci			HPI_VER_STRING);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#if defined CONFIG_SND_DEBUG_VERBOSE
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * snd_printddd - very verbose debug printk
4262306a36Sopenharmony_ci * @format: format string
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Works like snd_printk() for debugging purposes.
4562306a36Sopenharmony_ci * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
4662306a36Sopenharmony_ci * Must set snd module debug parameter to 3 to enable at runtime.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci#define snd_printddd(format, args...) \
4962306a36Sopenharmony_ci	__snd_printk(3, __FILE__, __LINE__, format, ##args)
5062306a36Sopenharmony_ci#else
5162306a36Sopenharmony_ci#define snd_printddd(format, args...) do { } while (0)
5262306a36Sopenharmony_ci#endif
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* index 0-MAX */
5562306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
5662306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
5762306a36Sopenharmony_cistatic bool enable_hpi_hwdep = 1;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
6062306a36Sopenharmony_ciMODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
6362306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
6662306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cimodule_param(enable_hpi_hwdep, bool, 0644);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable_hpi_hwdep,
7062306a36Sopenharmony_ci		"ALSA enable HPI hwdep for AudioScience soundcard ");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* identify driver */
7362306a36Sopenharmony_ci#ifdef KERNEL_ALSA_BUILD
7462306a36Sopenharmony_cistatic char *build_info = "Built using headers from kernel source";
7562306a36Sopenharmony_cimodule_param(build_info, charp, 0444);
7662306a36Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built using headers from kernel source");
7762306a36Sopenharmony_ci#else
7862306a36Sopenharmony_cistatic char *build_info = "Built within ALSA source";
7962306a36Sopenharmony_cimodule_param(build_info, charp, 0444);
8062306a36Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built within ALSA source");
8162306a36Sopenharmony_ci#endif
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* set to 1 to dump every control from adapter to log */
8462306a36Sopenharmony_cistatic const int mixer_dump;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define DEFAULT_SAMPLERATE 44100
8762306a36Sopenharmony_cistatic int adapter_fs = DEFAULT_SAMPLERATE;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* defaults */
9062306a36Sopenharmony_ci#define PERIODS_MIN 2
9162306a36Sopenharmony_ci#define PERIOD_BYTES_MIN  2048
9262306a36Sopenharmony_ci#define BUFFER_BYTES_MAX (512 * 1024)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct clk_source {
9762306a36Sopenharmony_ci	int source;
9862306a36Sopenharmony_ci	int index;
9962306a36Sopenharmony_ci	const char *name;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct clk_cache {
10362306a36Sopenharmony_ci	int count;
10462306a36Sopenharmony_ci	int has_local;
10562306a36Sopenharmony_ci	struct clk_source s[MAX_CLOCKSOURCES];
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Per card data */
10962306a36Sopenharmony_cistruct snd_card_asihpi {
11062306a36Sopenharmony_ci	struct snd_card *card;
11162306a36Sopenharmony_ci	struct pci_dev *pci;
11262306a36Sopenharmony_ci	struct hpi_adapter *hpi;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* In low latency mode there is only one stream, a pointer to its
11562306a36Sopenharmony_ci	 * private data is stored here on trigger and cleared on stop.
11662306a36Sopenharmony_ci	 * The interrupt handler uses it as a parameter when calling
11762306a36Sopenharmony_ci	 * snd_card_asihpi_timer_function().
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *llmode_streampriv;
12062306a36Sopenharmony_ci	void (*pcm_start)(struct snd_pcm_substream *substream);
12162306a36Sopenharmony_ci	void (*pcm_stop)(struct snd_pcm_substream *substream);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	u32 h_mixer;
12462306a36Sopenharmony_ci	struct clk_cache cc;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	u16 can_dma;
12762306a36Sopenharmony_ci	u16 support_grouping;
12862306a36Sopenharmony_ci	u16 support_mrx;
12962306a36Sopenharmony_ci	u16 update_interval_frames;
13062306a36Sopenharmony_ci	u16 in_max_chans;
13162306a36Sopenharmony_ci	u16 out_max_chans;
13262306a36Sopenharmony_ci	u16 in_min_chans;
13362306a36Sopenharmony_ci	u16 out_min_chans;
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* Per stream data */
13762306a36Sopenharmony_cistruct snd_card_asihpi_pcm {
13862306a36Sopenharmony_ci	struct timer_list timer;
13962306a36Sopenharmony_ci	unsigned int respawn_timer;
14062306a36Sopenharmony_ci	unsigned int hpi_buffer_attached;
14162306a36Sopenharmony_ci	unsigned int buffer_bytes;
14262306a36Sopenharmony_ci	unsigned int period_bytes;
14362306a36Sopenharmony_ci	unsigned int bytes_per_sec;
14462306a36Sopenharmony_ci	unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
14562306a36Sopenharmony_ci	unsigned int pcm_buf_dma_ofs;	/* DMA R/W offset in buffer */
14662306a36Sopenharmony_ci	unsigned int pcm_buf_elapsed_dma_ofs;	/* DMA R/W offset in buffer */
14762306a36Sopenharmony_ci	unsigned int drained_count;
14862306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
14962306a36Sopenharmony_ci	u32 h_stream;
15062306a36Sopenharmony_ci	struct hpi_format format;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* universal stream verbs work with out or in stream handles */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* Functions to allow driver to give a buffer to HPI for busmastering */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic u16 hpi_stream_host_buffer_attach(
15862306a36Sopenharmony_ci	u32 h_stream,   /* handle to outstream. */
15962306a36Sopenharmony_ci	u32 size_in_bytes, /* size in bytes of bus mastering buffer */
16062306a36Sopenharmony_ci	u32 pci_address
16162306a36Sopenharmony_ci)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct hpi_message hm;
16462306a36Sopenharmony_ci	struct hpi_response hr;
16562306a36Sopenharmony_ci	unsigned int obj = hpi_handle_object(h_stream);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!h_stream)
16862306a36Sopenharmony_ci		return HPI_ERROR_INVALID_OBJ;
16962306a36Sopenharmony_ci	hpi_init_message_response(&hm, &hr, obj,
17062306a36Sopenharmony_ci			obj == HPI_OBJ_OSTREAM ?
17162306a36Sopenharmony_ci				HPI_OSTREAM_HOSTBUFFER_ALLOC :
17262306a36Sopenharmony_ci				HPI_ISTREAM_HOSTBUFFER_ALLOC);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	hpi_handle_to_indexes(h_stream, &hm.adapter_index,
17562306a36Sopenharmony_ci				&hm.obj_index);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	hm.u.d.u.buffer.buffer_size = size_in_bytes;
17862306a36Sopenharmony_ci	hm.u.d.u.buffer.pci_address = pci_address;
17962306a36Sopenharmony_ci	hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER;
18062306a36Sopenharmony_ci	hpi_send_recv(&hm, &hr);
18162306a36Sopenharmony_ci	return hr.error;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic u16 hpi_stream_host_buffer_detach(u32  h_stream)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct hpi_message hm;
18762306a36Sopenharmony_ci	struct hpi_response hr;
18862306a36Sopenharmony_ci	unsigned int obj = hpi_handle_object(h_stream);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!h_stream)
19162306a36Sopenharmony_ci		return HPI_ERROR_INVALID_OBJ;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	hpi_init_message_response(&hm, &hr,  obj,
19462306a36Sopenharmony_ci			obj == HPI_OBJ_OSTREAM ?
19562306a36Sopenharmony_ci				HPI_OSTREAM_HOSTBUFFER_FREE :
19662306a36Sopenharmony_ci				HPI_ISTREAM_HOSTBUFFER_FREE);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	hpi_handle_to_indexes(h_stream, &hm.adapter_index,
19962306a36Sopenharmony_ci				&hm.obj_index);
20062306a36Sopenharmony_ci	hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER;
20162306a36Sopenharmony_ci	hpi_send_recv(&hm, &hr);
20262306a36Sopenharmony_ci	return hr.error;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic inline u16 hpi_stream_start(u32 h_stream)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
20862306a36Sopenharmony_ci		return hpi_outstream_start(h_stream);
20962306a36Sopenharmony_ci	else
21062306a36Sopenharmony_ci		return hpi_instream_start(h_stream);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic inline u16 hpi_stream_stop(u32 h_stream)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
21662306a36Sopenharmony_ci		return hpi_outstream_stop(h_stream);
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		return hpi_instream_stop(h_stream);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline u16 hpi_stream_get_info_ex(
22262306a36Sopenharmony_ci    u32 h_stream,
22362306a36Sopenharmony_ci    u16        *pw_state,
22462306a36Sopenharmony_ci    u32        *pbuffer_size,
22562306a36Sopenharmony_ci    u32        *pdata_in_buffer,
22662306a36Sopenharmony_ci    u32        *psample_count,
22762306a36Sopenharmony_ci    u32        *pauxiliary_data
22862306a36Sopenharmony_ci)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	u16 e;
23162306a36Sopenharmony_ci	if (hpi_handle_object(h_stream)  ==  HPI_OBJ_OSTREAM)
23262306a36Sopenharmony_ci		e = hpi_outstream_get_info_ex(h_stream, pw_state,
23362306a36Sopenharmony_ci					pbuffer_size, pdata_in_buffer,
23462306a36Sopenharmony_ci					psample_count, pauxiliary_data);
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		e = hpi_instream_get_info_ex(h_stream, pw_state,
23762306a36Sopenharmony_ci					pbuffer_size, pdata_in_buffer,
23862306a36Sopenharmony_ci					psample_count, pauxiliary_data);
23962306a36Sopenharmony_ci	return e;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic inline u16 hpi_stream_group_add(
24362306a36Sopenharmony_ci					u32 h_master,
24462306a36Sopenharmony_ci					u32 h_stream)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (hpi_handle_object(h_master) ==  HPI_OBJ_OSTREAM)
24762306a36Sopenharmony_ci		return hpi_outstream_group_add(h_master, h_stream);
24862306a36Sopenharmony_ci	else
24962306a36Sopenharmony_ci		return hpi_instream_group_add(h_master, h_stream);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic inline u16 hpi_stream_group_reset(u32 h_stream)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
25562306a36Sopenharmony_ci		return hpi_outstream_group_reset(h_stream);
25662306a36Sopenharmony_ci	else
25762306a36Sopenharmony_ci		return hpi_instream_group_reset(h_stream);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic u16 handle_error(u16 err, int line, char *filename)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	if (err)
26362306a36Sopenharmony_ci		printk(KERN_WARNING
26462306a36Sopenharmony_ci			"in file %s, line %d: HPI error %d\n",
26562306a36Sopenharmony_ci			filename, line, err);
26662306a36Sopenharmony_ci	return err;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci#define hpi_handle_error(x)  handle_error(x, __LINE__, __FILE__)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/***************************** GENERAL PCM ****************/
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void print_hwparams(struct snd_pcm_substream *substream,
27462306a36Sopenharmony_ci				struct snd_pcm_hw_params *p)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	char name[16];
27762306a36Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
27862306a36Sopenharmony_ci	snd_printdd("%s HWPARAMS\n", name);
27962306a36Sopenharmony_ci	snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n",
28062306a36Sopenharmony_ci		params_rate(p), params_channels(p),
28162306a36Sopenharmony_ci		params_format(p), params_subformat(p));
28262306a36Sopenharmony_ci	snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n",
28362306a36Sopenharmony_ci		params_buffer_bytes(p), params_period_bytes(p),
28462306a36Sopenharmony_ci		params_period_size(p), params_periods(p));
28562306a36Sopenharmony_ci	snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n",
28662306a36Sopenharmony_ci		params_buffer_size(p), params_access(p),
28762306a36Sopenharmony_ci		params_rate(p) * params_channels(p) *
28862306a36Sopenharmony_ci		snd_pcm_format_width(params_format(p)) / 8);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci#define INVALID_FORMAT	(__force snd_pcm_format_t)(-1)
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic const snd_pcm_format_t hpi_to_alsa_formats[] = {
29462306a36Sopenharmony_ci	INVALID_FORMAT,		/* INVALID */
29562306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_U8,	/* HPI_FORMAT_PCM8_UNSIGNED        1 */
29662306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_S16,	/* HPI_FORMAT_PCM16_SIGNED         2 */
29762306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_MPEG_L1              3 */
29862306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L2              4 */
29962306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L3              5 */
30062306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_DOLBY_AC2            6 */
30162306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_DOLBY_AC3            7 */
30262306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN      8 */
30362306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_HITS       9 */
30462306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_INSERTS   10 */
30562306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_S32,	/* HPI_FORMAT_PCM32_SIGNED        11 */
30662306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_RAW_BITSTREAM       12 */
30762306a36Sopenharmony_ci	INVALID_FORMAT,		/* HPI_FORMAT_AA_TAGIT1_HITS_EX1  13 */
30862306a36Sopenharmony_ci	SNDRV_PCM_FORMAT_FLOAT,	/* HPI_FORMAT_PCM32_FLOAT         14 */
30962306a36Sopenharmony_ci#if 1
31062306a36Sopenharmony_ci	/* ALSA can't handle 3 byte sample size together with power-of-2
31162306a36Sopenharmony_ci	 *  constraint on buffer_bytes, so disable this format
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	INVALID_FORMAT
31462306a36Sopenharmony_ci#else
31562306a36Sopenharmony_ci	/* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */
31662306a36Sopenharmony_ci#endif
31762306a36Sopenharmony_ci};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
32162306a36Sopenharmony_ci					   u16 *hpi_format)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	u16 format;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
32662306a36Sopenharmony_ci	     format <= HPI_FORMAT_PCM24_SIGNED; format++) {
32762306a36Sopenharmony_ci		if (hpi_to_alsa_formats[format] == alsa_format) {
32862306a36Sopenharmony_ci			*hpi_format = format;
32962306a36Sopenharmony_ci			return 0;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	snd_printd(KERN_WARNING "failed match for alsa format %d\n",
33462306a36Sopenharmony_ci		   alsa_format);
33562306a36Sopenharmony_ci	*hpi_format = 0;
33662306a36Sopenharmony_ci	return -EINVAL;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
34062306a36Sopenharmony_ci					 struct snd_pcm_hardware *pcmhw)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	u16 err;
34362306a36Sopenharmony_ci	u32 h_control;
34462306a36Sopenharmony_ci	u32 sample_rate;
34562306a36Sopenharmony_ci	int idx;
34662306a36Sopenharmony_ci	unsigned int rate_min = 200000;
34762306a36Sopenharmony_ci	unsigned int rate_max = 0;
34862306a36Sopenharmony_ci	unsigned int rates = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (asihpi->support_mrx) {
35162306a36Sopenharmony_ci		rates |= SNDRV_PCM_RATE_CONTINUOUS;
35262306a36Sopenharmony_ci		rates |= SNDRV_PCM_RATE_8000_96000;
35362306a36Sopenharmony_ci		rate_min = 8000;
35462306a36Sopenharmony_ci		rate_max = 100000;
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		/* on cards without SRC,
35762306a36Sopenharmony_ci		   valid rates are determined by sampleclock */
35862306a36Sopenharmony_ci		err = hpi_mixer_get_control(asihpi->h_mixer,
35962306a36Sopenharmony_ci					  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
36062306a36Sopenharmony_ci					  HPI_CONTROL_SAMPLECLOCK, &h_control);
36162306a36Sopenharmony_ci		if (err) {
36262306a36Sopenharmony_ci			dev_err(&asihpi->pci->dev,
36362306a36Sopenharmony_ci				"No local sampleclock, err %d\n", err);
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		for (idx = -1; idx < 100; idx++) {
36762306a36Sopenharmony_ci			if (idx == -1) {
36862306a36Sopenharmony_ci				if (hpi_sample_clock_get_sample_rate(h_control,
36962306a36Sopenharmony_ci								&sample_rate))
37062306a36Sopenharmony_ci					continue;
37162306a36Sopenharmony_ci			} else if (hpi_sample_clock_query_local_rate(h_control,
37262306a36Sopenharmony_ci							idx, &sample_rate)) {
37362306a36Sopenharmony_ci				break;
37462306a36Sopenharmony_ci			}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci			rate_min = min(rate_min, sample_rate);
37762306a36Sopenharmony_ci			rate_max = max(rate_max, sample_rate);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci			switch (sample_rate) {
38062306a36Sopenharmony_ci			case 5512:
38162306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_5512;
38262306a36Sopenharmony_ci				break;
38362306a36Sopenharmony_ci			case 8000:
38462306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_8000;
38562306a36Sopenharmony_ci				break;
38662306a36Sopenharmony_ci			case 11025:
38762306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_11025;
38862306a36Sopenharmony_ci				break;
38962306a36Sopenharmony_ci			case 16000:
39062306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_16000;
39162306a36Sopenharmony_ci				break;
39262306a36Sopenharmony_ci			case 22050:
39362306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_22050;
39462306a36Sopenharmony_ci				break;
39562306a36Sopenharmony_ci			case 32000:
39662306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_32000;
39762306a36Sopenharmony_ci				break;
39862306a36Sopenharmony_ci			case 44100:
39962306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_44100;
40062306a36Sopenharmony_ci				break;
40162306a36Sopenharmony_ci			case 48000:
40262306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_48000;
40362306a36Sopenharmony_ci				break;
40462306a36Sopenharmony_ci			case 64000:
40562306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_64000;
40662306a36Sopenharmony_ci				break;
40762306a36Sopenharmony_ci			case 88200:
40862306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_88200;
40962306a36Sopenharmony_ci				break;
41062306a36Sopenharmony_ci			case 96000:
41162306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_96000;
41262306a36Sopenharmony_ci				break;
41362306a36Sopenharmony_ci			case 176400:
41462306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_176400;
41562306a36Sopenharmony_ci				break;
41662306a36Sopenharmony_ci			case 192000:
41762306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_192000;
41862306a36Sopenharmony_ci				break;
41962306a36Sopenharmony_ci			default: /* some other rate */
42062306a36Sopenharmony_ci				rates |= SNDRV_PCM_RATE_KNOT;
42162306a36Sopenharmony_ci			}
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	pcmhw->rates = rates;
42662306a36Sopenharmony_ci	pcmhw->rate_min = rate_min;
42762306a36Sopenharmony_ci	pcmhw->rate_max = rate_max;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
43162306a36Sopenharmony_ci					 struct snd_pcm_hw_params *params)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
43462306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
43562306a36Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
43662306a36Sopenharmony_ci	int err;
43762306a36Sopenharmony_ci	u16 format;
43862306a36Sopenharmony_ci	int width;
43962306a36Sopenharmony_ci	unsigned int bytes_per_sec;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	print_hwparams(substream, params);
44262306a36Sopenharmony_ci	err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
44362306a36Sopenharmony_ci	if (err)
44462306a36Sopenharmony_ci		return err;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	hpi_handle_error(hpi_format_create(&dpcm->format,
44762306a36Sopenharmony_ci			params_channels(params),
44862306a36Sopenharmony_ci			format, params_rate(params), 0, 0));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
45162306a36Sopenharmony_ci		if (hpi_instream_reset(dpcm->h_stream) != 0)
45262306a36Sopenharmony_ci			return -EINVAL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		if (hpi_instream_set_format(
45562306a36Sopenharmony_ci			dpcm->h_stream, &dpcm->format) != 0)
45662306a36Sopenharmony_ci			return -EINVAL;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	dpcm->hpi_buffer_attached = 0;
46062306a36Sopenharmony_ci	if (card->can_dma) {
46162306a36Sopenharmony_ci		err = hpi_stream_host_buffer_attach(dpcm->h_stream,
46262306a36Sopenharmony_ci			params_buffer_bytes(params),  runtime->dma_addr);
46362306a36Sopenharmony_ci		if (err == 0) {
46462306a36Sopenharmony_ci			snd_printdd(
46562306a36Sopenharmony_ci				"stream_host_buffer_attach success %u %lu\n",
46662306a36Sopenharmony_ci				params_buffer_bytes(params),
46762306a36Sopenharmony_ci				(unsigned long)runtime->dma_addr);
46862306a36Sopenharmony_ci		} else {
46962306a36Sopenharmony_ci			snd_printd("stream_host_buffer_attach error %d\n",
47062306a36Sopenharmony_ci					err);
47162306a36Sopenharmony_ci			return -ENOMEM;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
47562306a36Sopenharmony_ci				&dpcm->hpi_buffer_attached, NULL, NULL, NULL);
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci	bytes_per_sec = params_rate(params) * params_channels(params);
47862306a36Sopenharmony_ci	width = snd_pcm_format_width(params_format(params));
47962306a36Sopenharmony_ci	bytes_per_sec *= width;
48062306a36Sopenharmony_ci	bytes_per_sec /= 8;
48162306a36Sopenharmony_ci	if (width < 0 || bytes_per_sec == 0)
48262306a36Sopenharmony_ci		return -EINVAL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	dpcm->bytes_per_sec = bytes_per_sec;
48562306a36Sopenharmony_ci	dpcm->buffer_bytes = params_buffer_bytes(params);
48662306a36Sopenharmony_ci	dpcm->period_bytes = params_period_bytes(params);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int
49262306a36Sopenharmony_cisnd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
49562306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
49662306a36Sopenharmony_ci	if (dpcm->hpi_buffer_attached)
49762306a36Sopenharmony_ci		hpi_stream_host_buffer_detach(dpcm->h_stream);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
50562306a36Sopenharmony_ci	kfree(dpcm);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
50962306a36Sopenharmony_ci					    substream)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
51262306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
51362306a36Sopenharmony_ci	int expiry;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	expiry = HZ / 200;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	expiry = max(expiry, 1); /* don't let it be zero! */
51862306a36Sopenharmony_ci	mod_timer(&dpcm->timer, jiffies + expiry);
51962306a36Sopenharmony_ci	dpcm->respawn_timer = 1;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
52562306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	dpcm->respawn_timer = 0;
52862306a36Sopenharmony_ci	del_timer(&dpcm->timer);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
53462306a36Sopenharmony_ci	struct snd_card_asihpi *card;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
53762306a36Sopenharmony_ci	card = snd_pcm_substream_chip(substream);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	WARN_ON(in_interrupt());
54062306a36Sopenharmony_ci	card->llmode_streampriv = dpcm;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
54362306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_IRQ_RATE,
54462306a36Sopenharmony_ci		card->update_interval_frames, 0));
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct snd_card_asihpi *card;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	card = snd_pcm_substream_chip(substream);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
55462306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	card->llmode_streampriv = NULL;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
56062306a36Sopenharmony_ci					   int cmd)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
56362306a36Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
56462306a36Sopenharmony_ci	struct snd_pcm_substream *s;
56562306a36Sopenharmony_ci	u16 e;
56662306a36Sopenharmony_ci	char name[16];
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	switch (cmd) {
57162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
57262306a36Sopenharmony_ci		snd_printdd("%s trigger start\n", name);
57362306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
57462306a36Sopenharmony_ci			struct snd_pcm_runtime *runtime = s->runtime;
57562306a36Sopenharmony_ci			struct snd_card_asihpi_pcm *ds = runtime->private_data;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci			if (snd_pcm_substream_chip(s) != card)
57862306a36Sopenharmony_ci				continue;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci			/* don't link Cap and Play */
58162306a36Sopenharmony_ci			if (substream->stream != s->stream)
58262306a36Sopenharmony_ci				continue;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci			ds->drained_count = 0;
58562306a36Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
58662306a36Sopenharmony_ci				/* How do I know how much valid data is present
58762306a36Sopenharmony_ci				* in buffer? Must be at least one period!
58862306a36Sopenharmony_ci				* Guessing 2 periods, but if
58962306a36Sopenharmony_ci				* buffer is bigger it may contain even more
59062306a36Sopenharmony_ci				* data??
59162306a36Sopenharmony_ci				*/
59262306a36Sopenharmony_ci				unsigned int preload = ds->period_bytes * 1;
59362306a36Sopenharmony_ci				snd_printddd("%d preload %d\n", s->number, preload);
59462306a36Sopenharmony_ci				hpi_handle_error(hpi_outstream_write_buf(
59562306a36Sopenharmony_ci						ds->h_stream,
59662306a36Sopenharmony_ci						&runtime->dma_area[0],
59762306a36Sopenharmony_ci						preload,
59862306a36Sopenharmony_ci						&ds->format));
59962306a36Sopenharmony_ci				ds->pcm_buf_host_rw_ofs = preload;
60062306a36Sopenharmony_ci			}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			if (card->support_grouping) {
60362306a36Sopenharmony_ci				snd_printdd("%d group\n", s->number);
60462306a36Sopenharmony_ci				e = hpi_stream_group_add(
60562306a36Sopenharmony_ci					dpcm->h_stream,
60662306a36Sopenharmony_ci					ds->h_stream);
60762306a36Sopenharmony_ci				if (!e) {
60862306a36Sopenharmony_ci					snd_pcm_trigger_done(s, substream);
60962306a36Sopenharmony_ci				} else {
61062306a36Sopenharmony_ci					hpi_handle_error(e);
61162306a36Sopenharmony_ci					break;
61262306a36Sopenharmony_ci				}
61362306a36Sopenharmony_ci			} else
61462306a36Sopenharmony_ci				break;
61562306a36Sopenharmony_ci		}
61662306a36Sopenharmony_ci		/* start the master stream */
61762306a36Sopenharmony_ci		card->pcm_start(substream);
61862306a36Sopenharmony_ci		if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
61962306a36Sopenharmony_ci			!card->can_dma)
62062306a36Sopenharmony_ci			hpi_handle_error(hpi_stream_start(dpcm->h_stream));
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
62462306a36Sopenharmony_ci		snd_printdd("%s trigger stop\n", name);
62562306a36Sopenharmony_ci		card->pcm_stop(substream);
62662306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
62762306a36Sopenharmony_ci			if (snd_pcm_substream_chip(s) != card)
62862306a36Sopenharmony_ci				continue;
62962306a36Sopenharmony_ci			/* don't link Cap and Play */
63062306a36Sopenharmony_ci			if (substream->stream != s->stream)
63162306a36Sopenharmony_ci				continue;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			/*? workaround linked streams don't
63462306a36Sopenharmony_ci			transition to SETUP 20070706*/
63562306a36Sopenharmony_ci			__snd_pcm_set_state(s->runtime, SNDRV_PCM_STATE_SETUP);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			if (card->support_grouping) {
63862306a36Sopenharmony_ci				snd_printdd("%d group\n", s->number);
63962306a36Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
64062306a36Sopenharmony_ci			} else
64162306a36Sopenharmony_ci				break;
64262306a36Sopenharmony_ci		}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		/* _prepare and _hwparams reset the stream */
64562306a36Sopenharmony_ci		hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
64662306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
64762306a36Sopenharmony_ci			hpi_handle_error(
64862306a36Sopenharmony_ci				hpi_outstream_reset(dpcm->h_stream));
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		if (card->support_grouping)
65162306a36Sopenharmony_ci			hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream));
65262306a36Sopenharmony_ci		break;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
65562306a36Sopenharmony_ci		snd_printdd("%s trigger pause release\n", name);
65662306a36Sopenharmony_ci		card->pcm_start(substream);
65762306a36Sopenharmony_ci		hpi_handle_error(hpi_stream_start(dpcm->h_stream));
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
66062306a36Sopenharmony_ci		snd_printdd("%s trigger pause push\n", name);
66162306a36Sopenharmony_ci		card->pcm_stop(substream);
66262306a36Sopenharmony_ci		hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
66362306a36Sopenharmony_ci		break;
66462306a36Sopenharmony_ci	default:
66562306a36Sopenharmony_ci		snd_printd(KERN_ERR "\tINVALID\n");
66662306a36Sopenharmony_ci		return -EINVAL;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return 0;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci/*algorithm outline
67362306a36Sopenharmony_ci Without linking degenerates to getting single stream pos etc
67462306a36Sopenharmony_ci Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
67562306a36Sopenharmony_ci*/
67662306a36Sopenharmony_ci/*
67762306a36Sopenharmony_cipcm_buf_dma_ofs=get_buf_pos(s);
67862306a36Sopenharmony_cifor_each_linked_stream(s) {
67962306a36Sopenharmony_ci	pcm_buf_dma_ofs=get_buf_pos(s);
68062306a36Sopenharmony_ci	min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes)
68162306a36Sopenharmony_ci	new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos)
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_citimer.expires = jiffies + predict_next_period_ready(min_buf_pos);
68462306a36Sopenharmony_cifor_each_linked_stream(s) {
68562306a36Sopenharmony_ci	s->pcm_buf_dma_ofs = min_buf_pos;
68662306a36Sopenharmony_ci	if (new_data > period_bytes) {
68762306a36Sopenharmony_ci		if (mmap) {
68862306a36Sopenharmony_ci			irq_pos = (irq_pos + period_bytes) % buffer_bytes;
68962306a36Sopenharmony_ci			if (playback) {
69062306a36Sopenharmony_ci				write(period_bytes);
69162306a36Sopenharmony_ci			} else {
69262306a36Sopenharmony_ci				read(period_bytes);
69362306a36Sopenharmony_ci			}
69462306a36Sopenharmony_ci		}
69562306a36Sopenharmony_ci		snd_pcm_period_elapsed(s);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci*/
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/** Minimum of 2 modulo values.  Works correctly when the difference between
70162306a36Sopenharmony_ci* the values is less than half the modulus
70262306a36Sopenharmony_ci*/
70362306a36Sopenharmony_cistatic inline unsigned int modulo_min(unsigned int a, unsigned int b,
70462306a36Sopenharmony_ci					unsigned long int modulus)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	unsigned int result;
70762306a36Sopenharmony_ci	if (((a-b) % modulus) < (modulus/2))
70862306a36Sopenharmony_ci		result = b;
70962306a36Sopenharmony_ci	else
71062306a36Sopenharmony_ci		result = a;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return result;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/** Timer function, equivalent to interrupt service routine for cards
71662306a36Sopenharmony_ci*/
71762306a36Sopenharmony_cistatic void snd_card_asihpi_timer_function(struct timer_list *t)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer);
72062306a36Sopenharmony_ci	struct snd_pcm_substream *substream = dpcm->substream;
72162306a36Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
72262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
72362306a36Sopenharmony_ci	struct snd_pcm_substream *s;
72462306a36Sopenharmony_ci	unsigned int newdata = 0;
72562306a36Sopenharmony_ci	unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
72662306a36Sopenharmony_ci	unsigned int remdata, xfercount, next_jiffies;
72762306a36Sopenharmony_ci	int first = 1;
72862306a36Sopenharmony_ci	u16 state;
72962306a36Sopenharmony_ci	u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
73062306a36Sopenharmony_ci	char name[16];
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* find minimum newdata and buffer pos in group */
73662306a36Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
73762306a36Sopenharmony_ci		struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
73862306a36Sopenharmony_ci		runtime = s->runtime;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (snd_pcm_substream_chip(s) != card)
74162306a36Sopenharmony_ci			continue;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		/* don't link Cap and Play */
74462306a36Sopenharmony_ci		if (substream->stream != s->stream)
74562306a36Sopenharmony_ci			continue;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		hpi_handle_error(hpi_stream_get_info_ex(
74862306a36Sopenharmony_ci					ds->h_stream, &state,
74962306a36Sopenharmony_ci					&buffer_size, &bytes_avail,
75062306a36Sopenharmony_ci					&samples_played, &on_card_bytes));
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		/* number of bytes in on-card buffer */
75362306a36Sopenharmony_ci		runtime->delay = on_card_bytes;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (!card->can_dma)
75662306a36Sopenharmony_ci			on_card_bytes = bytes_avail;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
75962306a36Sopenharmony_ci			pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
76062306a36Sopenharmony_ci			if (state == HPI_STATE_STOPPED) {
76162306a36Sopenharmony_ci				if (bytes_avail == 0) {
76262306a36Sopenharmony_ci					hpi_handle_error(hpi_stream_start(ds->h_stream));
76362306a36Sopenharmony_ci					snd_printdd("P%d start\n", s->number);
76462306a36Sopenharmony_ci					ds->drained_count = 0;
76562306a36Sopenharmony_ci				}
76662306a36Sopenharmony_ci			} else if (state == HPI_STATE_DRAINED) {
76762306a36Sopenharmony_ci				snd_printd(KERN_WARNING "P%d drained\n",
76862306a36Sopenharmony_ci						s->number);
76962306a36Sopenharmony_ci				ds->drained_count++;
77062306a36Sopenharmony_ci				if (ds->drained_count > 20) {
77162306a36Sopenharmony_ci					snd_pcm_stop_xrun(s);
77262306a36Sopenharmony_ci					continue;
77362306a36Sopenharmony_ci				}
77462306a36Sopenharmony_ci			} else {
77562306a36Sopenharmony_ci				ds->drained_count = 0;
77662306a36Sopenharmony_ci			}
77762306a36Sopenharmony_ci		} else
77862306a36Sopenharmony_ci			pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		if (first) {
78162306a36Sopenharmony_ci			/* can't statically init min when wrap is involved */
78262306a36Sopenharmony_ci			min_buf_pos = pcm_buf_dma_ofs;
78362306a36Sopenharmony_ci			newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes;
78462306a36Sopenharmony_ci			first = 0;
78562306a36Sopenharmony_ci		} else {
78662306a36Sopenharmony_ci			min_buf_pos =
78762306a36Sopenharmony_ci				modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L);
78862306a36Sopenharmony_ci			newdata = min(
78962306a36Sopenharmony_ci				(pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes,
79062306a36Sopenharmony_ci				newdata);
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		snd_printddd(
79462306a36Sopenharmony_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",
79562306a36Sopenharmony_ci			name, s->number, state,
79662306a36Sopenharmony_ci			ds->pcm_buf_elapsed_dma_ofs,
79762306a36Sopenharmony_ci			ds->pcm_buf_host_rw_ofs,
79862306a36Sopenharmony_ci			pcm_buf_dma_ofs,
79962306a36Sopenharmony_ci			(int)bytes_avail,
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci			(int)on_card_bytes,
80262306a36Sopenharmony_ci			buffer_size-bytes_avail,
80362306a36Sopenharmony_ci			(unsigned long)frames_to_bytes(runtime,
80462306a36Sopenharmony_ci						runtime->status->hw_ptr),
80562306a36Sopenharmony_ci			(unsigned long)frames_to_bytes(runtime,
80662306a36Sopenharmony_ci						runtime->control->appl_ptr)
80762306a36Sopenharmony_ci		);
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci	pcm_buf_dma_ofs = min_buf_pos;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	remdata = newdata % dpcm->period_bytes;
81262306a36Sopenharmony_ci	xfercount = newdata - remdata; /* a multiple of period_bytes */
81362306a36Sopenharmony_ci	/* come back when on_card_bytes has decreased enough to allow
81462306a36Sopenharmony_ci	   write to happen, or when data has been consumed to make another
81562306a36Sopenharmony_ci	   period
81662306a36Sopenharmony_ci	*/
81762306a36Sopenharmony_ci	if (xfercount && (on_card_bytes  > dpcm->period_bytes))
81862306a36Sopenharmony_ci		next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec);
81962306a36Sopenharmony_ci	else
82062306a36Sopenharmony_ci		next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	next_jiffies = max(next_jiffies, 1U);
82362306a36Sopenharmony_ci	dpcm->timer.expires = jiffies + next_jiffies;
82462306a36Sopenharmony_ci	snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
82562306a36Sopenharmony_ci			next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
82862306a36Sopenharmony_ci		struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		/* don't link Cap and Play */
83162306a36Sopenharmony_ci		if (substream->stream != s->stream)
83262306a36Sopenharmony_ci			continue;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		/* Store dma offset for use by pointer callback */
83562306a36Sopenharmony_ci		ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (xfercount &&
83862306a36Sopenharmony_ci			/* Limit use of on card fifo for playback */
83962306a36Sopenharmony_ci			((on_card_bytes <= ds->period_bytes) ||
84062306a36Sopenharmony_ci			(s->stream == SNDRV_PCM_STREAM_CAPTURE)))
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		{
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci			unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
84562306a36Sopenharmony_ci			unsigned int xfer1, xfer2;
84662306a36Sopenharmony_ci			char *pd = &s->runtime->dma_area[buf_ofs];
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci			if (card->can_dma) { /* buffer wrap is handled at lower level */
84962306a36Sopenharmony_ci				xfer1 = xfercount;
85062306a36Sopenharmony_ci				xfer2 = 0;
85162306a36Sopenharmony_ci			} else {
85262306a36Sopenharmony_ci				xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
85362306a36Sopenharmony_ci				xfer2 = xfercount - xfer1;
85462306a36Sopenharmony_ci			}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
85762306a36Sopenharmony_ci				snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n",
85862306a36Sopenharmony_ci					s->number, xfer1, buf_ofs);
85962306a36Sopenharmony_ci				hpi_handle_error(
86062306a36Sopenharmony_ci					hpi_outstream_write_buf(
86162306a36Sopenharmony_ci						ds->h_stream, pd, xfer1,
86262306a36Sopenharmony_ci						&ds->format));
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci				if (xfer2) {
86562306a36Sopenharmony_ci					pd = s->runtime->dma_area;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci					snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n",
86862306a36Sopenharmony_ci							s->number,
86962306a36Sopenharmony_ci							xfercount - xfer1, buf_ofs);
87062306a36Sopenharmony_ci					hpi_handle_error(
87162306a36Sopenharmony_ci						hpi_outstream_write_buf(
87262306a36Sopenharmony_ci							ds->h_stream, pd,
87362306a36Sopenharmony_ci							xfercount - xfer1,
87462306a36Sopenharmony_ci							&ds->format));
87562306a36Sopenharmony_ci				}
87662306a36Sopenharmony_ci			} else {
87762306a36Sopenharmony_ci				snd_printddd("read1, C=%d, xfer=%d\n",
87862306a36Sopenharmony_ci					s->number, xfer1);
87962306a36Sopenharmony_ci				hpi_handle_error(
88062306a36Sopenharmony_ci					hpi_instream_read_buf(
88162306a36Sopenharmony_ci						ds->h_stream,
88262306a36Sopenharmony_ci						pd, xfer1));
88362306a36Sopenharmony_ci				if (xfer2) {
88462306a36Sopenharmony_ci					pd = s->runtime->dma_area;
88562306a36Sopenharmony_ci					snd_printddd("read2, C=%d, xfer=%d\n",
88662306a36Sopenharmony_ci						s->number, xfer2);
88762306a36Sopenharmony_ci					hpi_handle_error(
88862306a36Sopenharmony_ci						hpi_instream_read_buf(
88962306a36Sopenharmony_ci							ds->h_stream,
89062306a36Sopenharmony_ci							pd, xfer2));
89162306a36Sopenharmony_ci				}
89262306a36Sopenharmony_ci			}
89362306a36Sopenharmony_ci			/* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
89462306a36Sopenharmony_ci			ds->pcm_buf_host_rw_ofs += xfercount;
89562306a36Sopenharmony_ci			ds->pcm_buf_elapsed_dma_ofs += xfercount;
89662306a36Sopenharmony_ci			snd_pcm_period_elapsed(s);
89762306a36Sopenharmony_ci		}
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
90162306a36Sopenharmony_ci		add_timer(&dpcm->timer);
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_cistatic void snd_card_asihpi_isr(struct hpi_adapter *a)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
90962306a36Sopenharmony_ci	asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
91062306a36Sopenharmony_ci	if (asihpi->llmode_streampriv)
91162306a36Sopenharmony_ci		snd_card_asihpi_timer_function(
91262306a36Sopenharmony_ci			&asihpi->llmode_streampriv->timer);
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci/***************************** PLAYBACK OPS ****************/
91662306a36Sopenharmony_cistatic int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
91762306a36Sopenharmony_ci					    substream)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
92062306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	snd_printdd("P%d prepare\n", substream->number);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
92562306a36Sopenharmony_ci	dpcm->pcm_buf_host_rw_ofs = 0;
92662306a36Sopenharmony_ci	dpcm->pcm_buf_dma_ofs = 0;
92762306a36Sopenharmony_ci	dpcm->pcm_buf_elapsed_dma_ofs = 0;
92862306a36Sopenharmony_ci	return 0;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic snd_pcm_uframes_t
93262306a36Sopenharmony_cisnd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
93562306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
93662306a36Sopenharmony_ci	snd_pcm_uframes_t ptr;
93762306a36Sopenharmony_ci	char name[16];
93862306a36Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs  % dpcm->buffer_bytes);
94162306a36Sopenharmony_ci	snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr);
94262306a36Sopenharmony_ci	return ptr;
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi,
94662306a36Sopenharmony_ci						u32 h_stream)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct hpi_format hpi_format;
94962306a36Sopenharmony_ci	u16 format;
95062306a36Sopenharmony_ci	u16 err;
95162306a36Sopenharmony_ci	u32 h_control;
95262306a36Sopenharmony_ci	u32 sample_rate = 48000;
95362306a36Sopenharmony_ci	u64 formats = 0;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* on cards without SRC, must query at valid rate,
95662306a36Sopenharmony_ci	* maybe set by external sync
95762306a36Sopenharmony_ci	*/
95862306a36Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
95962306a36Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
96062306a36Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (!err)
96362306a36Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control,
96462306a36Sopenharmony_ci				&sample_rate);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
96762306a36Sopenharmony_ci	     format <= HPI_FORMAT_PCM24_SIGNED; format++) {
96862306a36Sopenharmony_ci		err = hpi_format_create(&hpi_format, asihpi->out_max_chans,
96962306a36Sopenharmony_ci					format, sample_rate, 128000, 0);
97062306a36Sopenharmony_ci		if (!err)
97162306a36Sopenharmony_ci			err = hpi_outstream_query_format(h_stream, &hpi_format);
97262306a36Sopenharmony_ci		if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
97362306a36Sopenharmony_ci			formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	return formats;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
98162306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
98262306a36Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
98362306a36Sopenharmony_ci	struct snd_pcm_hardware snd_card_asihpi_playback;
98462306a36Sopenharmony_ci	int err;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
98762306a36Sopenharmony_ci	if (dpcm == NULL)
98862306a36Sopenharmony_ci		return -ENOMEM;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	err = hpi_outstream_open(card->hpi->adapter->index,
99162306a36Sopenharmony_ci			      substream->number, &dpcm->h_stream);
99262306a36Sopenharmony_ci	hpi_handle_error(err);
99362306a36Sopenharmony_ci	if (err)
99462306a36Sopenharmony_ci		kfree(dpcm);
99562306a36Sopenharmony_ci	if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
99662306a36Sopenharmony_ci		return -EBUSY;
99762306a36Sopenharmony_ci	if (err)
99862306a36Sopenharmony_ci		return -EIO;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/*? also check ASI5000 samplerate source
100162306a36Sopenharmony_ci	    If external, only support external rate.
100262306a36Sopenharmony_ci	    If internal and other stream playing, can't switch
100362306a36Sopenharmony_ci	*/
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
100662306a36Sopenharmony_ci	dpcm->substream = substream;
100762306a36Sopenharmony_ci	runtime->private_data = dpcm;
100862306a36Sopenharmony_ci	runtime->private_free = snd_card_asihpi_runtime_free;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
101162306a36Sopenharmony_ci	if (!card->hpi->interrupt_mode) {
101262306a36Sopenharmony_ci		snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
101362306a36Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
101462306a36Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
101562306a36Sopenharmony_ci		snd_card_asihpi_playback.periods_min = PERIODS_MIN;
101662306a36Sopenharmony_ci		snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
101762306a36Sopenharmony_ci	} else {
101862306a36Sopenharmony_ci		size_t pbmin = card->update_interval_frames *
101962306a36Sopenharmony_ci			card->out_max_chans;
102062306a36Sopenharmony_ci		snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
102162306a36Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_min = pbmin;
102262306a36Sopenharmony_ci		snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
102362306a36Sopenharmony_ci		snd_card_asihpi_playback.periods_min = PERIODS_MIN;
102462306a36Sopenharmony_ci		snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/* snd_card_asihpi_playback.fifo_size = 0; */
102862306a36Sopenharmony_ci	snd_card_asihpi_playback.channels_max = card->out_max_chans;
102962306a36Sopenharmony_ci	snd_card_asihpi_playback.channels_min = card->out_min_chans;
103062306a36Sopenharmony_ci	snd_card_asihpi_playback.formats =
103162306a36Sopenharmony_ci			snd_card_asihpi_playback_formats(card, dpcm->h_stream);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_playback);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED |
103662306a36Sopenharmony_ci					SNDRV_PCM_INFO_DOUBLE |
103762306a36Sopenharmony_ci					SNDRV_PCM_INFO_BATCH |
103862306a36Sopenharmony_ci					SNDRV_PCM_INFO_BLOCK_TRANSFER |
103962306a36Sopenharmony_ci					SNDRV_PCM_INFO_PAUSE |
104062306a36Sopenharmony_ci					SNDRV_PCM_INFO_MMAP |
104162306a36Sopenharmony_ci					SNDRV_PCM_INFO_MMAP_VALID;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (card->support_grouping) {
104462306a36Sopenharmony_ci		snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
104562306a36Sopenharmony_ci		snd_pcm_set_sync(substream);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	/* struct is copied, so can create initializer dynamically */
104962306a36Sopenharmony_ci	runtime->hw = snd_card_asihpi_playback;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (card->can_dma)
105262306a36Sopenharmony_ci		err = snd_pcm_hw_constraint_pow2(runtime, 0,
105362306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
105462306a36Sopenharmony_ci	if (err < 0)
105562306a36Sopenharmony_ci		return err;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
105862306a36Sopenharmony_ci		card->update_interval_frames);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
106162306a36Sopenharmony_ci		card->update_interval_frames, UINT_MAX);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	snd_printdd("playback open\n");
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	return 0;
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
107162306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
107462306a36Sopenharmony_ci	snd_printdd("playback close\n");
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
108062306a36Sopenharmony_ci	.open = snd_card_asihpi_playback_open,
108162306a36Sopenharmony_ci	.close = snd_card_asihpi_playback_close,
108262306a36Sopenharmony_ci	.hw_params = snd_card_asihpi_pcm_hw_params,
108362306a36Sopenharmony_ci	.hw_free = snd_card_asihpi_hw_free,
108462306a36Sopenharmony_ci	.prepare = snd_card_asihpi_playback_prepare,
108562306a36Sopenharmony_ci	.trigger = snd_card_asihpi_trigger,
108662306a36Sopenharmony_ci	.pointer = snd_card_asihpi_playback_pointer,
108762306a36Sopenharmony_ci};
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci/***************************** CAPTURE OPS ****************/
109062306a36Sopenharmony_cistatic snd_pcm_uframes_t
109162306a36Sopenharmony_cisnd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
109462306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
109562306a36Sopenharmony_ci	char name[16];
109662306a36Sopenharmony_ci	snd_pcm_debug_name(substream, name, sizeof(name));
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
109962306a36Sopenharmony_ci	/* NOTE Unlike playback can't use actual samples_played
110062306a36Sopenharmony_ci		for the capture position, because those samples aren't yet in
110162306a36Sopenharmony_ci		the local buffer available for reading.
110262306a36Sopenharmony_ci	*/
110362306a36Sopenharmony_ci	return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
110962306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	hpi_handle_error(hpi_instream_reset(dpcm->h_stream));
111262306a36Sopenharmony_ci	dpcm->pcm_buf_host_rw_ofs = 0;
111362306a36Sopenharmony_ci	dpcm->pcm_buf_dma_ofs = 0;
111462306a36Sopenharmony_ci	dpcm->pcm_buf_elapsed_dma_ofs = 0;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	snd_printdd("Capture Prepare %d\n", substream->number);
111762306a36Sopenharmony_ci	return 0;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
112162306a36Sopenharmony_ci					u32 h_stream)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct hpi_format hpi_format;
112462306a36Sopenharmony_ci	u16 format;
112562306a36Sopenharmony_ci	u16 err;
112662306a36Sopenharmony_ci	u32 h_control;
112762306a36Sopenharmony_ci	u32 sample_rate = 48000;
112862306a36Sopenharmony_ci	u64 formats = 0;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* on cards without SRC, must query at valid rate,
113162306a36Sopenharmony_ci		maybe set by external sync */
113262306a36Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
113362306a36Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
113462306a36Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (!err)
113762306a36Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control,
113862306a36Sopenharmony_ci			&sample_rate);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	for (format = HPI_FORMAT_PCM8_UNSIGNED;
114162306a36Sopenharmony_ci		format <= HPI_FORMAT_PCM24_SIGNED; format++) {
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci		err = hpi_format_create(&hpi_format, asihpi->in_max_chans,
114462306a36Sopenharmony_ci					format, sample_rate, 128000, 0);
114562306a36Sopenharmony_ci		if (!err)
114662306a36Sopenharmony_ci			err = hpi_instream_query_format(h_stream, &hpi_format);
114762306a36Sopenharmony_ci		if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
114862306a36Sopenharmony_ci			formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci	return formats;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
115662306a36Sopenharmony_ci	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
115762306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm;
115862306a36Sopenharmony_ci	struct snd_pcm_hardware snd_card_asihpi_capture;
115962306a36Sopenharmony_ci	int err;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
116262306a36Sopenharmony_ci	if (dpcm == NULL)
116362306a36Sopenharmony_ci		return -ENOMEM;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	snd_printdd("capture open adapter %d stream %d\n",
116662306a36Sopenharmony_ci			card->hpi->adapter->index, substream->number);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	err = hpi_handle_error(
116962306a36Sopenharmony_ci	    hpi_instream_open(card->hpi->adapter->index,
117062306a36Sopenharmony_ci			     substream->number, &dpcm->h_stream));
117162306a36Sopenharmony_ci	if (err)
117262306a36Sopenharmony_ci		kfree(dpcm);
117362306a36Sopenharmony_ci	if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
117462306a36Sopenharmony_ci		return -EBUSY;
117562306a36Sopenharmony_ci	if (err)
117662306a36Sopenharmony_ci		return -EIO;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
117962306a36Sopenharmony_ci	dpcm->substream = substream;
118062306a36Sopenharmony_ci	runtime->private_data = dpcm;
118162306a36Sopenharmony_ci	runtime->private_free = snd_card_asihpi_runtime_free;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
118462306a36Sopenharmony_ci	if (!card->hpi->interrupt_mode) {
118562306a36Sopenharmony_ci		snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
118662306a36Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
118762306a36Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
118862306a36Sopenharmony_ci		snd_card_asihpi_capture.periods_min = PERIODS_MIN;
118962306a36Sopenharmony_ci		snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
119062306a36Sopenharmony_ci	} else {
119162306a36Sopenharmony_ci		size_t pbmin = card->update_interval_frames *
119262306a36Sopenharmony_ci			card->out_max_chans;
119362306a36Sopenharmony_ci		snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
119462306a36Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_min = pbmin;
119562306a36Sopenharmony_ci		snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
119662306a36Sopenharmony_ci		snd_card_asihpi_capture.periods_min = PERIODS_MIN;
119762306a36Sopenharmony_ci		snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	/* snd_card_asihpi_capture.fifo_size = 0; */
120062306a36Sopenharmony_ci	snd_card_asihpi_capture.channels_max = card->in_max_chans;
120162306a36Sopenharmony_ci	snd_card_asihpi_capture.channels_min = card->in_min_chans;
120262306a36Sopenharmony_ci	snd_card_asihpi_capture.formats =
120362306a36Sopenharmony_ci		snd_card_asihpi_capture_formats(card, dpcm->h_stream);
120462306a36Sopenharmony_ci	snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_capture);
120562306a36Sopenharmony_ci	snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
120662306a36Sopenharmony_ci					SNDRV_PCM_INFO_MMAP |
120762306a36Sopenharmony_ci					SNDRV_PCM_INFO_MMAP_VALID;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (card->support_grouping)
121062306a36Sopenharmony_ci		snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	runtime->hw = snd_card_asihpi_capture;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (card->can_dma)
121562306a36Sopenharmony_ci		err = snd_pcm_hw_constraint_pow2(runtime, 0,
121662306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
121762306a36Sopenharmony_ci	if (err < 0)
121862306a36Sopenharmony_ci		return err;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
122162306a36Sopenharmony_ci		card->update_interval_frames);
122262306a36Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
122362306a36Sopenharmony_ci		card->update_interval_frames, UINT_MAX);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return 0;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	hpi_handle_error(hpi_instream_close(dpcm->h_stream));
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
123962306a36Sopenharmony_ci	.open = snd_card_asihpi_capture_open,
124062306a36Sopenharmony_ci	.close = snd_card_asihpi_capture_close,
124162306a36Sopenharmony_ci	.hw_params = snd_card_asihpi_pcm_hw_params,
124262306a36Sopenharmony_ci	.hw_free = snd_card_asihpi_hw_free,
124362306a36Sopenharmony_ci	.prepare = snd_card_asihpi_capture_prepare,
124462306a36Sopenharmony_ci	.trigger = snd_card_asihpi_trigger,
124562306a36Sopenharmony_ci	.pointer = snd_card_asihpi_capture_pointer,
124662306a36Sopenharmony_ci};
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct snd_pcm *pcm;
125162306a36Sopenharmony_ci	int err;
125262306a36Sopenharmony_ci	u16 num_instreams, num_outstreams, x16;
125362306a36Sopenharmony_ci	u32 x32;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	err = hpi_adapter_get_info(asihpi->hpi->adapter->index,
125662306a36Sopenharmony_ci			&num_outstreams, &num_instreams,
125762306a36Sopenharmony_ci			&x16, &x32, &x16);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	err = snd_pcm_new(asihpi->card, "Asihpi PCM", device,
126062306a36Sopenharmony_ci			num_outstreams,	num_instreams, &pcm);
126162306a36Sopenharmony_ci	if (err < 0)
126262306a36Sopenharmony_ci		return err;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	/* pointer to ops struct is stored, dont change ops afterwards! */
126562306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
126662306a36Sopenharmony_ci			&snd_card_asihpi_playback_mmap_ops);
126762306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
126862306a36Sopenharmony_ci			&snd_card_asihpi_capture_mmap_ops);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	pcm->private_data = asihpi;
127162306a36Sopenharmony_ci	pcm->info_flags = 0;
127262306a36Sopenharmony_ci	strcpy(pcm->name, "Asihpi PCM");
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	/*? do we want to emulate MMAP for non-BBM cards?
127562306a36Sopenharmony_ci	Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
127662306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
127762306a36Sopenharmony_ci				       &asihpi->pci->dev,
127862306a36Sopenharmony_ci				       64*1024, BUFFER_BYTES_MAX);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return 0;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci/***************************** MIXER CONTROLS ****************/
128462306a36Sopenharmony_cistruct hpi_control {
128562306a36Sopenharmony_ci	u32 h_control;
128662306a36Sopenharmony_ci	u16 control_type;
128762306a36Sopenharmony_ci	u16 src_node_type;
128862306a36Sopenharmony_ci	u16 src_node_index;
128962306a36Sopenharmony_ci	u16 dst_node_type;
129062306a36Sopenharmony_ci	u16 dst_node_index;
129162306a36Sopenharmony_ci	u16 band;
129262306a36Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */
129362306a36Sopenharmony_ci};
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic const char * const asihpi_tuner_band_names[] = {
129662306a36Sopenharmony_ci	"invalid",
129762306a36Sopenharmony_ci	"AM",
129862306a36Sopenharmony_ci	"FM mono",
129962306a36Sopenharmony_ci	"TV NTSC-M",
130062306a36Sopenharmony_ci	"FM stereo",
130162306a36Sopenharmony_ci	"AUX",
130262306a36Sopenharmony_ci	"TV PAL BG",
130362306a36Sopenharmony_ci	"TV PAL I",
130462306a36Sopenharmony_ci	"TV PAL DK",
130562306a36Sopenharmony_ci	"TV SECAM",
130662306a36Sopenharmony_ci	"TV DAB",
130762306a36Sopenharmony_ci};
130862306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */
130962306a36Sopenharmony_cicompile_time_assert(
131062306a36Sopenharmony_ci	(ARRAY_SIZE(asihpi_tuner_band_names) ==
131162306a36Sopenharmony_ci		(HPI_TUNER_BAND_LAST+1)),
131262306a36Sopenharmony_ci	assert_tuner_band_names_size);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic const char * const asihpi_src_names[] = {
131562306a36Sopenharmony_ci	"no source",
131662306a36Sopenharmony_ci	"PCM",
131762306a36Sopenharmony_ci	"Line",
131862306a36Sopenharmony_ci	"Digital",
131962306a36Sopenharmony_ci	"Tuner",
132062306a36Sopenharmony_ci	"RF",
132162306a36Sopenharmony_ci	"Clock",
132262306a36Sopenharmony_ci	"Bitstream",
132362306a36Sopenharmony_ci	"Mic",
132462306a36Sopenharmony_ci	"Net",
132562306a36Sopenharmony_ci	"Analog",
132662306a36Sopenharmony_ci	"Adapter",
132762306a36Sopenharmony_ci	"RTP",
132862306a36Sopenharmony_ci	"Internal",
132962306a36Sopenharmony_ci	"AVB",
133062306a36Sopenharmony_ci	"BLU-Link"
133162306a36Sopenharmony_ci};
133262306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */
133362306a36Sopenharmony_cicompile_time_assert(
133462306a36Sopenharmony_ci	(ARRAY_SIZE(asihpi_src_names) ==
133562306a36Sopenharmony_ci		(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
133662306a36Sopenharmony_ci	assert_src_names_size);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic const char * const asihpi_dst_names[] = {
133962306a36Sopenharmony_ci	"no destination",
134062306a36Sopenharmony_ci	"PCM",
134162306a36Sopenharmony_ci	"Line",
134262306a36Sopenharmony_ci	"Digital",
134362306a36Sopenharmony_ci	"RF",
134462306a36Sopenharmony_ci	"Speaker",
134562306a36Sopenharmony_ci	"Net",
134662306a36Sopenharmony_ci	"Analog",
134762306a36Sopenharmony_ci	"RTP",
134862306a36Sopenharmony_ci	"AVB",
134962306a36Sopenharmony_ci	"Internal",
135062306a36Sopenharmony_ci	"BLU-Link"
135162306a36Sopenharmony_ci};
135262306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */
135362306a36Sopenharmony_cicompile_time_assert(
135462306a36Sopenharmony_ci	(ARRAY_SIZE(asihpi_dst_names) ==
135562306a36Sopenharmony_ci		(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
135662306a36Sopenharmony_ci	assert_dst_names_size);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
135962306a36Sopenharmony_ci				struct snd_card_asihpi *asihpi)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	int err;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi));
136462306a36Sopenharmony_ci	if (err < 0)
136562306a36Sopenharmony_ci		return err;
136662306a36Sopenharmony_ci	else if (mixer_dump)
136762306a36Sopenharmony_ci		dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	return 0;
137062306a36Sopenharmony_ci}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci/* Convert HPI control name and location into ALSA control name */
137362306a36Sopenharmony_cistatic void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
137462306a36Sopenharmony_ci				struct hpi_control *hpi_ctl,
137562306a36Sopenharmony_ci				char *name)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	char *dir;
137862306a36Sopenharmony_ci	memset(snd_control, 0, sizeof(*snd_control));
137962306a36Sopenharmony_ci	snd_control->name = hpi_ctl->name;
138062306a36Sopenharmony_ci	snd_control->private_value = hpi_ctl->h_control;
138162306a36Sopenharmony_ci	snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
138262306a36Sopenharmony_ci	snd_control->index = 0;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
138562306a36Sopenharmony_ci		dir = ""; /* clock is neither capture nor playback */
138662306a36Sopenharmony_ci	else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
138762306a36Sopenharmony_ci		dir = "Capture ";  /* On or towards a PCM capture destination*/
138862306a36Sopenharmony_ci	else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
138962306a36Sopenharmony_ci		(!hpi_ctl->dst_node_type))
139062306a36Sopenharmony_ci		dir = "Capture "; /* On a source node that is not PCM playback */
139162306a36Sopenharmony_ci	else if (hpi_ctl->src_node_type &&
139262306a36Sopenharmony_ci		(hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
139362306a36Sopenharmony_ci		(hpi_ctl->dst_node_type))
139462306a36Sopenharmony_ci		dir = "Monitor Playback "; /* Between an input and an output */
139562306a36Sopenharmony_ci	else
139662306a36Sopenharmony_ci		dir = "Playback "; /* PCM Playback source, or  output node */
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
139962306a36Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
140062306a36Sopenharmony_ci			asihpi_src_names[hpi_ctl->src_node_type],
140162306a36Sopenharmony_ci			hpi_ctl->src_node_index,
140262306a36Sopenharmony_ci			asihpi_dst_names[hpi_ctl->dst_node_type],
140362306a36Sopenharmony_ci			hpi_ctl->dst_node_index,
140462306a36Sopenharmony_ci			dir, name);
140562306a36Sopenharmony_ci	else if (hpi_ctl->dst_node_type) {
140662306a36Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s%s",
140762306a36Sopenharmony_ci		asihpi_dst_names[hpi_ctl->dst_node_type],
140862306a36Sopenharmony_ci		hpi_ctl->dst_node_index,
140962306a36Sopenharmony_ci		dir, name);
141062306a36Sopenharmony_ci	} else {
141162306a36Sopenharmony_ci		sprintf(hpi_ctl->name, "%s %d %s%s",
141262306a36Sopenharmony_ci		asihpi_src_names[hpi_ctl->src_node_type],
141362306a36Sopenharmony_ci		hpi_ctl->src_node_index,
141462306a36Sopenharmony_ci		dir, name);
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci	/* printk(KERN_INFO "Adding %s %d to %d ",  hpi_ctl->name,
141762306a36Sopenharmony_ci		hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci/*------------------------------------------------------------
142162306a36Sopenharmony_ci   Volume controls
142262306a36Sopenharmony_ci ------------------------------------------------------------*/
142362306a36Sopenharmony_ci#define VOL_STEP_mB 1
142462306a36Sopenharmony_cistatic int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
142562306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
142862306a36Sopenharmony_ci	u32 count;
142962306a36Sopenharmony_ci	u16 err;
143062306a36Sopenharmony_ci	/* native gains are in millibels */
143162306a36Sopenharmony_ci	short min_gain_mB;
143262306a36Sopenharmony_ci	short max_gain_mB;
143362306a36Sopenharmony_ci	short step_gain_mB;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	err = hpi_volume_query_range(h_control,
143662306a36Sopenharmony_ci			&min_gain_mB, &max_gain_mB, &step_gain_mB);
143762306a36Sopenharmony_ci	if (err) {
143862306a36Sopenharmony_ci		max_gain_mB = 0;
143962306a36Sopenharmony_ci		min_gain_mB = -10000;
144062306a36Sopenharmony_ci		step_gain_mB = VOL_STEP_mB;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	err = hpi_meter_query_channels(h_control, &count);
144462306a36Sopenharmony_ci	if (err)
144562306a36Sopenharmony_ci		count = HPI_MAX_CHANNELS;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
144862306a36Sopenharmony_ci	uinfo->count = count;
144962306a36Sopenharmony_ci	uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB;
145062306a36Sopenharmony_ci	uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB;
145162306a36Sopenharmony_ci	uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB;
145262306a36Sopenharmony_ci	return 0;
145362306a36Sopenharmony_ci}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
145662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
145962306a36Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB));
146262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
146362306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	return 0;
146662306a36Sopenharmony_ci}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_cistatic int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
146962306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
147262306a36Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	an_gain_mB[0] =
147562306a36Sopenharmony_ci	    (ucontrol->value.integer.value[0]) * VOL_STEP_mB;
147662306a36Sopenharmony_ci	an_gain_mB[1] =
147762306a36Sopenharmony_ci	    (ucontrol->value.integer.value[1]) * VOL_STEP_mB;
147862306a36Sopenharmony_ci	/*  change = asihpi->mixer_volume[addr][0] != left ||
147962306a36Sopenharmony_ci	   asihpi->mixer_volume[addr][1] != right;
148062306a36Sopenharmony_ci	 */
148162306a36Sopenharmony_ci	hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
148262306a36Sopenharmony_ci	return 1;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci#define snd_asihpi_volume_mute_info	snd_ctl_boolean_mono_info
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol,
149062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
149362306a36Sopenharmony_ci	u32 mute;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	hpi_handle_error(hpi_volume_get_mute(h_control, &mute));
149662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = mute ? 0 : 1;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	return 0;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
150262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
150562306a36Sopenharmony_ci	/* HPI currently only supports all or none muting of multichannel volume
150662306a36Sopenharmony_ci	ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
150762306a36Sopenharmony_ci	*/
150862306a36Sopenharmony_ci	int mute =  ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
150962306a36Sopenharmony_ci	hpi_handle_error(hpi_volume_set_mute(h_control, mute));
151062306a36Sopenharmony_ci	return 1;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
151462306a36Sopenharmony_ci				 struct hpi_control *hpi_ctl)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
151762306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
151862306a36Sopenharmony_ci	int err;
151962306a36Sopenharmony_ci	u32 mute;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
152262306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
152362306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
152462306a36Sopenharmony_ci	snd_control.info = snd_asihpi_volume_info;
152562306a36Sopenharmony_ci	snd_control.get = snd_asihpi_volume_get;
152662306a36Sopenharmony_ci	snd_control.put = snd_asihpi_volume_put;
152762306a36Sopenharmony_ci	snd_control.tlv.p = db_scale_100;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	err = ctl_add(card, &snd_control, asihpi);
153062306a36Sopenharmony_ci	if (err)
153162306a36Sopenharmony_ci		return err;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) {
153462306a36Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Switch");
153562306a36Sopenharmony_ci		snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
153662306a36Sopenharmony_ci		snd_control.info = snd_asihpi_volume_mute_info;
153762306a36Sopenharmony_ci		snd_control.get = snd_asihpi_volume_mute_get;
153862306a36Sopenharmony_ci		snd_control.put = snd_asihpi_volume_mute_put;
153962306a36Sopenharmony_ci		err = ctl_add(card, &snd_control, asihpi);
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci	return err;
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci/*------------------------------------------------------------
154562306a36Sopenharmony_ci   Level controls
154662306a36Sopenharmony_ci ------------------------------------------------------------*/
154762306a36Sopenharmony_cistatic int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
154862306a36Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
155162306a36Sopenharmony_ci	u16 err;
155262306a36Sopenharmony_ci	short min_gain_mB;
155362306a36Sopenharmony_ci	short max_gain_mB;
155462306a36Sopenharmony_ci	short step_gain_mB;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	err =
155762306a36Sopenharmony_ci	    hpi_level_query_range(h_control, &min_gain_mB,
155862306a36Sopenharmony_ci			       &max_gain_mB, &step_gain_mB);
155962306a36Sopenharmony_ci	if (err) {
156062306a36Sopenharmony_ci		max_gain_mB = 2400;
156162306a36Sopenharmony_ci		min_gain_mB = -1000;
156262306a36Sopenharmony_ci		step_gain_mB = 100;
156362306a36Sopenharmony_ci	}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
156662306a36Sopenharmony_ci	uinfo->count = 2;
156762306a36Sopenharmony_ci	uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB;
156862306a36Sopenharmony_ci	uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB;
156962306a36Sopenharmony_ci	uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB;
157062306a36Sopenharmony_ci	return 0;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
157462306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
157762306a36Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB));
158062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
158162306a36Sopenharmony_ci	    an_gain_mB[0] / HPI_UNITS_PER_dB;
158262306a36Sopenharmony_ci	ucontrol->value.integer.value[1] =
158362306a36Sopenharmony_ci	    an_gain_mB[1] / HPI_UNITS_PER_dB;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	return 0;
158662306a36Sopenharmony_ci}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
158962306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
159062306a36Sopenharmony_ci{
159162306a36Sopenharmony_ci	int change;
159262306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
159362306a36Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS];
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	an_gain_mB[0] =
159662306a36Sopenharmony_ci	    (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
159762306a36Sopenharmony_ci	an_gain_mB[1] =
159862306a36Sopenharmony_ci	    (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB;
159962306a36Sopenharmony_ci	/*  change = asihpi->mixer_level[addr][0] != left ||
160062306a36Sopenharmony_ci	   asihpi->mixer_level[addr][1] != right;
160162306a36Sopenharmony_ci	 */
160262306a36Sopenharmony_ci	change = 1;
160362306a36Sopenharmony_ci	hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB));
160462306a36Sopenharmony_ci	return change;
160562306a36Sopenharmony_ci}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_cistatic int snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
161062306a36Sopenharmony_ci				struct hpi_control *hpi_ctl)
161162306a36Sopenharmony_ci{
161262306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
161362306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	/* can't use 'volume' cos some nodes have volume as well */
161662306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Level");
161762306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
161862306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
161962306a36Sopenharmony_ci	snd_control.info = snd_asihpi_level_info;
162062306a36Sopenharmony_ci	snd_control.get = snd_asihpi_level_get;
162162306a36Sopenharmony_ci	snd_control.put = snd_asihpi_level_put;
162262306a36Sopenharmony_ci	snd_control.tlv.p = db_scale_level;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci/*------------------------------------------------------------
162862306a36Sopenharmony_ci   AESEBU controls
162962306a36Sopenharmony_ci ------------------------------------------------------------*/
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci/* AESEBU format */
163262306a36Sopenharmony_cistatic const char * const asihpi_aesebu_format_names[] = {
163362306a36Sopenharmony_ci	"N/A", "S/PDIF", "AES/EBU" };
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
163662306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
163762306a36Sopenharmony_ci{
163862306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names);
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
164262306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol,
164362306a36Sopenharmony_ci			u16 (*func)(u32, u16 *))
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
164662306a36Sopenharmony_ci	u16 source, err;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	err = func(h_control, &source);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	/* default to N/A */
165162306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
165262306a36Sopenharmony_ci	/* return success but set the control to N/A */
165362306a36Sopenharmony_ci	if (err)
165462306a36Sopenharmony_ci		return 0;
165562306a36Sopenharmony_ci	if (source == HPI_AESEBU_FORMAT_SPDIF)
165662306a36Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 1;
165762306a36Sopenharmony_ci	if (source == HPI_AESEBU_FORMAT_AESEBU)
165862306a36Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 2;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	return 0;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
166462306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol,
166562306a36Sopenharmony_ci			 u16 (*func)(u32, u16))
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	/* default to S/PDIF */
167062306a36Sopenharmony_ci	u16 source = HPI_AESEBU_FORMAT_SPDIF;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 1)
167362306a36Sopenharmony_ci		source = HPI_AESEBU_FORMAT_SPDIF;
167462306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 2)
167562306a36Sopenharmony_ci		source = HPI_AESEBU_FORMAT_AESEBU;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	if (func(h_control, source) != 0)
167862306a36Sopenharmony_ci		return -EINVAL;
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	return 1;
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
168462306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
168562306a36Sopenharmony_ci	return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
168662306a36Sopenharmony_ci					hpi_aesebu_receiver_get_format);
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
169062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
169162306a36Sopenharmony_ci	return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
169262306a36Sopenharmony_ci					hpi_aesebu_receiver_set_format);
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
169662306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
169962306a36Sopenharmony_ci	uinfo->count = 1;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
170262306a36Sopenharmony_ci	uinfo->value.integer.max = 0X1F;
170362306a36Sopenharmony_ci	uinfo->value.integer.step = 1;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	return 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
170962306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
171262306a36Sopenharmony_ci	u16 status;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	hpi_handle_error(hpi_aesebu_receiver_get_error_status(
171562306a36Sopenharmony_ci					 h_control, &status));
171662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = status;
171762306a36Sopenharmony_ci	return 0;
171862306a36Sopenharmony_ci}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
172162306a36Sopenharmony_ci				    struct hpi_control *hpi_ctl)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
172462306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
172762306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
172862306a36Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_format_info;
172962306a36Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_rx_format_get;
173062306a36Sopenharmony_ci	snd_control.put = snd_asihpi_aesebu_rx_format_put;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
173462306a36Sopenharmony_ci		return -EINVAL;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Status");
173762306a36Sopenharmony_ci	snd_control.access =
173862306a36Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
173962306a36Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_rxstatus_info;
174062306a36Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_rxstatus_get;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
174662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
174762306a36Sopenharmony_ci	return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
174862306a36Sopenharmony_ci					hpi_aesebu_transmitter_get_format);
174962306a36Sopenharmony_ci}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
175262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol) {
175362306a36Sopenharmony_ci	return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
175462306a36Sopenharmony_ci					hpi_aesebu_transmitter_set_format);
175562306a36Sopenharmony_ci}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
175962306a36Sopenharmony_ci				    struct hpi_control *hpi_ctl)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
176262306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
176562306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
176662306a36Sopenharmony_ci	snd_control.info = snd_asihpi_aesebu_format_info;
176762306a36Sopenharmony_ci	snd_control.get = snd_asihpi_aesebu_tx_format_get;
176862306a36Sopenharmony_ci	snd_control.put = snd_asihpi_aesebu_tx_format_put;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci/*------------------------------------------------------------
177462306a36Sopenharmony_ci   Tuner controls
177562306a36Sopenharmony_ci ------------------------------------------------------------*/
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci/* Gain */
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
178062306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
178362306a36Sopenharmony_ci	u16 err;
178462306a36Sopenharmony_ci	short idx;
178562306a36Sopenharmony_ci	u16 gain_range[3];
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	for (idx = 0; idx < 3; idx++) {
178862306a36Sopenharmony_ci		err = hpi_tuner_query_gain(h_control,
178962306a36Sopenharmony_ci					  idx, &gain_range[idx]);
179062306a36Sopenharmony_ci		if (err != 0)
179162306a36Sopenharmony_ci			return err;
179262306a36Sopenharmony_ci	}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
179562306a36Sopenharmony_ci	uinfo->count = 1;
179662306a36Sopenharmony_ci	uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB;
179762306a36Sopenharmony_ci	uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB;
179862306a36Sopenharmony_ci	uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB;
179962306a36Sopenharmony_ci	return 0;
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
180362306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	/*
180662306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
180762306a36Sopenharmony_ci	*/
180862306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
180962306a36Sopenharmony_ci	short gain;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_gain(h_control, &gain));
181262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	return 0;
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
181862306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
181962306a36Sopenharmony_ci{
182062306a36Sopenharmony_ci	/*
182162306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
182262306a36Sopenharmony_ci	*/
182362306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
182462306a36Sopenharmony_ci	short gain;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
182762306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_gain(h_control, gain));
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	return 1;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci/* Band  */
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_cistatic int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
183562306a36Sopenharmony_ci					u16 *band_list, u32 len) {
183662306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
183762306a36Sopenharmony_ci	u16 err = 0;
183862306a36Sopenharmony_ci	u32 i;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
184162306a36Sopenharmony_ci		err = hpi_tuner_query_band(
184262306a36Sopenharmony_ci				h_control, i, &band_list[i]);
184362306a36Sopenharmony_ci		if (err != 0)
184462306a36Sopenharmony_ci			break;
184562306a36Sopenharmony_ci	}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX))
184862306a36Sopenharmony_ci		return -EIO;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	return i;
185162306a36Sopenharmony_ci}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
185462306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
185762306a36Sopenharmony_ci	int num_bands = 0;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
186062306a36Sopenharmony_ci				HPI_TUNER_BAND_LAST);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (num_bands < 0)
186362306a36Sopenharmony_ci		return num_bands;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names);
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
186962306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
187062306a36Sopenharmony_ci{
187162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
187262306a36Sopenharmony_ci	/*
187362306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
187462306a36Sopenharmony_ci	*/
187562306a36Sopenharmony_ci	u16 band, idx;
187662306a36Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
187762306a36Sopenharmony_ci	__always_unused u32 num_bands;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
188062306a36Sopenharmony_ci				HPI_TUNER_BAND_LAST);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_band(h_control, &band));
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = -1;
188562306a36Sopenharmony_ci	for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
188662306a36Sopenharmony_ci		if (tuner_bands[idx] == band) {
188762306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] = idx;
188862306a36Sopenharmony_ci			break;
188962306a36Sopenharmony_ci		}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	return 0;
189262306a36Sopenharmony_ci}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
189562306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	/*
189862306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
189962306a36Sopenharmony_ci	*/
190062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
190162306a36Sopenharmony_ci	unsigned int idx;
190262306a36Sopenharmony_ci	u16 band;
190362306a36Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
190462306a36Sopenharmony_ci	__always_unused u32 num_bands;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
190762306a36Sopenharmony_ci			HPI_TUNER_BAND_LAST);
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	idx = ucontrol->value.enumerated.item[0];
191062306a36Sopenharmony_ci	if (idx >= ARRAY_SIZE(tuner_bands))
191162306a36Sopenharmony_ci		idx = ARRAY_SIZE(tuner_bands) - 1;
191262306a36Sopenharmony_ci	band = tuner_bands[idx];
191362306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_band(h_control, band));
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return 1;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci/* Freq */
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
192162306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
192262306a36Sopenharmony_ci{
192362306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
192462306a36Sopenharmony_ci	u16 err;
192562306a36Sopenharmony_ci	u16 tuner_bands[HPI_TUNER_BAND_LAST];
192662306a36Sopenharmony_ci	u16 num_bands = 0, band_iter, idx;
192762306a36Sopenharmony_ci	u32 freq_range[3], temp_freq_range[3];
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
193062306a36Sopenharmony_ci			HPI_TUNER_BAND_LAST);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	freq_range[0] = INT_MAX;
193362306a36Sopenharmony_ci	freq_range[1] = 0;
193462306a36Sopenharmony_ci	freq_range[2] = INT_MAX;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	for (band_iter = 0; band_iter < num_bands; band_iter++) {
193762306a36Sopenharmony_ci		for (idx = 0; idx < 3; idx++) {
193862306a36Sopenharmony_ci			err = hpi_tuner_query_frequency(h_control,
193962306a36Sopenharmony_ci				idx, tuner_bands[band_iter],
194062306a36Sopenharmony_ci				&temp_freq_range[idx]);
194162306a36Sopenharmony_ci			if (err != 0)
194262306a36Sopenharmony_ci				return err;
194362306a36Sopenharmony_ci		}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci		/* skip band with bogus stepping */
194662306a36Sopenharmony_ci		if (temp_freq_range[2] <= 0)
194762306a36Sopenharmony_ci			continue;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci		if (temp_freq_range[0] < freq_range[0])
195062306a36Sopenharmony_ci			freq_range[0] = temp_freq_range[0];
195162306a36Sopenharmony_ci		if (temp_freq_range[1] > freq_range[1])
195262306a36Sopenharmony_ci			freq_range[1] = temp_freq_range[1];
195362306a36Sopenharmony_ci		if (temp_freq_range[2] < freq_range[2])
195462306a36Sopenharmony_ci			freq_range[2] = temp_freq_range[2];
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
195862306a36Sopenharmony_ci	uinfo->count = 1;
195962306a36Sopenharmony_ci	uinfo->value.integer.min = ((int)freq_range[0]);
196062306a36Sopenharmony_ci	uinfo->value.integer.max = ((int)freq_range[1]);
196162306a36Sopenharmony_ci	uinfo->value.integer.step = ((int)freq_range[2]);
196262306a36Sopenharmony_ci	return 0;
196362306a36Sopenharmony_ci}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
196662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
196962306a36Sopenharmony_ci	u32 freq;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq));
197262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = freq;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	return 0;
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
197862306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
197962306a36Sopenharmony_ci{
198062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
198162306a36Sopenharmony_ci	u32 freq;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	freq = ucontrol->value.integer.value[0];
198462306a36Sopenharmony_ci	hpi_handle_error(hpi_tuner_set_frequency(h_control, freq));
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	return 1;
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci/* Tuner control group initializer  */
199062306a36Sopenharmony_cistatic int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
199162306a36Sopenharmony_ci				struct hpi_control *hpi_ctl)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
199462306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	snd_control.private_value = hpi_ctl->h_control;
199762306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) {
200062306a36Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Gain");
200162306a36Sopenharmony_ci		snd_control.info = snd_asihpi_tuner_gain_info;
200262306a36Sopenharmony_ci		snd_control.get = snd_asihpi_tuner_gain_get;
200362306a36Sopenharmony_ci		snd_control.put = snd_asihpi_tuner_gain_put;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci		if (ctl_add(card, &snd_control, asihpi) < 0)
200662306a36Sopenharmony_ci			return -EINVAL;
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Band");
201062306a36Sopenharmony_ci	snd_control.info = snd_asihpi_tuner_band_info;
201162306a36Sopenharmony_ci	snd_control.get = snd_asihpi_tuner_band_get;
201262306a36Sopenharmony_ci	snd_control.put = snd_asihpi_tuner_band_put;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
201562306a36Sopenharmony_ci		return -EINVAL;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Freq");
201862306a36Sopenharmony_ci	snd_control.info = snd_asihpi_tuner_freq_info;
201962306a36Sopenharmony_ci	snd_control.get = snd_asihpi_tuner_freq_get;
202062306a36Sopenharmony_ci	snd_control.put = snd_asihpi_tuner_freq_put;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
202362306a36Sopenharmony_ci}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci/*------------------------------------------------------------
202662306a36Sopenharmony_ci   Meter controls
202762306a36Sopenharmony_ci ------------------------------------------------------------*/
202862306a36Sopenharmony_cistatic int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
202962306a36Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
203062306a36Sopenharmony_ci{
203162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
203262306a36Sopenharmony_ci	u32 count;
203362306a36Sopenharmony_ci	u16 err;
203462306a36Sopenharmony_ci	err = hpi_meter_query_channels(h_control, &count);
203562306a36Sopenharmony_ci	if (err)
203662306a36Sopenharmony_ci		count = HPI_MAX_CHANNELS;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
203962306a36Sopenharmony_ci	uinfo->count = count;
204062306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
204162306a36Sopenharmony_ci	uinfo->value.integer.max = 0x7FFFFFFF;
204262306a36Sopenharmony_ci	return 0;
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci/* linear values for 10dB steps */
204662306a36Sopenharmony_cistatic const int log2lin[] = {
204762306a36Sopenharmony_ci	0x7FFFFFFF, /* 0dB */
204862306a36Sopenharmony_ci	679093956,
204962306a36Sopenharmony_ci	214748365,
205062306a36Sopenharmony_ci	 67909396,
205162306a36Sopenharmony_ci	 21474837,
205262306a36Sopenharmony_ci	  6790940,
205362306a36Sopenharmony_ci	  2147484, /* -60dB */
205462306a36Sopenharmony_ci	   679094,
205562306a36Sopenharmony_ci	   214748, /* -80 */
205662306a36Sopenharmony_ci	    67909,
205762306a36Sopenharmony_ci	    21475, /* -100 */
205862306a36Sopenharmony_ci	     6791,
205962306a36Sopenharmony_ci	     2147,
206062306a36Sopenharmony_ci	      679,
206162306a36Sopenharmony_ci	      214,
206262306a36Sopenharmony_ci	       68,
206362306a36Sopenharmony_ci	       21,
206462306a36Sopenharmony_ci		7,
206562306a36Sopenharmony_ci		2
206662306a36Sopenharmony_ci};
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_cistatic int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
206962306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
207062306a36Sopenharmony_ci{
207162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
207262306a36Sopenharmony_ci	short an_gain_mB[HPI_MAX_CHANNELS], i;
207362306a36Sopenharmony_ci	u16 err;
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	err = hpi_meter_get_peak(h_control, an_gain_mB);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	for (i = 0; i < HPI_MAX_CHANNELS; i++) {
207862306a36Sopenharmony_ci		if (err) {
207962306a36Sopenharmony_ci			ucontrol->value.integer.value[i] = 0;
208062306a36Sopenharmony_ci		} else if (an_gain_mB[i] >= 0) {
208162306a36Sopenharmony_ci			ucontrol->value.integer.value[i] =
208262306a36Sopenharmony_ci				an_gain_mB[i] << 16;
208362306a36Sopenharmony_ci		} else {
208462306a36Sopenharmony_ci			/* -ve is log value in millibels < -60dB,
208562306a36Sopenharmony_ci			* convert to (roughly!) linear,
208662306a36Sopenharmony_ci			*/
208762306a36Sopenharmony_ci			ucontrol->value.integer.value[i] =
208862306a36Sopenharmony_ci					log2lin[an_gain_mB[i] / -1000];
208962306a36Sopenharmony_ci		}
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci	return 0;
209262306a36Sopenharmony_ci}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_cistatic int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
209562306a36Sopenharmony_ci				struct hpi_control *hpi_ctl, int subidx)
209662306a36Sopenharmony_ci{
209762306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
209862306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Meter");
210162306a36Sopenharmony_ci	snd_control.access =
210262306a36Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
210362306a36Sopenharmony_ci	snd_control.info = snd_asihpi_meter_info;
210462306a36Sopenharmony_ci	snd_control.get = snd_asihpi_meter_get;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	snd_control.index = subidx;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
210962306a36Sopenharmony_ci}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci/*------------------------------------------------------------
211262306a36Sopenharmony_ci   Multiplexer controls
211362306a36Sopenharmony_ci ------------------------------------------------------------*/
211462306a36Sopenharmony_cistatic int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
211562306a36Sopenharmony_ci{
211662306a36Sopenharmony_ci	u32 h_control = snd_control->private_value;
211762306a36Sopenharmony_ci	struct hpi_control hpi_ctl;
211862306a36Sopenharmony_ci	int s, err;
211962306a36Sopenharmony_ci	for (s = 0; s < 32; s++) {
212062306a36Sopenharmony_ci		err = hpi_multiplexer_query_source(h_control, s,
212162306a36Sopenharmony_ci						  &hpi_ctl.
212262306a36Sopenharmony_ci						  src_node_type,
212362306a36Sopenharmony_ci						  &hpi_ctl.
212462306a36Sopenharmony_ci						  src_node_index);
212562306a36Sopenharmony_ci		if (err)
212662306a36Sopenharmony_ci			break;
212762306a36Sopenharmony_ci	}
212862306a36Sopenharmony_ci	return s;
212962306a36Sopenharmony_ci}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_cistatic int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
213262306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
213362306a36Sopenharmony_ci{
213462306a36Sopenharmony_ci	u16 src_node_type, src_node_index;
213562306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
213862306a36Sopenharmony_ci	uinfo->count = 1;
213962306a36Sopenharmony_ci	uinfo->value.enumerated.items =
214062306a36Sopenharmony_ci	    snd_card_asihpi_mux_count_sources(kcontrol);
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
214362306a36Sopenharmony_ci		uinfo->value.enumerated.item =
214462306a36Sopenharmony_ci		    uinfo->value.enumerated.items - 1;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	hpi_multiplexer_query_source(h_control,
214762306a36Sopenharmony_ci				     uinfo->value.enumerated.item,
214862306a36Sopenharmony_ci				     &src_node_type, &src_node_index);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	sprintf(uinfo->value.enumerated.name, "%s %d",
215162306a36Sopenharmony_ci		asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE],
215262306a36Sopenharmony_ci		src_node_index);
215362306a36Sopenharmony_ci	return 0;
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_cistatic int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
215762306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
215862306a36Sopenharmony_ci{
215962306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
216062306a36Sopenharmony_ci	u16 source_type, source_index;
216162306a36Sopenharmony_ci	u16 src_node_type, src_node_index;
216262306a36Sopenharmony_ci	int s;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	hpi_handle_error(hpi_multiplexer_get_source(h_control,
216562306a36Sopenharmony_ci				&source_type, &source_index));
216662306a36Sopenharmony_ci	/* Should cache this search result! */
216762306a36Sopenharmony_ci	for (s = 0; s < 256; s++) {
216862306a36Sopenharmony_ci		if (hpi_multiplexer_query_source(h_control, s,
216962306a36Sopenharmony_ci					    &src_node_type, &src_node_index))
217062306a36Sopenharmony_ci			break;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci		if ((source_type == src_node_type)
217362306a36Sopenharmony_ci		    && (source_index == src_node_index)) {
217462306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] = s;
217562306a36Sopenharmony_ci			return 0;
217662306a36Sopenharmony_ci		}
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci	snd_printd(KERN_WARNING
217962306a36Sopenharmony_ci		"Control %x failed to match mux source %hu %hu\n",
218062306a36Sopenharmony_ci		h_control, source_type, source_index);
218162306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
218262306a36Sopenharmony_ci	return 0;
218362306a36Sopenharmony_ci}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_cistatic int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
218662306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
218762306a36Sopenharmony_ci{
218862306a36Sopenharmony_ci	int change;
218962306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
219062306a36Sopenharmony_ci	u16 source_type, source_index;
219162306a36Sopenharmony_ci	u16 e;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	change = 1;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	e = hpi_multiplexer_query_source(h_control,
219662306a36Sopenharmony_ci				    ucontrol->value.enumerated.item[0],
219762306a36Sopenharmony_ci				    &source_type, &source_index);
219862306a36Sopenharmony_ci	if (!e)
219962306a36Sopenharmony_ci		hpi_handle_error(
220062306a36Sopenharmony_ci			hpi_multiplexer_set_source(h_control,
220162306a36Sopenharmony_ci						source_type, source_index));
220262306a36Sopenharmony_ci	return change;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_cistatic int  snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
220762306a36Sopenharmony_ci			       struct hpi_control *hpi_ctl)
220862306a36Sopenharmony_ci{
220962306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
221062306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Route");
221362306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
221462306a36Sopenharmony_ci	snd_control.info = snd_asihpi_mux_info;
221562306a36Sopenharmony_ci	snd_control.get = snd_asihpi_mux_get;
221662306a36Sopenharmony_ci	snd_control.put = snd_asihpi_mux_put;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci/*------------------------------------------------------------
222362306a36Sopenharmony_ci   Channel mode controls
222462306a36Sopenharmony_ci ------------------------------------------------------------*/
222562306a36Sopenharmony_cistatic int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
222662306a36Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
222762306a36Sopenharmony_ci{
222862306a36Sopenharmony_ci	static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = {
222962306a36Sopenharmony_ci		"invalid",
223062306a36Sopenharmony_ci		"Normal", "Swap",
223162306a36Sopenharmony_ci		"From Left", "From Right",
223262306a36Sopenharmony_ci		"To Left", "To Right"
223362306a36Sopenharmony_ci	};
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
223662306a36Sopenharmony_ci	u16 mode;
223762306a36Sopenharmony_ci	int i;
223862306a36Sopenharmony_ci	const char *mapped_names[6];
223962306a36Sopenharmony_ci	int valid_modes = 0;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	/* HPI channel mode values can be from 1 to 6
224262306a36Sopenharmony_ci	Some adapters only support a contiguous subset
224362306a36Sopenharmony_ci	*/
224462306a36Sopenharmony_ci	for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
224562306a36Sopenharmony_ci		if (!hpi_channel_mode_query_mode(
224662306a36Sopenharmony_ci			h_control, i, &mode)) {
224762306a36Sopenharmony_ci			mapped_names[valid_modes] = mode_names[mode];
224862306a36Sopenharmony_ci			valid_modes++;
224962306a36Sopenharmony_ci			}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (!valid_modes)
225262306a36Sopenharmony_ci		return -EINVAL;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names);
225562306a36Sopenharmony_ci}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_cistatic int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
225862306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
225962306a36Sopenharmony_ci{
226062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
226162306a36Sopenharmony_ci	u16 mode;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	if (hpi_channel_mode_get(h_control, &mode))
226462306a36Sopenharmony_ci		mode = 1;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = mode - 1;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	return 0;
226962306a36Sopenharmony_ci}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_cistatic int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
227262306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	int change;
227562306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	change = 1;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	hpi_handle_error(hpi_channel_mode_set(h_control,
228062306a36Sopenharmony_ci			   ucontrol->value.enumerated.item[0] + 1));
228162306a36Sopenharmony_ci	return change;
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_cistatic int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
228662306a36Sopenharmony_ci				struct hpi_control *hpi_ctl)
228762306a36Sopenharmony_ci{
228862306a36Sopenharmony_ci	struct snd_card *card = asihpi->card;
228962306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Mode");
229262306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
229362306a36Sopenharmony_ci	snd_control.info = snd_asihpi_cmode_info;
229462306a36Sopenharmony_ci	snd_control.get = snd_asihpi_cmode_get;
229562306a36Sopenharmony_ci	snd_control.put = snd_asihpi_cmode_put;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
229862306a36Sopenharmony_ci}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci/*------------------------------------------------------------
230162306a36Sopenharmony_ci   Sampleclock source  controls
230262306a36Sopenharmony_ci ------------------------------------------------------------*/
230362306a36Sopenharmony_cistatic const char * const sampleclock_sources[] = {
230462306a36Sopenharmony_ci	"N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
230562306a36Sopenharmony_ci	"SMPTE", "Digital1", "Auto", "Network", "Invalid",
230662306a36Sopenharmony_ci	"Prev Module", "BLU-Link",
230762306a36Sopenharmony_ci	"Digital2", "Digital3", "Digital4", "Digital5",
230862306a36Sopenharmony_ci	"Digital6", "Digital7", "Digital8"};
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	/* Number of strings must match expected enumerated values */
231162306a36Sopenharmony_ci	compile_time_assert(
231262306a36Sopenharmony_ci		(ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES),
231362306a36Sopenharmony_ci		assert_sampleclock_sources_size);
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_cistatic int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
231662306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
231762306a36Sopenharmony_ci{
231862306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi =
231962306a36Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
232062306a36Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
232162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
232262306a36Sopenharmony_ci	uinfo->count = 1;
232362306a36Sopenharmony_ci	uinfo->value.enumerated.items = clkcache->count;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
232662306a36Sopenharmony_ci		uinfo->value.enumerated.item =
232762306a36Sopenharmony_ci				uinfo->value.enumerated.items - 1;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	strcpy(uinfo->value.enumerated.name,
233062306a36Sopenharmony_ci	       clkcache->s[uinfo->value.enumerated.item].name);
233162306a36Sopenharmony_ci	return 0;
233262306a36Sopenharmony_ci}
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_cistatic int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
233562306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi =
233862306a36Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
233962306a36Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
234062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
234162306a36Sopenharmony_ci	u16 source, srcindex = 0;
234262306a36Sopenharmony_ci	int i;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = 0;
234562306a36Sopenharmony_ci	if (hpi_sample_clock_get_source(h_control, &source))
234662306a36Sopenharmony_ci		source = 0;
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
234962306a36Sopenharmony_ci		if (hpi_sample_clock_get_source_index(h_control, &srcindex))
235062306a36Sopenharmony_ci			srcindex = 0;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	for (i = 0; i < clkcache->count; i++)
235362306a36Sopenharmony_ci		if ((clkcache->s[i].source == source) &&
235462306a36Sopenharmony_ci			(clkcache->s[i].index == srcindex))
235562306a36Sopenharmony_ci			break;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = i;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	return 0;
236062306a36Sopenharmony_ci}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_cistatic int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
236362306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
236462306a36Sopenharmony_ci{
236562306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi =
236662306a36Sopenharmony_ci			(struct snd_card_asihpi *)(kcontrol->private_data);
236762306a36Sopenharmony_ci	struct clk_cache *clkcache = &asihpi->cc;
236862306a36Sopenharmony_ci	unsigned int item;
236962306a36Sopenharmony_ci	int change;
237062306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	change = 1;
237362306a36Sopenharmony_ci	item = ucontrol->value.enumerated.item[0];
237462306a36Sopenharmony_ci	if (item >= clkcache->count)
237562306a36Sopenharmony_ci		item = clkcache->count-1;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	hpi_handle_error(hpi_sample_clock_set_source(
237862306a36Sopenharmony_ci				h_control, clkcache->s[item].source));
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
238162306a36Sopenharmony_ci		hpi_handle_error(hpi_sample_clock_set_source_index(
238262306a36Sopenharmony_ci				h_control, clkcache->s[item].index));
238362306a36Sopenharmony_ci	return change;
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci/*------------------------------------------------------------
238762306a36Sopenharmony_ci   Clkrate controls
238862306a36Sopenharmony_ci ------------------------------------------------------------*/
238962306a36Sopenharmony_ci/* Need to change this to enumerated control with list of rates */
239062306a36Sopenharmony_cistatic int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol,
239162306a36Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
239262306a36Sopenharmony_ci{
239362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
239462306a36Sopenharmony_ci	uinfo->count = 1;
239562306a36Sopenharmony_ci	uinfo->value.integer.min = 8000;
239662306a36Sopenharmony_ci	uinfo->value.integer.max = 192000;
239762306a36Sopenharmony_ci	uinfo->value.integer.step = 100;
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	return 0;
240062306a36Sopenharmony_ci}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_cistatic int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
240362306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
240462306a36Sopenharmony_ci{
240562306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
240662306a36Sopenharmony_ci	u32 rate;
240762306a36Sopenharmony_ci	u16 e;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	e = hpi_sample_clock_get_local_rate(h_control, &rate);
241062306a36Sopenharmony_ci	if (!e)
241162306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = rate;
241262306a36Sopenharmony_ci	else
241362306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
241462306a36Sopenharmony_ci	return 0;
241562306a36Sopenharmony_ci}
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_cistatic int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
241862306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	int change;
242162306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	/*  change = asihpi->mixer_clkrate[addr][0] != left ||
242462306a36Sopenharmony_ci	   asihpi->mixer_clkrate[addr][1] != right;
242562306a36Sopenharmony_ci	 */
242662306a36Sopenharmony_ci	change = 1;
242762306a36Sopenharmony_ci	hpi_handle_error(hpi_sample_clock_set_local_rate(h_control,
242862306a36Sopenharmony_ci				      ucontrol->value.integer.value[0]));
242962306a36Sopenharmony_ci	return change;
243062306a36Sopenharmony_ci}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol,
243362306a36Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
243462306a36Sopenharmony_ci{
243562306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
243662306a36Sopenharmony_ci	uinfo->count = 1;
243762306a36Sopenharmony_ci	uinfo->value.integer.min = 8000;
243862306a36Sopenharmony_ci	uinfo->value.integer.max = 192000;
243962306a36Sopenharmony_ci	uinfo->value.integer.step = 100;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	return 0;
244262306a36Sopenharmony_ci}
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_cistatic int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
244562306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
244662306a36Sopenharmony_ci{
244762306a36Sopenharmony_ci	u32 h_control = kcontrol->private_value;
244862306a36Sopenharmony_ci	u32 rate;
244962306a36Sopenharmony_ci	u16 e;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	e = hpi_sample_clock_get_sample_rate(h_control, &rate);
245262306a36Sopenharmony_ci	if (!e)
245362306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = rate;
245462306a36Sopenharmony_ci	else
245562306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
245662306a36Sopenharmony_ci	return 0;
245762306a36Sopenharmony_ci}
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_cistatic int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
246062306a36Sopenharmony_ci				      struct hpi_control *hpi_ctl)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	struct snd_card *card;
246362306a36Sopenharmony_ci	struct snd_kcontrol_new snd_control;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	struct clk_cache *clkcache;
246662306a36Sopenharmony_ci	u32 hSC =  hpi_ctl->h_control;
246762306a36Sopenharmony_ci	int has_aes_in = 0;
246862306a36Sopenharmony_ci	int i, j;
246962306a36Sopenharmony_ci	u16 source;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	if (snd_BUG_ON(!asihpi))
247262306a36Sopenharmony_ci		return -EINVAL;
247362306a36Sopenharmony_ci	card = asihpi->card;
247462306a36Sopenharmony_ci	clkcache = &asihpi->cc;
247562306a36Sopenharmony_ci	snd_control.private_value = hpi_ctl->h_control;
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	clkcache->has_local = 0;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
248062306a36Sopenharmony_ci		if  (hpi_sample_clock_query_source(hSC,
248162306a36Sopenharmony_ci				i, &source))
248262306a36Sopenharmony_ci			break;
248362306a36Sopenharmony_ci		clkcache->s[i].source = source;
248462306a36Sopenharmony_ci		clkcache->s[i].index = 0;
248562306a36Sopenharmony_ci		clkcache->s[i].name = sampleclock_sources[source];
248662306a36Sopenharmony_ci		if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
248762306a36Sopenharmony_ci			has_aes_in = 1;
248862306a36Sopenharmony_ci		if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL)
248962306a36Sopenharmony_ci			clkcache->has_local = 1;
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci	if (has_aes_in)
249262306a36Sopenharmony_ci		/* already will have picked up index 0 above */
249362306a36Sopenharmony_ci		for (j = 1; j < 8; j++) {
249462306a36Sopenharmony_ci			if (hpi_sample_clock_query_source_index(hSC,
249562306a36Sopenharmony_ci				j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
249662306a36Sopenharmony_ci				&source))
249762306a36Sopenharmony_ci				break;
249862306a36Sopenharmony_ci			clkcache->s[i].source =
249962306a36Sopenharmony_ci				HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT;
250062306a36Sopenharmony_ci			clkcache->s[i].index = j;
250162306a36Sopenharmony_ci			clkcache->s[i].name = sampleclock_sources[
250262306a36Sopenharmony_ci					j+HPI_SAMPLECLOCK_SOURCE_LAST];
250362306a36Sopenharmony_ci			i++;
250462306a36Sopenharmony_ci		}
250562306a36Sopenharmony_ci	clkcache->count = i;
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Source");
250862306a36Sopenharmony_ci	snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
250962306a36Sopenharmony_ci	snd_control.info = snd_asihpi_clksrc_info;
251062306a36Sopenharmony_ci	snd_control.get = snd_asihpi_clksrc_get;
251162306a36Sopenharmony_ci	snd_control.put = snd_asihpi_clksrc_put;
251262306a36Sopenharmony_ci	if (ctl_add(card, &snd_control, asihpi) < 0)
251362306a36Sopenharmony_ci		return -EINVAL;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	if (clkcache->has_local) {
251762306a36Sopenharmony_ci		asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate");
251862306a36Sopenharmony_ci		snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
251962306a36Sopenharmony_ci		snd_control.info = snd_asihpi_clklocal_info;
252062306a36Sopenharmony_ci		snd_control.get = snd_asihpi_clklocal_get;
252162306a36Sopenharmony_ci		snd_control.put = snd_asihpi_clklocal_put;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci		if (ctl_add(card, &snd_control, asihpi) < 0)
252562306a36Sopenharmony_ci			return -EINVAL;
252662306a36Sopenharmony_ci	}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	asihpi_ctl_init(&snd_control, hpi_ctl, "Rate");
252962306a36Sopenharmony_ci	snd_control.access =
253062306a36Sopenharmony_ci	    SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
253162306a36Sopenharmony_ci	snd_control.info = snd_asihpi_clkrate_info;
253262306a36Sopenharmony_ci	snd_control.get = snd_asihpi_clkrate_get;
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	return ctl_add(card, &snd_control, asihpi);
253562306a36Sopenharmony_ci}
253662306a36Sopenharmony_ci/*------------------------------------------------------------
253762306a36Sopenharmony_ci   Mixer
253862306a36Sopenharmony_ci ------------------------------------------------------------*/
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_cistatic int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
254162306a36Sopenharmony_ci{
254262306a36Sopenharmony_ci	struct snd_card *card;
254362306a36Sopenharmony_ci	unsigned int idx = 0;
254462306a36Sopenharmony_ci	unsigned int subindex = 0;
254562306a36Sopenharmony_ci	int err;
254662306a36Sopenharmony_ci	struct hpi_control hpi_ctl, prev_ctl;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	if (snd_BUG_ON(!asihpi))
254962306a36Sopenharmony_ci		return -EINVAL;
255062306a36Sopenharmony_ci	card = asihpi->card;
255162306a36Sopenharmony_ci	strcpy(card->mixername, "Asihpi Mixer");
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	err =
255462306a36Sopenharmony_ci	    hpi_mixer_open(asihpi->hpi->adapter->index,
255562306a36Sopenharmony_ci			  &asihpi->h_mixer);
255662306a36Sopenharmony_ci	hpi_handle_error(err);
255762306a36Sopenharmony_ci	if (err)
255862306a36Sopenharmony_ci		return -err;
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	memset(&prev_ctl, 0, sizeof(prev_ctl));
256162306a36Sopenharmony_ci	prev_ctl.control_type = -1;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	for (idx = 0; idx < 2000; idx++) {
256462306a36Sopenharmony_ci		err = hpi_mixer_get_control_by_index(
256562306a36Sopenharmony_ci				asihpi->h_mixer,
256662306a36Sopenharmony_ci				idx,
256762306a36Sopenharmony_ci				&hpi_ctl.src_node_type,
256862306a36Sopenharmony_ci				&hpi_ctl.src_node_index,
256962306a36Sopenharmony_ci				&hpi_ctl.dst_node_type,
257062306a36Sopenharmony_ci				&hpi_ctl.dst_node_index,
257162306a36Sopenharmony_ci				&hpi_ctl.control_type,
257262306a36Sopenharmony_ci				&hpi_ctl.h_control);
257362306a36Sopenharmony_ci		if (err) {
257462306a36Sopenharmony_ci			if (err == HPI_ERROR_CONTROL_DISABLED) {
257562306a36Sopenharmony_ci				if (mixer_dump)
257662306a36Sopenharmony_ci					dev_info(&asihpi->pci->dev,
257762306a36Sopenharmony_ci						   "Disabled HPI Control(%d)\n",
257862306a36Sopenharmony_ci						   idx);
257962306a36Sopenharmony_ci				continue;
258062306a36Sopenharmony_ci			} else
258162306a36Sopenharmony_ci				break;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci		}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci		hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE;
258662306a36Sopenharmony_ci		hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci		/* ASI50xx in SSX mode has multiple meters on the same node.
258962306a36Sopenharmony_ci		   Use subindex to create distinct ALSA controls
259062306a36Sopenharmony_ci		   for any duplicated controls.
259162306a36Sopenharmony_ci		*/
259262306a36Sopenharmony_ci		if ((hpi_ctl.control_type == prev_ctl.control_type) &&
259362306a36Sopenharmony_ci		    (hpi_ctl.src_node_type == prev_ctl.src_node_type) &&
259462306a36Sopenharmony_ci		    (hpi_ctl.src_node_index == prev_ctl.src_node_index) &&
259562306a36Sopenharmony_ci		    (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) &&
259662306a36Sopenharmony_ci		    (hpi_ctl.dst_node_index == prev_ctl.dst_node_index))
259762306a36Sopenharmony_ci			subindex++;
259862306a36Sopenharmony_ci		else
259962306a36Sopenharmony_ci			subindex = 0;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci		prev_ctl = hpi_ctl;
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci		switch (hpi_ctl.control_type) {
260462306a36Sopenharmony_ci		case HPI_CONTROL_VOLUME:
260562306a36Sopenharmony_ci			err = snd_asihpi_volume_add(asihpi, &hpi_ctl);
260662306a36Sopenharmony_ci			break;
260762306a36Sopenharmony_ci		case HPI_CONTROL_LEVEL:
260862306a36Sopenharmony_ci			err = snd_asihpi_level_add(asihpi, &hpi_ctl);
260962306a36Sopenharmony_ci			break;
261062306a36Sopenharmony_ci		case HPI_CONTROL_MULTIPLEXER:
261162306a36Sopenharmony_ci			err = snd_asihpi_mux_add(asihpi, &hpi_ctl);
261262306a36Sopenharmony_ci			break;
261362306a36Sopenharmony_ci		case HPI_CONTROL_CHANNEL_MODE:
261462306a36Sopenharmony_ci			err = snd_asihpi_cmode_add(asihpi, &hpi_ctl);
261562306a36Sopenharmony_ci			break;
261662306a36Sopenharmony_ci		case HPI_CONTROL_METER:
261762306a36Sopenharmony_ci			err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex);
261862306a36Sopenharmony_ci			break;
261962306a36Sopenharmony_ci		case HPI_CONTROL_SAMPLECLOCK:
262062306a36Sopenharmony_ci			err = snd_asihpi_sampleclock_add(
262162306a36Sopenharmony_ci						asihpi, &hpi_ctl);
262262306a36Sopenharmony_ci			break;
262362306a36Sopenharmony_ci		case HPI_CONTROL_CONNECTION:	/* ignore these */
262462306a36Sopenharmony_ci			continue;
262562306a36Sopenharmony_ci		case HPI_CONTROL_TUNER:
262662306a36Sopenharmony_ci			err = snd_asihpi_tuner_add(asihpi, &hpi_ctl);
262762306a36Sopenharmony_ci			break;
262862306a36Sopenharmony_ci		case HPI_CONTROL_AESEBU_TRANSMITTER:
262962306a36Sopenharmony_ci			err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl);
263062306a36Sopenharmony_ci			break;
263162306a36Sopenharmony_ci		case HPI_CONTROL_AESEBU_RECEIVER:
263262306a36Sopenharmony_ci			err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl);
263362306a36Sopenharmony_ci			break;
263462306a36Sopenharmony_ci		case HPI_CONTROL_VOX:
263562306a36Sopenharmony_ci		case HPI_CONTROL_BITSTREAM:
263662306a36Sopenharmony_ci		case HPI_CONTROL_MICROPHONE:
263762306a36Sopenharmony_ci		case HPI_CONTROL_PARAMETRIC_EQ:
263862306a36Sopenharmony_ci		case HPI_CONTROL_COMPANDER:
263962306a36Sopenharmony_ci		default:
264062306a36Sopenharmony_ci			if (mixer_dump)
264162306a36Sopenharmony_ci				dev_info(&asihpi->pci->dev,
264262306a36Sopenharmony_ci					"Untranslated HPI Control (%d) %d %d %d %d %d\n",
264362306a36Sopenharmony_ci					idx,
264462306a36Sopenharmony_ci					hpi_ctl.control_type,
264562306a36Sopenharmony_ci					hpi_ctl.src_node_type,
264662306a36Sopenharmony_ci					hpi_ctl.src_node_index,
264762306a36Sopenharmony_ci					hpi_ctl.dst_node_type,
264862306a36Sopenharmony_ci					hpi_ctl.dst_node_index);
264962306a36Sopenharmony_ci			continue;
265062306a36Sopenharmony_ci		}
265162306a36Sopenharmony_ci		if (err < 0)
265262306a36Sopenharmony_ci			return err;
265362306a36Sopenharmony_ci	}
265462306a36Sopenharmony_ci	if (HPI_ERROR_INVALID_OBJ_INDEX != err)
265562306a36Sopenharmony_ci		hpi_handle_error(err);
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx);
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci	return 0;
266062306a36Sopenharmony_ci}
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci/*------------------------------------------------------------
266362306a36Sopenharmony_ci   /proc interface
266462306a36Sopenharmony_ci ------------------------------------------------------------*/
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_cistatic void
266762306a36Sopenharmony_cisnd_asihpi_proc_read(struct snd_info_entry *entry,
266862306a36Sopenharmony_ci			struct snd_info_buffer *buffer)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi = entry->private_data;
267162306a36Sopenharmony_ci	u32 h_control;
267262306a36Sopenharmony_ci	u32 rate = 0;
267362306a36Sopenharmony_ci	u16 source = 0;
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	u16 num_outstreams;
267662306a36Sopenharmony_ci	u16 num_instreams;
267762306a36Sopenharmony_ci	u16 version;
267862306a36Sopenharmony_ci	u32 serial_number;
267962306a36Sopenharmony_ci	u16 type;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	int err;
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	snd_iprintf(buffer, "ASIHPI driver proc file\n");
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index,
268662306a36Sopenharmony_ci			&num_outstreams, &num_instreams,
268762306a36Sopenharmony_ci			&version, &serial_number, &type));
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	snd_iprintf(buffer,
269062306a36Sopenharmony_ci			"Adapter type ASI%4X\nHardware Index %d\n"
269162306a36Sopenharmony_ci			"%d outstreams\n%d instreams\n",
269262306a36Sopenharmony_ci			type, asihpi->hpi->adapter->index,
269362306a36Sopenharmony_ci			num_outstreams, num_instreams);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	snd_iprintf(buffer,
269662306a36Sopenharmony_ci		"Serial#%d\nHardware version %c%d\nDSP code version %03d\n",
269762306a36Sopenharmony_ci		serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7,
269862306a36Sopenharmony_ci		((version >> 13) * 100) + ((version >> 7) & 0x3f));
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
270162306a36Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
270262306a36Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	if (!err) {
270562306a36Sopenharmony_ci		err = hpi_sample_clock_get_sample_rate(h_control, &rate);
270662306a36Sopenharmony_ci		err += hpi_sample_clock_get_source(h_control, &source);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci		if (!err)
270962306a36Sopenharmony_ci			snd_iprintf(buffer, "Sample Clock %dHz, source %s\n",
271062306a36Sopenharmony_ci			rate, sampleclock_sources[source]);
271162306a36Sopenharmony_ci	}
271262306a36Sopenharmony_ci}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cistatic void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
271562306a36Sopenharmony_ci{
271662306a36Sopenharmony_ci	snd_card_ro_proc_new(asihpi->card, "info", asihpi,
271762306a36Sopenharmony_ci			     snd_asihpi_proc_read);
271862306a36Sopenharmony_ci}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci/*------------------------------------------------------------
272162306a36Sopenharmony_ci   HWDEP
272262306a36Sopenharmony_ci ------------------------------------------------------------*/
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_cistatic int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file)
272562306a36Sopenharmony_ci{
272662306a36Sopenharmony_ci	if (enable_hpi_hwdep)
272762306a36Sopenharmony_ci		return 0;
272862306a36Sopenharmony_ci	else
272962306a36Sopenharmony_ci		return -ENODEV;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_cistatic int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file)
273462306a36Sopenharmony_ci{
273562306a36Sopenharmony_ci	if (enable_hpi_hwdep)
273662306a36Sopenharmony_ci		return asihpi_hpi_release(file);
273762306a36Sopenharmony_ci	else
273862306a36Sopenharmony_ci		return -ENODEV;
273962306a36Sopenharmony_ci}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_cistatic int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file,
274262306a36Sopenharmony_ci				unsigned int cmd, unsigned long arg)
274362306a36Sopenharmony_ci{
274462306a36Sopenharmony_ci	if (enable_hpi_hwdep)
274562306a36Sopenharmony_ci		return asihpi_hpi_ioctl(file, cmd, arg);
274662306a36Sopenharmony_ci	else
274762306a36Sopenharmony_ci		return -ENODEV;
274862306a36Sopenharmony_ci}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci/* results in /dev/snd/hwC#D0 file for each card with index #
275262306a36Sopenharmony_ci   also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card'
275362306a36Sopenharmony_ci*/
275462306a36Sopenharmony_cistatic int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device)
275562306a36Sopenharmony_ci{
275662306a36Sopenharmony_ci	struct snd_hwdep *hw;
275762306a36Sopenharmony_ci	int err;
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
276062306a36Sopenharmony_ci	if (err < 0)
276162306a36Sopenharmony_ci		return err;
276262306a36Sopenharmony_ci	strcpy(hw->name, "asihpi (HPI)");
276362306a36Sopenharmony_ci	hw->iface = SNDRV_HWDEP_IFACE_LAST;
276462306a36Sopenharmony_ci	hw->ops.open = snd_asihpi_hpi_open;
276562306a36Sopenharmony_ci	hw->ops.ioctl = snd_asihpi_hpi_ioctl;
276662306a36Sopenharmony_ci	hw->ops.release = snd_asihpi_hpi_release;
276762306a36Sopenharmony_ci	hw->private_data = asihpi;
276862306a36Sopenharmony_ci	return 0;
276962306a36Sopenharmony_ci}
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci/*------------------------------------------------------------
277262306a36Sopenharmony_ci   CARD
277362306a36Sopenharmony_ci ------------------------------------------------------------*/
277462306a36Sopenharmony_cistatic int snd_asihpi_probe(struct pci_dev *pci_dev,
277562306a36Sopenharmony_ci			    const struct pci_device_id *pci_id)
277662306a36Sopenharmony_ci{
277762306a36Sopenharmony_ci	int err;
277862306a36Sopenharmony_ci	struct hpi_adapter *hpi;
277962306a36Sopenharmony_ci	struct snd_card *card;
278062306a36Sopenharmony_ci	struct snd_card_asihpi *asihpi;
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	u32 h_control;
278362306a36Sopenharmony_ci	u32 h_stream;
278462306a36Sopenharmony_ci	u32 adapter_index;
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	static int dev;
278762306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
278862306a36Sopenharmony_ci		return -ENODEV;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	/* Should this be enable[hpi->index] ? */
279162306a36Sopenharmony_ci	if (!enable[dev]) {
279262306a36Sopenharmony_ci		dev++;
279362306a36Sopenharmony_ci		return -ENOENT;
279462306a36Sopenharmony_ci	}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	/* Initialise low-level HPI driver */
279762306a36Sopenharmony_ci	err = asihpi_adapter_probe(pci_dev, pci_id);
279862306a36Sopenharmony_ci	if (err < 0)
279962306a36Sopenharmony_ci		return err;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	hpi = pci_get_drvdata(pci_dev);
280262306a36Sopenharmony_ci	adapter_index = hpi->adapter->index;
280362306a36Sopenharmony_ci	/* first try to give the card the same index as its hardware index */
280462306a36Sopenharmony_ci	err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index],
280562306a36Sopenharmony_ci			   THIS_MODULE, sizeof(struct snd_card_asihpi), &card);
280662306a36Sopenharmony_ci	if (err < 0) {
280762306a36Sopenharmony_ci		/* if that fails, try the default index==next available */
280862306a36Sopenharmony_ci		err = snd_card_new(&pci_dev->dev, index[dev], id[dev],
280962306a36Sopenharmony_ci				   THIS_MODULE, sizeof(struct snd_card_asihpi),
281062306a36Sopenharmony_ci				   &card);
281162306a36Sopenharmony_ci		if (err < 0)
281262306a36Sopenharmony_ci			return err;
281362306a36Sopenharmony_ci		dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n",
281462306a36Sopenharmony_ci			adapter_index, card->number);
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	asihpi = card->private_data;
281862306a36Sopenharmony_ci	asihpi->card = card;
281962306a36Sopenharmony_ci	asihpi->pci = pci_dev;
282062306a36Sopenharmony_ci	asihpi->hpi = hpi;
282162306a36Sopenharmony_ci	hpi->snd_card = card;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
282462306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CAPS1,
282562306a36Sopenharmony_ci		NULL, &asihpi->support_grouping);
282662306a36Sopenharmony_ci	if (err)
282762306a36Sopenharmony_ci		asihpi->support_grouping = 0;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
283062306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CAPS2,
283162306a36Sopenharmony_ci		&asihpi->support_mrx, NULL);
283262306a36Sopenharmony_ci	if (err)
283362306a36Sopenharmony_ci		asihpi->support_mrx = 0;
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
283662306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_INTERVAL,
283762306a36Sopenharmony_ci		NULL, &asihpi->update_interval_frames);
283862306a36Sopenharmony_ci	if (err)
283962306a36Sopenharmony_ci		asihpi->update_interval_frames = 512;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	if (hpi->interrupt_mode) {
284262306a36Sopenharmony_ci		asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
284362306a36Sopenharmony_ci		asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
284462306a36Sopenharmony_ci		hpi->interrupt_callback = snd_card_asihpi_isr;
284562306a36Sopenharmony_ci	} else {
284662306a36Sopenharmony_ci		asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
284762306a36Sopenharmony_ci		asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
284862306a36Sopenharmony_ci	}
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	hpi_handle_error(hpi_instream_open(adapter_index,
285162306a36Sopenharmony_ci			     0, &h_stream));
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	err = hpi_instream_host_buffer_free(h_stream);
285462306a36Sopenharmony_ci	asihpi->can_dma = (!err);
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	hpi_handle_error(hpi_instream_close(h_stream));
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	if (!asihpi->can_dma)
285962306a36Sopenharmony_ci		asihpi->update_interval_frames *= 2;
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	err = hpi_adapter_get_property(adapter_index,
286262306a36Sopenharmony_ci		HPI_ADAPTER_PROPERTY_CURCHANNELS,
286362306a36Sopenharmony_ci		&asihpi->in_max_chans, &asihpi->out_max_chans);
286462306a36Sopenharmony_ci	if (err) {
286562306a36Sopenharmony_ci		asihpi->in_max_chans = 2;
286662306a36Sopenharmony_ci		asihpi->out_max_chans = 2;
286762306a36Sopenharmony_ci	}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	if (asihpi->out_max_chans > 2) { /* assume LL mode */
287062306a36Sopenharmony_ci		asihpi->out_min_chans = asihpi->out_max_chans;
287162306a36Sopenharmony_ci		asihpi->in_min_chans = asihpi->in_max_chans;
287262306a36Sopenharmony_ci		asihpi->support_grouping = 0;
287362306a36Sopenharmony_ci	} else {
287462306a36Sopenharmony_ci		asihpi->out_min_chans = 1;
287562306a36Sopenharmony_ci		asihpi->in_min_chans = 1;
287662306a36Sopenharmony_ci	}
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n",
287962306a36Sopenharmony_ci			asihpi->can_dma,
288062306a36Sopenharmony_ci			asihpi->support_grouping,
288162306a36Sopenharmony_ci			asihpi->support_mrx,
288262306a36Sopenharmony_ci			asihpi->update_interval_frames
288362306a36Sopenharmony_ci	      );
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	err = snd_card_asihpi_pcm_new(asihpi, 0);
288662306a36Sopenharmony_ci	if (err < 0) {
288762306a36Sopenharmony_ci		dev_err(&pci_dev->dev, "pcm_new failed\n");
288862306a36Sopenharmony_ci		goto __nodev;
288962306a36Sopenharmony_ci	}
289062306a36Sopenharmony_ci	err = snd_card_asihpi_mixer_new(asihpi);
289162306a36Sopenharmony_ci	if (err < 0) {
289262306a36Sopenharmony_ci		dev_err(&pci_dev->dev, "mixer_new failed\n");
289362306a36Sopenharmony_ci		goto __nodev;
289462306a36Sopenharmony_ci	}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	err = hpi_mixer_get_control(asihpi->h_mixer,
289762306a36Sopenharmony_ci				  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
289862306a36Sopenharmony_ci				  HPI_CONTROL_SAMPLECLOCK, &h_control);
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	if (!err)
290162306a36Sopenharmony_ci		err = hpi_sample_clock_set_local_rate(
290262306a36Sopenharmony_ci			h_control, adapter_fs);
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci	snd_asihpi_proc_init(asihpi);
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	/* always create, can be enabled or disabled dynamically
290762306a36Sopenharmony_ci	    by enable_hwdep  module param*/
290862306a36Sopenharmony_ci	snd_asihpi_hpi_new(asihpi, 0);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	strcpy(card->driver, "ASIHPI");
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci	sprintf(card->shortname, "AudioScience ASI%4X",
291362306a36Sopenharmony_ci			asihpi->hpi->adapter->type);
291462306a36Sopenharmony_ci	sprintf(card->longname, "%s %i",
291562306a36Sopenharmony_ci			card->shortname, adapter_index);
291662306a36Sopenharmony_ci	err = snd_card_register(card);
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	if (!err) {
291962306a36Sopenharmony_ci		dev++;
292062306a36Sopenharmony_ci		return 0;
292162306a36Sopenharmony_ci	}
292262306a36Sopenharmony_ci__nodev:
292362306a36Sopenharmony_ci	snd_card_free(card);
292462306a36Sopenharmony_ci	dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err);
292562306a36Sopenharmony_ci	return err;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci}
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_cistatic void snd_asihpi_remove(struct pci_dev *pci_dev)
293062306a36Sopenharmony_ci{
293162306a36Sopenharmony_ci	struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	/* Stop interrupts */
293462306a36Sopenharmony_ci	if (hpi->interrupt_mode) {
293562306a36Sopenharmony_ci		hpi->interrupt_callback = NULL;
293662306a36Sopenharmony_ci		hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
293762306a36Sopenharmony_ci			HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	snd_card_free(hpi->snd_card);
294162306a36Sopenharmony_ci	hpi->snd_card = NULL;
294262306a36Sopenharmony_ci	asihpi_adapter_remove(pci_dev);
294362306a36Sopenharmony_ci}
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_cistatic const struct pci_device_id asihpi_pci_tbl[] = {
294662306a36Sopenharmony_ci	{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
294762306a36Sopenharmony_ci		HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
294862306a36Sopenharmony_ci		(kernel_ulong_t)HPI_6205},
294962306a36Sopenharmony_ci	{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
295062306a36Sopenharmony_ci		HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
295162306a36Sopenharmony_ci		(kernel_ulong_t)HPI_6000},
295262306a36Sopenharmony_ci	{0,}
295362306a36Sopenharmony_ci};
295462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_cistatic struct pci_driver driver = {
295762306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
295862306a36Sopenharmony_ci	.id_table = asihpi_pci_tbl,
295962306a36Sopenharmony_ci	.probe = snd_asihpi_probe,
296062306a36Sopenharmony_ci	.remove = snd_asihpi_remove,
296162306a36Sopenharmony_ci};
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_cistatic int __init snd_asihpi_init(void)
296462306a36Sopenharmony_ci{
296562306a36Sopenharmony_ci	asihpi_init();
296662306a36Sopenharmony_ci	return pci_register_driver(&driver);
296762306a36Sopenharmony_ci}
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_cistatic void __exit snd_asihpi_exit(void)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	pci_unregister_driver(&driver);
297362306a36Sopenharmony_ci	asihpi_exit();
297462306a36Sopenharmony_ci}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_cimodule_init(snd_asihpi_init)
297762306a36Sopenharmony_cimodule_exit(snd_asihpi_exit)
297862306a36Sopenharmony_ci
2979