18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2015
48c2ecf20Sopenharmony_ci * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
58c2ecf20Sopenharmony_ci *          for STMicroelectronics.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
128c2ecf20Sopenharmony_ci#include <sound/soc.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "uniperif.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * Some hardware-related definitions
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* sys config registers definitions */
218c2ecf20Sopenharmony_ci#define SYS_CFG_AUDIO_GLUE 0xA4
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * Driver specific types.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define UNIPERIF_PLAYER_CLK_ADJ_MIN  -999999
288c2ecf20Sopenharmony_ci#define UNIPERIF_PLAYER_CLK_ADJ_MAX  1000000
298c2ecf20Sopenharmony_ci#define UNIPERIF_PLAYER_I2S_OUT 1 /* player id connected to I2S/TDM TX bus */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * Note: snd_pcm_hardware is linked to DMA controller but is declared here to
338c2ecf20Sopenharmony_ci * integrate  DAI_CPU capability in term of rate and supported channels
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware uni_player_pcm_hw = {
368c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
378c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
388c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
398c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE,
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_CONTINUOUS,
428c2ecf20Sopenharmony_ci	.rate_min = 8000,
438c2ecf20Sopenharmony_ci	.rate_max = 192000,
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	.channels_min = 2,
468c2ecf20Sopenharmony_ci	.channels_max = 8,
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	.periods_min = 2,
498c2ecf20Sopenharmony_ci	.periods_max = 48,
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	.period_bytes_min = 128,
528c2ecf20Sopenharmony_ci	.period_bytes_max = 64 * PAGE_SIZE,
538c2ecf20Sopenharmony_ci	.buffer_bytes_max = 256 * PAGE_SIZE
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * uni_player_irq_handler
588c2ecf20Sopenharmony_ci * In case of error audio stream is stopped; stop action is protected via PCM
598c2ecf20Sopenharmony_ci * stream lock to avoid race condition with trigger callback.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
648c2ecf20Sopenharmony_ci	struct uniperif *player = dev_id;
658c2ecf20Sopenharmony_ci	unsigned int status;
668c2ecf20Sopenharmony_ci	unsigned int tmp;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	spin_lock(&player->irq_lock);
698c2ecf20Sopenharmony_ci	if (!player->substream)
708c2ecf20Sopenharmony_ci		goto irq_spin_unlock;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	snd_pcm_stream_lock(player->substream);
738c2ecf20Sopenharmony_ci	if (player->state == UNIPERIF_STATE_STOPPED)
748c2ecf20Sopenharmony_ci		goto stream_unlock;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Get interrupt status & clear them immediately */
778c2ecf20Sopenharmony_ci	status = GET_UNIPERIF_ITS(player);
788c2ecf20Sopenharmony_ci	SET_UNIPERIF_ITS_BCLR(player, status);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Check for fifo error (underrun) */
818c2ecf20Sopenharmony_ci	if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
828c2ecf20Sopenharmony_ci		dev_err(player->dev, "FIFO underflow error detected\n");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		/* Interrupt is just for information when underflow recovery */
858c2ecf20Sopenharmony_ci		if (player->underflow_enabled) {
868c2ecf20Sopenharmony_ci			/* Update state to underflow */
878c2ecf20Sopenharmony_ci			player->state = UNIPERIF_STATE_UNDERFLOW;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		} else {
908c2ecf20Sopenharmony_ci			/* Disable interrupt so doesn't continually fire */
918c2ecf20Sopenharmony_ci			SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci			/* Stop the player */
948c2ecf20Sopenharmony_ci			snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Check for dma error (overrun) */
1018c2ecf20Sopenharmony_ci	if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) {
1028c2ecf20Sopenharmony_ci		dev_err(player->dev, "DMA error detected\n");
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		/* Disable interrupt so doesn't continually fire */
1058c2ecf20Sopenharmony_ci		SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		/* Stop the player */
1088c2ecf20Sopenharmony_ci		snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* Check for underflow recovery done */
1148c2ecf20Sopenharmony_ci	if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) {
1158c2ecf20Sopenharmony_ci		if (!player->underflow_enabled) {
1168c2ecf20Sopenharmony_ci			dev_err(player->dev,
1178c2ecf20Sopenharmony_ci				"unexpected Underflow recovering\n");
1188c2ecf20Sopenharmony_ci			ret = -EPERM;
1198c2ecf20Sopenharmony_ci			goto stream_unlock;
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci		/* Read the underflow recovery duration */
1228c2ecf20Sopenharmony_ci		tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player);
1238c2ecf20Sopenharmony_ci		dev_dbg(player->dev, "Underflow recovered (%d LR clocks max)\n",
1248c2ecf20Sopenharmony_ci			tmp);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		/* Clear the underflow recovery duration */
1278c2ecf20Sopenharmony_ci		SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		/* Update state to started */
1308c2ecf20Sopenharmony_ci		player->state = UNIPERIF_STATE_STARTED;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Check if underflow recovery failed */
1368c2ecf20Sopenharmony_ci	if (unlikely(status &
1378c2ecf20Sopenharmony_ci		     UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) {
1388c2ecf20Sopenharmony_ci		dev_err(player->dev, "Underflow recovery failed\n");
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		/* Stop the player */
1418c2ecf20Sopenharmony_ci		snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistream_unlock:
1478c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock(player->substream);
1488c2ecf20Sopenharmony_ciirq_spin_unlock:
1498c2ecf20Sopenharmony_ci	spin_unlock(&player->irq_lock);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return ret;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int uni_player_clk_set_rate(struct uniperif *player, unsigned long rate)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int rate_adjusted, rate_achieved, delta, ret;
1578c2ecf20Sopenharmony_ci	int adjustment = player->clk_adj;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/*
1608c2ecf20Sopenharmony_ci	 *             a
1618c2ecf20Sopenharmony_ci	 * F = f + --------- * f = f + d
1628c2ecf20Sopenharmony_ci	 *          1000000
1638c2ecf20Sopenharmony_ci	 *
1648c2ecf20Sopenharmony_ci	 *         a
1658c2ecf20Sopenharmony_ci	 * d = --------- * f
1668c2ecf20Sopenharmony_ci	 *      1000000
1678c2ecf20Sopenharmony_ci	 *
1688c2ecf20Sopenharmony_ci	 * where:
1698c2ecf20Sopenharmony_ci	 *   f - nominal rate
1708c2ecf20Sopenharmony_ci	 *   a - adjustment in ppm (parts per milion)
1718c2ecf20Sopenharmony_ci	 *   F - rate to be set in synthesizer
1728c2ecf20Sopenharmony_ci	 *   d - delta (difference) between f and F
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	if (adjustment < 0) {
1758c2ecf20Sopenharmony_ci		/* div64_64 operates on unsigned values... */
1768c2ecf20Sopenharmony_ci		delta = -1;
1778c2ecf20Sopenharmony_ci		adjustment = -adjustment;
1788c2ecf20Sopenharmony_ci	} else {
1798c2ecf20Sopenharmony_ci		delta = 1;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	/* 500000 ppm is 0.5, which is used to round up values */
1828c2ecf20Sopenharmony_ci	delta *= (int)div64_u64((uint64_t)rate *
1838c2ecf20Sopenharmony_ci				(uint64_t)adjustment + 500000, 1000000);
1848c2ecf20Sopenharmony_ci	rate_adjusted = rate + delta;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Adjusted rate should never be == 0 */
1878c2ecf20Sopenharmony_ci	if (!rate_adjusted)
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	ret = clk_set_rate(player->clk, rate_adjusted);
1918c2ecf20Sopenharmony_ci	if (ret < 0)
1928c2ecf20Sopenharmony_ci		return ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	rate_achieved = clk_get_rate(player->clk);
1958c2ecf20Sopenharmony_ci	if (!rate_achieved)
1968c2ecf20Sopenharmony_ci		/* If value is 0 means that clock or parent not valid */
1978c2ecf20Sopenharmony_ci		return -EINVAL;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/*
2008c2ecf20Sopenharmony_ci	 * Using ALSA's adjustment control, we can modify the rate to be up
2018c2ecf20Sopenharmony_ci	 * to twice as much as requested, but no more
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	delta = rate_achieved - rate;
2048c2ecf20Sopenharmony_ci	if (delta < 0) {
2058c2ecf20Sopenharmony_ci		/* div64_64 operates on unsigned values... */
2068c2ecf20Sopenharmony_ci		delta = -delta;
2078c2ecf20Sopenharmony_ci		adjustment = -1;
2088c2ecf20Sopenharmony_ci	} else {
2098c2ecf20Sopenharmony_ci		adjustment = 1;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	/* Frequency/2 is added to round up result */
2128c2ecf20Sopenharmony_ci	adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2,
2138c2ecf20Sopenharmony_ci				     rate);
2148c2ecf20Sopenharmony_ci	player->clk_adj = adjustment;
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void uni_player_set_channel_status(struct uniperif *player,
2198c2ecf20Sopenharmony_ci					  struct snd_pcm_runtime *runtime)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int n;
2228c2ecf20Sopenharmony_ci	unsigned int status;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * Some AVRs and TVs require the channel status to contain a correct
2268c2ecf20Sopenharmony_ci	 * sampling frequency. If no sample rate is already specified, then
2278c2ecf20Sopenharmony_ci	 * set one.
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	if (runtime) {
2308c2ecf20Sopenharmony_ci		switch (runtime->rate) {
2318c2ecf20Sopenharmony_ci		case 22050:
2328c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2338c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_22050;
2348c2ecf20Sopenharmony_ci			break;
2358c2ecf20Sopenharmony_ci		case 44100:
2368c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2378c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_44100;
2388c2ecf20Sopenharmony_ci			break;
2398c2ecf20Sopenharmony_ci		case 88200:
2408c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2418c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_88200;
2428c2ecf20Sopenharmony_ci			break;
2438c2ecf20Sopenharmony_ci		case 176400:
2448c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2458c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_176400;
2468c2ecf20Sopenharmony_ci			break;
2478c2ecf20Sopenharmony_ci		case 24000:
2488c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2498c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_24000;
2508c2ecf20Sopenharmony_ci			break;
2518c2ecf20Sopenharmony_ci		case 48000:
2528c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2538c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_48000;
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci		case 96000:
2568c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2578c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_96000;
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci		case 192000:
2608c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2618c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_192000;
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci		case 32000:
2648c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2658c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_32000;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		default:
2688c2ecf20Sopenharmony_ci			/* Mark as sampling frequency not indicated */
2698c2ecf20Sopenharmony_ci			player->stream_settings.iec958.status[3] =
2708c2ecf20Sopenharmony_ci						IEC958_AES3_CON_FS_NOTID;
2718c2ecf20Sopenharmony_ci			break;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Audio mode:
2768c2ecf20Sopenharmony_ci	 * Use audio mode status to select PCM or encoded mode
2778c2ecf20Sopenharmony_ci	 */
2788c2ecf20Sopenharmony_ci	if (player->stream_settings.iec958.status[0] & IEC958_AES0_NONAUDIO)
2798c2ecf20Sopenharmony_ci		player->stream_settings.encoding_mode =
2808c2ecf20Sopenharmony_ci			UNIPERIF_IEC958_ENCODING_MODE_ENCODED;
2818c2ecf20Sopenharmony_ci	else
2828c2ecf20Sopenharmony_ci		player->stream_settings.encoding_mode =
2838c2ecf20Sopenharmony_ci			UNIPERIF_IEC958_ENCODING_MODE_PCM;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (player->stream_settings.encoding_mode ==
2868c2ecf20Sopenharmony_ci		UNIPERIF_IEC958_ENCODING_MODE_PCM)
2878c2ecf20Sopenharmony_ci		/* Clear user validity bits */
2888c2ecf20Sopenharmony_ci		SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0);
2898c2ecf20Sopenharmony_ci	else
2908c2ecf20Sopenharmony_ci		/* Set user validity bits */
2918c2ecf20Sopenharmony_ci		SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 1);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Program the new channel status */
2948c2ecf20Sopenharmony_ci	for (n = 0; n < 6; ++n) {
2958c2ecf20Sopenharmony_ci		status  =
2968c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[0 + (n * 4)] & 0xf;
2978c2ecf20Sopenharmony_ci		status |=
2988c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[1 + (n * 4)] << 8;
2998c2ecf20Sopenharmony_ci		status |=
3008c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[2 + (n * 4)] << 16;
3018c2ecf20Sopenharmony_ci		status |=
3028c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[3 + (n * 4)] << 24;
3038c2ecf20Sopenharmony_ci		SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Update the channel status */
3078c2ecf20Sopenharmony_ci	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
3088c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player);
3098c2ecf20Sopenharmony_ci	else
3108c2ecf20Sopenharmony_ci		SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int uni_player_prepare_iec958(struct uniperif *player,
3148c2ecf20Sopenharmony_ci				     struct snd_pcm_runtime *runtime)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	int clk_div;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	clk_div = player->mclk / runtime->rate;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Oversampling must be multiple of 128 as iec958 frame is 32-bits */
3218c2ecf20Sopenharmony_ci	if ((clk_div % 128) || (clk_div <= 0)) {
3228c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid clk_div %d\n",
3238c2ecf20Sopenharmony_ci			__func__, clk_div);
3248c2ecf20Sopenharmony_ci		return -EINVAL;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	switch (runtime->format) {
3288c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
3298c2ecf20Sopenharmony_ci		/* 16/16 memory format */
3308c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player);
3318c2ecf20Sopenharmony_ci		/* 16-bits per sub-frame */
3328c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_NBIT_32(player);
3338c2ecf20Sopenharmony_ci		/* Set 16-bit sample precision */
3348c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player);
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
3378c2ecf20Sopenharmony_ci		/* 16/0 memory format */
3388c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
3398c2ecf20Sopenharmony_ci		/* 32-bits per sub-frame */
3408c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_NBIT_32(player);
3418c2ecf20Sopenharmony_ci		/* Set 24-bit sample precision */
3428c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player);
3438c2ecf20Sopenharmony_ci		break;
3448c2ecf20Sopenharmony_ci	default:
3458c2ecf20Sopenharmony_ci		dev_err(player->dev, "format not supported\n");
3468c2ecf20Sopenharmony_ci		return -EINVAL;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Set parity to be calculated by the hardware */
3508c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(player);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* Set channel status bits to be inserted by the hardware */
3538c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(player);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Set user data bits to be inserted by the hardware */
3568c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(player);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Set validity bits to be inserted by the hardware */
3598c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(player);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* Set full software control to disabled */
3628c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(player);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
3678c2ecf20Sopenharmony_ci	/* Update the channel status */
3688c2ecf20Sopenharmony_ci	uni_player_set_channel_status(player, runtime);
3698c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* Clear the user validity user bits */
3728c2ecf20Sopenharmony_ci	SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Disable one-bit audio mode */
3758c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* Enable consecutive frames repetition of Z preamble (not for HBRA) */
3788c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(player);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Change to SUF0_SUBF1 and left/right channels swap! */
3818c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(player);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Set data output as MSB first */
3848c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (player->stream_settings.encoding_mode ==
3878c2ecf20Sopenharmony_ci				UNIPERIF_IEC958_ENCODING_MODE_ENCODED)
3888c2ecf20Sopenharmony_ci		SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(player);
3898c2ecf20Sopenharmony_ci	else
3908c2ecf20Sopenharmony_ci		SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(player);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Set rounding to off */
3958c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Set clock divisor */
3988c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / 128);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Set the spdif latency to not wait before starting player */
4018c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/*
4048c2ecf20Sopenharmony_ci	 * Ensure iec958 formatting is off. It will be enabled in function
4058c2ecf20Sopenharmony_ci	 * uni_player_start() at the same time as the operation
4068c2ecf20Sopenharmony_ci	 * mode is set to work around a silicon issue.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
4098c2ecf20Sopenharmony_ci		SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
4108c2ecf20Sopenharmony_ci	else
4118c2ecf20Sopenharmony_ci		SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int uni_player_prepare_pcm(struct uniperif *player,
4178c2ecf20Sopenharmony_ci				  struct snd_pcm_runtime *runtime)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	int output_frame_size, slot_width, clk_div;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* Force slot width to 32 in I2S mode (HW constraint) */
4228c2ecf20Sopenharmony_ci	if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
4238c2ecf20Sopenharmony_ci		SND_SOC_DAIFMT_I2S)
4248c2ecf20Sopenharmony_ci		slot_width = 32;
4258c2ecf20Sopenharmony_ci	else
4268c2ecf20Sopenharmony_ci		slot_width = snd_pcm_format_width(runtime->format);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	output_frame_size = slot_width * runtime->channels;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	clk_div = player->mclk / runtime->rate;
4318c2ecf20Sopenharmony_ci	/*
4328c2ecf20Sopenharmony_ci	 * For 32 bits subframe clk_div must be a multiple of 128,
4338c2ecf20Sopenharmony_ci	 * for 16 bits must be a multiple of 64
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	if ((slot_width == 32) && (clk_div % 128)) {
4368c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid clk_div\n", __func__);
4378c2ecf20Sopenharmony_ci		return -EINVAL;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if ((slot_width == 16) && (clk_div % 64)) {
4418c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid clk_div\n", __func__);
4428c2ecf20Sopenharmony_ci		return -EINVAL;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/*
4468c2ecf20Sopenharmony_ci	 * Number of bits per subframe (which is one channel sample)
4478c2ecf20Sopenharmony_ci	 * on output - Transfer 16 or 32 bits from FIFO
4488c2ecf20Sopenharmony_ci	 */
4498c2ecf20Sopenharmony_ci	switch (slot_width) {
4508c2ecf20Sopenharmony_ci	case 32:
4518c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_NBIT_32(player);
4528c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player);
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci	case 16:
4558c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_NBIT_16(player);
4568c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player);
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	default:
4598c2ecf20Sopenharmony_ci		dev_err(player->dev, "subframe format not supported\n");
4608c2ecf20Sopenharmony_ci		return -EINVAL;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Configure data memory format */
4648c2ecf20Sopenharmony_ci	switch (runtime->format) {
4658c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
4668c2ecf20Sopenharmony_ci		/* One data word contains two samples */
4678c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player);
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
4718c2ecf20Sopenharmony_ci		/*
4728c2ecf20Sopenharmony_ci		 * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits
4738c2ecf20Sopenharmony_ci		 * on the left than zeros (if less than 32 bytes)"... ;-)
4748c2ecf20Sopenharmony_ci		 */
4758c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	default:
4798c2ecf20Sopenharmony_ci		dev_err(player->dev, "format not supported\n");
4808c2ecf20Sopenharmony_ci		return -EINVAL;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Set rounding to off */
4848c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Set clock divisor */
4878c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_DIVIDER(player, clk_div / (2 * output_frame_size));
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Number of channelsmust be even*/
4908c2ecf20Sopenharmony_ci	if ((runtime->channels % 2) || (runtime->channels < 2) ||
4918c2ecf20Sopenharmony_ci	    (runtime->channels > 10)) {
4928c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid nb of channels\n", __func__);
4938c2ecf20Sopenharmony_ci		return -EINVAL;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* Set 1-bit audio format to disabled */
4998c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* No iec958 formatting as outputting to DAC  */
5048c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int uni_player_prepare_tdm(struct uniperif *player,
5108c2ecf20Sopenharmony_ci				  struct snd_pcm_runtime *runtime)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	int tdm_frame_size; /* unip tdm frame size in bytes */
5138c2ecf20Sopenharmony_ci	int user_frame_size; /* user tdm frame size in bytes */
5148c2ecf20Sopenharmony_ci	/* default unip TDM_WORD_POS_X_Y */
5158c2ecf20Sopenharmony_ci	unsigned int word_pos[4] = {
5168c2ecf20Sopenharmony_ci		0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
5178c2ecf20Sopenharmony_ci	int freq, ret;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	tdm_frame_size =
5208c2ecf20Sopenharmony_ci		sti_uniperiph_get_unip_tdm_frame_size(player);
5218c2ecf20Sopenharmony_ci	user_frame_size =
5228c2ecf20Sopenharmony_ci		sti_uniperiph_get_user_frame_size(runtime);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* fix 16/0 format */
5258c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
5268c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* number of words inserted on the TDM line */
5298c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_NUM_CH(player, user_frame_size / 4 / 2);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
5328c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Enable the tdm functionality */
5358c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(player);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* number of 8 bits timeslots avail in unip tdm frame */
5388c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(player, tdm_frame_size);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* set the timeslot allocation for words in FIFO */
5418c2ecf20Sopenharmony_ci	sti_uniperiph_get_tdm_word_pos(player, word_pos);
5428c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_WORD_POS(player, 1_2, word_pos[WORD_1_2]);
5438c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_WORD_POS(player, 3_4, word_pos[WORD_3_4]);
5448c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_WORD_POS(player, 5_6, word_pos[WORD_5_6]);
5458c2ecf20Sopenharmony_ci	SET_UNIPERIF_TDM_WORD_POS(player, 7_8, word_pos[WORD_7_8]);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* set unip clk rate (not done vai set_sysclk ops) */
5488c2ecf20Sopenharmony_ci	freq = runtime->rate * tdm_frame_size * 8;
5498c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
5508c2ecf20Sopenharmony_ci	ret = uni_player_clk_set_rate(player, freq);
5518c2ecf20Sopenharmony_ci	if (!ret)
5528c2ecf20Sopenharmony_ci		player->mclk = freq;
5538c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/*
5598c2ecf20Sopenharmony_ci * ALSA uniperipheral iec958 controls
5608c2ecf20Sopenharmony_ci */
5618c2ecf20Sopenharmony_cistatic int  uni_player_ctl_iec958_info(struct snd_kcontrol *kcontrol,
5628c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
5658c2ecf20Sopenharmony_ci	uinfo->count = 1;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	return 0;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic int uni_player_ctl_iec958_get(struct snd_kcontrol *kcontrol,
5718c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
5748c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
5758c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
5768c2ecf20Sopenharmony_ci	struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
5798c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = iec958->status[0];
5808c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = iec958->status[1];
5818c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = iec958->status[2];
5828c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = iec958->status[3];
5838c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
5848c2ecf20Sopenharmony_ci	return 0;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
5888c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
5918c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
5928c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
5938c2ecf20Sopenharmony_ci	struct snd_aes_iec958 *iec958 =  &player->stream_settings.iec958;
5948c2ecf20Sopenharmony_ci	unsigned long flags;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
5978c2ecf20Sopenharmony_ci	iec958->status[0] = ucontrol->value.iec958.status[0];
5988c2ecf20Sopenharmony_ci	iec958->status[1] = ucontrol->value.iec958.status[1];
5998c2ecf20Sopenharmony_ci	iec958->status[2] = ucontrol->value.iec958.status[2];
6008c2ecf20Sopenharmony_ci	iec958->status[3] = ucontrol->value.iec958.status[3];
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&player->irq_lock, flags);
6038c2ecf20Sopenharmony_ci	if (player->substream && player->substream->runtime)
6048c2ecf20Sopenharmony_ci		uni_player_set_channel_status(player,
6058c2ecf20Sopenharmony_ci					      player->substream->runtime);
6068c2ecf20Sopenharmony_ci	else
6078c2ecf20Sopenharmony_ci		uni_player_set_channel_status(player, NULL);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&player->irq_lock, flags);
6108c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	return 0;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new uni_player_iec958_ctl = {
6168c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
6178c2ecf20Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
6188c2ecf20Sopenharmony_ci	.info = uni_player_ctl_iec958_info,
6198c2ecf20Sopenharmony_ci	.get = uni_player_ctl_iec958_get,
6208c2ecf20Sopenharmony_ci	.put = uni_player_ctl_iec958_put,
6218c2ecf20Sopenharmony_ci};
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/*
6248c2ecf20Sopenharmony_ci * uniperif rate adjustement control
6258c2ecf20Sopenharmony_ci */
6268c2ecf20Sopenharmony_cistatic int snd_sti_clk_adjustment_info(struct snd_kcontrol *kcontrol,
6278c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6308c2ecf20Sopenharmony_ci	uinfo->count = 1;
6318c2ecf20Sopenharmony_ci	uinfo->value.integer.min = UNIPERIF_PLAYER_CLK_ADJ_MIN;
6328c2ecf20Sopenharmony_ci	uinfo->value.integer.max = UNIPERIF_PLAYER_CLK_ADJ_MAX;
6338c2ecf20Sopenharmony_ci	uinfo->value.integer.step = 1;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return 0;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
6398c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
6428c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
6438c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
6468c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = player->clk_adj;
6478c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	return 0;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int snd_sti_clk_adjustment_put(struct snd_kcontrol *kcontrol,
6538c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
6568c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
6578c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
6588c2ecf20Sopenharmony_ci	int ret = 0;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if ((ucontrol->value.integer.value[0] < UNIPERIF_PLAYER_CLK_ADJ_MIN) ||
6618c2ecf20Sopenharmony_ci	    (ucontrol->value.integer.value[0] > UNIPERIF_PLAYER_CLK_ADJ_MAX))
6628c2ecf20Sopenharmony_ci		return -EINVAL;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
6658c2ecf20Sopenharmony_ci	player->clk_adj = ucontrol->value.integer.value[0];
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (player->mclk)
6688c2ecf20Sopenharmony_ci		ret = uni_player_clk_set_rate(player, player->mclk);
6698c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	return ret;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new uni_player_clk_adj_ctl = {
6758c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
6768c2ecf20Sopenharmony_ci	.name = "PCM Playback Oversampling Freq. Adjustment",
6778c2ecf20Sopenharmony_ci	.info = snd_sti_clk_adjustment_info,
6788c2ecf20Sopenharmony_ci	.get = snd_sti_clk_adjustment_get,
6798c2ecf20Sopenharmony_ci	.put = snd_sti_clk_adjustment_put,
6808c2ecf20Sopenharmony_ci};
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new *snd_sti_pcm_ctl[] = {
6838c2ecf20Sopenharmony_ci	&uni_player_clk_adj_ctl,
6848c2ecf20Sopenharmony_ci};
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new *snd_sti_iec_ctl[] = {
6878c2ecf20Sopenharmony_ci	&uni_player_iec958_ctl,
6888c2ecf20Sopenharmony_ci	&uni_player_clk_adj_ctl,
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic int uni_player_startup(struct snd_pcm_substream *substream,
6928c2ecf20Sopenharmony_ci			      struct snd_soc_dai *dai)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
6958c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
6968c2ecf20Sopenharmony_ci	unsigned long flags;
6978c2ecf20Sopenharmony_ci	int ret;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&player->irq_lock, flags);
7008c2ecf20Sopenharmony_ci	player->substream = substream;
7018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&player->irq_lock, flags);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	player->clk_adj = 0;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (!UNIPERIF_TYPE_IS_TDM(player))
7068c2ecf20Sopenharmony_ci		return 0;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* refine hw constraint in tdm mode */
7098c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
7108c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
7118c2ecf20Sopenharmony_ci				  sti_uniperiph_fix_tdm_chan,
7128c2ecf20Sopenharmony_ci				  player, SNDRV_PCM_HW_PARAM_CHANNELS,
7138c2ecf20Sopenharmony_ci				  -1);
7148c2ecf20Sopenharmony_ci	if (ret < 0)
7158c2ecf20Sopenharmony_ci		return ret;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return snd_pcm_hw_rule_add(substream->runtime, 0,
7188c2ecf20Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_FORMAT,
7198c2ecf20Sopenharmony_ci				   sti_uniperiph_fix_tdm_format,
7208c2ecf20Sopenharmony_ci				   player, SNDRV_PCM_HW_PARAM_FORMAT,
7218c2ecf20Sopenharmony_ci				   -1);
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
7258c2ecf20Sopenharmony_ci				 unsigned int freq, int dir)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
7288c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
7298c2ecf20Sopenharmony_ci	int ret;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (UNIPERIF_TYPE_IS_TDM(player) || (dir == SND_SOC_CLOCK_IN))
7328c2ecf20Sopenharmony_ci		return 0;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (clk_id != 0)
7358c2ecf20Sopenharmony_ci		return -EINVAL;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	mutex_lock(&player->ctrl_lock);
7388c2ecf20Sopenharmony_ci	ret = uni_player_clk_set_rate(player, freq);
7398c2ecf20Sopenharmony_ci	if (!ret)
7408c2ecf20Sopenharmony_ci		player->mclk = freq;
7418c2ecf20Sopenharmony_ci	mutex_unlock(&player->ctrl_lock);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	return ret;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic int uni_player_prepare(struct snd_pcm_substream *substream,
7478c2ecf20Sopenharmony_ci			      struct snd_soc_dai *dai)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
7508c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
7518c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7528c2ecf20Sopenharmony_ci	int transfer_size, trigger_limit;
7538c2ecf20Sopenharmony_ci	int ret;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* The player should be stopped */
7568c2ecf20Sopenharmony_ci	if (player->state != UNIPERIF_STATE_STOPPED) {
7578c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid player state %d\n", __func__,
7588c2ecf20Sopenharmony_ci			player->state);
7598c2ecf20Sopenharmony_ci		return -EINVAL;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	/* Calculate transfer size (in fifo cells and bytes) for frame count */
7638c2ecf20Sopenharmony_ci	if (player->type == SND_ST_UNIPERIF_TYPE_TDM) {
7648c2ecf20Sopenharmony_ci		/* transfer size = user frame size (in 32 bits FIFO cell) */
7658c2ecf20Sopenharmony_ci		transfer_size =
7668c2ecf20Sopenharmony_ci			sti_uniperiph_get_user_frame_size(runtime) / 4;
7678c2ecf20Sopenharmony_ci	} else {
7688c2ecf20Sopenharmony_ci		transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Calculate number of empty cells available before asserting DREQ */
7728c2ecf20Sopenharmony_ci	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
7738c2ecf20Sopenharmony_ci		trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
7748c2ecf20Sopenharmony_ci	} else {
7758c2ecf20Sopenharmony_ci		/*
7768c2ecf20Sopenharmony_ci		 * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
7778c2ecf20Sopenharmony_ci		 * FDMA_TRIGGER_LIMIT also controls when the state switches
7788c2ecf20Sopenharmony_ci		 * from OFF or STANDBY to AUDIO DATA.
7798c2ecf20Sopenharmony_ci		 */
7808c2ecf20Sopenharmony_ci		trigger_limit = transfer_size;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* Trigger limit must be an even number */
7848c2ecf20Sopenharmony_ci	if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) ||
7858c2ecf20Sopenharmony_ci	    (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) {
7868c2ecf20Sopenharmony_ci		dev_err(player->dev, "invalid trigger limit %d\n",
7878c2ecf20Sopenharmony_ci			trigger_limit);
7888c2ecf20Sopenharmony_ci		return -EINVAL;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* Uniperipheral setup depends on player type */
7948c2ecf20Sopenharmony_ci	switch (player->type) {
7958c2ecf20Sopenharmony_ci	case SND_ST_UNIPERIF_TYPE_HDMI:
7968c2ecf20Sopenharmony_ci		ret = uni_player_prepare_iec958(player, runtime);
7978c2ecf20Sopenharmony_ci		break;
7988c2ecf20Sopenharmony_ci	case SND_ST_UNIPERIF_TYPE_PCM:
7998c2ecf20Sopenharmony_ci		ret = uni_player_prepare_pcm(player, runtime);
8008c2ecf20Sopenharmony_ci		break;
8018c2ecf20Sopenharmony_ci	case SND_ST_UNIPERIF_TYPE_SPDIF:
8028c2ecf20Sopenharmony_ci		ret = uni_player_prepare_iec958(player, runtime);
8038c2ecf20Sopenharmony_ci		break;
8048c2ecf20Sopenharmony_ci	case SND_ST_UNIPERIF_TYPE_TDM:
8058c2ecf20Sopenharmony_ci		ret = uni_player_prepare_tdm(player, runtime);
8068c2ecf20Sopenharmony_ci		break;
8078c2ecf20Sopenharmony_ci	default:
8088c2ecf20Sopenharmony_ci		dev_err(player->dev, "invalid player type\n");
8098c2ecf20Sopenharmony_ci		return -EINVAL;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (ret)
8138c2ecf20Sopenharmony_ci		return ret;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	switch (player->daifmt & SND_SOC_DAIFMT_INV_MASK) {
8168c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
8178c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
8188c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
8198c2ecf20Sopenharmony_ci		break;
8208c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
8218c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
8228c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
8238c2ecf20Sopenharmony_ci		break;
8248c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
8258c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
8268c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
8278c2ecf20Sopenharmony_ci		break;
8288c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
8298c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
8308c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
8318c2ecf20Sopenharmony_ci		break;
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	switch (player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
8358c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
8368c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
8378c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(player);
8388c2ecf20Sopenharmony_ci		break;
8398c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
8408c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
8418c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
8428c2ecf20Sopenharmony_ci		break;
8438c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
8448c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(player);
8458c2ecf20Sopenharmony_ci		SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
8468c2ecf20Sopenharmony_ci		break;
8478c2ecf20Sopenharmony_ci	default:
8488c2ecf20Sopenharmony_ci		dev_err(player->dev, "format not supported\n");
8498c2ecf20Sopenharmony_ci		return -EINVAL;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return sti_uniperiph_reset(player);
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic int uni_player_start(struct uniperif *player)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	int ret;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/* The player should be stopped */
8638c2ecf20Sopenharmony_ci	if (player->state != UNIPERIF_STATE_STOPPED) {
8648c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid player state\n", __func__);
8658c2ecf20Sopenharmony_ci		return -EINVAL;
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(player->clk);
8698c2ecf20Sopenharmony_ci	if (ret) {
8708c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: Failed to enable clock\n", __func__);
8718c2ecf20Sopenharmony_ci		return ret;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* Clear any pending interrupts */
8758c2ecf20Sopenharmony_ci	SET_UNIPERIF_ITS_BCLR(player, GET_UNIPERIF_ITS(player));
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Set the interrupt mask */
8788c2ecf20Sopenharmony_ci	SET_UNIPERIF_ITM_BSET_DMA_ERROR(player);
8798c2ecf20Sopenharmony_ci	SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Enable underflow recovery interrupts */
8828c2ecf20Sopenharmony_ci	if (player->underflow_enabled) {
8838c2ecf20Sopenharmony_ci		SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player);
8848c2ecf20Sopenharmony_ci		SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player);
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	ret = sti_uniperiph_reset(player);
8888c2ecf20Sopenharmony_ci	if (ret < 0) {
8898c2ecf20Sopenharmony_ci		clk_disable_unprepare(player->clk);
8908c2ecf20Sopenharmony_ci		return ret;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	/*
8948c2ecf20Sopenharmony_ci	 * Does not use IEC61937 features of the uniperipheral hardware.
8958c2ecf20Sopenharmony_ci	 * Instead it performs IEC61937 in software and inserts it directly
8968c2ecf20Sopenharmony_ci	 * into the audio data stream. As such, when encoded mode is selected,
8978c2ecf20Sopenharmony_ci	 * linear pcm mode is still used, but with the differences of the
8988c2ecf20Sopenharmony_ci	 * channel status bits set for encoded mode and the validity bits set.
8998c2ecf20Sopenharmony_ci	 */
9008c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(player);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/*
9038c2ecf20Sopenharmony_ci	 * If iec958 formatting is required for hdmi or spdif, then it must be
9048c2ecf20Sopenharmony_ci	 * enabled after the operation mode is set. If set prior to this, it
9058c2ecf20Sopenharmony_ci	 * will not take affect and hang the player.
9068c2ecf20Sopenharmony_ci	 */
9078c2ecf20Sopenharmony_ci	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
9088c2ecf20Sopenharmony_ci		if (UNIPERIF_TYPE_IS_IEC958(player))
9098c2ecf20Sopenharmony_ci			SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/* Force channel status update (no update if clk disable) */
9128c2ecf20Sopenharmony_ci	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
9138c2ecf20Sopenharmony_ci		SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player);
9148c2ecf20Sopenharmony_ci	else
9158c2ecf20Sopenharmony_ci		SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* Update state to started */
9188c2ecf20Sopenharmony_ci	player->state = UNIPERIF_STATE_STARTED;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	return 0;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic int uni_player_stop(struct uniperif *player)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	int ret;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* The player should not be in stopped state */
9288c2ecf20Sopenharmony_ci	if (player->state == UNIPERIF_STATE_STOPPED) {
9298c2ecf20Sopenharmony_ci		dev_err(player->dev, "%s: invalid player state\n", __func__);
9308c2ecf20Sopenharmony_ci		return -EINVAL;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Turn the player off */
9348c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_OPERATION_OFF(player);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	ret = sti_uniperiph_reset(player);
9378c2ecf20Sopenharmony_ci	if (ret < 0)
9388c2ecf20Sopenharmony_ci		return ret;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	/* Disable interrupts */
9418c2ecf20Sopenharmony_ci	SET_UNIPERIF_ITM_BCLR(player, GET_UNIPERIF_ITM(player));
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* Disable clock */
9448c2ecf20Sopenharmony_ci	clk_disable_unprepare(player->clk);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* Update state to stopped and return */
9478c2ecf20Sopenharmony_ci	player->state = UNIPERIF_STATE_STOPPED;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	return 0;
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ciint uni_player_resume(struct uniperif *player)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	int ret;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	/* Select the frequency synthesizer clock */
9578c2ecf20Sopenharmony_ci	if (player->clk_sel) {
9588c2ecf20Sopenharmony_ci		ret = regmap_field_write(player->clk_sel, 1);
9598c2ecf20Sopenharmony_ci		if (ret) {
9608c2ecf20Sopenharmony_ci			dev_err(player->dev,
9618c2ecf20Sopenharmony_ci				"%s: Failed to select freq synth clock\n",
9628c2ecf20Sopenharmony_ci				__func__);
9638c2ecf20Sopenharmony_ci			return ret;
9648c2ecf20Sopenharmony_ci		}
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
9688c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
9698c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
9708c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	return 0;
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(uni_player_resume);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic int uni_player_trigger(struct snd_pcm_substream *substream,
9778c2ecf20Sopenharmony_ci			      int cmd, struct snd_soc_dai *dai)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
9808c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	switch (cmd) {
9838c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
9848c2ecf20Sopenharmony_ci		return uni_player_start(player);
9858c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
9868c2ecf20Sopenharmony_ci		return uni_player_stop(player);
9878c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
9888c2ecf20Sopenharmony_ci		return uni_player_resume(player);
9898c2ecf20Sopenharmony_ci	default:
9908c2ecf20Sopenharmony_ci		return -EINVAL;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic void uni_player_shutdown(struct snd_pcm_substream *substream,
9958c2ecf20Sopenharmony_ci				struct snd_soc_dai *dai)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
9988c2ecf20Sopenharmony_ci	struct uniperif *player = priv->dai_data.uni;
9998c2ecf20Sopenharmony_ci	unsigned long flags;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&player->irq_lock, flags);
10028c2ecf20Sopenharmony_ci	if (player->state != UNIPERIF_STATE_STOPPED)
10038c2ecf20Sopenharmony_ci		/* Stop the player */
10048c2ecf20Sopenharmony_ci		uni_player_stop(player);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	player->substream = NULL;
10078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&player->irq_lock, flags);
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
10118c2ecf20Sopenharmony_ci					  struct uniperif *player)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
10148c2ecf20Sopenharmony_ci	struct regmap *regmap;
10158c2ecf20Sopenharmony_ci	struct reg_field regfield[2] = {
10168c2ecf20Sopenharmony_ci		/* PCM_CLK_SEL */
10178c2ecf20Sopenharmony_ci		REG_FIELD(SYS_CFG_AUDIO_GLUE,
10188c2ecf20Sopenharmony_ci			  8 + player->id,
10198c2ecf20Sopenharmony_ci			  8 + player->id),
10208c2ecf20Sopenharmony_ci		/* PCMP_VALID_SEL */
10218c2ecf20Sopenharmony_ci		REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1)
10228c2ecf20Sopenharmony_ci	};
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
10278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
10288c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	player->clk_sel = regmap_field_alloc(regmap, regfield[0]);
10328c2ecf20Sopenharmony_ci	player->valid_sel = regmap_field_alloc(regmap, regfield[1]);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	return 0;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops uni_player_dai_ops = {
10388c2ecf20Sopenharmony_ci		.startup = uni_player_startup,
10398c2ecf20Sopenharmony_ci		.shutdown = uni_player_shutdown,
10408c2ecf20Sopenharmony_ci		.prepare = uni_player_prepare,
10418c2ecf20Sopenharmony_ci		.trigger = uni_player_trigger,
10428c2ecf20Sopenharmony_ci		.hw_params = sti_uniperiph_dai_hw_params,
10438c2ecf20Sopenharmony_ci		.set_fmt = sti_uniperiph_dai_set_fmt,
10448c2ecf20Sopenharmony_ci		.set_sysclk = uni_player_set_sysclk,
10458c2ecf20Sopenharmony_ci		.set_tdm_slot = sti_uniperiph_set_tdm_slot
10468c2ecf20Sopenharmony_ci};
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ciint uni_player_init(struct platform_device *pdev,
10498c2ecf20Sopenharmony_ci		    struct uniperif *player)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	int ret = 0;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	player->dev = &pdev->dev;
10548c2ecf20Sopenharmony_ci	player->state = UNIPERIF_STATE_STOPPED;
10558c2ecf20Sopenharmony_ci	player->dai_ops = &uni_player_dai_ops;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
10588c2ecf20Sopenharmony_ci	ret = uni_player_parse_dt_audio_glue(pdev, player);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (ret < 0) {
10618c2ecf20Sopenharmony_ci		dev_err(player->dev, "Failed to parse DeviceTree\n");
10628c2ecf20Sopenharmony_ci		return ret;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	/* Underflow recovery is only supported on later ip revisions */
10668c2ecf20Sopenharmony_ci	if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
10678c2ecf20Sopenharmony_ci		player->underflow_enabled = 1;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (UNIPERIF_TYPE_IS_TDM(player))
10708c2ecf20Sopenharmony_ci		player->hw = &uni_tdm_hw;
10718c2ecf20Sopenharmony_ci	else
10728c2ecf20Sopenharmony_ci		player->hw = &uni_player_pcm_hw;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/* Get uniperif resource */
10758c2ecf20Sopenharmony_ci	player->clk = of_clk_get(pdev->dev.of_node, 0);
10768c2ecf20Sopenharmony_ci	if (IS_ERR(player->clk)) {
10778c2ecf20Sopenharmony_ci		dev_err(player->dev, "Failed to get clock\n");
10788c2ecf20Sopenharmony_ci		return PTR_ERR(player->clk);
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/* Select the frequency synthesizer clock */
10828c2ecf20Sopenharmony_ci	if (player->clk_sel) {
10838c2ecf20Sopenharmony_ci		ret = regmap_field_write(player->clk_sel, 1);
10848c2ecf20Sopenharmony_ci		if (ret) {
10858c2ecf20Sopenharmony_ci			dev_err(player->dev,
10868c2ecf20Sopenharmony_ci				"%s: Failed to select freq synth clock\n",
10878c2ecf20Sopenharmony_ci				__func__);
10888c2ecf20Sopenharmony_ci			return ret;
10898c2ecf20Sopenharmony_ci		}
10908c2ecf20Sopenharmony_ci	}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/* connect to I2S/TDM TX bus */
10938c2ecf20Sopenharmony_ci	if (player->valid_sel &&
10948c2ecf20Sopenharmony_ci	    (player->id == UNIPERIF_PLAYER_I2S_OUT)) {
10958c2ecf20Sopenharmony_ci		ret = regmap_field_write(player->valid_sel, player->id);
10968c2ecf20Sopenharmony_ci		if (ret) {
10978c2ecf20Sopenharmony_ci			dev_err(player->dev,
10988c2ecf20Sopenharmony_ci				"%s: unable to connect to tdm bus\n", __func__);
10998c2ecf20Sopenharmony_ci			return ret;
11008c2ecf20Sopenharmony_ci		}
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, player->irq,
11048c2ecf20Sopenharmony_ci			       uni_player_irq_handler, IRQF_SHARED,
11058c2ecf20Sopenharmony_ci			       dev_name(&pdev->dev), player);
11068c2ecf20Sopenharmony_ci	if (ret < 0) {
11078c2ecf20Sopenharmony_ci		dev_err(player->dev, "unable to request IRQ %d\n", player->irq);
11088c2ecf20Sopenharmony_ci		return ret;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	mutex_init(&player->ctrl_lock);
11128c2ecf20Sopenharmony_ci	spin_lock_init(&player->irq_lock);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* Ensure that disabled by default */
11158c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
11168c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
11178c2ecf20Sopenharmony_ci	SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
11188c2ecf20Sopenharmony_ci	SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (UNIPERIF_TYPE_IS_IEC958(player)) {
11218c2ecf20Sopenharmony_ci		/* Set default iec958 status bits  */
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		/* Consumer, PCM, copyright, 2ch, mode 0 */
11248c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[0] = 0x00;
11258c2ecf20Sopenharmony_ci		/* Broadcast reception category */
11268c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[1] =
11278c2ecf20Sopenharmony_ci					IEC958_AES1_CON_GENERAL;
11288c2ecf20Sopenharmony_ci		/* Do not take into account source or channel number */
11298c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[2] =
11308c2ecf20Sopenharmony_ci					IEC958_AES2_CON_SOURCE_UNSPEC;
11318c2ecf20Sopenharmony_ci		/* Sampling frequency not indicated */
11328c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[3] =
11338c2ecf20Sopenharmony_ci					IEC958_AES3_CON_FS_NOTID;
11348c2ecf20Sopenharmony_ci		/* Max sample word 24-bit, sample word length not indicated */
11358c2ecf20Sopenharmony_ci		player->stream_settings.iec958.status[4] =
11368c2ecf20Sopenharmony_ci					IEC958_AES4_CON_MAX_WORDLEN_24 |
11378c2ecf20Sopenharmony_ci					IEC958_AES4_CON_WORDLEN_24_20;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci		player->num_ctrls = ARRAY_SIZE(snd_sti_iec_ctl);
11408c2ecf20Sopenharmony_ci		player->snd_ctrls = snd_sti_iec_ctl[0];
11418c2ecf20Sopenharmony_ci	} else {
11428c2ecf20Sopenharmony_ci		player->num_ctrls = ARRAY_SIZE(snd_sti_pcm_ctl);
11438c2ecf20Sopenharmony_ci		player->snd_ctrls = snd_sti_pcm_ctl[0];
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	return 0;
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(uni_player_init);
1149