18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * @File	ctpcm.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @Brief
88c2ecf20Sopenharmony_ci * This file contains the definition of the pcm device functions.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * @Author	Liu Chun
118c2ecf20Sopenharmony_ci * @Date 	Apr 2 2008
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "ctpcm.h"
158c2ecf20Sopenharmony_ci#include "cttimer.h"
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <sound/pcm.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Hardware descriptions for playback */
208c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware ct_pcm_playback_hw = {
218c2ecf20Sopenharmony_ci	.info			= (SNDRV_PCM_INFO_MMAP |
228c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_INTERLEAVED |
238c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
248c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_MMAP_VALID |
258c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_PAUSE),
268c2ecf20Sopenharmony_ci	.formats		= (SNDRV_PCM_FMTBIT_U8 |
278c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S16_LE |
288c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S24_3LE |
298c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S32_LE |
308c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_FLOAT_LE),
318c2ecf20Sopenharmony_ci	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |
328c2ecf20Sopenharmony_ci				   SNDRV_PCM_RATE_8000_192000),
338c2ecf20Sopenharmony_ci	.rate_min		= 8000,
348c2ecf20Sopenharmony_ci	.rate_max		= 192000,
358c2ecf20Sopenharmony_ci	.channels_min		= 1,
368c2ecf20Sopenharmony_ci	.channels_max		= 2,
378c2ecf20Sopenharmony_ci	.buffer_bytes_max	= (128*1024),
388c2ecf20Sopenharmony_ci	.period_bytes_min	= (64),
398c2ecf20Sopenharmony_ci	.period_bytes_max	= (128*1024),
408c2ecf20Sopenharmony_ci	.periods_min		= 2,
418c2ecf20Sopenharmony_ci	.periods_max		= 1024,
428c2ecf20Sopenharmony_ci	.fifo_size		= 0,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
468c2ecf20Sopenharmony_ci	.info			= (SNDRV_PCM_INFO_MMAP |
478c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_INTERLEAVED |
488c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
498c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_MMAP_VALID |
508c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_PAUSE),
518c2ecf20Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
528c2ecf20Sopenharmony_ci	.rates			= (SNDRV_PCM_RATE_48000 |
538c2ecf20Sopenharmony_ci				   SNDRV_PCM_RATE_44100 |
548c2ecf20Sopenharmony_ci				   SNDRV_PCM_RATE_32000),
558c2ecf20Sopenharmony_ci	.rate_min		= 32000,
568c2ecf20Sopenharmony_ci	.rate_max		= 48000,
578c2ecf20Sopenharmony_ci	.channels_min		= 2,
588c2ecf20Sopenharmony_ci	.channels_max		= 2,
598c2ecf20Sopenharmony_ci	.buffer_bytes_max	= (128*1024),
608c2ecf20Sopenharmony_ci	.period_bytes_min	= (64),
618c2ecf20Sopenharmony_ci	.period_bytes_max	= (128*1024),
628c2ecf20Sopenharmony_ci	.periods_min		= 2,
638c2ecf20Sopenharmony_ci	.periods_max		= 1024,
648c2ecf20Sopenharmony_ci	.fifo_size		= 0,
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* Hardware descriptions for capture */
688c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware ct_pcm_capture_hw = {
698c2ecf20Sopenharmony_ci	.info			= (SNDRV_PCM_INFO_MMAP |
708c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_INTERLEAVED |
718c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
728c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_PAUSE |
738c2ecf20Sopenharmony_ci				   SNDRV_PCM_INFO_MMAP_VALID),
748c2ecf20Sopenharmony_ci	.formats		= (SNDRV_PCM_FMTBIT_U8 |
758c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S16_LE |
768c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S24_3LE |
778c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_S32_LE |
788c2ecf20Sopenharmony_ci				   SNDRV_PCM_FMTBIT_FLOAT_LE),
798c2ecf20Sopenharmony_ci	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |
808c2ecf20Sopenharmony_ci				   SNDRV_PCM_RATE_8000_96000),
818c2ecf20Sopenharmony_ci	.rate_min		= 8000,
828c2ecf20Sopenharmony_ci	.rate_max		= 96000,
838c2ecf20Sopenharmony_ci	.channels_min		= 1,
848c2ecf20Sopenharmony_ci	.channels_max		= 2,
858c2ecf20Sopenharmony_ci	.buffer_bytes_max	= (128*1024),
868c2ecf20Sopenharmony_ci	.period_bytes_min	= (384),
878c2ecf20Sopenharmony_ci	.period_bytes_max	= (64*1024),
888c2ecf20Sopenharmony_ci	.periods_min		= 2,
898c2ecf20Sopenharmony_ci	.periods_max		= 1024,
908c2ecf20Sopenharmony_ci	.fifo_size		= 0,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = atc_pcm;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!apcm->substream)
988c2ecf20Sopenharmony_ci		return;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(apcm->substream);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
1068c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	atc->pcm_release_resources(atc, apcm);
1098c2ecf20Sopenharmony_ci	ct_timer_instance_free(apcm->timer);
1108c2ecf20Sopenharmony_ci	kfree(apcm);
1118c2ecf20Sopenharmony_ci	runtime->private_data = NULL;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* pcm playback operations */
1158c2ecf20Sopenharmony_cistatic int ct_pcm_playback_open(struct snd_pcm_substream *substream)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
1188c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1198c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm;
1208c2ecf20Sopenharmony_ci	int err;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
1238c2ecf20Sopenharmony_ci	if (!apcm)
1248c2ecf20Sopenharmony_ci		return -ENOMEM;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	apcm->substream = substream;
1278c2ecf20Sopenharmony_ci	apcm->interrupt = ct_atc_pcm_interrupt;
1288c2ecf20Sopenharmony_ci	if (IEC958 == substream->pcm->device) {
1298c2ecf20Sopenharmony_ci		runtime->hw = ct_spdif_passthru_playback_hw;
1308c2ecf20Sopenharmony_ci		atc->spdif_out_passthru(atc, 1);
1318c2ecf20Sopenharmony_ci	} else {
1328c2ecf20Sopenharmony_ci		runtime->hw = ct_pcm_playback_hw;
1338c2ecf20Sopenharmony_ci		if (FRONT == substream->pcm->device)
1348c2ecf20Sopenharmony_ci			runtime->hw.channels_max = 8;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
1388c2ecf20Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
1398c2ecf20Sopenharmony_ci	if (err < 0)
1408c2ecf20Sopenharmony_ci		goto free_pcm;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
1438c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1448c2ecf20Sopenharmony_ci					   1024, UINT_MAX);
1458c2ecf20Sopenharmony_ci	if (err < 0)
1468c2ecf20Sopenharmony_ci		goto free_pcm;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	apcm->timer = ct_timer_instance_new(atc->timer, apcm);
1498c2ecf20Sopenharmony_ci	if (!apcm->timer) {
1508c2ecf20Sopenharmony_ci		err = -ENOMEM;
1518c2ecf20Sopenharmony_ci		goto free_pcm;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	runtime->private_data = apcm;
1548c2ecf20Sopenharmony_ci	runtime->private_free = ct_atc_pcm_free_substream;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cifree_pcm:
1598c2ecf20Sopenharmony_ci	kfree(apcm);
1608c2ecf20Sopenharmony_ci	return err;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int ct_pcm_playback_close(struct snd_pcm_substream *substream)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* TODO: Notify mixer inactive. */
1688c2ecf20Sopenharmony_ci	if (IEC958 == substream->pcm->device)
1698c2ecf20Sopenharmony_ci		atc->spdif_out_passthru(atc, 0);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* The ct_atc_pcm object will be freed by runtime->private_free */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int ct_pcm_hw_params(struct snd_pcm_substream *substream,
1778c2ecf20Sopenharmony_ci				     struct snd_pcm_hw_params *hw_params)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
1808c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = substream->runtime->private_data;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* clear previous resources */
1838c2ecf20Sopenharmony_ci	atc->pcm_release_resources(atc, apcm);
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int ct_pcm_hw_free(struct snd_pcm_substream *substream)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
1908c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = substream->runtime->private_data;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* clear previous resources */
1938c2ecf20Sopenharmony_ci	atc->pcm_release_resources(atc, apcm);
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int err;
2018c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
2028c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2038c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (IEC958 == substream->pcm->device)
2068c2ecf20Sopenharmony_ci		err = atc->spdif_passthru_playback_prepare(atc, apcm);
2078c2ecf20Sopenharmony_ci	else
2088c2ecf20Sopenharmony_ci		err = atc->pcm_playback_prepare(atc, apcm);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (err < 0) {
2118c2ecf20Sopenharmony_ci		dev_err(atc->card->dev,
2128c2ecf20Sopenharmony_ci			"Preparing pcm playback failed!!!\n");
2138c2ecf20Sopenharmony_ci		return err;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int
2208c2ecf20Sopenharmony_cict_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
2238c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2248c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	switch (cmd) {
2278c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2288c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2298c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2308c2ecf20Sopenharmony_ci		atc->pcm_playback_start(atc, apcm);
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2338c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2348c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2358c2ecf20Sopenharmony_ci		atc->pcm_playback_stop(atc, apcm);
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	default:
2388c2ecf20Sopenharmony_ci		break;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
2458c2ecf20Sopenharmony_cict_pcm_playback_pointer(struct snd_pcm_substream *substream)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	unsigned long position;
2488c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
2498c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2508c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Read out playback position */
2538c2ecf20Sopenharmony_ci	position = atc->pcm_playback_position(atc, apcm);
2548c2ecf20Sopenharmony_ci	position = bytes_to_frames(runtime, position);
2558c2ecf20Sopenharmony_ci	if (position >= runtime->buffer_size)
2568c2ecf20Sopenharmony_ci		position = 0;
2578c2ecf20Sopenharmony_ci	return position;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* pcm capture operations */
2618c2ecf20Sopenharmony_cistatic int ct_pcm_capture_open(struct snd_pcm_substream *substream)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
2648c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2658c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm;
2668c2ecf20Sopenharmony_ci	int err;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
2698c2ecf20Sopenharmony_ci	if (!apcm)
2708c2ecf20Sopenharmony_ci		return -ENOMEM;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	apcm->started = 0;
2738c2ecf20Sopenharmony_ci	apcm->substream = substream;
2748c2ecf20Sopenharmony_ci	apcm->interrupt = ct_atc_pcm_interrupt;
2758c2ecf20Sopenharmony_ci	runtime->hw = ct_pcm_capture_hw;
2768c2ecf20Sopenharmony_ci	runtime->hw.rate_max = atc->rsr * atc->msr;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
2798c2ecf20Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
2808c2ecf20Sopenharmony_ci	if (err < 0)
2818c2ecf20Sopenharmony_ci		goto free_pcm;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
2848c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
2858c2ecf20Sopenharmony_ci					   1024, UINT_MAX);
2868c2ecf20Sopenharmony_ci	if (err < 0)
2878c2ecf20Sopenharmony_ci		goto free_pcm;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	apcm->timer = ct_timer_instance_new(atc->timer, apcm);
2908c2ecf20Sopenharmony_ci	if (!apcm->timer) {
2918c2ecf20Sopenharmony_ci		err = -ENOMEM;
2928c2ecf20Sopenharmony_ci		goto free_pcm;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	runtime->private_data = apcm;
2958c2ecf20Sopenharmony_ci	runtime->private_free = ct_atc_pcm_free_substream;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cifree_pcm:
3008c2ecf20Sopenharmony_ci	kfree(apcm);
3018c2ecf20Sopenharmony_ci	return err;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int ct_pcm_capture_close(struct snd_pcm_substream *substream)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	/* The ct_atc_pcm object will be freed by runtime->private_free */
3078c2ecf20Sopenharmony_ci	/* TODO: Notify mixer inactive. */
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	int err;
3148c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
3158c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3168c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	err = atc->pcm_capture_prepare(atc, apcm);
3198c2ecf20Sopenharmony_ci	if (err < 0) {
3208c2ecf20Sopenharmony_ci		dev_err(atc->card->dev,
3218c2ecf20Sopenharmony_ci			"Preparing pcm capture failed!!!\n");
3228c2ecf20Sopenharmony_ci		return err;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int
3298c2ecf20Sopenharmony_cict_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
3328c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3338c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	switch (cmd) {
3368c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3378c2ecf20Sopenharmony_ci		atc->pcm_capture_start(atc, apcm);
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3408c2ecf20Sopenharmony_ci		atc->pcm_capture_stop(atc, apcm);
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	default:
3438c2ecf20Sopenharmony_ci		atc->pcm_capture_stop(atc, apcm);
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
3518c2ecf20Sopenharmony_cict_pcm_capture_pointer(struct snd_pcm_substream *substream)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	unsigned long position;
3548c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_pcm_substream_chip(substream);
3558c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3568c2ecf20Sopenharmony_ci	struct ct_atc_pcm *apcm = runtime->private_data;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Read out playback position */
3598c2ecf20Sopenharmony_ci	position = atc->pcm_capture_position(atc, apcm);
3608c2ecf20Sopenharmony_ci	position = bytes_to_frames(runtime, position);
3618c2ecf20Sopenharmony_ci	if (position >= runtime->buffer_size)
3628c2ecf20Sopenharmony_ci		position = 0;
3638c2ecf20Sopenharmony_ci	return position;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/* PCM operators for playback */
3678c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops ct_pcm_playback_ops = {
3688c2ecf20Sopenharmony_ci	.open	 	= ct_pcm_playback_open,
3698c2ecf20Sopenharmony_ci	.close		= ct_pcm_playback_close,
3708c2ecf20Sopenharmony_ci	.hw_params	= ct_pcm_hw_params,
3718c2ecf20Sopenharmony_ci	.hw_free	= ct_pcm_hw_free,
3728c2ecf20Sopenharmony_ci	.prepare	= ct_pcm_playback_prepare,
3738c2ecf20Sopenharmony_ci	.trigger	= ct_pcm_playback_trigger,
3748c2ecf20Sopenharmony_ci	.pointer	= ct_pcm_playback_pointer,
3758c2ecf20Sopenharmony_ci};
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci/* PCM operators for capture */
3788c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops ct_pcm_capture_ops = {
3798c2ecf20Sopenharmony_ci	.open	 	= ct_pcm_capture_open,
3808c2ecf20Sopenharmony_ci	.close		= ct_pcm_capture_close,
3818c2ecf20Sopenharmony_ci	.hw_params	= ct_pcm_hw_params,
3828c2ecf20Sopenharmony_ci	.hw_free	= ct_pcm_hw_free,
3838c2ecf20Sopenharmony_ci	.prepare	= ct_pcm_capture_prepare,
3848c2ecf20Sopenharmony_ci	.trigger	= ct_pcm_capture_trigger,
3858c2ecf20Sopenharmony_ci	.pointer	= ct_pcm_capture_pointer,
3868c2ecf20Sopenharmony_ci};
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic const struct snd_pcm_chmap_elem surround_map[] = {
3898c2ecf20Sopenharmony_ci	{ .channels = 1,
3908c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_MONO } },
3918c2ecf20Sopenharmony_ci	{ .channels = 2,
3928c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
3938c2ecf20Sopenharmony_ci	{ }
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic const struct snd_pcm_chmap_elem clfe_map[] = {
3978c2ecf20Sopenharmony_ci	{ .channels = 1,
3988c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_MONO } },
3998c2ecf20Sopenharmony_ci	{ .channels = 2,
4008c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
4018c2ecf20Sopenharmony_ci	{ }
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic const struct snd_pcm_chmap_elem side_map[] = {
4058c2ecf20Sopenharmony_ci	{ .channels = 1,
4068c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_MONO } },
4078c2ecf20Sopenharmony_ci	{ .channels = 2,
4088c2ecf20Sopenharmony_ci	  .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
4098c2ecf20Sopenharmony_ci	{ }
4108c2ecf20Sopenharmony_ci};
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci/* Create ALSA pcm device */
4138c2ecf20Sopenharmony_ciint ct_alsa_pcm_create(struct ct_atc *atc,
4148c2ecf20Sopenharmony_ci		       enum CTALSADEVS device,
4158c2ecf20Sopenharmony_ci		       const char *device_name)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
4188c2ecf20Sopenharmony_ci	const struct snd_pcm_chmap_elem *map;
4198c2ecf20Sopenharmony_ci	int chs;
4208c2ecf20Sopenharmony_ci	int err;
4218c2ecf20Sopenharmony_ci	int playback_count, capture_count;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	playback_count = (IEC958 == device) ? 1 : 256;
4248c2ecf20Sopenharmony_ci	capture_count = (FRONT == device) ? 1 : 0;
4258c2ecf20Sopenharmony_ci	err = snd_pcm_new(atc->card, "ctxfi", device,
4268c2ecf20Sopenharmony_ci			  playback_count, capture_count, &pcm);
4278c2ecf20Sopenharmony_ci	if (err < 0) {
4288c2ecf20Sopenharmony_ci		dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
4298c2ecf20Sopenharmony_ci			err);
4308c2ecf20Sopenharmony_ci		return err;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	pcm->private_data = atc;
4348c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
4358c2ecf20Sopenharmony_ci	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
4368c2ecf20Sopenharmony_ci	strlcpy(pcm->name, device_name, sizeof(pcm->name));
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (FRONT == device)
4418c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm,
4428c2ecf20Sopenharmony_ci				SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
4458c2ecf20Sopenharmony_ci				       &atc->pci->dev, 128*1024, 128*1024);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	chs = 2;
4488c2ecf20Sopenharmony_ci	switch (device) {
4498c2ecf20Sopenharmony_ci	case FRONT:
4508c2ecf20Sopenharmony_ci		chs = 8;
4518c2ecf20Sopenharmony_ci		map = snd_pcm_std_chmaps;
4528c2ecf20Sopenharmony_ci		break;
4538c2ecf20Sopenharmony_ci	case SURROUND:
4548c2ecf20Sopenharmony_ci		map = surround_map;
4558c2ecf20Sopenharmony_ci		break;
4568c2ecf20Sopenharmony_ci	case CLFE:
4578c2ecf20Sopenharmony_ci		map = clfe_map;
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	case SIDE:
4608c2ecf20Sopenharmony_ci		map = side_map;
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	default:
4638c2ecf20Sopenharmony_ci		map = snd_pcm_std_chmaps;
4648c2ecf20Sopenharmony_ci		break;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci	err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
4678c2ecf20Sopenharmony_ci				     0, NULL);
4688c2ecf20Sopenharmony_ci	if (err < 0)
4698c2ecf20Sopenharmony_ci		return err;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4728c2ecf20Sopenharmony_ci	atc->pcms[device] = pcm;
4738c2ecf20Sopenharmony_ci#endif
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
477