18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Sound Core PDAudioCF soundcards
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * PCM part
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <sound/core.h>
128c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
138c2ecf20Sopenharmony_ci#include "pdaudiocf.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * clear the SRAM contents
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_cistatic int pdacf_pcm_clear_sram(struct snd_pdacf *chip)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	int max_loop = 64 * 1024;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) {
248c2ecf20Sopenharmony_ci		if (max_loop-- < 0)
258c2ecf20Sopenharmony_ci			return -EIO;
268c2ecf20Sopenharmony_ci		inw(chip->port + PDAUDIOCF_REG_MD);
278c2ecf20Sopenharmony_ci	}
288c2ecf20Sopenharmony_ci	return 0;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * pdacf_pcm_trigger - trigger callback for capture
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
378c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
388c2ecf20Sopenharmony_ci	int inc, ret = 0, rate;
398c2ecf20Sopenharmony_ci	unsigned short mask, val, tmp;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
428c2ecf20Sopenharmony_ci		return -EBUSY;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	switch (cmd) {
458c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
468c2ecf20Sopenharmony_ci		chip->pcm_hwptr = 0;
478c2ecf20Sopenharmony_ci		chip->pcm_tdone = 0;
488c2ecf20Sopenharmony_ci		fallthrough;
498c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
508c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
518c2ecf20Sopenharmony_ci		mask = 0;
528c2ecf20Sopenharmony_ci		val = PDAUDIOCF_RECORD;
538c2ecf20Sopenharmony_ci		inc = 1;
548c2ecf20Sopenharmony_ci		rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE);
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
578c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
588c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
598c2ecf20Sopenharmony_ci		mask = PDAUDIOCF_RECORD;
608c2ecf20Sopenharmony_ci		val = 0;
618c2ecf20Sopenharmony_ci		inc = -1;
628c2ecf20Sopenharmony_ci		rate = 0;
638c2ecf20Sopenharmony_ci		break;
648c2ecf20Sopenharmony_ci	default:
658c2ecf20Sopenharmony_ci		return -EINVAL;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci	mutex_lock(&chip->reg_lock);
688c2ecf20Sopenharmony_ci	chip->pcm_running += inc;
698c2ecf20Sopenharmony_ci	tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
708c2ecf20Sopenharmony_ci	if (chip->pcm_running) {
718c2ecf20Sopenharmony_ci		if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) {
728c2ecf20Sopenharmony_ci			chip->pcm_running -= inc;
738c2ecf20Sopenharmony_ci			ret = -EIO;
748c2ecf20Sopenharmony_ci			goto __end;
758c2ecf20Sopenharmony_ci		}
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	tmp &= ~mask;
788c2ecf20Sopenharmony_ci	tmp |= val;
798c2ecf20Sopenharmony_ci	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
808c2ecf20Sopenharmony_ci      __end:
818c2ecf20Sopenharmony_ci	mutex_unlock(&chip->reg_lock);
828c2ecf20Sopenharmony_ci	snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE);
838c2ecf20Sopenharmony_ci	return ret;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * pdacf_pcm_prepare - prepare callback for playback and capture
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
928c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
938c2ecf20Sopenharmony_ci	u16 val, nval, aval;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
968c2ecf20Sopenharmony_ci		return -EBUSY;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	chip->pcm_channels = runtime->channels;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0;
1018c2ecf20Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN
1028c2ecf20Sopenharmony_ci	chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0;
1038c2ecf20Sopenharmony_ci#else
1048c2ecf20Sopenharmony_ci	chip->pcm_swab = chip->pcm_little;
1058c2ecf20Sopenharmony_ci#endif
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (snd_pcm_format_unsigned(runtime->format))
1088c2ecf20Sopenharmony_ci		chip->pcm_xor = 0x80008000;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (pdacf_pcm_clear_sram(chip) < 0)
1118c2ecf20Sopenharmony_ci		return -EIO;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
1148c2ecf20Sopenharmony_ci	nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1);
1158c2ecf20Sopenharmony_ci	switch (runtime->format) {
1168c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
1178c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	default: /* 24-bit */
1208c2ecf20Sopenharmony_ci		nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	aval = 0;
1248c2ecf20Sopenharmony_ci	chip->pcm_sample = 4;
1258c2ecf20Sopenharmony_ci	switch (runtime->format) {
1268c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
1278c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
1288c2ecf20Sopenharmony_ci		aval = AK4117_DIF_16R;
1298c2ecf20Sopenharmony_ci		chip->pcm_frame = 2;
1308c2ecf20Sopenharmony_ci		chip->pcm_sample = 2;
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3LE:
1338c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3BE:
1348c2ecf20Sopenharmony_ci		chip->pcm_sample = 3;
1358c2ecf20Sopenharmony_ci		fallthrough;
1368c2ecf20Sopenharmony_ci	default: /* 24-bit */
1378c2ecf20Sopenharmony_ci		aval = AK4117_DIF_24R;
1388c2ecf20Sopenharmony_ci		chip->pcm_frame = 3;
1398c2ecf20Sopenharmony_ci		chip->pcm_xor &= 0xffff0000;
1408c2ecf20Sopenharmony_ci		break;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (val != nval) {
1448c2ecf20Sopenharmony_ci		snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval);
1458c2ecf20Sopenharmony_ci		pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	val = pdacf_reg_read(chip,  PDAUDIOCF_REG_IER);
1498c2ecf20Sopenharmony_ci	val &= ~(PDAUDIOCF_IRQLVLEN1);
1508c2ecf20Sopenharmony_ci	val |= PDAUDIOCF_IRQLVLEN0;
1518c2ecf20Sopenharmony_ci	pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	chip->pcm_size = runtime->buffer_size;
1548c2ecf20Sopenharmony_ci	chip->pcm_period = runtime->period_size;
1558c2ecf20Sopenharmony_ci	chip->pcm_area = runtime->dma_area;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/*
1628c2ecf20Sopenharmony_ci * capture hw information
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware pdacf_pcm_capture_hw = {
1668c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1678c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
1688c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
1698c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BATCH),
1708c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
1718c2ecf20Sopenharmony_ci				SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
1728c2ecf20Sopenharmony_ci				SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
1738c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_32000 |
1748c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_44100 |
1758c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_48000 |
1768c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_88200 |
1778c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_96000 |
1788c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_176400 |
1798c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_192000,
1808c2ecf20Sopenharmony_ci	.rate_min =		32000,
1818c2ecf20Sopenharmony_ci	.rate_max =		192000,
1828c2ecf20Sopenharmony_ci	.channels_min =		1,
1838c2ecf20Sopenharmony_ci	.channels_max =		2,
1848c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(512*1024),
1858c2ecf20Sopenharmony_ci	.period_bytes_min =	8*1024,
1868c2ecf20Sopenharmony_ci	.period_bytes_max =	(64*1024),
1878c2ecf20Sopenharmony_ci	.periods_min =		2,
1888c2ecf20Sopenharmony_ci	.periods_max =		128,
1898c2ecf20Sopenharmony_ci	.fifo_size =		0,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * pdacf_pcm_capture_open - open callback for capture
1958c2ecf20Sopenharmony_ci */
1968c2ecf20Sopenharmony_cistatic int pdacf_pcm_capture_open(struct snd_pcm_substream *subs)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
1998c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
2028c2ecf20Sopenharmony_ci		return -EBUSY;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	runtime->hw = pdacf_pcm_capture_hw;
2058c2ecf20Sopenharmony_ci	runtime->private_data = chip;
2068c2ecf20Sopenharmony_ci	chip->pcm_substream = subs;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/*
2128c2ecf20Sopenharmony_ci * pdacf_pcm_capture_close - close callback for capture
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_cistatic int pdacf_pcm_capture_close(struct snd_pcm_substream *subs)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!chip)
2198c2ecf20Sopenharmony_ci		return -EINVAL;
2208c2ecf20Sopenharmony_ci	pdacf_reinit(chip, 0);
2218c2ecf20Sopenharmony_ci	chip->pcm_substream = NULL;
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/*
2278c2ecf20Sopenharmony_ci * pdacf_pcm_capture_pointer - pointer callback for capture
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *subs)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
2328c2ecf20Sopenharmony_ci	return chip->pcm_hwptr;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/*
2368c2ecf20Sopenharmony_ci * operators for PCM capture
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops pdacf_pcm_capture_ops = {
2398c2ecf20Sopenharmony_ci	.open =		pdacf_pcm_capture_open,
2408c2ecf20Sopenharmony_ci	.close =	pdacf_pcm_capture_close,
2418c2ecf20Sopenharmony_ci	.prepare =	pdacf_pcm_prepare,
2428c2ecf20Sopenharmony_ci	.trigger =	pdacf_pcm_trigger,
2438c2ecf20Sopenharmony_ci	.pointer =	pdacf_pcm_capture_pointer,
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/*
2488c2ecf20Sopenharmony_ci * snd_pdacf_pcm_new - create and initialize a pcm
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_ciint snd_pdacf_pcm_new(struct snd_pdacf *chip)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
2538c2ecf20Sopenharmony_ci	int err;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm);
2568c2ecf20Sopenharmony_ci	if (err < 0)
2578c2ecf20Sopenharmony_ci		return err;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops);
2608c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
2618c2ecf20Sopenharmony_ci				       snd_dma_continuous_data(GFP_KERNEL | GFP_DMA32),
2628c2ecf20Sopenharmony_ci				       0, 0);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	pcm->private_data = chip;
2658c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
2668c2ecf20Sopenharmony_ci	pcm->nonatomic = true;
2678c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
2688c2ecf20Sopenharmony_ci	chip->pcm = pcm;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
2718c2ecf20Sopenharmony_ci	if (err < 0)
2728c2ecf20Sopenharmony_ci		return err;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
276