xref: /kernel/linux/linux-5.10/sound/soc/samsung/i2s.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// ALSA SoC Audio Layer - Samsung I2S Controller driver
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2010 Samsung Electronics Co. Ltd.
68c2ecf20Sopenharmony_ci//	Jaswinder Singh <jassisinghbrar@gmail.com>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <dt-bindings/sound/samsung-i2s.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <sound/soc.h>
218c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-s3c.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "dma.h"
268c2ecf20Sopenharmony_ci#include "idma.h"
278c2ecf20Sopenharmony_ci#include "i2s.h"
288c2ecf20Sopenharmony_ci#include "i2s-regs.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define SAMSUNG_I2S_ID_PRIMARY		1
338c2ecf20Sopenharmony_ci#define SAMSUNG_I2S_ID_SECONDARY	2
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct samsung_i2s_variant_regs {
368c2ecf20Sopenharmony_ci	unsigned int	bfs_off;
378c2ecf20Sopenharmony_ci	unsigned int	rfs_off;
388c2ecf20Sopenharmony_ci	unsigned int	sdf_off;
398c2ecf20Sopenharmony_ci	unsigned int	txr_off;
408c2ecf20Sopenharmony_ci	unsigned int	rclksrc_off;
418c2ecf20Sopenharmony_ci	unsigned int	mss_off;
428c2ecf20Sopenharmony_ci	unsigned int	cdclkcon_off;
438c2ecf20Sopenharmony_ci	unsigned int	lrp_off;
448c2ecf20Sopenharmony_ci	unsigned int	bfs_mask;
458c2ecf20Sopenharmony_ci	unsigned int	rfs_mask;
468c2ecf20Sopenharmony_ci	unsigned int	ftx0cnt_off;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct samsung_i2s_dai_data {
508c2ecf20Sopenharmony_ci	u32 quirks;
518c2ecf20Sopenharmony_ci	unsigned int pcm_rates;
528c2ecf20Sopenharmony_ci	const struct samsung_i2s_variant_regs *i2s_variant_regs;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct i2s_dai {
568c2ecf20Sopenharmony_ci	/* Platform device for this DAI */
578c2ecf20Sopenharmony_ci	struct platform_device *pdev;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* Frame clock */
608c2ecf20Sopenharmony_ci	unsigned frmclk;
618c2ecf20Sopenharmony_ci	/*
628c2ecf20Sopenharmony_ci	 * Specifically requested RCLK, BCLK by machine driver.
638c2ecf20Sopenharmony_ci	 * 0 indicates CPU driver is free to choose any value.
648c2ecf20Sopenharmony_ci	 */
658c2ecf20Sopenharmony_ci	unsigned rfs, bfs;
668c2ecf20Sopenharmony_ci	/* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
678c2ecf20Sopenharmony_ci	struct i2s_dai *pri_dai;
688c2ecf20Sopenharmony_ci	/* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
698c2ecf20Sopenharmony_ci	struct i2s_dai *sec_dai;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define DAI_OPENED	(1 << 0) /* DAI is opened */
728c2ecf20Sopenharmony_ci#define DAI_MANAGER	(1 << 1) /* DAI is the manager */
738c2ecf20Sopenharmony_ci	unsigned mode;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Driver for this DAI */
768c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *drv;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* DMA parameters */
798c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_playback;
808c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_capture;
818c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data idma_playback;
828c2ecf20Sopenharmony_ci	dma_filter_fn filter;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistruct samsung_i2s_priv {
888c2ecf20Sopenharmony_ci	struct platform_device *pdev;
898c2ecf20Sopenharmony_ci	struct platform_device *pdev_sec;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* Lock for cross interface checks */
928c2ecf20Sopenharmony_ci	spinlock_t pcm_lock;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* CPU DAIs and their corresponding drivers */
958c2ecf20Sopenharmony_ci	struct i2s_dai *dai;
968c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *dai_drv;
978c2ecf20Sopenharmony_ci	int num_dais;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* The I2S controller's core clock */
1008c2ecf20Sopenharmony_ci	struct clk *clk;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Clock for generating I2S signals */
1038c2ecf20Sopenharmony_ci	struct clk *op_clk;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Rate of RCLK source clock */
1068c2ecf20Sopenharmony_ci	unsigned long rclk_srcrate;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* Cache of selected I2S registers for system suspend */
1098c2ecf20Sopenharmony_ci	u32 suspend_i2smod;
1108c2ecf20Sopenharmony_ci	u32 suspend_i2scon;
1118c2ecf20Sopenharmony_ci	u32 suspend_i2spsr;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	const struct samsung_i2s_variant_regs *variant_regs;
1148c2ecf20Sopenharmony_ci	u32 quirks;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* The clock provider's data */
1178c2ecf20Sopenharmony_ci	struct clk *clk_table[3];
1188c2ecf20Sopenharmony_ci	struct clk_onecell_data clk_data;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Spinlock protecting member fields below */
1218c2ecf20Sopenharmony_ci	spinlock_t lock;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Memory mapped SFR region */
1248c2ecf20Sopenharmony_ci	void __iomem *addr;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* A flag indicating the I2S slave mode operation */
1278c2ecf20Sopenharmony_ci	bool slave_mode;
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* Returns true if this is the 'overlay' stereo DAI */
1318c2ecf20Sopenharmony_cistatic inline bool is_secondary(struct i2s_dai *i2s)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* If this interface of the controller is transmitting data */
1378c2ecf20Sopenharmony_cistatic inline bool tx_active(struct i2s_dai *i2s)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u32 active;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (!i2s)
1428c2ecf20Sopenharmony_ci		return false;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	active = readl(i2s->priv->addr + I2SCON);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (is_secondary(i2s))
1478c2ecf20Sopenharmony_ci		active &= CON_TXSDMA_ACTIVE;
1488c2ecf20Sopenharmony_ci	else
1498c2ecf20Sopenharmony_ci		active &= CON_TXDMA_ACTIVE;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return active ? true : false;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* Return pointer to the other DAI */
1558c2ecf20Sopenharmony_cistatic inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	return i2s->pri_dai ? : i2s->sec_dai;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* If the other interface of the controller is transmitting data */
1618c2ecf20Sopenharmony_cistatic inline bool other_tx_active(struct i2s_dai *i2s)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return tx_active(other);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/* If any interface of the controller is transmitting data */
1698c2ecf20Sopenharmony_cistatic inline bool any_tx_active(struct i2s_dai *i2s)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	return tx_active(i2s) || other_tx_active(i2s);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* If this interface of the controller is receiving data */
1758c2ecf20Sopenharmony_cistatic inline bool rx_active(struct i2s_dai *i2s)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	u32 active;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (!i2s)
1808c2ecf20Sopenharmony_ci		return false;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return active ? true : false;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/* If the other interface of the controller is receiving data */
1888c2ecf20Sopenharmony_cistatic inline bool other_rx_active(struct i2s_dai *i2s)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return rx_active(other);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* If any interface of the controller is receiving data */
1968c2ecf20Sopenharmony_cistatic inline bool any_rx_active(struct i2s_dai *i2s)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	return rx_active(i2s) || other_rx_active(i2s);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* If the other DAI is transmitting or receiving data */
2028c2ecf20Sopenharmony_cistatic inline bool other_active(struct i2s_dai *i2s)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	return other_rx_active(i2s) || other_tx_active(i2s);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* If this DAI is transmitting or receiving data */
2088c2ecf20Sopenharmony_cistatic inline bool this_active(struct i2s_dai *i2s)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	return tx_active(i2s) || rx_active(i2s);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/* If the controller is active anyway */
2148c2ecf20Sopenharmony_cistatic inline bool any_active(struct i2s_dai *i2s)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return this_active(i2s) || other_active(i2s);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return &priv->dai[dai->id - 1];
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic inline bool is_opened(struct i2s_dai *i2s)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	if (i2s && (i2s->mode & DAI_OPENED))
2298c2ecf20Sopenharmony_ci		return true;
2308c2ecf20Sopenharmony_ci	else
2318c2ecf20Sopenharmony_ci		return false;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic inline bool is_manager(struct i2s_dai *i2s)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	if (is_opened(i2s) && (i2s->mode & DAI_MANAGER))
2378c2ecf20Sopenharmony_ci		return true;
2388c2ecf20Sopenharmony_ci	else
2398c2ecf20Sopenharmony_ci		return false;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* Read RCLK of I2S (in multiples of LRCLK) */
2438c2ecf20Sopenharmony_cistatic inline unsigned get_rfs(struct i2s_dai *i2s)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
2468c2ecf20Sopenharmony_ci	u32 rfs;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off;
2498c2ecf20Sopenharmony_ci	rfs &= priv->variant_regs->rfs_mask;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	switch (rfs) {
2528c2ecf20Sopenharmony_ci	case 7: return 192;
2538c2ecf20Sopenharmony_ci	case 6: return 96;
2548c2ecf20Sopenharmony_ci	case 5: return 128;
2558c2ecf20Sopenharmony_ci	case 4: return 64;
2568c2ecf20Sopenharmony_ci	case 3:	return 768;
2578c2ecf20Sopenharmony_ci	case 2: return 384;
2588c2ecf20Sopenharmony_ci	case 1:	return 512;
2598c2ecf20Sopenharmony_ci	default: return 256;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/* Write RCLK of I2S (in multiples of LRCLK) */
2648c2ecf20Sopenharmony_cistatic inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
2678c2ecf20Sopenharmony_ci	u32 mod = readl(priv->addr + I2SMOD);
2688c2ecf20Sopenharmony_ci	int rfs_shift = priv->variant_regs->rfs_off;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	mod &= ~(priv->variant_regs->rfs_mask << rfs_shift);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	switch (rfs) {
2738c2ecf20Sopenharmony_ci	case 192:
2748c2ecf20Sopenharmony_ci		mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift);
2758c2ecf20Sopenharmony_ci		break;
2768c2ecf20Sopenharmony_ci	case 96:
2778c2ecf20Sopenharmony_ci		mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift);
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	case 128:
2808c2ecf20Sopenharmony_ci		mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift);
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	case 64:
2838c2ecf20Sopenharmony_ci		mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift);
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	case 768:
2868c2ecf20Sopenharmony_ci		mod |= (MOD_RCLK_768FS << rfs_shift);
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	case 512:
2898c2ecf20Sopenharmony_ci		mod |= (MOD_RCLK_512FS << rfs_shift);
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci	case 384:
2928c2ecf20Sopenharmony_ci		mod |= (MOD_RCLK_384FS << rfs_shift);
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	default:
2958c2ecf20Sopenharmony_ci		mod |= (MOD_RCLK_256FS << rfs_shift);
2968c2ecf20Sopenharmony_ci		break;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	writel(mod, priv->addr + I2SMOD);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* Read bit-clock of I2S (in multiples of LRCLK) */
3038c2ecf20Sopenharmony_cistatic inline unsigned get_bfs(struct i2s_dai *i2s)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
3068c2ecf20Sopenharmony_ci	u32 bfs;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off;
3098c2ecf20Sopenharmony_ci	bfs &= priv->variant_regs->bfs_mask;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	switch (bfs) {
3128c2ecf20Sopenharmony_ci	case 8: return 256;
3138c2ecf20Sopenharmony_ci	case 7: return 192;
3148c2ecf20Sopenharmony_ci	case 6: return 128;
3158c2ecf20Sopenharmony_ci	case 5: return 96;
3168c2ecf20Sopenharmony_ci	case 4: return 64;
3178c2ecf20Sopenharmony_ci	case 3: return 24;
3188c2ecf20Sopenharmony_ci	case 2: return 16;
3198c2ecf20Sopenharmony_ci	case 1:	return 48;
3208c2ecf20Sopenharmony_ci	default: return 32;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/* Write bit-clock of I2S (in multiples of LRCLK) */
3258c2ecf20Sopenharmony_cistatic inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
3288c2ecf20Sopenharmony_ci	u32 mod = readl(priv->addr + I2SMOD);
3298c2ecf20Sopenharmony_ci	int tdm = priv->quirks & QUIRK_SUPPORTS_TDM;
3308c2ecf20Sopenharmony_ci	int bfs_shift = priv->variant_regs->bfs_off;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
3338c2ecf20Sopenharmony_ci	if (!tdm && bfs > 48) {
3348c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n");
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	mod &= ~(priv->variant_regs->bfs_mask << bfs_shift);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	switch (bfs) {
3418c2ecf20Sopenharmony_ci	case 48:
3428c2ecf20Sopenharmony_ci		mod |= (MOD_BCLK_48FS << bfs_shift);
3438c2ecf20Sopenharmony_ci		break;
3448c2ecf20Sopenharmony_ci	case 32:
3458c2ecf20Sopenharmony_ci		mod |= (MOD_BCLK_32FS << bfs_shift);
3468c2ecf20Sopenharmony_ci		break;
3478c2ecf20Sopenharmony_ci	case 24:
3488c2ecf20Sopenharmony_ci		mod |= (MOD_BCLK_24FS << bfs_shift);
3498c2ecf20Sopenharmony_ci		break;
3508c2ecf20Sopenharmony_ci	case 16:
3518c2ecf20Sopenharmony_ci		mod |= (MOD_BCLK_16FS << bfs_shift);
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci	case 64:
3548c2ecf20Sopenharmony_ci		mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift);
3558c2ecf20Sopenharmony_ci		break;
3568c2ecf20Sopenharmony_ci	case 96:
3578c2ecf20Sopenharmony_ci		mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift);
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci	case 128:
3608c2ecf20Sopenharmony_ci		mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift);
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	case 192:
3638c2ecf20Sopenharmony_ci		mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift);
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case 256:
3668c2ecf20Sopenharmony_ci		mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift);
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	default:
3698c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	writel(mod, priv->addr + I2SMOD);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/* Sample size */
3778c2ecf20Sopenharmony_cistatic inline int get_blc(struct i2s_dai *i2s)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	int blc = readl(i2s->priv->addr + I2SMOD);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	blc = (blc >> 13) & 0x3;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	switch (blc) {
3848c2ecf20Sopenharmony_ci	case 2: return 24;
3858c2ecf20Sopenharmony_ci	case 1:	return 8;
3868c2ecf20Sopenharmony_ci	default: return 16;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/* TX channel control */
3918c2ecf20Sopenharmony_cistatic void i2s_txctrl(struct i2s_dai *i2s, int on)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
3948c2ecf20Sopenharmony_ci	void __iomem *addr = priv->addr;
3958c2ecf20Sopenharmony_ci	int txr_off = priv->variant_regs->txr_off;
3968c2ecf20Sopenharmony_ci	u32 con = readl(addr + I2SCON);
3978c2ecf20Sopenharmony_ci	u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (on) {
4008c2ecf20Sopenharmony_ci		con |= CON_ACTIVE;
4018c2ecf20Sopenharmony_ci		con &= ~CON_TXCH_PAUSE;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		if (is_secondary(i2s)) {
4048c2ecf20Sopenharmony_ci			con |= CON_TXSDMA_ACTIVE;
4058c2ecf20Sopenharmony_ci			con &= ~CON_TXSDMA_PAUSE;
4068c2ecf20Sopenharmony_ci		} else {
4078c2ecf20Sopenharmony_ci			con |= CON_TXDMA_ACTIVE;
4088c2ecf20Sopenharmony_ci			con &= ~CON_TXDMA_PAUSE;
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		if (any_rx_active(i2s))
4128c2ecf20Sopenharmony_ci			mod |= 2 << txr_off;
4138c2ecf20Sopenharmony_ci		else
4148c2ecf20Sopenharmony_ci			mod |= 0 << txr_off;
4158c2ecf20Sopenharmony_ci	} else {
4168c2ecf20Sopenharmony_ci		if (is_secondary(i2s)) {
4178c2ecf20Sopenharmony_ci			con |=  CON_TXSDMA_PAUSE;
4188c2ecf20Sopenharmony_ci			con &= ~CON_TXSDMA_ACTIVE;
4198c2ecf20Sopenharmony_ci		} else {
4208c2ecf20Sopenharmony_ci			con |=  CON_TXDMA_PAUSE;
4218c2ecf20Sopenharmony_ci			con &= ~CON_TXDMA_ACTIVE;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (other_tx_active(i2s)) {
4258c2ecf20Sopenharmony_ci			writel(con, addr + I2SCON);
4268c2ecf20Sopenharmony_ci			return;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		con |=  CON_TXCH_PAUSE;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		if (any_rx_active(i2s))
4328c2ecf20Sopenharmony_ci			mod |= 1 << txr_off;
4338c2ecf20Sopenharmony_ci		else
4348c2ecf20Sopenharmony_ci			con &= ~CON_ACTIVE;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	writel(mod, addr + I2SMOD);
4388c2ecf20Sopenharmony_ci	writel(con, addr + I2SCON);
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/* RX Channel Control */
4428c2ecf20Sopenharmony_cistatic void i2s_rxctrl(struct i2s_dai *i2s, int on)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
4458c2ecf20Sopenharmony_ci	void __iomem *addr = priv->addr;
4468c2ecf20Sopenharmony_ci	int txr_off = priv->variant_regs->txr_off;
4478c2ecf20Sopenharmony_ci	u32 con = readl(addr + I2SCON);
4488c2ecf20Sopenharmony_ci	u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (on) {
4518c2ecf20Sopenharmony_ci		con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
4528c2ecf20Sopenharmony_ci		con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		if (any_tx_active(i2s))
4558c2ecf20Sopenharmony_ci			mod |= 2 << txr_off;
4568c2ecf20Sopenharmony_ci		else
4578c2ecf20Sopenharmony_ci			mod |= 1 << txr_off;
4588c2ecf20Sopenharmony_ci	} else {
4598c2ecf20Sopenharmony_ci		con |=  CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
4608c2ecf20Sopenharmony_ci		con &= ~CON_RXDMA_ACTIVE;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		if (any_tx_active(i2s))
4638c2ecf20Sopenharmony_ci			mod |= 0 << txr_off;
4648c2ecf20Sopenharmony_ci		else
4658c2ecf20Sopenharmony_ci			con &= ~CON_ACTIVE;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	writel(mod, addr + I2SMOD);
4698c2ecf20Sopenharmony_ci	writel(con, addr + I2SCON);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci/* Flush FIFO of an interface */
4738c2ecf20Sopenharmony_cistatic inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	void __iomem *fic;
4768c2ecf20Sopenharmony_ci	u32 val;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (!i2s)
4798c2ecf20Sopenharmony_ci		return;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (is_secondary(i2s))
4828c2ecf20Sopenharmony_ci		fic = i2s->priv->addr + I2SFICS;
4838c2ecf20Sopenharmony_ci	else
4848c2ecf20Sopenharmony_ci		fic = i2s->priv->addr + I2SFIC;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Flush the FIFO */
4878c2ecf20Sopenharmony_ci	writel(readl(fic) | flush, fic);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Be patient */
4908c2ecf20Sopenharmony_ci	val = msecs_to_loops(1) / 1000; /* 1 usec */
4918c2ecf20Sopenharmony_ci	while (--val)
4928c2ecf20Sopenharmony_ci		cpu_relax();
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	writel(readl(fic) & ~flush, fic);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs,
4988c2ecf20Sopenharmony_ci			  int dir)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
5018c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
5028c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
5038c2ecf20Sopenharmony_ci	const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs;
5048c2ecf20Sopenharmony_ci	unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
5058c2ecf20Sopenharmony_ci	unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
5068c2ecf20Sopenharmony_ci	u32 mod, mask, val = 0;
5078c2ecf20Sopenharmony_ci	unsigned long flags;
5088c2ecf20Sopenharmony_ci	int ret = 0;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dai->dev);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
5138c2ecf20Sopenharmony_ci	mod = readl(priv->addr + I2SMOD);
5148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	switch (clk_id) {
5178c2ecf20Sopenharmony_ci	case SAMSUNG_I2S_OPCLK:
5188c2ecf20Sopenharmony_ci		mask = MOD_OPCLK_MASK;
5198c2ecf20Sopenharmony_ci		val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK;
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case SAMSUNG_I2S_CDCLK:
5228c2ecf20Sopenharmony_ci		mask = 1 << i2s_regs->cdclkcon_off;
5238c2ecf20Sopenharmony_ci		/* Shouldn't matter in GATING(CLOCK_IN) mode */
5248c2ecf20Sopenharmony_ci		if (dir == SND_SOC_CLOCK_IN)
5258c2ecf20Sopenharmony_ci			rfs = 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		if ((rfs && other && other->rfs && (other->rfs != rfs)) ||
5288c2ecf20Sopenharmony_ci				(any_active(i2s) &&
5298c2ecf20Sopenharmony_ci				(((dir == SND_SOC_CLOCK_IN)
5308c2ecf20Sopenharmony_ci					&& !(mod & cdcon_mask)) ||
5318c2ecf20Sopenharmony_ci				((dir == SND_SOC_CLOCK_OUT)
5328c2ecf20Sopenharmony_ci					&& (mod & cdcon_mask))))) {
5338c2ecf20Sopenharmony_ci			dev_err(&i2s->pdev->dev,
5348c2ecf20Sopenharmony_ci				"%s:%d Other DAI busy\n", __func__, __LINE__);
5358c2ecf20Sopenharmony_ci			ret = -EAGAIN;
5368c2ecf20Sopenharmony_ci			goto err;
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		if (dir == SND_SOC_CLOCK_IN)
5408c2ecf20Sopenharmony_ci			val = 1 << i2s_regs->cdclkcon_off;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		i2s->rfs = rfs;
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */
5468c2ecf20Sopenharmony_ci	case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
5478c2ecf20Sopenharmony_ci		mask = 1 << i2s_regs->rclksrc_off;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		if ((priv->quirks & QUIRK_NO_MUXPSR)
5508c2ecf20Sopenharmony_ci				|| (clk_id == SAMSUNG_I2S_RCLKSRC_0))
5518c2ecf20Sopenharmony_ci			clk_id = 0;
5528c2ecf20Sopenharmony_ci		else
5538c2ecf20Sopenharmony_ci			clk_id = 1;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		if (!any_active(i2s)) {
5568c2ecf20Sopenharmony_ci			if (priv->op_clk && !IS_ERR(priv->op_clk)) {
5578c2ecf20Sopenharmony_ci				if ((clk_id && !(mod & rsrc_mask)) ||
5588c2ecf20Sopenharmony_ci					(!clk_id && (mod & rsrc_mask))) {
5598c2ecf20Sopenharmony_ci					clk_disable_unprepare(priv->op_clk);
5608c2ecf20Sopenharmony_ci					clk_put(priv->op_clk);
5618c2ecf20Sopenharmony_ci				} else {
5628c2ecf20Sopenharmony_ci					priv->rclk_srcrate =
5638c2ecf20Sopenharmony_ci						clk_get_rate(priv->op_clk);
5648c2ecf20Sopenharmony_ci					goto done;
5658c2ecf20Sopenharmony_ci				}
5668c2ecf20Sopenharmony_ci			}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci			if (clk_id)
5698c2ecf20Sopenharmony_ci				priv->op_clk = clk_get(&i2s->pdev->dev,
5708c2ecf20Sopenharmony_ci						"i2s_opclk1");
5718c2ecf20Sopenharmony_ci			else
5728c2ecf20Sopenharmony_ci				priv->op_clk = clk_get(&i2s->pdev->dev,
5738c2ecf20Sopenharmony_ci						"i2s_opclk0");
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci			if (WARN_ON(IS_ERR(priv->op_clk))) {
5768c2ecf20Sopenharmony_ci				ret = PTR_ERR(priv->op_clk);
5778c2ecf20Sopenharmony_ci				priv->op_clk = NULL;
5788c2ecf20Sopenharmony_ci				goto err;
5798c2ecf20Sopenharmony_ci			}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(priv->op_clk);
5828c2ecf20Sopenharmony_ci			if (ret) {
5838c2ecf20Sopenharmony_ci				clk_put(priv->op_clk);
5848c2ecf20Sopenharmony_ci				priv->op_clk = NULL;
5858c2ecf20Sopenharmony_ci				goto err;
5868c2ecf20Sopenharmony_ci			}
5878c2ecf20Sopenharmony_ci			priv->rclk_srcrate = clk_get_rate(priv->op_clk);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		} else if ((!clk_id && (mod & rsrc_mask))
5908c2ecf20Sopenharmony_ci				|| (clk_id && !(mod & rsrc_mask))) {
5918c2ecf20Sopenharmony_ci			dev_err(&i2s->pdev->dev,
5928c2ecf20Sopenharmony_ci				"%s:%d Other DAI busy\n", __func__, __LINE__);
5938c2ecf20Sopenharmony_ci			ret = -EAGAIN;
5948c2ecf20Sopenharmony_ci			goto err;
5958c2ecf20Sopenharmony_ci		} else {
5968c2ecf20Sopenharmony_ci			/* Call can't be on the active DAI */
5978c2ecf20Sopenharmony_ci			goto done;
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		if (clk_id == 1)
6018c2ecf20Sopenharmony_ci			val = 1 << i2s_regs->rclksrc_off;
6028c2ecf20Sopenharmony_ci		break;
6038c2ecf20Sopenharmony_ci	default:
6048c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "We don't serve that!\n");
6058c2ecf20Sopenharmony_ci		ret = -EINVAL;
6068c2ecf20Sopenharmony_ci		goto err;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6108c2ecf20Sopenharmony_ci	mod = readl(priv->addr + I2SMOD);
6118c2ecf20Sopenharmony_ci	mod = (mod & ~mask) | val;
6128c2ecf20Sopenharmony_ci	writel(mod, priv->addr + I2SMOD);
6138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6148c2ecf20Sopenharmony_cidone:
6158c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return 0;
6188c2ecf20Sopenharmony_cierr:
6198c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
6208c2ecf20Sopenharmony_ci	return ret;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
6268c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
6278c2ecf20Sopenharmony_ci	int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
6288c2ecf20Sopenharmony_ci	u32 mod, tmp = 0;
6298c2ecf20Sopenharmony_ci	unsigned long flags;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	lrp_shift = priv->variant_regs->lrp_off;
6328c2ecf20Sopenharmony_ci	sdf_shift = priv->variant_regs->sdf_off;
6338c2ecf20Sopenharmony_ci	mod_slave = 1 << priv->variant_regs->mss_off;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	sdf_mask = MOD_SDF_MASK << sdf_shift;
6368c2ecf20Sopenharmony_ci	lrp_rlow = MOD_LR_RLOW << lrp_shift;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/* Format is priority */
6398c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
6408c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
6418c2ecf20Sopenharmony_ci		tmp |= lrp_rlow;
6428c2ecf20Sopenharmony_ci		tmp |= (MOD_SDF_MSB << sdf_shift);
6438c2ecf20Sopenharmony_ci		break;
6448c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
6458c2ecf20Sopenharmony_ci		tmp |= lrp_rlow;
6468c2ecf20Sopenharmony_ci		tmp |= (MOD_SDF_LSB << sdf_shift);
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
6498c2ecf20Sopenharmony_ci		tmp |= (MOD_SDF_IIS << sdf_shift);
6508c2ecf20Sopenharmony_ci		break;
6518c2ecf20Sopenharmony_ci	default:
6528c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "Format not supported\n");
6538c2ecf20Sopenharmony_ci		return -EINVAL;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/*
6578c2ecf20Sopenharmony_ci	 * INV flag is relative to the FORMAT flag - if set it simply
6588c2ecf20Sopenharmony_ci	 * flips the polarity specified by the Standard
6598c2ecf20Sopenharmony_ci	 */
6608c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
6618c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
6648c2ecf20Sopenharmony_ci		if (tmp & lrp_rlow)
6658c2ecf20Sopenharmony_ci			tmp &= ~lrp_rlow;
6668c2ecf20Sopenharmony_ci		else
6678c2ecf20Sopenharmony_ci			tmp |= lrp_rlow;
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	default:
6708c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "Polarity not supported\n");
6718c2ecf20Sopenharmony_ci		return -EINVAL;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
6758c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBM_CFM:
6768c2ecf20Sopenharmony_ci		tmp |= mod_slave;
6778c2ecf20Sopenharmony_ci		break;
6788c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBS_CFS:
6798c2ecf20Sopenharmony_ci		/*
6808c2ecf20Sopenharmony_ci		 * Set default source clock in Master mode, only when the
6818c2ecf20Sopenharmony_ci		 * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
6828c2ecf20Sopenharmony_ci		 * clock configuration assigned in DT is not overwritten.
6838c2ecf20Sopenharmony_ci		 */
6848c2ecf20Sopenharmony_ci		if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL)
6858c2ecf20Sopenharmony_ci			i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
6868c2ecf20Sopenharmony_ci							0, SND_SOC_CLOCK_IN);
6878c2ecf20Sopenharmony_ci		break;
6888c2ecf20Sopenharmony_ci	default:
6898c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "master/slave format not supported\n");
6908c2ecf20Sopenharmony_ci		return -EINVAL;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dai->dev);
6948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6958c2ecf20Sopenharmony_ci	mod = readl(priv->addr + I2SMOD);
6968c2ecf20Sopenharmony_ci	/*
6978c2ecf20Sopenharmony_ci	 * Don't change the I2S mode if any controller is active on this
6988c2ecf20Sopenharmony_ci	 * channel.
6998c2ecf20Sopenharmony_ci	 */
7008c2ecf20Sopenharmony_ci	if (any_active(i2s) &&
7018c2ecf20Sopenharmony_ci		((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
7028c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
7038c2ecf20Sopenharmony_ci		pm_runtime_put(dai->dev);
7048c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev,
7058c2ecf20Sopenharmony_ci				"%s:%d Other DAI busy\n", __func__, __LINE__);
7068c2ecf20Sopenharmony_ci		return -EAGAIN;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	mod &= ~(sdf_mask | lrp_rlow | mod_slave);
7108c2ecf20Sopenharmony_ci	mod |= tmp;
7118c2ecf20Sopenharmony_ci	writel(mod, priv->addr + I2SMOD);
7128c2ecf20Sopenharmony_ci	priv->slave_mode = (mod & mod_slave);
7138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7148c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	return 0;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic int i2s_hw_params(struct snd_pcm_substream *substream,
7208c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
7238c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
7248c2ecf20Sopenharmony_ci	u32 mod, mask = 0, val = 0;
7258c2ecf20Sopenharmony_ci	struct clk *rclksrc;
7268c2ecf20Sopenharmony_ci	unsigned long flags;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	WARN_ON(!pm_runtime_active(dai->dev));
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (!is_secondary(i2s))
7318c2ecf20Sopenharmony_ci		mask |= (MOD_DC2_EN | MOD_DC1_EN);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	switch (params_channels(params)) {
7348c2ecf20Sopenharmony_ci	case 6:
7358c2ecf20Sopenharmony_ci		val |= MOD_DC2_EN;
7368c2ecf20Sopenharmony_ci		fallthrough;
7378c2ecf20Sopenharmony_ci	case 4:
7388c2ecf20Sopenharmony_ci		val |= MOD_DC1_EN;
7398c2ecf20Sopenharmony_ci		break;
7408c2ecf20Sopenharmony_ci	case 2:
7418c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
7428c2ecf20Sopenharmony_ci			i2s->dma_playback.addr_width = 4;
7438c2ecf20Sopenharmony_ci		else
7448c2ecf20Sopenharmony_ci			i2s->dma_capture.addr_width = 4;
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	case 1:
7478c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
7488c2ecf20Sopenharmony_ci			i2s->dma_playback.addr_width = 2;
7498c2ecf20Sopenharmony_ci		else
7508c2ecf20Sopenharmony_ci			i2s->dma_capture.addr_width = 2;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci		break;
7538c2ecf20Sopenharmony_ci	default:
7548c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "%d channels not supported\n",
7558c2ecf20Sopenharmony_ci				params_channels(params));
7568c2ecf20Sopenharmony_ci		return -EINVAL;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (is_secondary(i2s))
7608c2ecf20Sopenharmony_ci		mask |= MOD_BLCS_MASK;
7618c2ecf20Sopenharmony_ci	else
7628c2ecf20Sopenharmony_ci		mask |= MOD_BLCP_MASK;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (is_manager(i2s))
7658c2ecf20Sopenharmony_ci		mask |= MOD_BLC_MASK;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	switch (params_width(params)) {
7688c2ecf20Sopenharmony_ci	case 8:
7698c2ecf20Sopenharmony_ci		if (is_secondary(i2s))
7708c2ecf20Sopenharmony_ci			val |= MOD_BLCS_8BIT;
7718c2ecf20Sopenharmony_ci		else
7728c2ecf20Sopenharmony_ci			val |= MOD_BLCP_8BIT;
7738c2ecf20Sopenharmony_ci		if (is_manager(i2s))
7748c2ecf20Sopenharmony_ci			val |= MOD_BLC_8BIT;
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	case 16:
7778c2ecf20Sopenharmony_ci		if (is_secondary(i2s))
7788c2ecf20Sopenharmony_ci			val |= MOD_BLCS_16BIT;
7798c2ecf20Sopenharmony_ci		else
7808c2ecf20Sopenharmony_ci			val |= MOD_BLCP_16BIT;
7818c2ecf20Sopenharmony_ci		if (is_manager(i2s))
7828c2ecf20Sopenharmony_ci			val |= MOD_BLC_16BIT;
7838c2ecf20Sopenharmony_ci		break;
7848c2ecf20Sopenharmony_ci	case 24:
7858c2ecf20Sopenharmony_ci		if (is_secondary(i2s))
7868c2ecf20Sopenharmony_ci			val |= MOD_BLCS_24BIT;
7878c2ecf20Sopenharmony_ci		else
7888c2ecf20Sopenharmony_ci			val |= MOD_BLCP_24BIT;
7898c2ecf20Sopenharmony_ci		if (is_manager(i2s))
7908c2ecf20Sopenharmony_ci			val |= MOD_BLC_24BIT;
7918c2ecf20Sopenharmony_ci		break;
7928c2ecf20Sopenharmony_ci	default:
7938c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev, "Format(%d) not supported\n",
7948c2ecf20Sopenharmony_ci				params_format(params));
7958c2ecf20Sopenharmony_ci		return -EINVAL;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7998c2ecf20Sopenharmony_ci	mod = readl(priv->addr + I2SMOD);
8008c2ecf20Sopenharmony_ci	mod = (mod & ~mask) | val;
8018c2ecf20Sopenharmony_ci	writel(mod, priv->addr + I2SMOD);
8028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	i2s->frmclk = params_rate(params);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC];
8098c2ecf20Sopenharmony_ci	if (rclksrc && !IS_ERR(rclksrc))
8108c2ecf20Sopenharmony_ci		priv->rclk_srcrate = clk_get_rate(rclksrc);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/* We set constraints on the substream according to the version of I2S */
8168c2ecf20Sopenharmony_cistatic int i2s_startup(struct snd_pcm_substream *substream,
8178c2ecf20Sopenharmony_ci	  struct snd_soc_dai *dai)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
8208c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
8218c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
8228c2ecf20Sopenharmony_ci	unsigned long flags;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dai->dev);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->pcm_lock, flags);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	i2s->mode |= DAI_OPENED;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (is_manager(other))
8318c2ecf20Sopenharmony_ci		i2s->mode &= ~DAI_MANAGER;
8328c2ecf20Sopenharmony_ci	else
8338c2ecf20Sopenharmony_ci		i2s->mode |= DAI_MANAGER;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR))
8368c2ecf20Sopenharmony_ci		writel(CON_RSTCLR, i2s->priv->addr + I2SCON);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->pcm_lock, flags);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic void i2s_shutdown(struct snd_pcm_substream *substream,
8448c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
8478c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
8488c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
8498c2ecf20Sopenharmony_ci	unsigned long flags;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->pcm_lock, flags);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	i2s->mode &= ~DAI_OPENED;
8548c2ecf20Sopenharmony_ci	i2s->mode &= ~DAI_MANAGER;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	if (is_opened(other))
8578c2ecf20Sopenharmony_ci		other->mode |= DAI_MANAGER;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Reset any constraint on RFS and BFS */
8608c2ecf20Sopenharmony_ci	i2s->rfs = 0;
8618c2ecf20Sopenharmony_ci	i2s->bfs = 0;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->pcm_lock, flags);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic int config_setup(struct i2s_dai *i2s)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = i2s->priv;
8718c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
8728c2ecf20Sopenharmony_ci	unsigned rfs, bfs, blc;
8738c2ecf20Sopenharmony_ci	u32 psr;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	blc = get_blc(i2s);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	bfs = i2s->bfs;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (!bfs && other)
8808c2ecf20Sopenharmony_ci		bfs = other->bfs;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* Select least possible multiple(2) if no constraint set */
8838c2ecf20Sopenharmony_ci	if (!bfs)
8848c2ecf20Sopenharmony_ci		bfs = blc * 2;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	rfs = i2s->rfs;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (!rfs && other)
8898c2ecf20Sopenharmony_ci		rfs = other->rfs;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if ((rfs == 256 || rfs == 512) && (blc == 24)) {
8928c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev,
8938c2ecf20Sopenharmony_ci			"%d-RFS not supported for 24-blc\n", rfs);
8948c2ecf20Sopenharmony_ci		return -EINVAL;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (!rfs) {
8988c2ecf20Sopenharmony_ci		if (bfs == 16 || bfs == 32)
8998c2ecf20Sopenharmony_ci			rfs = 256;
9008c2ecf20Sopenharmony_ci		else
9018c2ecf20Sopenharmony_ci			rfs = 384;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* If already setup and running */
9058c2ecf20Sopenharmony_ci	if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) {
9068c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev,
9078c2ecf20Sopenharmony_ci				"%s:%d Other DAI busy\n", __func__, __LINE__);
9088c2ecf20Sopenharmony_ci		return -EAGAIN;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	set_bfs(i2s, bfs);
9128c2ecf20Sopenharmony_ci	set_rfs(i2s, rfs);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/* Don't bother with PSR in Slave mode */
9158c2ecf20Sopenharmony_ci	if (priv->slave_mode)
9168c2ecf20Sopenharmony_ci		return 0;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
9198c2ecf20Sopenharmony_ci		psr = priv->rclk_srcrate / i2s->frmclk / rfs;
9208c2ecf20Sopenharmony_ci		writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR);
9218c2ecf20Sopenharmony_ci		dev_dbg(&i2s->pdev->dev,
9228c2ecf20Sopenharmony_ci			"RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",
9238c2ecf20Sopenharmony_ci				priv->rclk_srcrate, psr, rfs, bfs);
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return 0;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic int i2s_trigger(struct snd_pcm_substream *substream,
9308c2ecf20Sopenharmony_ci	int cmd, struct snd_soc_dai *dai)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
9338c2ecf20Sopenharmony_ci	int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
9348c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
9358c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0));
9368c2ecf20Sopenharmony_ci	unsigned long flags;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	switch (cmd) {
9398c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
9408c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
9418c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
9428c2ecf20Sopenharmony_ci		pm_runtime_get_sync(dai->dev);
9438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&priv->lock, flags);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		if (config_setup(i2s)) {
9468c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
9478c2ecf20Sopenharmony_ci			return -EINVAL;
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		if (capture)
9518c2ecf20Sopenharmony_ci			i2s_rxctrl(i2s, 1);
9528c2ecf20Sopenharmony_ci		else
9538c2ecf20Sopenharmony_ci			i2s_txctrl(i2s, 1);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
9568c2ecf20Sopenharmony_ci		break;
9578c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
9588c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
9598c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
9608c2ecf20Sopenharmony_ci		spin_lock_irqsave(&priv->lock, flags);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		if (capture) {
9638c2ecf20Sopenharmony_ci			i2s_rxctrl(i2s, 0);
9648c2ecf20Sopenharmony_ci			i2s_fifo(i2s, FIC_RXFLUSH);
9658c2ecf20Sopenharmony_ci		} else {
9668c2ecf20Sopenharmony_ci			i2s_txctrl(i2s, 0);
9678c2ecf20Sopenharmony_ci			i2s_fifo(i2s, FIC_TXFLUSH);
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
9718c2ecf20Sopenharmony_ci		pm_runtime_put(dai->dev);
9728c2ecf20Sopenharmony_ci		break;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	return 0;
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic int i2s_set_clkdiv(struct snd_soc_dai *dai,
9798c2ecf20Sopenharmony_ci	int div_id, int div)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
9828c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	switch (div_id) {
9858c2ecf20Sopenharmony_ci	case SAMSUNG_I2S_DIV_BCLK:
9868c2ecf20Sopenharmony_ci		pm_runtime_get_sync(dai->dev);
9878c2ecf20Sopenharmony_ci		if ((any_active(i2s) && div && (get_bfs(i2s) != div))
9888c2ecf20Sopenharmony_ci			|| (other && other->bfs && (other->bfs != div))) {
9898c2ecf20Sopenharmony_ci			pm_runtime_put(dai->dev);
9908c2ecf20Sopenharmony_ci			dev_err(&i2s->pdev->dev,
9918c2ecf20Sopenharmony_ci				"%s:%d Other DAI busy\n", __func__, __LINE__);
9928c2ecf20Sopenharmony_ci			return -EAGAIN;
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci		i2s->bfs = div;
9958c2ecf20Sopenharmony_ci		pm_runtime_put(dai->dev);
9968c2ecf20Sopenharmony_ci		break;
9978c2ecf20Sopenharmony_ci	default:
9988c2ecf20Sopenharmony_ci		dev_err(&i2s->pdev->dev,
9998c2ecf20Sopenharmony_ci			"Invalid clock divider(%d)\n", div_id);
10008c2ecf20Sopenharmony_ci		return -EINVAL;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	return 0;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t
10078c2ecf20Sopenharmony_cii2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
10108c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
10118c2ecf20Sopenharmony_ci	u32 reg = readl(priv->addr + I2SFIC);
10128c2ecf20Sopenharmony_ci	snd_pcm_sframes_t delay;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	WARN_ON(!pm_runtime_active(dai->dev));
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
10178c2ecf20Sopenharmony_ci		delay = FIC_RXCOUNT(reg);
10188c2ecf20Sopenharmony_ci	else if (is_secondary(i2s))
10198c2ecf20Sopenharmony_ci		delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS));
10208c2ecf20Sopenharmony_ci	else
10218c2ecf20Sopenharmony_ci		delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	return delay;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
10278c2ecf20Sopenharmony_cistatic int i2s_suspend(struct snd_soc_component *component)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	return pm_runtime_force_suspend(component->dev);
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic int i2s_resume(struct snd_soc_component *component)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	return pm_runtime_force_resume(component->dev);
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci#else
10378c2ecf20Sopenharmony_ci#define i2s_suspend NULL
10388c2ecf20Sopenharmony_ci#define i2s_resume  NULL
10398c2ecf20Sopenharmony_ci#endif
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
10448c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
10458c2ecf20Sopenharmony_ci	struct i2s_dai *other = get_other_dai(i2s);
10468c2ecf20Sopenharmony_ci	unsigned long flags;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dai->dev);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (is_secondary(i2s)) {
10518c2ecf20Sopenharmony_ci		/* If this is probe on the secondary DAI */
10528c2ecf20Sopenharmony_ci		snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL);
10538c2ecf20Sopenharmony_ci	} else {
10548c2ecf20Sopenharmony_ci		snd_soc_dai_init_dma_data(dai, &i2s->dma_playback,
10558c2ecf20Sopenharmony_ci					  &i2s->dma_capture);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci		if (priv->quirks & QUIRK_NEED_RSTCLR)
10588c2ecf20Sopenharmony_ci			writel(CON_RSTCLR, priv->addr + I2SCON);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci		if (priv->quirks & QUIRK_SUPPORTS_IDMA)
10618c2ecf20Sopenharmony_ci			idma_reg_addr_init(priv->addr,
10628c2ecf20Sopenharmony_ci					   other->idma_playback.addr);
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	/* Reset any constraint on RFS and BFS */
10668c2ecf20Sopenharmony_ci	i2s->rfs = 0;
10678c2ecf20Sopenharmony_ci	i2s->bfs = 0;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
10708c2ecf20Sopenharmony_ci	i2s_txctrl(i2s, 0);
10718c2ecf20Sopenharmony_ci	i2s_rxctrl(i2s, 0);
10728c2ecf20Sopenharmony_ci	i2s_fifo(i2s, FIC_TXFLUSH);
10738c2ecf20Sopenharmony_ci	i2s_fifo(other, FIC_TXFLUSH);
10748c2ecf20Sopenharmony_ci	i2s_fifo(i2s, FIC_RXFLUSH);
10758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/* Gate CDCLK by default */
10788c2ecf20Sopenharmony_ci	if (!is_opened(other))
10798c2ecf20Sopenharmony_ci		i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
10808c2ecf20Sopenharmony_ci				0, SND_SOC_CLOCK_IN);
10818c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return 0;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
10898c2ecf20Sopenharmony_ci	struct i2s_dai *i2s = to_info(dai);
10908c2ecf20Sopenharmony_ci	unsigned long flags;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dai->dev);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	if (!is_secondary(i2s)) {
10958c2ecf20Sopenharmony_ci		if (priv->quirks & QUIRK_NEED_RSTCLR) {
10968c2ecf20Sopenharmony_ci			spin_lock_irqsave(&priv->lock, flags);
10978c2ecf20Sopenharmony_ci			writel(0, priv->addr + I2SCON);
10988c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
10998c2ecf20Sopenharmony_ci		}
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	pm_runtime_put(dai->dev);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	return 0;
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
11088c2ecf20Sopenharmony_ci	.trigger = i2s_trigger,
11098c2ecf20Sopenharmony_ci	.hw_params = i2s_hw_params,
11108c2ecf20Sopenharmony_ci	.set_fmt = i2s_set_fmt,
11118c2ecf20Sopenharmony_ci	.set_clkdiv = i2s_set_clkdiv,
11128c2ecf20Sopenharmony_ci	.set_sysclk = i2s_set_sysclk,
11138c2ecf20Sopenharmony_ci	.startup = i2s_startup,
11148c2ecf20Sopenharmony_ci	.shutdown = i2s_shutdown,
11158c2ecf20Sopenharmony_ci	.delay = i2s_delay,
11168c2ecf20Sopenharmony_ci};
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget samsung_i2s_widgets[] = {
11198c2ecf20Sopenharmony_ci	/* Backend DAI  */
11208c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0),
11218c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0),
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* Playback Mixer */
11248c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
11258c2ecf20Sopenharmony_ci};
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = {
11288c2ecf20Sopenharmony_ci	{ "Playback Mixer", NULL, "Primary Playback" },
11298c2ecf20Sopenharmony_ci	{ "Playback Mixer", NULL, "Secondary Playback" },
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	{ "Mixer DAI TX", NULL, "Playback Mixer" },
11328c2ecf20Sopenharmony_ci	{ "Primary Capture", NULL, "Mixer DAI RX" },
11338c2ecf20Sopenharmony_ci};
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver samsung_i2s_component = {
11368c2ecf20Sopenharmony_ci	.name = "samsung-i2s",
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	.dapm_widgets = samsung_i2s_widgets,
11398c2ecf20Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets),
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	.dapm_routes = samsung_i2s_dapm_routes,
11428c2ecf20Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes),
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	.suspend = i2s_suspend,
11458c2ecf20Sopenharmony_ci	.resume = i2s_resume,
11468c2ecf20Sopenharmony_ci};
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
11498c2ecf20Sopenharmony_ci			  SNDRV_PCM_FMTBIT_S24_LE)
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_cistatic int i2s_alloc_dais(struct samsung_i2s_priv *priv,
11528c2ecf20Sopenharmony_ci			  const struct samsung_i2s_dai_data *i2s_dai_data,
11538c2ecf20Sopenharmony_ci			  int num_dais)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" };
11568c2ecf20Sopenharmony_ci	static const char *stream_names[] = { "Primary Playback",
11578c2ecf20Sopenharmony_ci					      "Secondary Playback" };
11588c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *dai_drv;
11598c2ecf20Sopenharmony_ci	struct i2s_dai *dai;
11608c2ecf20Sopenharmony_ci	int i;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais,
11638c2ecf20Sopenharmony_ci				     sizeof(*dai), GFP_KERNEL);
11648c2ecf20Sopenharmony_ci	if (!priv->dai)
11658c2ecf20Sopenharmony_ci		return -ENOMEM;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais,
11688c2ecf20Sopenharmony_ci				     sizeof(*dai_drv), GFP_KERNEL);
11698c2ecf20Sopenharmony_ci	if (!priv->dai_drv)
11708c2ecf20Sopenharmony_ci		return -ENOMEM;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	for (i = 0; i < num_dais; i++) {
11738c2ecf20Sopenharmony_ci		dai_drv = &priv->dai_drv[i];
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci		dai_drv->probe = samsung_i2s_dai_probe;
11768c2ecf20Sopenharmony_ci		dai_drv->remove = samsung_i2s_dai_remove;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		dai_drv->symmetric_rates = 1;
11798c2ecf20Sopenharmony_ci		dai_drv->ops = &samsung_i2s_dai_ops;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		dai_drv->playback.channels_min = 1;
11828c2ecf20Sopenharmony_ci		dai_drv->playback.channels_max = 2;
11838c2ecf20Sopenharmony_ci		dai_drv->playback.rates = i2s_dai_data->pcm_rates;
11848c2ecf20Sopenharmony_ci		dai_drv->playback.formats = SAMSUNG_I2S_FMTS;
11858c2ecf20Sopenharmony_ci		dai_drv->playback.stream_name = stream_names[i];
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		dai_drv->id = i + 1;
11888c2ecf20Sopenharmony_ci		dai_drv->name = dai_names[i];
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		priv->dai[i].drv = &priv->dai_drv[i];
11918c2ecf20Sopenharmony_ci		priv->dai[i].pdev = priv->pdev;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* Initialize capture only for the primary DAI */
11958c2ecf20Sopenharmony_ci	dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1];
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	dai_drv->capture.channels_min = 1;
11988c2ecf20Sopenharmony_ci	dai_drv->capture.channels_max = 2;
11998c2ecf20Sopenharmony_ci	dai_drv->capture.rates = i2s_dai_data->pcm_rates;
12008c2ecf20Sopenharmony_ci	dai_drv->capture.formats = SAMSUNG_I2S_FMTS;
12018c2ecf20Sopenharmony_ci	dai_drv->capture.stream_name = "Primary Capture";
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return 0;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
12078c2ecf20Sopenharmony_cistatic int i2s_runtime_suspend(struct device *dev)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	priv->suspend_i2smod = readl(priv->addr + I2SMOD);
12128c2ecf20Sopenharmony_ci	priv->suspend_i2scon = readl(priv->addr + I2SCON);
12138c2ecf20Sopenharmony_ci	priv->suspend_i2spsr = readl(priv->addr + I2SPSR);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	if (priv->op_clk)
12168c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->op_clk);
12178c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	return 0;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic int i2s_runtime_resume(struct device *dev)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
12258c2ecf20Sopenharmony_ci	int ret;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
12288c2ecf20Sopenharmony_ci	if (ret)
12298c2ecf20Sopenharmony_ci		return ret;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	if (priv->op_clk) {
12328c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(priv->op_clk);
12338c2ecf20Sopenharmony_ci		if (ret) {
12348c2ecf20Sopenharmony_ci			clk_disable_unprepare(priv->clk);
12358c2ecf20Sopenharmony_ci			return ret;
12368c2ecf20Sopenharmony_ci		}
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	writel(priv->suspend_i2scon, priv->addr + I2SCON);
12408c2ecf20Sopenharmony_ci	writel(priv->suspend_i2smod, priv->addr + I2SMOD);
12418c2ecf20Sopenharmony_ci	writel(priv->suspend_i2spsr, priv->addr + I2SPSR);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	return 0;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic void i2s_unregister_clocks(struct samsung_i2s_priv *priv)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	int i;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	for (i = 0; i < priv->clk_data.clk_num; i++) {
12528c2ecf20Sopenharmony_ci		if (!IS_ERR(priv->clk_table[i]))
12538c2ecf20Sopenharmony_ci			clk_unregister(priv->clk_table[i]);
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv)
12588c2ecf20Sopenharmony_ci{
12598c2ecf20Sopenharmony_ci	of_clk_del_provider(priv->pdev->dev.of_node);
12608c2ecf20Sopenharmony_ci	i2s_unregister_clocks(priv);
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_cistatic int i2s_register_clock_provider(struct samsung_i2s_priv *priv)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" };
12688c2ecf20Sopenharmony_ci	const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
12698c2ecf20Sopenharmony_ci	const char *p_names[2] = { NULL };
12708c2ecf20Sopenharmony_ci	struct device *dev = &priv->pdev->dev;
12718c2ecf20Sopenharmony_ci	const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs;
12728c2ecf20Sopenharmony_ci	const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)];
12738c2ecf20Sopenharmony_ci	struct clk *rclksrc;
12748c2ecf20Sopenharmony_ci	int ret, i;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* Register the clock provider only if it's expected in the DTB */
12778c2ecf20Sopenharmony_ci	if (!of_find_property(dev->of_node, "#clock-cells", NULL))
12788c2ecf20Sopenharmony_ci		return 0;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	/* Get the RCLKSRC mux clock parent clock names */
12818c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(p_names); i++) {
12828c2ecf20Sopenharmony_ci		rclksrc = clk_get(dev, clk_name[i]);
12838c2ecf20Sopenharmony_ci		if (IS_ERR(rclksrc))
12848c2ecf20Sopenharmony_ci			continue;
12858c2ecf20Sopenharmony_ci		p_names[i] = __clk_get_name(rclksrc);
12868c2ecf20Sopenharmony_ci		clk_put(rclksrc);
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) {
12908c2ecf20Sopenharmony_ci		i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
12918c2ecf20Sopenharmony_ci						dev_name(dev), i2s_clk_desc[i]);
12928c2ecf20Sopenharmony_ci		if (!i2s_clk_name[i])
12938c2ecf20Sopenharmony_ci			return -ENOMEM;
12948c2ecf20Sopenharmony_ci	}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
12978c2ecf20Sopenharmony_ci		/* Activate the prescaler */
12988c2ecf20Sopenharmony_ci		u32 val = readl(priv->addr + I2SPSR);
12998c2ecf20Sopenharmony_ci		writel(val | PSR_PSREN, priv->addr + I2SPSR);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci		priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
13028c2ecf20Sopenharmony_ci				i2s_clk_name[CLK_I2S_RCLK_SRC], p_names,
13038c2ecf20Sopenharmony_ci				ARRAY_SIZE(p_names),
13048c2ecf20Sopenharmony_ci				CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
13058c2ecf20Sopenharmony_ci				priv->addr + I2SMOD, reg_info->rclksrc_off,
13068c2ecf20Sopenharmony_ci				1, 0, &priv->lock);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
13098c2ecf20Sopenharmony_ci				i2s_clk_name[CLK_I2S_RCLK_PSR],
13108c2ecf20Sopenharmony_ci				i2s_clk_name[CLK_I2S_RCLK_SRC],
13118c2ecf20Sopenharmony_ci				CLK_SET_RATE_PARENT,
13128c2ecf20Sopenharmony_ci				priv->addr + I2SPSR, 8, 6, 0, &priv->lock);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci		p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR];
13158c2ecf20Sopenharmony_ci		priv->clk_data.clk_num = 2;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev,
13198c2ecf20Sopenharmony_ci				i2s_clk_name[CLK_I2S_CDCLK], p_names[0],
13208c2ecf20Sopenharmony_ci				CLK_SET_RATE_PARENT,
13218c2ecf20Sopenharmony_ci				priv->addr + I2SMOD, reg_info->cdclkcon_off,
13228c2ecf20Sopenharmony_ci				CLK_GATE_SET_TO_DISABLE, &priv->lock);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	priv->clk_data.clk_num += 1;
13258c2ecf20Sopenharmony_ci	priv->clk_data.clks = priv->clk_table;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
13288c2ecf20Sopenharmony_ci				  &priv->clk_data);
13298c2ecf20Sopenharmony_ci	if (ret < 0) {
13308c2ecf20Sopenharmony_ci		dev_err(dev, "failed to add clock provider: %d\n", ret);
13318c2ecf20Sopenharmony_ci		i2s_unregister_clocks(priv);
13328c2ecf20Sopenharmony_ci	}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	return ret;
13358c2ecf20Sopenharmony_ci}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci/* Create platform device for the secondary PCM */
13388c2ecf20Sopenharmony_cistatic int i2s_create_secondary_device(struct samsung_i2s_priv *priv)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	struct platform_device *pdev_sec;
13418c2ecf20Sopenharmony_ci	const char *devname;
13428c2ecf20Sopenharmony_ci	int ret;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	devname = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s-sec",
13458c2ecf20Sopenharmony_ci				 dev_name(&priv->pdev->dev));
13468c2ecf20Sopenharmony_ci	if (!devname)
13478c2ecf20Sopenharmony_ci		return -ENOMEM;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	pdev_sec = platform_device_alloc(devname, -1);
13508c2ecf20Sopenharmony_ci	if (!pdev_sec)
13518c2ecf20Sopenharmony_ci		return -ENOMEM;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	ret = platform_device_add(pdev_sec);
13568c2ecf20Sopenharmony_ci	if (ret < 0) {
13578c2ecf20Sopenharmony_ci		platform_device_put(pdev_sec);
13588c2ecf20Sopenharmony_ci		return ret;
13598c2ecf20Sopenharmony_ci	}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	ret = device_attach(&pdev_sec->dev);
13628c2ecf20Sopenharmony_ci	if (ret <= 0) {
13638c2ecf20Sopenharmony_ci		platform_device_unregister(priv->pdev_sec);
13648c2ecf20Sopenharmony_ci		dev_info(&pdev_sec->dev, "device_attach() failed\n");
13658c2ecf20Sopenharmony_ci		return ret;
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	priv->pdev_sec = pdev_sec;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	return 0;
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic void i2s_delete_secondary_device(struct samsung_i2s_priv *priv)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	platform_device_unregister(priv->pdev_sec);
13768c2ecf20Sopenharmony_ci	priv->pdev_sec = NULL;
13778c2ecf20Sopenharmony_ci}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_cistatic int samsung_i2s_probe(struct platform_device *pdev)
13808c2ecf20Sopenharmony_ci{
13818c2ecf20Sopenharmony_ci	struct i2s_dai *pri_dai, *sec_dai = NULL;
13828c2ecf20Sopenharmony_ci	struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
13838c2ecf20Sopenharmony_ci	u32 regs_base, idma_addr = 0;
13848c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
13858c2ecf20Sopenharmony_ci	const struct samsung_i2s_dai_data *i2s_dai_data;
13868c2ecf20Sopenharmony_ci	const struct platform_device_id *id;
13878c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv;
13888c2ecf20Sopenharmony_ci	struct resource *res;
13898c2ecf20Sopenharmony_ci	int num_dais, ret;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
13928c2ecf20Sopenharmony_ci		i2s_dai_data = of_device_get_match_data(&pdev->dev);
13938c2ecf20Sopenharmony_ci	} else {
13948c2ecf20Sopenharmony_ci		id = platform_get_device_id(pdev);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		/* Nothing to do if it is the secondary device probe */
13978c2ecf20Sopenharmony_ci		if (!id)
13988c2ecf20Sopenharmony_ci			return 0;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci		i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data;
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
14048c2ecf20Sopenharmony_ci	if (!priv)
14058c2ecf20Sopenharmony_ci		return -ENOMEM;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	if (np) {
14088c2ecf20Sopenharmony_ci		priv->quirks = i2s_dai_data->quirks;
14098c2ecf20Sopenharmony_ci	} else {
14108c2ecf20Sopenharmony_ci		if (!i2s_pdata) {
14118c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Missing platform data\n");
14128c2ecf20Sopenharmony_ci			return -EINVAL;
14138c2ecf20Sopenharmony_ci		}
14148c2ecf20Sopenharmony_ci		priv->quirks = i2s_pdata->type.quirks;
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1;
14188c2ecf20Sopenharmony_ci	priv->pdev = pdev;
14198c2ecf20Sopenharmony_ci	priv->variant_regs = i2s_dai_data->i2s_variant_regs;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais);
14228c2ecf20Sopenharmony_ci	if (ret < 0)
14238c2ecf20Sopenharmony_ci		return ret;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1];
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
14288c2ecf20Sopenharmony_ci	spin_lock_init(&priv->pcm_lock);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	if (!np) {
14318c2ecf20Sopenharmony_ci		pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback;
14328c2ecf20Sopenharmony_ci		pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
14338c2ecf20Sopenharmony_ci		pri_dai->filter = i2s_pdata->dma_filter;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci		idma_addr = i2s_pdata->type.idma_addr;
14368c2ecf20Sopenharmony_ci	} else {
14378c2ecf20Sopenharmony_ci		if (of_property_read_u32(np, "samsung,idma-addr",
14388c2ecf20Sopenharmony_ci					 &idma_addr)) {
14398c2ecf20Sopenharmony_ci			if (priv->quirks & QUIRK_SUPPORTS_IDMA) {
14408c2ecf20Sopenharmony_ci				dev_info(&pdev->dev, "idma address is not"\
14418c2ecf20Sopenharmony_ci						"specified");
14428c2ecf20Sopenharmony_ci			}
14438c2ecf20Sopenharmony_ci		}
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14478c2ecf20Sopenharmony_ci	priv->addr = devm_ioremap_resource(&pdev->dev, res);
14488c2ecf20Sopenharmony_ci	if (IS_ERR(priv->addr))
14498c2ecf20Sopenharmony_ci		return PTR_ERR(priv->addr);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	regs_base = res->start;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	priv->clk = devm_clk_get(&pdev->dev, "iis");
14548c2ecf20Sopenharmony_ci	if (IS_ERR(priv->clk)) {
14558c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get iis clock\n");
14568c2ecf20Sopenharmony_ci		return PTR_ERR(priv->clk);
14578c2ecf20Sopenharmony_ci	}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
14608c2ecf20Sopenharmony_ci	if (ret != 0) {
14618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
14628c2ecf20Sopenharmony_ci		return ret;
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci	pri_dai->dma_playback.addr = regs_base + I2STXD;
14658c2ecf20Sopenharmony_ci	pri_dai->dma_capture.addr = regs_base + I2SRXD;
14668c2ecf20Sopenharmony_ci	pri_dai->dma_playback.chan_name = "tx";
14678c2ecf20Sopenharmony_ci	pri_dai->dma_capture.chan_name = "rx";
14688c2ecf20Sopenharmony_ci	pri_dai->dma_playback.addr_width = 4;
14698c2ecf20Sopenharmony_ci	pri_dai->dma_capture.addr_width = 4;
14708c2ecf20Sopenharmony_ci	pri_dai->priv = priv;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	if (priv->quirks & QUIRK_PRI_6CHAN)
14738c2ecf20Sopenharmony_ci		pri_dai->drv->playback.channels_max = 6;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
14768c2ecf20Sopenharmony_ci						 "tx", "rx", NULL);
14778c2ecf20Sopenharmony_ci	if (ret < 0)
14788c2ecf20Sopenharmony_ci		goto err_disable_clk;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (priv->quirks & QUIRK_SEC_DAI) {
14818c2ecf20Sopenharmony_ci		sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1];
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		sec_dai->dma_playback.addr = regs_base + I2STXDS;
14848c2ecf20Sopenharmony_ci		sec_dai->dma_playback.chan_name = "tx-sec";
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		if (!np) {
14878c2ecf20Sopenharmony_ci			sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec;
14888c2ecf20Sopenharmony_ci			sec_dai->filter = i2s_pdata->dma_filter;
14898c2ecf20Sopenharmony_ci		}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci		sec_dai->dma_playback.addr_width = 4;
14928c2ecf20Sopenharmony_ci		sec_dai->idma_playback.addr = idma_addr;
14938c2ecf20Sopenharmony_ci		sec_dai->pri_dai = pri_dai;
14948c2ecf20Sopenharmony_ci		sec_dai->priv = priv;
14958c2ecf20Sopenharmony_ci		pri_dai->sec_dai = sec_dai;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci		ret = i2s_create_secondary_device(priv);
14988c2ecf20Sopenharmony_ci		if (ret < 0)
14998c2ecf20Sopenharmony_ci			goto err_disable_clk;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci		ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev,
15028c2ecf20Sopenharmony_ci						sec_dai->filter, "tx-sec", NULL,
15038c2ecf20Sopenharmony_ci						&pdev->dev);
15048c2ecf20Sopenharmony_ci		if (ret < 0)
15058c2ecf20Sopenharmony_ci			goto err_del_sec;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
15108c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to configure gpio\n");
15118c2ecf20Sopenharmony_ci		ret = -EINVAL;
15128c2ecf20Sopenharmony_ci		goto err_del_sec;
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev,
15188c2ecf20Sopenharmony_ci					&samsung_i2s_component,
15198c2ecf20Sopenharmony_ci					priv->dai_drv, num_dais);
15208c2ecf20Sopenharmony_ci	if (ret < 0)
15218c2ecf20Sopenharmony_ci		goto err_del_sec;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
15248c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	ret = i2s_register_clock_provider(priv);
15278c2ecf20Sopenharmony_ci	if (ret < 0)
15288c2ecf20Sopenharmony_ci		goto err_disable_pm;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	return 0;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cierr_disable_pm:
15358c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
15368c2ecf20Sopenharmony_cierr_del_sec:
15378c2ecf20Sopenharmony_ci	i2s_delete_secondary_device(priv);
15388c2ecf20Sopenharmony_cierr_disable_clk:
15398c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
15408c2ecf20Sopenharmony_ci	return ret;
15418c2ecf20Sopenharmony_ci}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic int samsung_i2s_remove(struct platform_device *pdev)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev);
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	/* The secondary device has no driver data assigned */
15488c2ecf20Sopenharmony_ci	if (!priv)
15498c2ecf20Sopenharmony_ci		return 0;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
15528c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	i2s_unregister_clock_provider(priv);
15558c2ecf20Sopenharmony_ci	i2s_delete_secondary_device(priv);
15568c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	return 0;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic const struct samsung_i2s_variant_regs i2sv3_regs = {
15648c2ecf20Sopenharmony_ci	.bfs_off = 1,
15658c2ecf20Sopenharmony_ci	.rfs_off = 3,
15668c2ecf20Sopenharmony_ci	.sdf_off = 5,
15678c2ecf20Sopenharmony_ci	.txr_off = 8,
15688c2ecf20Sopenharmony_ci	.rclksrc_off = 10,
15698c2ecf20Sopenharmony_ci	.mss_off = 11,
15708c2ecf20Sopenharmony_ci	.cdclkcon_off = 12,
15718c2ecf20Sopenharmony_ci	.lrp_off = 7,
15728c2ecf20Sopenharmony_ci	.bfs_mask = 0x3,
15738c2ecf20Sopenharmony_ci	.rfs_mask = 0x3,
15748c2ecf20Sopenharmony_ci	.ftx0cnt_off = 8,
15758c2ecf20Sopenharmony_ci};
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_cistatic const struct samsung_i2s_variant_regs i2sv6_regs = {
15788c2ecf20Sopenharmony_ci	.bfs_off = 0,
15798c2ecf20Sopenharmony_ci	.rfs_off = 4,
15808c2ecf20Sopenharmony_ci	.sdf_off = 6,
15818c2ecf20Sopenharmony_ci	.txr_off = 8,
15828c2ecf20Sopenharmony_ci	.rclksrc_off = 10,
15838c2ecf20Sopenharmony_ci	.mss_off = 11,
15848c2ecf20Sopenharmony_ci	.cdclkcon_off = 12,
15858c2ecf20Sopenharmony_ci	.lrp_off = 15,
15868c2ecf20Sopenharmony_ci	.bfs_mask = 0xf,
15878c2ecf20Sopenharmony_ci	.rfs_mask = 0x3,
15888c2ecf20Sopenharmony_ci	.ftx0cnt_off = 8,
15898c2ecf20Sopenharmony_ci};
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cistatic const struct samsung_i2s_variant_regs i2sv7_regs = {
15928c2ecf20Sopenharmony_ci	.bfs_off = 0,
15938c2ecf20Sopenharmony_ci	.rfs_off = 4,
15948c2ecf20Sopenharmony_ci	.sdf_off = 7,
15958c2ecf20Sopenharmony_ci	.txr_off = 9,
15968c2ecf20Sopenharmony_ci	.rclksrc_off = 11,
15978c2ecf20Sopenharmony_ci	.mss_off = 12,
15988c2ecf20Sopenharmony_ci	.cdclkcon_off = 22,
15998c2ecf20Sopenharmony_ci	.lrp_off = 15,
16008c2ecf20Sopenharmony_ci	.bfs_mask = 0xf,
16018c2ecf20Sopenharmony_ci	.rfs_mask = 0x7,
16028c2ecf20Sopenharmony_ci	.ftx0cnt_off = 0,
16038c2ecf20Sopenharmony_ci};
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cistatic const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
16068c2ecf20Sopenharmony_ci	.bfs_off = 0,
16078c2ecf20Sopenharmony_ci	.rfs_off = 3,
16088c2ecf20Sopenharmony_ci	.sdf_off = 6,
16098c2ecf20Sopenharmony_ci	.txr_off = 8,
16108c2ecf20Sopenharmony_ci	.rclksrc_off = 10,
16118c2ecf20Sopenharmony_ci	.mss_off = 11,
16128c2ecf20Sopenharmony_ci	.cdclkcon_off = 12,
16138c2ecf20Sopenharmony_ci	.lrp_off = 15,
16148c2ecf20Sopenharmony_ci	.bfs_mask = 0x7,
16158c2ecf20Sopenharmony_ci	.rfs_mask = 0x7,
16168c2ecf20Sopenharmony_ci	.ftx0cnt_off = 8,
16178c2ecf20Sopenharmony_ci};
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cistatic const struct samsung_i2s_dai_data i2sv3_dai_type = {
16208c2ecf20Sopenharmony_ci	.quirks = QUIRK_NO_MUXPSR,
16218c2ecf20Sopenharmony_ci	.pcm_rates = SNDRV_PCM_RATE_8000_96000,
16228c2ecf20Sopenharmony_ci	.i2s_variant_regs = &i2sv3_regs,
16238c2ecf20Sopenharmony_ci};
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic const struct samsung_i2s_dai_data i2sv5_dai_type = {
16268c2ecf20Sopenharmony_ci	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
16278c2ecf20Sopenharmony_ci			QUIRK_SUPPORTS_IDMA,
16288c2ecf20Sopenharmony_ci	.pcm_rates = SNDRV_PCM_RATE_8000_96000,
16298c2ecf20Sopenharmony_ci	.i2s_variant_regs = &i2sv3_regs,
16308c2ecf20Sopenharmony_ci};
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_cistatic const struct samsung_i2s_dai_data i2sv6_dai_type = {
16338c2ecf20Sopenharmony_ci	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
16348c2ecf20Sopenharmony_ci			QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
16358c2ecf20Sopenharmony_ci	.pcm_rates = SNDRV_PCM_RATE_8000_96000,
16368c2ecf20Sopenharmony_ci	.i2s_variant_regs = &i2sv6_regs,
16378c2ecf20Sopenharmony_ci};
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_cistatic const struct samsung_i2s_dai_data i2sv7_dai_type = {
16408c2ecf20Sopenharmony_ci	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
16418c2ecf20Sopenharmony_ci			QUIRK_SUPPORTS_TDM,
16428c2ecf20Sopenharmony_ci	.pcm_rates = SNDRV_PCM_RATE_8000_192000,
16438c2ecf20Sopenharmony_ci	.i2s_variant_regs = &i2sv7_regs,
16448c2ecf20Sopenharmony_ci};
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_cistatic const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
16478c2ecf20Sopenharmony_ci	.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
16488c2ecf20Sopenharmony_ci	.pcm_rates = SNDRV_PCM_RATE_8000_96000,
16498c2ecf20Sopenharmony_ci	.i2s_variant_regs = &i2sv5_i2s1_regs,
16508c2ecf20Sopenharmony_ci};
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cistatic const struct platform_device_id samsung_i2s_driver_ids[] = {
16538c2ecf20Sopenharmony_ci	{
16548c2ecf20Sopenharmony_ci		.name           = "samsung-i2s",
16558c2ecf20Sopenharmony_ci		.driver_data	= (kernel_ulong_t)&i2sv3_dai_type,
16568c2ecf20Sopenharmony_ci	},
16578c2ecf20Sopenharmony_ci	{},
16588c2ecf20Sopenharmony_ci};
16598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
16628c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_i2s_match[] = {
16638c2ecf20Sopenharmony_ci	{
16648c2ecf20Sopenharmony_ci		.compatible = "samsung,s3c6410-i2s",
16658c2ecf20Sopenharmony_ci		.data = &i2sv3_dai_type,
16668c2ecf20Sopenharmony_ci	}, {
16678c2ecf20Sopenharmony_ci		.compatible = "samsung,s5pv210-i2s",
16688c2ecf20Sopenharmony_ci		.data = &i2sv5_dai_type,
16698c2ecf20Sopenharmony_ci	}, {
16708c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos5420-i2s",
16718c2ecf20Sopenharmony_ci		.data = &i2sv6_dai_type,
16728c2ecf20Sopenharmony_ci	}, {
16738c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos7-i2s",
16748c2ecf20Sopenharmony_ci		.data = &i2sv7_dai_type,
16758c2ecf20Sopenharmony_ci	}, {
16768c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos7-i2s1",
16778c2ecf20Sopenharmony_ci		.data = &i2sv5_dai_type_i2s1,
16788c2ecf20Sopenharmony_ci	},
16798c2ecf20Sopenharmony_ci	{},
16808c2ecf20Sopenharmony_ci};
16818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_i2s_match);
16828c2ecf20Sopenharmony_ci#endif
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic const struct dev_pm_ops samsung_i2s_pm = {
16858c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
16868c2ecf20Sopenharmony_ci				i2s_runtime_resume, NULL)
16878c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
16888c2ecf20Sopenharmony_ci				     pm_runtime_force_resume)
16898c2ecf20Sopenharmony_ci};
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic struct platform_driver samsung_i2s_driver = {
16928c2ecf20Sopenharmony_ci	.probe  = samsung_i2s_probe,
16938c2ecf20Sopenharmony_ci	.remove = samsung_i2s_remove,
16948c2ecf20Sopenharmony_ci	.id_table = samsung_i2s_driver_ids,
16958c2ecf20Sopenharmony_ci	.driver = {
16968c2ecf20Sopenharmony_ci		.name = "samsung-i2s",
16978c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(exynos_i2s_match),
16988c2ecf20Sopenharmony_ci		.pm = &samsung_i2s_pm,
16998c2ecf20Sopenharmony_ci	},
17008c2ecf20Sopenharmony_ci};
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_cimodule_platform_driver(samsung_i2s_driver);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci/* Module information */
17058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
17068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung I2S Interface");
17078c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:samsung-i2s");
17088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1709