18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci/*
68c2ecf20Sopenharmony_ci * Vortex PCM ALSA driver.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Supports ADB and WT DMA. Unfortunately, WT channels do not run yet.
98c2ecf20Sopenharmony_ci * It remains stuck,and DMA transfers do not happen.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
128c2ecf20Sopenharmony_ci#include <linux/time.h>
138c2ecf20Sopenharmony_ci#include <sound/core.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
168c2ecf20Sopenharmony_ci#include "au88x0.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define VORTEX_PCM_TYPE(x) (x->name[40])
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* hardware definition */
218c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
228c2ecf20Sopenharmony_ci	.info =
238c2ecf20Sopenharmony_ci	    (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
248c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
258c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_MMAP_VALID),
268c2ecf20Sopenharmony_ci	.formats =
278c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
288c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
298c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_CONTINUOUS,
308c2ecf20Sopenharmony_ci	.rate_min = 5000,
318c2ecf20Sopenharmony_ci	.rate_max = 48000,
328c2ecf20Sopenharmony_ci	.channels_min = 1,
338c2ecf20Sopenharmony_ci	.channels_max = 2,
348c2ecf20Sopenharmony_ci	.buffer_bytes_max = 0x10000,
358c2ecf20Sopenharmony_ci	.period_bytes_min = 0x20,
368c2ecf20Sopenharmony_ci	.period_bytes_max = 0x1000,
378c2ecf20Sopenharmony_ci	.periods_min = 2,
388c2ecf20Sopenharmony_ci	.periods_max = 1024,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifndef CHIP_AU8820
428c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
438c2ecf20Sopenharmony_ci	.info =
448c2ecf20Sopenharmony_ci	    (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
458c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
468c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_MMAP_VALID),
478c2ecf20Sopenharmony_ci	.formats =
488c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
498c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
508c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_CONTINUOUS,
518c2ecf20Sopenharmony_ci	.rate_min = 5000,
528c2ecf20Sopenharmony_ci	.rate_max = 48000,
538c2ecf20Sopenharmony_ci	.channels_min = 1,
548c2ecf20Sopenharmony_ci	.channels_max = 1,
558c2ecf20Sopenharmony_ci	.buffer_bytes_max = 0x10000,
568c2ecf20Sopenharmony_ci	.period_bytes_min = 0x100,
578c2ecf20Sopenharmony_ci	.period_bytes_max = 0x1000,
588c2ecf20Sopenharmony_ci	.periods_min = 2,
598c2ecf20Sopenharmony_ci	.periods_max = 64,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci#endif
628c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
638c2ecf20Sopenharmony_ci	.info =
648c2ecf20Sopenharmony_ci	    (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
658c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
668c2ecf20Sopenharmony_ci	     SNDRV_PCM_INFO_MMAP_VALID),
678c2ecf20Sopenharmony_ci	.formats =
688c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
698c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | SNDRV_PCM_FMTBIT_MU_LAW |
708c2ecf20Sopenharmony_ci	    SNDRV_PCM_FMTBIT_A_LAW,
718c2ecf20Sopenharmony_ci	.rates =
728c2ecf20Sopenharmony_ci	    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
738c2ecf20Sopenharmony_ci	.rate_min = 32000,
748c2ecf20Sopenharmony_ci	.rate_max = 48000,
758c2ecf20Sopenharmony_ci	.channels_min = 1,
768c2ecf20Sopenharmony_ci	.channels_max = 2,
778c2ecf20Sopenharmony_ci	.buffer_bytes_max = 0x10000,
788c2ecf20Sopenharmony_ci	.period_bytes_min = 0x100,
798c2ecf20Sopenharmony_ci	.period_bytes_max = 0x1000,
808c2ecf20Sopenharmony_ci	.periods_min = 2,
818c2ecf20Sopenharmony_ci	.periods_max = 64,
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
858c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
868c2ecf20Sopenharmony_ci	.info = (SNDRV_PCM_INFO_MMAP |
878c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_INTERLEAVED |
888c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
898c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
908c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,	// SNDRV_PCM_RATE_48000,
918c2ecf20Sopenharmony_ci	.rate_min = 8000,
928c2ecf20Sopenharmony_ci	.rate_max = 48000,
938c2ecf20Sopenharmony_ci	.channels_min = 1,
948c2ecf20Sopenharmony_ci	.channels_max = 2,
958c2ecf20Sopenharmony_ci	.buffer_bytes_max = 0x10000,
968c2ecf20Sopenharmony_ci	.period_bytes_min = 0x0400,
978c2ecf20Sopenharmony_ci	.period_bytes_max = 0x1000,
988c2ecf20Sopenharmony_ci	.periods_min = 2,
998c2ecf20Sopenharmony_ci	.periods_max = 64,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci#endif
1028c2ecf20Sopenharmony_ci#ifdef CHIP_AU8830
1038c2ecf20Sopenharmony_cistatic const unsigned int au8830_channels[3] = {
1048c2ecf20Sopenharmony_ci	1, 2, 4,
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
1088c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(au8830_channels),
1098c2ecf20Sopenharmony_ci	.list = au8830_channels,
1108c2ecf20Sopenharmony_ci	.mask = 0,
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci#endif
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void vortex_notify_pcm_vol_change(struct snd_card *card,
1158c2ecf20Sopenharmony_ci			struct snd_kcontrol *kctl, int activate)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (activate)
1188c2ecf20Sopenharmony_ci		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1198c2ecf20Sopenharmony_ci	else
1208c2ecf20Sopenharmony_ci		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1218c2ecf20Sopenharmony_ci	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
1228c2ecf20Sopenharmony_ci				SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* open callback */
1268c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_pcm_substream_chip(substream);
1298c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1308c2ecf20Sopenharmony_ci	int err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Force equal size periods */
1338c2ecf20Sopenharmony_ci	if ((err =
1348c2ecf20Sopenharmony_ci	     snd_pcm_hw_constraint_integer(runtime,
1358c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
1368c2ecf20Sopenharmony_ci		return err;
1378c2ecf20Sopenharmony_ci	/* Avoid PAGE_SIZE boundary to fall inside of a period. */
1388c2ecf20Sopenharmony_ci	if ((err =
1398c2ecf20Sopenharmony_ci	     snd_pcm_hw_constraint_pow2(runtime, 0,
1408c2ecf20Sopenharmony_ci					SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
1418c2ecf20Sopenharmony_ci		return err;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
1448c2ecf20Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
1478c2ecf20Sopenharmony_ci#ifndef CHIP_AU8820
1488c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
1498c2ecf20Sopenharmony_ci			runtime->hw = snd_vortex_playback_hw_a3d;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci#endif
1528c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_SPDIF) {
1538c2ecf20Sopenharmony_ci			runtime->hw = snd_vortex_playback_hw_spdif;
1548c2ecf20Sopenharmony_ci			switch (vortex->spdif_sr) {
1558c2ecf20Sopenharmony_ci			case 32000:
1568c2ecf20Sopenharmony_ci				runtime->hw.rates = SNDRV_PCM_RATE_32000;
1578c2ecf20Sopenharmony_ci				break;
1588c2ecf20Sopenharmony_ci			case 44100:
1598c2ecf20Sopenharmony_ci				runtime->hw.rates = SNDRV_PCM_RATE_44100;
1608c2ecf20Sopenharmony_ci				break;
1618c2ecf20Sopenharmony_ci			case 48000:
1628c2ecf20Sopenharmony_ci				runtime->hw.rates = SNDRV_PCM_RATE_48000;
1638c2ecf20Sopenharmony_ci				break;
1648c2ecf20Sopenharmony_ci			}
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB
1678c2ecf20Sopenharmony_ci		    || VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S)
1688c2ecf20Sopenharmony_ci			runtime->hw = snd_vortex_playback_hw_adb;
1698c2ecf20Sopenharmony_ci#ifdef CHIP_AU8830
1708c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
1718c2ecf20Sopenharmony_ci			VORTEX_IS_QUAD(vortex) &&
1728c2ecf20Sopenharmony_ci			VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
1738c2ecf20Sopenharmony_ci			runtime->hw.channels_max = 4;
1748c2ecf20Sopenharmony_ci			snd_pcm_hw_constraint_list(runtime, 0,
1758c2ecf20Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS,
1768c2ecf20Sopenharmony_ci				&hw_constraints_au8830_channels);
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci#endif
1798c2ecf20Sopenharmony_ci		substream->runtime->private_data = NULL;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
1828c2ecf20Sopenharmony_ci	else {
1838c2ecf20Sopenharmony_ci		runtime->hw = snd_vortex_playback_hw_wt;
1848c2ecf20Sopenharmony_ci		substream->runtime->private_data = NULL;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci#endif
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/* close callback */
1918c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_close(struct snd_pcm_substream *substream)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	//vortex_t *chip = snd_pcm_substream_chip(substream);
1948c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) substream->runtime->private_data;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	// the hardware-specific codes will be here
1978c2ecf20Sopenharmony_ci	if (stream != NULL) {
1988c2ecf20Sopenharmony_ci		stream->substream = NULL;
1998c2ecf20Sopenharmony_ci		stream->nr_ch = 0;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	substream->runtime->private_data = NULL;
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/* hw_params callback */
2068c2ecf20Sopenharmony_cistatic int
2078c2ecf20Sopenharmony_cisnd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
2088c2ecf20Sopenharmony_ci			 struct snd_pcm_hw_params *hw_params)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	vortex_t *chip = snd_pcm_substream_chip(substream);
2118c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) (substream->runtime->private_data);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/*
2148c2ecf20Sopenharmony_ci	   pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
2158c2ecf20Sopenharmony_ci	   params_period_bytes(hw_params), params_channels(hw_params));
2168c2ecf20Sopenharmony_ci	 */
2178c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
2188c2ecf20Sopenharmony_ci	// Make audio routes and config buffer DMA.
2198c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
2208c2ecf20Sopenharmony_ci		int dma, type = VORTEX_PCM_TYPE(substream->pcm);
2218c2ecf20Sopenharmony_ci		/* Dealloc any routes. */
2228c2ecf20Sopenharmony_ci		if (stream != NULL)
2238c2ecf20Sopenharmony_ci			vortex_adb_allocroute(chip, stream->dma,
2248c2ecf20Sopenharmony_ci					      stream->nr_ch, stream->dir,
2258c2ecf20Sopenharmony_ci					      stream->type,
2268c2ecf20Sopenharmony_ci					      substream->number);
2278c2ecf20Sopenharmony_ci		/* Alloc routes. */
2288c2ecf20Sopenharmony_ci		dma =
2298c2ecf20Sopenharmony_ci		    vortex_adb_allocroute(chip, -1,
2308c2ecf20Sopenharmony_ci					  params_channels(hw_params),
2318c2ecf20Sopenharmony_ci					  substream->stream, type,
2328c2ecf20Sopenharmony_ci					  substream->number);
2338c2ecf20Sopenharmony_ci		if (dma < 0) {
2348c2ecf20Sopenharmony_ci			spin_unlock_irq(&chip->lock);
2358c2ecf20Sopenharmony_ci			return dma;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci		stream = substream->runtime->private_data = &chip->dma_adb[dma];
2388c2ecf20Sopenharmony_ci		stream->substream = substream;
2398c2ecf20Sopenharmony_ci		/* Setup Buffers. */
2408c2ecf20Sopenharmony_ci		vortex_adbdma_setbuffers(chip, dma,
2418c2ecf20Sopenharmony_ci					 params_period_bytes(hw_params),
2428c2ecf20Sopenharmony_ci					 params_periods(hw_params));
2438c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
2448c2ecf20Sopenharmony_ci			chip->pcm_vol[substream->number].active = 1;
2458c2ecf20Sopenharmony_ci			vortex_notify_pcm_vol_change(chip->card,
2468c2ecf20Sopenharmony_ci				chip->pcm_vol[substream->number].kctl, 1);
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
2508c2ecf20Sopenharmony_ci	else {
2518c2ecf20Sopenharmony_ci		/* if (stream != NULL)
2528c2ecf20Sopenharmony_ci		   vortex_wt_allocroute(chip, substream->number, 0); */
2538c2ecf20Sopenharmony_ci		vortex_wt_allocroute(chip, substream->number,
2548c2ecf20Sopenharmony_ci				     params_channels(hw_params));
2558c2ecf20Sopenharmony_ci		stream = substream->runtime->private_data =
2568c2ecf20Sopenharmony_ci		    &chip->dma_wt[substream->number];
2578c2ecf20Sopenharmony_ci		stream->dma = substream->number;
2588c2ecf20Sopenharmony_ci		stream->substream = substream;
2598c2ecf20Sopenharmony_ci		vortex_wtdma_setbuffers(chip, substream->number,
2608c2ecf20Sopenharmony_ci					params_period_bytes(hw_params),
2618c2ecf20Sopenharmony_ci					params_periods(hw_params));
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/* hw_free callback */
2698c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	vortex_t *chip = snd_pcm_substream_chip(substream);
2728c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) (substream->runtime->private_data);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
2758c2ecf20Sopenharmony_ci	// Delete audio routes.
2768c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
2778c2ecf20Sopenharmony_ci		if (stream != NULL) {
2788c2ecf20Sopenharmony_ci			if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
2798c2ecf20Sopenharmony_ci				chip->pcm_vol[substream->number].active = 0;
2808c2ecf20Sopenharmony_ci				vortex_notify_pcm_vol_change(chip->card,
2818c2ecf20Sopenharmony_ci					chip->pcm_vol[substream->number].kctl,
2828c2ecf20Sopenharmony_ci					0);
2838c2ecf20Sopenharmony_ci			}
2848c2ecf20Sopenharmony_ci			vortex_adb_allocroute(chip, stream->dma,
2858c2ecf20Sopenharmony_ci					      stream->nr_ch, stream->dir,
2868c2ecf20Sopenharmony_ci					      stream->type,
2878c2ecf20Sopenharmony_ci					      substream->number);
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
2918c2ecf20Sopenharmony_ci	else {
2928c2ecf20Sopenharmony_ci		if (stream != NULL)
2938c2ecf20Sopenharmony_ci			vortex_wt_allocroute(chip, stream->dma, 0);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci#endif
2968c2ecf20Sopenharmony_ci	substream->runtime->private_data = NULL;
2978c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* prepare callback */
3038c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	vortex_t *chip = snd_pcm_substream_chip(substream);
3068c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3078c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) substream->runtime->private_data;
3088c2ecf20Sopenharmony_ci	int dma = stream->dma, fmt, dir;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	// set up the hardware with the current configuration.
3118c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3128c2ecf20Sopenharmony_ci		dir = 1;
3138c2ecf20Sopenharmony_ci	else
3148c2ecf20Sopenharmony_ci		dir = 0;
3158c2ecf20Sopenharmony_ci	fmt = vortex_alsafmt_aspfmt(runtime->format, chip);
3168c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
3178c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
3188c2ecf20Sopenharmony_ci		vortex_adbdma_setmode(chip, dma, 1, dir, fmt,
3198c2ecf20Sopenharmony_ci				runtime->channels == 1 ? 0 : 1, 0);
3208c2ecf20Sopenharmony_ci		vortex_adbdma_setstartbuffer(chip, dma, 0);
3218c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
3228c2ecf20Sopenharmony_ci			vortex_adb_setsrc(chip, dma, runtime->rate, dir);
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
3258c2ecf20Sopenharmony_ci	else {
3268c2ecf20Sopenharmony_ci		vortex_wtdma_setmode(chip, dma, 1, fmt, 0, 0);
3278c2ecf20Sopenharmony_ci		// FIXME: Set rate (i guess using vortex_wt_writereg() somehow).
3288c2ecf20Sopenharmony_ci		vortex_wtdma_setstartbuffer(chip, dma, 0);
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci#endif
3318c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/* trigger callback */
3368c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	vortex_t *chip = snd_pcm_substream_chip(substream);
3398c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) substream->runtime->private_data;
3408c2ecf20Sopenharmony_ci	int dma = stream->dma;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	spin_lock(&chip->lock);
3438c2ecf20Sopenharmony_ci	switch (cmd) {
3448c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3458c2ecf20Sopenharmony_ci		// do something to start the PCM engine
3468c2ecf20Sopenharmony_ci		//printk(KERN_INFO "vortex: start %d\n", dma);
3478c2ecf20Sopenharmony_ci		stream->fifo_enabled = 1;
3488c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
3498c2ecf20Sopenharmony_ci			vortex_adbdma_resetup(chip, dma);
3508c2ecf20Sopenharmony_ci			vortex_adbdma_startfifo(chip, dma);
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
3538c2ecf20Sopenharmony_ci		else {
3548c2ecf20Sopenharmony_ci			dev_info(chip->card->dev, "wt start %d\n", dma);
3558c2ecf20Sopenharmony_ci			vortex_wtdma_startfifo(chip, dma);
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci#endif
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3608c2ecf20Sopenharmony_ci		// do something to stop the PCM engine
3618c2ecf20Sopenharmony_ci		//printk(KERN_INFO "vortex: stop %d\n", dma);
3628c2ecf20Sopenharmony_ci		stream->fifo_enabled = 0;
3638c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
3648c2ecf20Sopenharmony_ci			vortex_adbdma_stopfifo(chip, dma);
3658c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
3668c2ecf20Sopenharmony_ci		else {
3678c2ecf20Sopenharmony_ci			dev_info(chip->card->dev, "wt stop %d\n", dma);
3688c2ecf20Sopenharmony_ci			vortex_wtdma_stopfifo(chip, dma);
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci#endif
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3738c2ecf20Sopenharmony_ci		//printk(KERN_INFO "vortex: pause %d\n", dma);
3748c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
3758c2ecf20Sopenharmony_ci			vortex_adbdma_pausefifo(chip, dma);
3768c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
3778c2ecf20Sopenharmony_ci		else
3788c2ecf20Sopenharmony_ci			vortex_wtdma_pausefifo(chip, dma);
3798c2ecf20Sopenharmony_ci#endif
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3828c2ecf20Sopenharmony_ci		//printk(KERN_INFO "vortex: resume %d\n", dma);
3838c2ecf20Sopenharmony_ci		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
3848c2ecf20Sopenharmony_ci			vortex_adbdma_resumefifo(chip, dma);
3858c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
3868c2ecf20Sopenharmony_ci		else
3878c2ecf20Sopenharmony_ci			vortex_wtdma_resumefifo(chip, dma);
3888c2ecf20Sopenharmony_ci#endif
3898c2ecf20Sopenharmony_ci		break;
3908c2ecf20Sopenharmony_ci	default:
3918c2ecf20Sopenharmony_ci		spin_unlock(&chip->lock);
3928c2ecf20Sopenharmony_ci		return -EINVAL;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	spin_unlock(&chip->lock);
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/* pointer callback */
3998c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substream)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	vortex_t *chip = snd_pcm_substream_chip(substream);
4028c2ecf20Sopenharmony_ci	stream_t *stream = (stream_t *) substream->runtime->private_data;
4038c2ecf20Sopenharmony_ci	int dma = stream->dma;
4048c2ecf20Sopenharmony_ci	snd_pcm_uframes_t current_ptr = 0;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	spin_lock(&chip->lock);
4078c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
4088c2ecf20Sopenharmony_ci		current_ptr = vortex_adbdma_getlinearpos(chip, dma);
4098c2ecf20Sopenharmony_ci#ifndef CHIP_AU8810
4108c2ecf20Sopenharmony_ci	else
4118c2ecf20Sopenharmony_ci		current_ptr = vortex_wtdma_getlinearpos(chip, dma);
4128c2ecf20Sopenharmony_ci#endif
4138c2ecf20Sopenharmony_ci	//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
4148c2ecf20Sopenharmony_ci	spin_unlock(&chip->lock);
4158c2ecf20Sopenharmony_ci	current_ptr = bytes_to_frames(substream->runtime, current_ptr);
4168c2ecf20Sopenharmony_ci	if (current_ptr >= substream->runtime->buffer_size)
4178c2ecf20Sopenharmony_ci		current_ptr = 0;
4188c2ecf20Sopenharmony_ci	return current_ptr;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci/* operators */
4228c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_vortex_playback_ops = {
4238c2ecf20Sopenharmony_ci	.open = snd_vortex_pcm_open,
4248c2ecf20Sopenharmony_ci	.close = snd_vortex_pcm_close,
4258c2ecf20Sopenharmony_ci	.hw_params = snd_vortex_pcm_hw_params,
4268c2ecf20Sopenharmony_ci	.hw_free = snd_vortex_pcm_hw_free,
4278c2ecf20Sopenharmony_ci	.prepare = snd_vortex_pcm_prepare,
4288c2ecf20Sopenharmony_ci	.trigger = snd_vortex_pcm_trigger,
4298c2ecf20Sopenharmony_ci	.pointer = snd_vortex_pcm_pointer,
4308c2ecf20Sopenharmony_ci};
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/*
4338c2ecf20Sopenharmony_ci*  definitions of capture are omitted here...
4348c2ecf20Sopenharmony_ci*/
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic const char * const vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
4378c2ecf20Sopenharmony_ci	CARD_NAME " ADB",
4388c2ecf20Sopenharmony_ci	CARD_NAME " SPDIF",
4398c2ecf20Sopenharmony_ci	CARD_NAME " A3D",
4408c2ecf20Sopenharmony_ci	CARD_NAME " WT",
4418c2ecf20Sopenharmony_ci	CARD_NAME " I2S",
4428c2ecf20Sopenharmony_ci};
4438c2ecf20Sopenharmony_cistatic const char * const vortex_pcm_name[VORTEX_PCM_LAST] = {
4448c2ecf20Sopenharmony_ci	"adb",
4458c2ecf20Sopenharmony_ci	"spdif",
4468c2ecf20Sopenharmony_ci	"a3d",
4478c2ecf20Sopenharmony_ci	"wt",
4488c2ecf20Sopenharmony_ci	"i2s",
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/* SPDIF kcontrol */
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int snd_vortex_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
4568c2ecf20Sopenharmony_ci	uinfo->count = 1;
4578c2ecf20Sopenharmony_ci	return 0;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int snd_vortex_spdif_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0xff;
4638c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
4648c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0xff;
4658c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int snd_vortex_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
4728c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0x00;
4738c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL|IEC958_AES1_CON_DIGDIGCONV_ID;
4748c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0x00;
4758c2ecf20Sopenharmony_ci	switch (vortex->spdif_sr) {
4768c2ecf20Sopenharmony_ci	case 32000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_32000; break;
4778c2ecf20Sopenharmony_ci	case 44100: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_44100; break;
4788c2ecf20Sopenharmony_ci	case 48000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; break;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int snd_vortex_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
4868c2ecf20Sopenharmony_ci	int spdif_sr = 48000;
4878c2ecf20Sopenharmony_ci	switch (ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) {
4888c2ecf20Sopenharmony_ci	case IEC958_AES3_CON_FS_32000: spdif_sr = 32000; break;
4898c2ecf20Sopenharmony_ci	case IEC958_AES3_CON_FS_44100: spdif_sr = 44100; break;
4908c2ecf20Sopenharmony_ci	case IEC958_AES3_CON_FS_48000: spdif_sr = 48000; break;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci	if (spdif_sr == vortex->spdif_sr)
4938c2ecf20Sopenharmony_ci		return 0;
4948c2ecf20Sopenharmony_ci	vortex->spdif_sr = spdif_sr;
4958c2ecf20Sopenharmony_ci	vortex_spdif_init(vortex, vortex->spdif_sr, 1);
4968c2ecf20Sopenharmony_ci	return 1;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/* spdif controls */
5008c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_vortex_mixer_spdif[] = {
5018c2ecf20Sopenharmony_ci	{
5028c2ecf20Sopenharmony_ci		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
5038c2ecf20Sopenharmony_ci		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
5048c2ecf20Sopenharmony_ci		.info =		snd_vortex_spdif_info,
5058c2ecf20Sopenharmony_ci		.get =		snd_vortex_spdif_get,
5068c2ecf20Sopenharmony_ci		.put =		snd_vortex_spdif_put,
5078c2ecf20Sopenharmony_ci	},
5088c2ecf20Sopenharmony_ci	{
5098c2ecf20Sopenharmony_ci		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
5108c2ecf20Sopenharmony_ci		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
5118c2ecf20Sopenharmony_ci		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
5128c2ecf20Sopenharmony_ci		.info =		snd_vortex_spdif_info,
5138c2ecf20Sopenharmony_ci		.get =		snd_vortex_spdif_mask_get
5148c2ecf20Sopenharmony_ci	},
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci/* subdevice PCM Volume control */
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
5208c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
5238c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
5248c2ecf20Sopenharmony_ci	uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
5258c2ecf20Sopenharmony_ci	uinfo->value.integer.min = -128;
5268c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 32;
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
5318c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	int i;
5348c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
5358c2ecf20Sopenharmony_ci	int subdev = kcontrol->id.subdevice;
5368c2ecf20Sopenharmony_ci	struct pcm_vol *p = &vortex->pcm_vol[subdev];
5378c2ecf20Sopenharmony_ci	int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
5388c2ecf20Sopenharmony_ci	for (i = 0; i < max_chn; i++)
5398c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[i] = p->vol[i];
5408c2ecf20Sopenharmony_ci	return 0;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
5448c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	int i;
5478c2ecf20Sopenharmony_ci	int changed = 0;
5488c2ecf20Sopenharmony_ci	int mixin;
5498c2ecf20Sopenharmony_ci	unsigned char vol;
5508c2ecf20Sopenharmony_ci	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
5518c2ecf20Sopenharmony_ci	int subdev = kcontrol->id.subdevice;
5528c2ecf20Sopenharmony_ci	struct pcm_vol *p = &vortex->pcm_vol[subdev];
5538c2ecf20Sopenharmony_ci	int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
5548c2ecf20Sopenharmony_ci	for (i = 0; i < max_chn; i++) {
5558c2ecf20Sopenharmony_ci		if (p->vol[i] != ucontrol->value.integer.value[i]) {
5568c2ecf20Sopenharmony_ci			p->vol[i] = ucontrol->value.integer.value[i];
5578c2ecf20Sopenharmony_ci			if (p->active) {
5588c2ecf20Sopenharmony_ci				switch (vortex->dma_adb[p->dma].nr_ch) {
5598c2ecf20Sopenharmony_ci				case 1:
5608c2ecf20Sopenharmony_ci					mixin = p->mixin[0];
5618c2ecf20Sopenharmony_ci					break;
5628c2ecf20Sopenharmony_ci				case 2:
5638c2ecf20Sopenharmony_ci				default:
5648c2ecf20Sopenharmony_ci					mixin = p->mixin[(i < 2) ? i : (i - 2)];
5658c2ecf20Sopenharmony_ci					break;
5668c2ecf20Sopenharmony_ci				case 4:
5678c2ecf20Sopenharmony_ci					mixin = p->mixin[i];
5688c2ecf20Sopenharmony_ci					break;
5698c2ecf20Sopenharmony_ci				}
5708c2ecf20Sopenharmony_ci				vol = p->vol[i];
5718c2ecf20Sopenharmony_ci				vortex_mix_setinputvolumebyte(vortex,
5728c2ecf20Sopenharmony_ci					vortex->mixplayb[i], mixin, vol);
5738c2ecf20Sopenharmony_ci			}
5748c2ecf20Sopenharmony_ci			changed = 1;
5758c2ecf20Sopenharmony_ci		}
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci	return changed;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_vortex_pcm_vol = {
5838c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
5848c2ecf20Sopenharmony_ci	.name = "PCM Playback Volume",
5858c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
5868c2ecf20Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_TLV_READ |
5878c2ecf20Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_INACTIVE,
5888c2ecf20Sopenharmony_ci	.info = snd_vortex_pcm_vol_info,
5898c2ecf20Sopenharmony_ci	.get = snd_vortex_pcm_vol_get,
5908c2ecf20Sopenharmony_ci	.put = snd_vortex_pcm_vol_put,
5918c2ecf20Sopenharmony_ci	.tlv = { .p = vortex_pcm_vol_db_scale },
5928c2ecf20Sopenharmony_ci};
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci/* create a pcm device */
5958c2ecf20Sopenharmony_cistatic int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
5988c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
5998c2ecf20Sopenharmony_ci	int i;
6008c2ecf20Sopenharmony_ci	int err, nr_capt;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST)
6038c2ecf20Sopenharmony_ci		return -ENODEV;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
6068c2ecf20Sopenharmony_ci	 * same dma engine. WT uses it own separate dma engine which can't capture. */
6078c2ecf20Sopenharmony_ci	if (idx == VORTEX_PCM_ADB)
6088c2ecf20Sopenharmony_ci		nr_capt = nr;
6098c2ecf20Sopenharmony_ci	else
6108c2ecf20Sopenharmony_ci		nr_capt = 0;
6118c2ecf20Sopenharmony_ci	err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
6128c2ecf20Sopenharmony_ci			  nr_capt, &pcm);
6138c2ecf20Sopenharmony_ci	if (err < 0)
6148c2ecf20Sopenharmony_ci		return err;
6158c2ecf20Sopenharmony_ci	snprintf(pcm->name, sizeof(pcm->name),
6168c2ecf20Sopenharmony_ci		"%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
6178c2ecf20Sopenharmony_ci	chip->pcm[idx] = pcm;
6188c2ecf20Sopenharmony_ci	// This is an evil hack, but it saves a lot of duplicated code.
6198c2ecf20Sopenharmony_ci	VORTEX_PCM_TYPE(pcm) = idx;
6208c2ecf20Sopenharmony_ci	pcm->private_data = chip;
6218c2ecf20Sopenharmony_ci	/* set operators */
6228c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6238c2ecf20Sopenharmony_ci			&snd_vortex_playback_ops);
6248c2ecf20Sopenharmony_ci	if (idx == VORTEX_PCM_ADB)
6258c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6268c2ecf20Sopenharmony_ci				&snd_vortex_playback_ops);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/* pre-allocation of Scatter-Gather buffers */
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
6318c2ecf20Sopenharmony_ci				       &chip->pci_dev->dev, 0x10000, 0x10000);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	switch (VORTEX_PCM_TYPE(pcm)) {
6348c2ecf20Sopenharmony_ci	case VORTEX_PCM_ADB:
6358c2ecf20Sopenharmony_ci		err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6368c2ecf20Sopenharmony_ci					     snd_pcm_std_chmaps,
6378c2ecf20Sopenharmony_ci					     VORTEX_IS_QUAD(chip) ? 4 : 2,
6388c2ecf20Sopenharmony_ci					     0, NULL);
6398c2ecf20Sopenharmony_ci		if (err < 0)
6408c2ecf20Sopenharmony_ci			return err;
6418c2ecf20Sopenharmony_ci		err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
6428c2ecf20Sopenharmony_ci					     snd_pcm_std_chmaps, 2, 0, NULL);
6438c2ecf20Sopenharmony_ci		if (err < 0)
6448c2ecf20Sopenharmony_ci			return err;
6458c2ecf20Sopenharmony_ci		break;
6468c2ecf20Sopenharmony_ci#ifdef CHIP_AU8830
6478c2ecf20Sopenharmony_ci	case VORTEX_PCM_A3D:
6488c2ecf20Sopenharmony_ci		err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6498c2ecf20Sopenharmony_ci					     snd_pcm_std_chmaps, 1, 0, NULL);
6508c2ecf20Sopenharmony_ci		if (err < 0)
6518c2ecf20Sopenharmony_ci			return err;
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci#endif
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
6578c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
6588c2ecf20Sopenharmony_ci			kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
6598c2ecf20Sopenharmony_ci			if (!kctl)
6608c2ecf20Sopenharmony_ci				return -ENOMEM;
6618c2ecf20Sopenharmony_ci			if ((err = snd_ctl_add(chip->card, kctl)) < 0)
6628c2ecf20Sopenharmony_ci				return err;
6638c2ecf20Sopenharmony_ci		}
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci	if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
6668c2ecf20Sopenharmony_ci		for (i = 0; i < NR_PCM; i++) {
6678c2ecf20Sopenharmony_ci			chip->pcm_vol[i].active = 0;
6688c2ecf20Sopenharmony_ci			chip->pcm_vol[i].dma = -1;
6698c2ecf20Sopenharmony_ci			kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
6708c2ecf20Sopenharmony_ci			if (!kctl)
6718c2ecf20Sopenharmony_ci				return -ENOMEM;
6728c2ecf20Sopenharmony_ci			chip->pcm_vol[i].kctl = kctl;
6738c2ecf20Sopenharmony_ci			kctl->id.device = 0;
6748c2ecf20Sopenharmony_ci			kctl->id.subdevice = i;
6758c2ecf20Sopenharmony_ci			err = snd_ctl_add(chip->card, kctl);
6768c2ecf20Sopenharmony_ci			if (err < 0)
6778c2ecf20Sopenharmony_ci				return err;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
682