xref: /openbmc/linux/sound/soc/atmel/mchp-spdiftx.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
106ca24e9SCodrin Ciubotariu // SPDX-License-Identifier: GPL-2.0
206ca24e9SCodrin Ciubotariu //
306ca24e9SCodrin Ciubotariu // Driver for Microchip S/PDIF TX Controller
406ca24e9SCodrin Ciubotariu //
506ca24e9SCodrin Ciubotariu // Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
606ca24e9SCodrin Ciubotariu //
706ca24e9SCodrin Ciubotariu // Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
806ca24e9SCodrin Ciubotariu 
924c86c8aSClaudiu Beznea #include <linux/bitfield.h>
1006ca24e9SCodrin Ciubotariu #include <linux/clk.h>
1106ca24e9SCodrin Ciubotariu #include <linux/io.h>
1206ca24e9SCodrin Ciubotariu #include <linux/module.h>
134bf54ca6SClaudiu Beznea #include <linux/pm_runtime.h>
1406ca24e9SCodrin Ciubotariu #include <linux/spinlock.h>
1506ca24e9SCodrin Ciubotariu 
1606ca24e9SCodrin Ciubotariu #include <sound/asoundef.h>
1706ca24e9SCodrin Ciubotariu #include <sound/dmaengine_pcm.h>
1806ca24e9SCodrin Ciubotariu #include <sound/pcm_params.h>
1906ca24e9SCodrin Ciubotariu #include <sound/soc.h>
2006ca24e9SCodrin Ciubotariu 
2106ca24e9SCodrin Ciubotariu /*
2206ca24e9SCodrin Ciubotariu  * ---- S/PDIF Transmitter Controller Register map ----
2306ca24e9SCodrin Ciubotariu  */
2406ca24e9SCodrin Ciubotariu #define SPDIFTX_CR			0x00	/* Control Register */
2506ca24e9SCodrin Ciubotariu #define SPDIFTX_MR			0x04	/* Mode Register */
2606ca24e9SCodrin Ciubotariu #define SPDIFTX_CDR			0x0C	/* Common Data Register */
2706ca24e9SCodrin Ciubotariu 
2806ca24e9SCodrin Ciubotariu #define SPDIFTX_IER			0x14	/* Interrupt Enable Register */
2906ca24e9SCodrin Ciubotariu #define SPDIFTX_IDR			0x18	/* Interrupt Disable Register */
3006ca24e9SCodrin Ciubotariu #define SPDIFTX_IMR			0x1C	/* Interrupt Mask Register */
3106ca24e9SCodrin Ciubotariu #define SPDIFTX_ISR			0x20	/* Interrupt Status Register */
3206ca24e9SCodrin Ciubotariu 
3306ca24e9SCodrin Ciubotariu #define SPDIFTX_CH1UD(reg)	(0x50 + (reg) * 4)	/* User Data 1 Register x */
3406ca24e9SCodrin Ciubotariu #define SPDIFTX_CH1S(reg)	(0x80 + (reg) * 4)	/* Channel Status 1 Register x */
3506ca24e9SCodrin Ciubotariu 
3606ca24e9SCodrin Ciubotariu #define SPDIFTX_VERSION			0xF0
3706ca24e9SCodrin Ciubotariu 
3806ca24e9SCodrin Ciubotariu /*
3906ca24e9SCodrin Ciubotariu  * ---- Control Register (Write-only) ----
4006ca24e9SCodrin Ciubotariu  */
4106ca24e9SCodrin Ciubotariu #define SPDIFTX_CR_SWRST		BIT(0)	/* Software Reset */
4206ca24e9SCodrin Ciubotariu #define SPDIFTX_CR_FCLR			BIT(1)	/* FIFO clear */
4306ca24e9SCodrin Ciubotariu 
4406ca24e9SCodrin Ciubotariu /*
4506ca24e9SCodrin Ciubotariu  * ---- Mode Register (Read/Write) ----
4606ca24e9SCodrin Ciubotariu  */
4706ca24e9SCodrin Ciubotariu /* Transmit Enable */
4806ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_TXEN_MASK		GENMASK(0, 0)
4906ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_TXEN_DISABLE		(0 << 0)
5006ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_TXEN_ENABLE		(1 << 0)
5106ca24e9SCodrin Ciubotariu 
5206ca24e9SCodrin Ciubotariu /* Multichannel Transfer */
5306ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_MULTICH_MASK		GENAMSK(1, 1)
5406ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_MULTICH_MONO		(0 << 1)
5506ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_MULTICH_DUAL		(1 << 1)
5606ca24e9SCodrin Ciubotariu 
5706ca24e9SCodrin Ciubotariu /* Data Word Endian Mode */
5806ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_ENDIAN_MASK		GENMASK(2, 2)
5906ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_ENDIAN_LITTLE	(0 << 2)
6006ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_ENDIAN_BIG		(1 << 2)
6106ca24e9SCodrin Ciubotariu 
6206ca24e9SCodrin Ciubotariu /* Data Justification */
6306ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_JUSTIFY_MASK		GENMASK(3, 3)
6406ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_JUSTIFY_LSB		(0 << 3)
6506ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_JUSTIFY_MSB		(1 << 3)
6606ca24e9SCodrin Ciubotariu 
6706ca24e9SCodrin Ciubotariu /* Common Audio Register Transfer Mode */
6806ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_CMODE_MASK			GENMASK(5, 4)
6906ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_CMODE_INDEX_ACCESS		(0 << 4)
7006ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_CMODE_TOGGLE_ACCESS		(1 << 4)
7106ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_CMODE_INTERLVD_ACCESS	(2 << 4)
7206ca24e9SCodrin Ciubotariu 
7306ca24e9SCodrin Ciubotariu /* Valid Bits per Sample */
7406ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_VBPS_MASK		GENMASK(13, 8)
7506ca24e9SCodrin Ciubotariu 
7606ca24e9SCodrin Ciubotariu /* Chunk Size */
7706ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_CHUNK_MASK		GENMASK(19, 16)
7806ca24e9SCodrin Ciubotariu 
7906ca24e9SCodrin Ciubotariu /* Validity Bits for Channels 1 and 2 */
8006ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_VALID1			BIT(24)
8106ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_VALID2			BIT(25)
8206ca24e9SCodrin Ciubotariu 
8355233b22SGu Shengxian /* Disable Null Frame on underrun */
8406ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_DNFR_MASK		GENMASK(27, 27)
8506ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_DNFR_INVALID		(0 << 27)
8606ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_DNFR_VALID		(1 << 27)
8706ca24e9SCodrin Ciubotariu 
8806ca24e9SCodrin Ciubotariu /* Bytes per Sample */
8906ca24e9SCodrin Ciubotariu #define SPDIFTX_MR_BPS_MASK		GENMASK(29, 28)
9006ca24e9SCodrin Ciubotariu 
9106ca24e9SCodrin Ciubotariu /*
9206ca24e9SCodrin Ciubotariu  * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
9306ca24e9SCodrin Ciubotariu  */
9406ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXRDY		BIT(0)
9506ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXEMPTY		BIT(1)
9606ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXFULL		BIT(2)
9706ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXCHUNK		BIT(3)
9806ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXUDR		BIT(4)
9906ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXOVR		BIT(5)
10006ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_CSRDY		BIT(6)
10106ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_UDRDY		BIT(7)
10206ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXRDYCH(ch)		BIT((ch) + 8)
10306ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_SECE			BIT(10)
10406ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_TXUDRCH(ch)		BIT((ch) + 11)
10506ca24e9SCodrin Ciubotariu #define SPDIFTX_IR_BEND			BIT(13)
10606ca24e9SCodrin Ciubotariu 
mchp_spdiftx_readable_reg(struct device * dev,unsigned int reg)10706ca24e9SCodrin Ciubotariu static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg)
10806ca24e9SCodrin Ciubotariu {
10906ca24e9SCodrin Ciubotariu 	switch (reg) {
11006ca24e9SCodrin Ciubotariu 	case SPDIFTX_MR:
11106ca24e9SCodrin Ciubotariu 	case SPDIFTX_IMR:
11206ca24e9SCodrin Ciubotariu 	case SPDIFTX_ISR:
11306ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(0):
11406ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(1):
11506ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(2):
11606ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(3):
11706ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(4):
11806ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(5):
11906ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(0):
12006ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(1):
12106ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(2):
12206ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(3):
12306ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(4):
12406ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(5):
12506ca24e9SCodrin Ciubotariu 		return true;
12606ca24e9SCodrin Ciubotariu 	default:
12706ca24e9SCodrin Ciubotariu 		return false;
12806ca24e9SCodrin Ciubotariu 	}
12906ca24e9SCodrin Ciubotariu }
13006ca24e9SCodrin Ciubotariu 
mchp_spdiftx_writeable_reg(struct device * dev,unsigned int reg)13106ca24e9SCodrin Ciubotariu static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg)
13206ca24e9SCodrin Ciubotariu {
13306ca24e9SCodrin Ciubotariu 	switch (reg) {
13406ca24e9SCodrin Ciubotariu 	case SPDIFTX_CR:
13506ca24e9SCodrin Ciubotariu 	case SPDIFTX_MR:
13606ca24e9SCodrin Ciubotariu 	case SPDIFTX_CDR:
13706ca24e9SCodrin Ciubotariu 	case SPDIFTX_IER:
13806ca24e9SCodrin Ciubotariu 	case SPDIFTX_IDR:
13906ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(0):
14006ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(1):
14106ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(2):
14206ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(3):
14306ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(4):
14406ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1UD(5):
14506ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(0):
14606ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(1):
14706ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(2):
14806ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(3):
14906ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(4):
15006ca24e9SCodrin Ciubotariu 	case SPDIFTX_CH1S(5):
15106ca24e9SCodrin Ciubotariu 		return true;
15206ca24e9SCodrin Ciubotariu 	default:
15306ca24e9SCodrin Ciubotariu 		return false;
15406ca24e9SCodrin Ciubotariu 	}
15506ca24e9SCodrin Ciubotariu }
15606ca24e9SCodrin Ciubotariu 
mchp_spdiftx_precious_reg(struct device * dev,unsigned int reg)15706ca24e9SCodrin Ciubotariu static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg)
15806ca24e9SCodrin Ciubotariu {
15906ca24e9SCodrin Ciubotariu 	switch (reg) {
16006ca24e9SCodrin Ciubotariu 	case SPDIFTX_CDR:
16106ca24e9SCodrin Ciubotariu 	case SPDIFTX_ISR:
16206ca24e9SCodrin Ciubotariu 		return true;
16306ca24e9SCodrin Ciubotariu 	default:
16406ca24e9SCodrin Ciubotariu 		return false;
16506ca24e9SCodrin Ciubotariu 	}
16606ca24e9SCodrin Ciubotariu }
16706ca24e9SCodrin Ciubotariu 
16806ca24e9SCodrin Ciubotariu static const struct regmap_config mchp_spdiftx_regmap_config = {
16906ca24e9SCodrin Ciubotariu 	.reg_bits = 32,
17006ca24e9SCodrin Ciubotariu 	.reg_stride = 4,
17106ca24e9SCodrin Ciubotariu 	.val_bits = 32,
17206ca24e9SCodrin Ciubotariu 	.max_register = SPDIFTX_VERSION,
17306ca24e9SCodrin Ciubotariu 	.readable_reg = mchp_spdiftx_readable_reg,
17406ca24e9SCodrin Ciubotariu 	.writeable_reg = mchp_spdiftx_writeable_reg,
17506ca24e9SCodrin Ciubotariu 	.precious_reg = mchp_spdiftx_precious_reg,
1764bf54ca6SClaudiu Beznea 	.cache_type = REGCACHE_FLAT,
17706ca24e9SCodrin Ciubotariu };
17806ca24e9SCodrin Ciubotariu 
17906ca24e9SCodrin Ciubotariu #define SPDIFTX_GCLK_RATIO	128
18006ca24e9SCodrin Ciubotariu 
18106ca24e9SCodrin Ciubotariu #define SPDIFTX_CS_BITS		192
18206ca24e9SCodrin Ciubotariu #define SPDIFTX_UD_BITS		192
18306ca24e9SCodrin Ciubotariu 
18406ca24e9SCodrin Ciubotariu struct mchp_spdiftx_mixer_control {
18506ca24e9SCodrin Ciubotariu 	unsigned char				ch_stat[SPDIFTX_CS_BITS / 8];
18606ca24e9SCodrin Ciubotariu 	unsigned char				user_data[SPDIFTX_UD_BITS / 8];
18706ca24e9SCodrin Ciubotariu 	spinlock_t				lock; /* exclusive access to control data */
18806ca24e9SCodrin Ciubotariu };
18906ca24e9SCodrin Ciubotariu 
19006ca24e9SCodrin Ciubotariu struct mchp_spdiftx_dev {
19106ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control	control;
19206ca24e9SCodrin Ciubotariu 	struct snd_dmaengine_dai_dma_data	playback;
19306ca24e9SCodrin Ciubotariu 	struct device				*dev;
19406ca24e9SCodrin Ciubotariu 	struct regmap				*regmap;
19506ca24e9SCodrin Ciubotariu 	struct clk				*pclk;
19606ca24e9SCodrin Ciubotariu 	struct clk				*gclk;
19706ca24e9SCodrin Ciubotariu 	unsigned int				fmt;
198abc7edb0SClaudiu Beznea 	unsigned int				suspend_irq;
19906ca24e9SCodrin Ciubotariu };
20006ca24e9SCodrin Ciubotariu 
mchp_spdiftx_is_running(struct mchp_spdiftx_dev * dev)20106ca24e9SCodrin Ciubotariu static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev)
20206ca24e9SCodrin Ciubotariu {
20306ca24e9SCodrin Ciubotariu 	u32 mr;
20406ca24e9SCodrin Ciubotariu 
20506ca24e9SCodrin Ciubotariu 	regmap_read(dev->regmap, SPDIFTX_MR, &mr);
20606ca24e9SCodrin Ciubotariu 	return !!(mr & SPDIFTX_MR_TXEN_ENABLE);
20706ca24e9SCodrin Ciubotariu }
20806ca24e9SCodrin Ciubotariu 
mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev * dev)20906ca24e9SCodrin Ciubotariu static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev)
21006ca24e9SCodrin Ciubotariu {
21106ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
21206ca24e9SCodrin Ciubotariu 	u32 val;
21306ca24e9SCodrin Ciubotariu 	int i;
21406ca24e9SCodrin Ciubotariu 
21506ca24e9SCodrin Ciubotariu 	for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) {
21606ca24e9SCodrin Ciubotariu 		val = (ctrl->ch_stat[(i * 4) + 0] << 0) |
21706ca24e9SCodrin Ciubotariu 		      (ctrl->ch_stat[(i * 4) + 1] << 8) |
21806ca24e9SCodrin Ciubotariu 		      (ctrl->ch_stat[(i * 4) + 2] << 16) |
21906ca24e9SCodrin Ciubotariu 		      (ctrl->ch_stat[(i * 4) + 3] << 24);
22006ca24e9SCodrin Ciubotariu 
22106ca24e9SCodrin Ciubotariu 		regmap_write(dev->regmap, SPDIFTX_CH1S(i), val);
22206ca24e9SCodrin Ciubotariu 	}
22306ca24e9SCodrin Ciubotariu }
22406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev * dev)22506ca24e9SCodrin Ciubotariu static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev)
22606ca24e9SCodrin Ciubotariu {
22706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
22806ca24e9SCodrin Ciubotariu 	u32 val;
22906ca24e9SCodrin Ciubotariu 	int i;
23006ca24e9SCodrin Ciubotariu 
23106ca24e9SCodrin Ciubotariu 	for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) {
23206ca24e9SCodrin Ciubotariu 		val = (ctrl->user_data[(i * 4) + 0] << 0) |
23306ca24e9SCodrin Ciubotariu 		      (ctrl->user_data[(i * 4) + 1] << 8) |
23406ca24e9SCodrin Ciubotariu 		      (ctrl->user_data[(i * 4) + 2] << 16) |
23506ca24e9SCodrin Ciubotariu 		      (ctrl->user_data[(i * 4) + 3] << 24);
23606ca24e9SCodrin Ciubotariu 
23706ca24e9SCodrin Ciubotariu 		regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val);
23806ca24e9SCodrin Ciubotariu 	}
23906ca24e9SCodrin Ciubotariu }
24006ca24e9SCodrin Ciubotariu 
mchp_spdiftx_interrupt(int irq,void * dev_id)24106ca24e9SCodrin Ciubotariu static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id)
24206ca24e9SCodrin Ciubotariu {
24306ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = dev_id;
24406ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
24506ca24e9SCodrin Ciubotariu 	u32 sr, imr, pending, idr = 0;
24606ca24e9SCodrin Ciubotariu 
24706ca24e9SCodrin Ciubotariu 	regmap_read(dev->regmap, SPDIFTX_ISR, &sr);
24806ca24e9SCodrin Ciubotariu 	regmap_read(dev->regmap, SPDIFTX_IMR, &imr);
24906ca24e9SCodrin Ciubotariu 	pending = sr & imr;
25006ca24e9SCodrin Ciubotariu 
25106ca24e9SCodrin Ciubotariu 	if (!pending)
25206ca24e9SCodrin Ciubotariu 		return IRQ_NONE;
25306ca24e9SCodrin Ciubotariu 
25406ca24e9SCodrin Ciubotariu 	if (pending & SPDIFTX_IR_TXUDR) {
25506ca24e9SCodrin Ciubotariu 		dev_warn(dev->dev, "underflow detected\n");
25606ca24e9SCodrin Ciubotariu 		idr |= SPDIFTX_IR_TXUDR;
25706ca24e9SCodrin Ciubotariu 	}
25806ca24e9SCodrin Ciubotariu 
25906ca24e9SCodrin Ciubotariu 	if (pending & SPDIFTX_IR_TXOVR) {
26006ca24e9SCodrin Ciubotariu 		dev_warn(dev->dev, "overflow detected\n");
26106ca24e9SCodrin Ciubotariu 		idr |= SPDIFTX_IR_TXOVR;
26206ca24e9SCodrin Ciubotariu 	}
26306ca24e9SCodrin Ciubotariu 
26406ca24e9SCodrin Ciubotariu 	if (pending & SPDIFTX_IR_UDRDY) {
26506ca24e9SCodrin Ciubotariu 		spin_lock(&ctrl->lock);
26606ca24e9SCodrin Ciubotariu 		mchp_spdiftx_user_data_write(dev);
26706ca24e9SCodrin Ciubotariu 		spin_unlock(&ctrl->lock);
26806ca24e9SCodrin Ciubotariu 		idr |= SPDIFTX_IR_UDRDY;
26906ca24e9SCodrin Ciubotariu 	}
27006ca24e9SCodrin Ciubotariu 
27106ca24e9SCodrin Ciubotariu 	if (pending & SPDIFTX_IR_CSRDY) {
27206ca24e9SCodrin Ciubotariu 		spin_lock(&ctrl->lock);
27306ca24e9SCodrin Ciubotariu 		mchp_spdiftx_channel_status_write(dev);
27406ca24e9SCodrin Ciubotariu 		spin_unlock(&ctrl->lock);
27506ca24e9SCodrin Ciubotariu 		idr |= SPDIFTX_IR_CSRDY;
27606ca24e9SCodrin Ciubotariu 	}
27706ca24e9SCodrin Ciubotariu 
27806ca24e9SCodrin Ciubotariu 	regmap_write(dev->regmap, SPDIFTX_IDR, idr);
27906ca24e9SCodrin Ciubotariu 
28006ca24e9SCodrin Ciubotariu 	return IRQ_HANDLED;
28106ca24e9SCodrin Ciubotariu }
28206ca24e9SCodrin Ciubotariu 
mchp_spdiftx_dai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)28306ca24e9SCodrin Ciubotariu static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream,
28406ca24e9SCodrin Ciubotariu 				    struct snd_soc_dai *dai)
28506ca24e9SCodrin Ciubotariu {
28606ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
28706ca24e9SCodrin Ciubotariu 
28806ca24e9SCodrin Ciubotariu 	/* Software reset the IP */
28906ca24e9SCodrin Ciubotariu 	regmap_write(dev->regmap, SPDIFTX_CR,
29006ca24e9SCodrin Ciubotariu 		     SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
29106ca24e9SCodrin Ciubotariu 
29206ca24e9SCodrin Ciubotariu 	return 0;
29306ca24e9SCodrin Ciubotariu }
29406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_dai_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)29506ca24e9SCodrin Ciubotariu static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream,
29606ca24e9SCodrin Ciubotariu 				      struct snd_soc_dai *dai)
29706ca24e9SCodrin Ciubotariu {
29806ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
29906ca24e9SCodrin Ciubotariu 
30006ca24e9SCodrin Ciubotariu 	/* Disable interrupts */
30106ca24e9SCodrin Ciubotariu 	regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff);
30206ca24e9SCodrin Ciubotariu }
30306ca24e9SCodrin Ciubotariu 
mchp_spdiftx_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)30406ca24e9SCodrin Ciubotariu static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
30506ca24e9SCodrin Ciubotariu 				struct snd_soc_dai *dai)
30606ca24e9SCodrin Ciubotariu {
30706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
30806ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
30906ca24e9SCodrin Ciubotariu 	int ret;
31006ca24e9SCodrin Ciubotariu 
31106ca24e9SCodrin Ciubotariu 	/* do not start/stop while channel status or user data is updated */
31206ca24e9SCodrin Ciubotariu 	spin_lock(&ctrl->lock);
31306ca24e9SCodrin Ciubotariu 	switch (cmd) {
31406ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_TRIGGER_RESUME:
315abc7edb0SClaudiu Beznea 	case SNDRV_PCM_TRIGGER_START:
316abc7edb0SClaudiu Beznea 		regmap_write(dev->regmap, SPDIFTX_IER, dev->suspend_irq |
317abc7edb0SClaudiu Beznea 			     SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
318abc7edb0SClaudiu Beznea 		dev->suspend_irq = 0;
319abc7edb0SClaudiu Beznea 		fallthrough;
32006ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3210ab4bd5bSClaudiu Beznea 		ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
3220ab4bd5bSClaudiu Beznea 					 SPDIFTX_MR_TXEN_ENABLE);
32306ca24e9SCodrin Ciubotariu 		break;
32406ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_TRIGGER_SUSPEND:
325abc7edb0SClaudiu Beznea 		regmap_read(dev->regmap, SPDIFTX_IMR, &dev->suspend_irq);
326abc7edb0SClaudiu Beznea 		fallthrough;
327abc7edb0SClaudiu Beznea 	case SNDRV_PCM_TRIGGER_STOP:
328abc7edb0SClaudiu Beznea 		regmap_write(dev->regmap, SPDIFTX_IDR, dev->suspend_irq |
329abc7edb0SClaudiu Beznea 			     SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
330abc7edb0SClaudiu Beznea 		fallthrough;
33106ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3320ab4bd5bSClaudiu Beznea 		ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
3330ab4bd5bSClaudiu Beznea 					 SPDIFTX_MR_TXEN_DISABLE);
33406ca24e9SCodrin Ciubotariu 		break;
33506ca24e9SCodrin Ciubotariu 	default:
3360ab4bd5bSClaudiu Beznea 		ret = -EINVAL;
33706ca24e9SCodrin Ciubotariu 	}
33806ca24e9SCodrin Ciubotariu 	spin_unlock(&ctrl->lock);
339d346a4adSClaudiu Beznea 	if (ret)
3402d8dad4dSClaudiu Beznea 		dev_err(dev->dev, "unable to start/stop TX: %d\n", ret);
34106ca24e9SCodrin Ciubotariu 
342d346a4adSClaudiu Beznea 	return ret;
34306ca24e9SCodrin Ciubotariu }
34406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)34506ca24e9SCodrin Ciubotariu static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
34606ca24e9SCodrin Ciubotariu 				  struct snd_pcm_hw_params *params,
34706ca24e9SCodrin Ciubotariu 				  struct snd_soc_dai *dai)
34806ca24e9SCodrin Ciubotariu {
34906ca24e9SCodrin Ciubotariu 	unsigned long flags;
35006ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
35106ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
35206ca24e9SCodrin Ciubotariu 	u32 mr;
35306ca24e9SCodrin Ciubotariu 	unsigned int bps = params_physical_width(params) / 8;
354215450ebSClaudiu Beznea 	unsigned char aes3;
35506ca24e9SCodrin Ciubotariu 	int ret;
35606ca24e9SCodrin Ciubotariu 
35706ca24e9SCodrin Ciubotariu 	dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
35806ca24e9SCodrin Ciubotariu 		__func__, params_rate(params), params_format(params),
35906ca24e9SCodrin Ciubotariu 		params_width(params), params_channels(params));
36006ca24e9SCodrin Ciubotariu 
36106ca24e9SCodrin Ciubotariu 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
36206ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "Capture is not supported\n");
36306ca24e9SCodrin Ciubotariu 		return -EINVAL;
36406ca24e9SCodrin Ciubotariu 	}
36506ca24e9SCodrin Ciubotariu 
36606ca24e9SCodrin Ciubotariu 	regmap_read(dev->regmap, SPDIFTX_MR, &mr);
36706ca24e9SCodrin Ciubotariu 
36806ca24e9SCodrin Ciubotariu 	if (mr & SPDIFTX_MR_TXEN_ENABLE) {
36906ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "PCM already running\n");
37006ca24e9SCodrin Ciubotariu 		return -EBUSY;
37106ca24e9SCodrin Ciubotariu 	}
37206ca24e9SCodrin Ciubotariu 
37306ca24e9SCodrin Ciubotariu 	/* Defaults: Toggle mode, justify to LSB, chunksize 1 */
37406ca24e9SCodrin Ciubotariu 	mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB;
37506ca24e9SCodrin Ciubotariu 	dev->playback.maxburst = 1;
37606ca24e9SCodrin Ciubotariu 	switch (params_channels(params)) {
37706ca24e9SCodrin Ciubotariu 	case 1:
37806ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_MULTICH_MONO;
37906ca24e9SCodrin Ciubotariu 		break;
38006ca24e9SCodrin Ciubotariu 	case 2:
38106ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_MULTICH_DUAL;
38206ca24e9SCodrin Ciubotariu 		if (bps > 2)
38306ca24e9SCodrin Ciubotariu 			dev->playback.maxburst = 2;
38406ca24e9SCodrin Ciubotariu 		break;
38506ca24e9SCodrin Ciubotariu 	default:
38606ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "unsupported number of channels: %d\n",
38706ca24e9SCodrin Ciubotariu 			params_channels(params));
38806ca24e9SCodrin Ciubotariu 		return -EINVAL;
38906ca24e9SCodrin Ciubotariu 	}
39028ce5698SClaudiu Beznea 	mr |= FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, dev->playback.maxburst);
39106ca24e9SCodrin Ciubotariu 
39206ca24e9SCodrin Ciubotariu 	switch (params_format(params)) {
39306ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S8:
39428ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 8);
39506ca24e9SCodrin Ciubotariu 		break;
39606ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S16_BE:
39706ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
39806ca24e9SCodrin Ciubotariu 		fallthrough;
39906ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S16_LE:
40028ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 16);
40106ca24e9SCodrin Ciubotariu 		break;
40206ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S18_3BE:
40306ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
40406ca24e9SCodrin Ciubotariu 		fallthrough;
40506ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S18_3LE:
40628ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 18);
40706ca24e9SCodrin Ciubotariu 		break;
40806ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S20_3BE:
40906ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
41006ca24e9SCodrin Ciubotariu 		fallthrough;
41106ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S20_3LE:
41228ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 20);
41306ca24e9SCodrin Ciubotariu 		break;
41406ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S24_3BE:
41506ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
41606ca24e9SCodrin Ciubotariu 		fallthrough;
41706ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S24_3LE:
41828ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
41906ca24e9SCodrin Ciubotariu 		break;
42006ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S24_BE:
42106ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
42206ca24e9SCodrin Ciubotariu 		fallthrough;
42306ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S24_LE:
42428ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
42506ca24e9SCodrin Ciubotariu 		break;
42606ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S32_BE:
42706ca24e9SCodrin Ciubotariu 		mr |= SPDIFTX_MR_ENDIAN_BIG;
42806ca24e9SCodrin Ciubotariu 		fallthrough;
42906ca24e9SCodrin Ciubotariu 	case SNDRV_PCM_FORMAT_S32_LE:
43028ce5698SClaudiu Beznea 		mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 32);
43106ca24e9SCodrin Ciubotariu 		break;
43206ca24e9SCodrin Ciubotariu 	default:
43306ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "unsupported PCM format: %d\n",
43406ca24e9SCodrin Ciubotariu 			params_format(params));
43506ca24e9SCodrin Ciubotariu 		return -EINVAL;
43606ca24e9SCodrin Ciubotariu 	}
43706ca24e9SCodrin Ciubotariu 
43828ce5698SClaudiu Beznea 	mr |= FIELD_PREP(SPDIFTX_MR_BPS_MASK, bps - 1);
43906ca24e9SCodrin Ciubotariu 
44006ca24e9SCodrin Ciubotariu 	switch (params_rate(params)) {
44106ca24e9SCodrin Ciubotariu 	case 22050:
442215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_22050;
44306ca24e9SCodrin Ciubotariu 		break;
44406ca24e9SCodrin Ciubotariu 	case 24000:
445215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_24000;
44606ca24e9SCodrin Ciubotariu 		break;
44706ca24e9SCodrin Ciubotariu 	case 32000:
448215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_32000;
44906ca24e9SCodrin Ciubotariu 		break;
45006ca24e9SCodrin Ciubotariu 	case 44100:
451215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_44100;
45206ca24e9SCodrin Ciubotariu 		break;
45306ca24e9SCodrin Ciubotariu 	case 48000:
454215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_48000;
45506ca24e9SCodrin Ciubotariu 		break;
45606ca24e9SCodrin Ciubotariu 	case 88200:
457215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_88200;
45806ca24e9SCodrin Ciubotariu 		break;
45906ca24e9SCodrin Ciubotariu 	case 96000:
460215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_96000;
46106ca24e9SCodrin Ciubotariu 		break;
46206ca24e9SCodrin Ciubotariu 	case 176400:
463215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_176400;
46406ca24e9SCodrin Ciubotariu 		break;
46506ca24e9SCodrin Ciubotariu 	case 192000:
466215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_192000;
46706ca24e9SCodrin Ciubotariu 		break;
46806ca24e9SCodrin Ciubotariu 	case 8000:
46906ca24e9SCodrin Ciubotariu 	case 11025:
47006ca24e9SCodrin Ciubotariu 	case 16000:
47106ca24e9SCodrin Ciubotariu 	case 64000:
472215450ebSClaudiu Beznea 		aes3 = IEC958_AES3_CON_FS_NOTID;
47306ca24e9SCodrin Ciubotariu 		break;
47406ca24e9SCodrin Ciubotariu 	default:
47506ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "unsupported sample frequency: %u\n",
47606ca24e9SCodrin Ciubotariu 			params_rate(params));
47706ca24e9SCodrin Ciubotariu 		return -EINVAL;
47806ca24e9SCodrin Ciubotariu 	}
479215450ebSClaudiu Beznea 	spin_lock_irqsave(&ctrl->lock, flags);
480215450ebSClaudiu Beznea 	ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS;
481215450ebSClaudiu Beznea 	ctrl->ch_stat[3] |= aes3;
48206ca24e9SCodrin Ciubotariu 	mchp_spdiftx_channel_status_write(dev);
48306ca24e9SCodrin Ciubotariu 	spin_unlock_irqrestore(&ctrl->lock, flags);
48406ca24e9SCodrin Ciubotariu 
4854bf54ca6SClaudiu Beznea 	/* GCLK is enabled by runtime PM. */
48606ca24e9SCodrin Ciubotariu 	clk_disable_unprepare(dev->gclk);
4874bf54ca6SClaudiu Beznea 
48806ca24e9SCodrin Ciubotariu 	ret = clk_set_rate(dev->gclk, params_rate(params) *
48906ca24e9SCodrin Ciubotariu 				      SPDIFTX_GCLK_RATIO);
49006ca24e9SCodrin Ciubotariu 	if (ret) {
49106ca24e9SCodrin Ciubotariu 		dev_err(dev->dev,
49206ca24e9SCodrin Ciubotariu 			"unable to change gclk rate to: rate %u * ratio %u\n",
49306ca24e9SCodrin Ciubotariu 			params_rate(params), SPDIFTX_GCLK_RATIO);
49406ca24e9SCodrin Ciubotariu 		return ret;
49506ca24e9SCodrin Ciubotariu 	}
49606ca24e9SCodrin Ciubotariu 	ret = clk_prepare_enable(dev->gclk);
49706ca24e9SCodrin Ciubotariu 	if (ret) {
49806ca24e9SCodrin Ciubotariu 		dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
49906ca24e9SCodrin Ciubotariu 		return ret;
50006ca24e9SCodrin Ciubotariu 	}
5014bf54ca6SClaudiu Beznea 
50206ca24e9SCodrin Ciubotariu 	dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__,
50306ca24e9SCodrin Ciubotariu 		params_rate(params) * SPDIFTX_GCLK_RATIO);
50406ca24e9SCodrin Ciubotariu 
50506ca24e9SCodrin Ciubotariu 	regmap_write(dev->regmap, SPDIFTX_MR, mr);
50606ca24e9SCodrin Ciubotariu 
50706ca24e9SCodrin Ciubotariu 	return 0;
50806ca24e9SCodrin Ciubotariu }
50906ca24e9SCodrin Ciubotariu 
mchp_spdiftx_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)51006ca24e9SCodrin Ciubotariu static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream,
51106ca24e9SCodrin Ciubotariu 				struct snd_soc_dai *dai)
51206ca24e9SCodrin Ciubotariu {
51306ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
51406ca24e9SCodrin Ciubotariu 
51506ca24e9SCodrin Ciubotariu 	return regmap_write(dev->regmap, SPDIFTX_CR,
51606ca24e9SCodrin Ciubotariu 			    SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
51706ca24e9SCodrin Ciubotariu }
51806ca24e9SCodrin Ciubotariu 
51906ca24e9SCodrin Ciubotariu #define MCHP_SPDIFTX_RATES	SNDRV_PCM_RATE_8000_192000
52006ca24e9SCodrin Ciubotariu 
52106ca24e9SCodrin Ciubotariu #define MCHP_SPDIFTX_FORMATS	(SNDRV_PCM_FMTBIT_S8 |		\
52206ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S16_LE |	\
52306ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_U16_BE |	\
52406ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S18_3LE |	\
52506ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S18_3BE |	\
52606ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S20_3LE |	\
52706ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S20_3BE |	\
52806ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S24_3LE |	\
52906ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S24_3BE |	\
53006ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S24_LE |	\
53106ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S24_BE |	\
53206ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S32_LE |	\
53306ca24e9SCodrin Ciubotariu 				 SNDRV_PCM_FMTBIT_S32_BE	\
53406ca24e9SCodrin Ciubotariu 				 )
53506ca24e9SCodrin Ciubotariu 
mchp_spdiftx_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)53606ca24e9SCodrin Ciubotariu static int mchp_spdiftx_info(struct snd_kcontrol *kcontrol,
53706ca24e9SCodrin Ciubotariu 			     struct snd_ctl_elem_info *uinfo)
53806ca24e9SCodrin Ciubotariu {
53906ca24e9SCodrin Ciubotariu 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
54006ca24e9SCodrin Ciubotariu 	uinfo->count = 1;
54106ca24e9SCodrin Ciubotariu 
54206ca24e9SCodrin Ciubotariu 	return 0;
54306ca24e9SCodrin Ciubotariu }
54406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_cs_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uvalue)54506ca24e9SCodrin Ciubotariu static int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol,
54606ca24e9SCodrin Ciubotariu 			       struct snd_ctl_elem_value *uvalue)
54706ca24e9SCodrin Ciubotariu {
54806ca24e9SCodrin Ciubotariu 	unsigned long flags;
54906ca24e9SCodrin Ciubotariu 	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
55006ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
55106ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
55206ca24e9SCodrin Ciubotariu 
55306ca24e9SCodrin Ciubotariu 	spin_lock_irqsave(&ctrl->lock, flags);
55406ca24e9SCodrin Ciubotariu 	memcpy(uvalue->value.iec958.status, ctrl->ch_stat,
55506ca24e9SCodrin Ciubotariu 	       sizeof(ctrl->ch_stat));
55606ca24e9SCodrin Ciubotariu 	spin_unlock_irqrestore(&ctrl->lock, flags);
55706ca24e9SCodrin Ciubotariu 
55806ca24e9SCodrin Ciubotariu 	return 0;
55906ca24e9SCodrin Ciubotariu }
56006ca24e9SCodrin Ciubotariu 
mchp_spdiftx_cs_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uvalue)56106ca24e9SCodrin Ciubotariu static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol,
56206ca24e9SCodrin Ciubotariu 			       struct snd_ctl_elem_value *uvalue)
56306ca24e9SCodrin Ciubotariu {
56406ca24e9SCodrin Ciubotariu 	unsigned long flags;
56506ca24e9SCodrin Ciubotariu 	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
56606ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
56706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
56806ca24e9SCodrin Ciubotariu 	int changed = 0;
56906ca24e9SCodrin Ciubotariu 	int i;
57006ca24e9SCodrin Ciubotariu 
57106ca24e9SCodrin Ciubotariu 	spin_lock_irqsave(&ctrl->lock, flags);
57206ca24e9SCodrin Ciubotariu 	for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) {
57306ca24e9SCodrin Ciubotariu 		if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i])
57406ca24e9SCodrin Ciubotariu 			changed = 1;
57506ca24e9SCodrin Ciubotariu 		ctrl->ch_stat[i] = uvalue->value.iec958.status[i];
57606ca24e9SCodrin Ciubotariu 	}
57706ca24e9SCodrin Ciubotariu 
57806ca24e9SCodrin Ciubotariu 	if (changed) {
57906ca24e9SCodrin Ciubotariu 		/* don't enable IP while we copy the channel status */
58006ca24e9SCodrin Ciubotariu 		if (mchp_spdiftx_is_running(dev)) {
58106ca24e9SCodrin Ciubotariu 			/*
58206ca24e9SCodrin Ciubotariu 			 * if SPDIF is running, wait for interrupt to write
58306ca24e9SCodrin Ciubotariu 			 * channel status
58406ca24e9SCodrin Ciubotariu 			 */
58506ca24e9SCodrin Ciubotariu 			regmap_write(dev->regmap, SPDIFTX_IER,
58606ca24e9SCodrin Ciubotariu 				     SPDIFTX_IR_CSRDY);
58706ca24e9SCodrin Ciubotariu 		} else {
58806ca24e9SCodrin Ciubotariu 			mchp_spdiftx_channel_status_write(dev);
58906ca24e9SCodrin Ciubotariu 		}
59006ca24e9SCodrin Ciubotariu 	}
59106ca24e9SCodrin Ciubotariu 	spin_unlock_irqrestore(&ctrl->lock, flags);
59206ca24e9SCodrin Ciubotariu 
59306ca24e9SCodrin Ciubotariu 	return changed;
59406ca24e9SCodrin Ciubotariu }
59506ca24e9SCodrin Ciubotariu 
mchp_spdiftx_cs_mask(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uvalue)59606ca24e9SCodrin Ciubotariu static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol,
59706ca24e9SCodrin Ciubotariu 				struct snd_ctl_elem_value *uvalue)
59806ca24e9SCodrin Ciubotariu {
59906ca24e9SCodrin Ciubotariu 	memset(uvalue->value.iec958.status, 0xff,
60006ca24e9SCodrin Ciubotariu 	       sizeof(uvalue->value.iec958.status));
60106ca24e9SCodrin Ciubotariu 
60206ca24e9SCodrin Ciubotariu 	return 0;
60306ca24e9SCodrin Ciubotariu }
60406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_subcode_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uvalue)60506ca24e9SCodrin Ciubotariu static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol,
60606ca24e9SCodrin Ciubotariu 				    struct snd_ctl_elem_value *uvalue)
60706ca24e9SCodrin Ciubotariu {
60806ca24e9SCodrin Ciubotariu 	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
60906ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
61006ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
61106ca24e9SCodrin Ciubotariu 	unsigned long flags;
61206ca24e9SCodrin Ciubotariu 
61306ca24e9SCodrin Ciubotariu 	spin_lock_irqsave(&ctrl->lock, flags);
61406ca24e9SCodrin Ciubotariu 	memcpy(uvalue->value.iec958.subcode, ctrl->user_data,
61506ca24e9SCodrin Ciubotariu 	       sizeof(ctrl->user_data));
61606ca24e9SCodrin Ciubotariu 	spin_unlock_irqrestore(&ctrl->lock, flags);
61706ca24e9SCodrin Ciubotariu 
61806ca24e9SCodrin Ciubotariu 	return 0;
61906ca24e9SCodrin Ciubotariu }
62006ca24e9SCodrin Ciubotariu 
mchp_spdiftx_subcode_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * uvalue)62106ca24e9SCodrin Ciubotariu static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol,
62206ca24e9SCodrin Ciubotariu 				    struct snd_ctl_elem_value *uvalue)
62306ca24e9SCodrin Ciubotariu {
62406ca24e9SCodrin Ciubotariu 	unsigned long flags;
62506ca24e9SCodrin Ciubotariu 	struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
62606ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
62706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
62806ca24e9SCodrin Ciubotariu 	int changed = 0;
62906ca24e9SCodrin Ciubotariu 	int i;
63006ca24e9SCodrin Ciubotariu 
63106ca24e9SCodrin Ciubotariu 	spin_lock_irqsave(&ctrl->lock, flags);
63206ca24e9SCodrin Ciubotariu 	for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) {
63306ca24e9SCodrin Ciubotariu 		if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i])
63406ca24e9SCodrin Ciubotariu 			changed = 1;
63506ca24e9SCodrin Ciubotariu 
63606ca24e9SCodrin Ciubotariu 		ctrl->user_data[i] = uvalue->value.iec958.subcode[i];
63706ca24e9SCodrin Ciubotariu 	}
63806ca24e9SCodrin Ciubotariu 	if (changed) {
63906ca24e9SCodrin Ciubotariu 		if (mchp_spdiftx_is_running(dev)) {
64006ca24e9SCodrin Ciubotariu 			/*
64106ca24e9SCodrin Ciubotariu 			 * if SPDIF is running, wait for interrupt to write
64206ca24e9SCodrin Ciubotariu 			 * user data
64306ca24e9SCodrin Ciubotariu 			 */
64406ca24e9SCodrin Ciubotariu 			regmap_write(dev->regmap, SPDIFTX_IER,
64506ca24e9SCodrin Ciubotariu 				     SPDIFTX_IR_UDRDY);
64606ca24e9SCodrin Ciubotariu 		} else {
64706ca24e9SCodrin Ciubotariu 			mchp_spdiftx_user_data_write(dev);
64806ca24e9SCodrin Ciubotariu 		}
64906ca24e9SCodrin Ciubotariu 	}
65006ca24e9SCodrin Ciubotariu 	spin_unlock_irqrestore(&ctrl->lock, flags);
65106ca24e9SCodrin Ciubotariu 
65206ca24e9SCodrin Ciubotariu 	return changed;
65306ca24e9SCodrin Ciubotariu }
65406ca24e9SCodrin Ciubotariu 
65506ca24e9SCodrin Ciubotariu static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = {
65606ca24e9SCodrin Ciubotariu 	/* Channel status controller */
65706ca24e9SCodrin Ciubotariu 	{
65806ca24e9SCodrin Ciubotariu 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
65906ca24e9SCodrin Ciubotariu 		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
66006ca24e9SCodrin Ciubotariu 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
66106ca24e9SCodrin Ciubotariu 			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
66206ca24e9SCodrin Ciubotariu 		.info = mchp_spdiftx_info,
66306ca24e9SCodrin Ciubotariu 		.get = mchp_spdiftx_cs_get,
66406ca24e9SCodrin Ciubotariu 		.put = mchp_spdiftx_cs_put,
66506ca24e9SCodrin Ciubotariu 	},
66606ca24e9SCodrin Ciubotariu 	{
66706ca24e9SCodrin Ciubotariu 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
66806ca24e9SCodrin Ciubotariu 		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
66906ca24e9SCodrin Ciubotariu 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
67006ca24e9SCodrin Ciubotariu 			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
67106ca24e9SCodrin Ciubotariu 		.info = mchp_spdiftx_info,
67206ca24e9SCodrin Ciubotariu 		.get = mchp_spdiftx_cs_mask,
67306ca24e9SCodrin Ciubotariu 	},
67406ca24e9SCodrin Ciubotariu 	/* User bits controller */
67506ca24e9SCodrin Ciubotariu 	{
67606ca24e9SCodrin Ciubotariu 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
67706ca24e9SCodrin Ciubotariu 		.name = "IEC958 Subcode Playback Default",
67806ca24e9SCodrin Ciubotariu 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
67906ca24e9SCodrin Ciubotariu 		.info = mchp_spdiftx_info,
68006ca24e9SCodrin Ciubotariu 		.get = mchp_spdiftx_subcode_get,
68106ca24e9SCodrin Ciubotariu 		.put = mchp_spdiftx_subcode_put,
68206ca24e9SCodrin Ciubotariu 	},
68306ca24e9SCodrin Ciubotariu };
68406ca24e9SCodrin Ciubotariu 
mchp_spdiftx_dai_probe(struct snd_soc_dai * dai)68506ca24e9SCodrin Ciubotariu static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai)
68606ca24e9SCodrin Ciubotariu {
68706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
68806ca24e9SCodrin Ciubotariu 
68906ca24e9SCodrin Ciubotariu 	snd_soc_dai_init_dma_data(dai, &dev->playback, NULL);
69006ca24e9SCodrin Ciubotariu 
69106ca24e9SCodrin Ciubotariu 	/* Add controls */
69206ca24e9SCodrin Ciubotariu 	snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls,
69306ca24e9SCodrin Ciubotariu 				 ARRAY_SIZE(mchp_spdiftx_ctrls));
69406ca24e9SCodrin Ciubotariu 
69506ca24e9SCodrin Ciubotariu 	return 0;
69606ca24e9SCodrin Ciubotariu }
69706ca24e9SCodrin Ciubotariu 
698*2ff8a43dSKuninori Morimoto static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = {
699*2ff8a43dSKuninori Morimoto 	.probe		= mchp_spdiftx_dai_probe,
700*2ff8a43dSKuninori Morimoto 	.startup	= mchp_spdiftx_dai_startup,
701*2ff8a43dSKuninori Morimoto 	.shutdown	= mchp_spdiftx_dai_shutdown,
702*2ff8a43dSKuninori Morimoto 	.trigger	= mchp_spdiftx_trigger,
703*2ff8a43dSKuninori Morimoto 	.hw_params	= mchp_spdiftx_hw_params,
704*2ff8a43dSKuninori Morimoto 	.hw_free	= mchp_spdiftx_hw_free,
705*2ff8a43dSKuninori Morimoto };
706*2ff8a43dSKuninori Morimoto 
70706ca24e9SCodrin Ciubotariu static struct snd_soc_dai_driver mchp_spdiftx_dai = {
70806ca24e9SCodrin Ciubotariu 	.name = "mchp-spdiftx",
70906ca24e9SCodrin Ciubotariu 	.playback = {
710b899e4fdSCodrin Ciubotariu 		.stream_name = "S/PDIF Playback",
71106ca24e9SCodrin Ciubotariu 		.channels_min = 1,
71206ca24e9SCodrin Ciubotariu 		.channels_max = 2,
71306ca24e9SCodrin Ciubotariu 		.rates = MCHP_SPDIFTX_RATES,
71406ca24e9SCodrin Ciubotariu 		.formats = MCHP_SPDIFTX_FORMATS,
71506ca24e9SCodrin Ciubotariu 	},
71606ca24e9SCodrin Ciubotariu 	.ops = &mchp_spdiftx_dai_ops,
71706ca24e9SCodrin Ciubotariu };
71806ca24e9SCodrin Ciubotariu 
71906ca24e9SCodrin Ciubotariu static const struct snd_soc_component_driver mchp_spdiftx_component = {
72006ca24e9SCodrin Ciubotariu 	.name			= "mchp-spdiftx",
7217593e008SCharles Keepax 	.legacy_dai_naming	= 1,
72206ca24e9SCodrin Ciubotariu };
72306ca24e9SCodrin Ciubotariu 
72406ca24e9SCodrin Ciubotariu static const struct of_device_id mchp_spdiftx_dt_ids[] = {
72506ca24e9SCodrin Ciubotariu 	{
72606ca24e9SCodrin Ciubotariu 		.compatible = "microchip,sama7g5-spdiftx",
72706ca24e9SCodrin Ciubotariu 	},
72806ca24e9SCodrin Ciubotariu 	{ /* sentinel */ }
72906ca24e9SCodrin Ciubotariu };
73006ca24e9SCodrin Ciubotariu MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids);
73196f6017dSClaudiu Beznea 
mchp_spdiftx_runtime_suspend(struct device * dev)7324bf54ca6SClaudiu Beznea static int mchp_spdiftx_runtime_suspend(struct device *dev)
7334bf54ca6SClaudiu Beznea {
7344bf54ca6SClaudiu Beznea 	struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
7354bf54ca6SClaudiu Beznea 
7364bf54ca6SClaudiu Beznea 	regcache_cache_only(spdiftx->regmap, true);
7374bf54ca6SClaudiu Beznea 
7384bf54ca6SClaudiu Beznea 	clk_disable_unprepare(spdiftx->gclk);
7394bf54ca6SClaudiu Beznea 	clk_disable_unprepare(spdiftx->pclk);
7404bf54ca6SClaudiu Beznea 
7414bf54ca6SClaudiu Beznea 	return 0;
7424bf54ca6SClaudiu Beznea }
7434bf54ca6SClaudiu Beznea 
mchp_spdiftx_runtime_resume(struct device * dev)7444bf54ca6SClaudiu Beznea static int mchp_spdiftx_runtime_resume(struct device *dev)
7454bf54ca6SClaudiu Beznea {
7464bf54ca6SClaudiu Beznea 	struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
7474bf54ca6SClaudiu Beznea 	int ret;
7484bf54ca6SClaudiu Beznea 
7494bf54ca6SClaudiu Beznea 	ret = clk_prepare_enable(spdiftx->pclk);
7504bf54ca6SClaudiu Beznea 	if (ret) {
7514bf54ca6SClaudiu Beznea 		dev_err(spdiftx->dev,
7524bf54ca6SClaudiu Beznea 			"failed to enable the peripheral clock: %d\n", ret);
7534bf54ca6SClaudiu Beznea 		return ret;
7544bf54ca6SClaudiu Beznea 	}
7554bf54ca6SClaudiu Beznea 	ret = clk_prepare_enable(spdiftx->gclk);
7564bf54ca6SClaudiu Beznea 	if (ret) {
7574bf54ca6SClaudiu Beznea 		dev_err(spdiftx->dev,
7584bf54ca6SClaudiu Beznea 			"failed to enable generic clock: %d\n", ret);
7594bf54ca6SClaudiu Beznea 		goto disable_pclk;
7604bf54ca6SClaudiu Beznea 	}
7614bf54ca6SClaudiu Beznea 
7624bf54ca6SClaudiu Beznea 	regcache_cache_only(spdiftx->regmap, false);
7634bf54ca6SClaudiu Beznea 	regcache_mark_dirty(spdiftx->regmap);
7644bf54ca6SClaudiu Beznea 	ret = regcache_sync(spdiftx->regmap);
7654bf54ca6SClaudiu Beznea 	if (ret) {
7664bf54ca6SClaudiu Beznea 		regcache_cache_only(spdiftx->regmap, true);
7674bf54ca6SClaudiu Beznea 		clk_disable_unprepare(spdiftx->gclk);
7684bf54ca6SClaudiu Beznea disable_pclk:
7694bf54ca6SClaudiu Beznea 		clk_disable_unprepare(spdiftx->pclk);
7704bf54ca6SClaudiu Beznea 	}
7714bf54ca6SClaudiu Beznea 
7724bf54ca6SClaudiu Beznea 	return ret;
7734bf54ca6SClaudiu Beznea }
7744bf54ca6SClaudiu Beznea 
7754bf54ca6SClaudiu Beznea static const struct dev_pm_ops mchp_spdiftx_pm_ops = {
776abc7edb0SClaudiu Beznea 	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
7774bf54ca6SClaudiu Beznea 	RUNTIME_PM_OPS(mchp_spdiftx_runtime_suspend, mchp_spdiftx_runtime_resume,
7784bf54ca6SClaudiu Beznea 		       NULL)
7794bf54ca6SClaudiu Beznea };
7804bf54ca6SClaudiu Beznea 
mchp_spdiftx_probe(struct platform_device * pdev)78106ca24e9SCodrin Ciubotariu static int mchp_spdiftx_probe(struct platform_device *pdev)
78206ca24e9SCodrin Ciubotariu {
78306ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_dev *dev;
78406ca24e9SCodrin Ciubotariu 	struct resource *mem;
78506ca24e9SCodrin Ciubotariu 	struct regmap *regmap;
78606ca24e9SCodrin Ciubotariu 	void __iomem *base;
78706ca24e9SCodrin Ciubotariu 	struct mchp_spdiftx_mixer_control *ctrl;
78806ca24e9SCodrin Ciubotariu 	int irq;
78906ca24e9SCodrin Ciubotariu 	int err;
79006ca24e9SCodrin Ciubotariu 
79106ca24e9SCodrin Ciubotariu 	/* Get memory for driver data. */
79206ca24e9SCodrin Ciubotariu 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
79306ca24e9SCodrin Ciubotariu 	if (!dev)
79406ca24e9SCodrin Ciubotariu 		return -ENOMEM;
79506ca24e9SCodrin Ciubotariu 
79606ca24e9SCodrin Ciubotariu 	/* Map I/O registers. */
79706ca24e9SCodrin Ciubotariu 	base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
79806ca24e9SCodrin Ciubotariu 	if (IS_ERR(base))
79906ca24e9SCodrin Ciubotariu 		return PTR_ERR(base);
80006ca24e9SCodrin Ciubotariu 
80106ca24e9SCodrin Ciubotariu 	regmap = devm_regmap_init_mmio(&pdev->dev, base,
80206ca24e9SCodrin Ciubotariu 				       &mchp_spdiftx_regmap_config);
80306ca24e9SCodrin Ciubotariu 	if (IS_ERR(regmap))
80406ca24e9SCodrin Ciubotariu 		return PTR_ERR(regmap);
80506ca24e9SCodrin Ciubotariu 
80606ca24e9SCodrin Ciubotariu 	/* Request IRQ */
80706ca24e9SCodrin Ciubotariu 	irq = platform_get_irq(pdev, 0);
80806ca24e9SCodrin Ciubotariu 	if (irq < 0)
80906ca24e9SCodrin Ciubotariu 		return irq;
81006ca24e9SCodrin Ciubotariu 
81106ca24e9SCodrin Ciubotariu 	err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0,
81206ca24e9SCodrin Ciubotariu 			       dev_name(&pdev->dev), dev);
81306ca24e9SCodrin Ciubotariu 	if (err)
81406ca24e9SCodrin Ciubotariu 		return err;
81506ca24e9SCodrin Ciubotariu 
81606ca24e9SCodrin Ciubotariu 	/* Get the peripheral clock */
81706ca24e9SCodrin Ciubotariu 	dev->pclk = devm_clk_get(&pdev->dev, "pclk");
81806ca24e9SCodrin Ciubotariu 	if (IS_ERR(dev->pclk)) {
81906ca24e9SCodrin Ciubotariu 		err = PTR_ERR(dev->pclk);
82006ca24e9SCodrin Ciubotariu 		dev_err(&pdev->dev,
82106ca24e9SCodrin Ciubotariu 			"failed to get the peripheral clock: %d\n", err);
82206ca24e9SCodrin Ciubotariu 		return err;
82306ca24e9SCodrin Ciubotariu 	}
82406ca24e9SCodrin Ciubotariu 
82506ca24e9SCodrin Ciubotariu 	/* Get the generic clock */
82606ca24e9SCodrin Ciubotariu 	dev->gclk = devm_clk_get(&pdev->dev, "gclk");
82706ca24e9SCodrin Ciubotariu 	if (IS_ERR(dev->gclk)) {
82806ca24e9SCodrin Ciubotariu 		err = PTR_ERR(dev->gclk);
82906ca24e9SCodrin Ciubotariu 		dev_err(&pdev->dev,
83006ca24e9SCodrin Ciubotariu 			"failed to get the PMC generic clock: %d\n", err);
83106ca24e9SCodrin Ciubotariu 		return err;
83206ca24e9SCodrin Ciubotariu 	}
83306ca24e9SCodrin Ciubotariu 
83406ca24e9SCodrin Ciubotariu 	ctrl = &dev->control;
83506ca24e9SCodrin Ciubotariu 	spin_lock_init(&ctrl->lock);
83606ca24e9SCodrin Ciubotariu 
83706ca24e9SCodrin Ciubotariu 	/* Init channel status */
83806ca24e9SCodrin Ciubotariu 	ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT |
83906ca24e9SCodrin Ciubotariu 			   IEC958_AES0_CON_EMPHASIS_NONE;
84006ca24e9SCodrin Ciubotariu 
84106ca24e9SCodrin Ciubotariu 	dev->dev = &pdev->dev;
84206ca24e9SCodrin Ciubotariu 	dev->regmap = regmap;
84306ca24e9SCodrin Ciubotariu 	platform_set_drvdata(pdev, dev);
84406ca24e9SCodrin Ciubotariu 
8454bf54ca6SClaudiu Beznea 	pm_runtime_enable(dev->dev);
8464bf54ca6SClaudiu Beznea 	if (!pm_runtime_enabled(dev->dev)) {
8474bf54ca6SClaudiu Beznea 		err = mchp_spdiftx_runtime_resume(dev->dev);
8484bf54ca6SClaudiu Beznea 		if (err)
8494bf54ca6SClaudiu Beznea 			return err;
8504bf54ca6SClaudiu Beznea 	}
8514bf54ca6SClaudiu Beznea 
85206ca24e9SCodrin Ciubotariu 	dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR;
85306ca24e9SCodrin Ciubotariu 	dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
85406ca24e9SCodrin Ciubotariu 
85506ca24e9SCodrin Ciubotariu 	err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
85606ca24e9SCodrin Ciubotariu 	if (err) {
85706ca24e9SCodrin Ciubotariu 		dev_err(&pdev->dev, "failed to register PMC: %d\n", err);
8584bf54ca6SClaudiu Beznea 		goto pm_runtime_suspend;
85906ca24e9SCodrin Ciubotariu 	}
86006ca24e9SCodrin Ciubotariu 
86106ca24e9SCodrin Ciubotariu 	err = devm_snd_soc_register_component(&pdev->dev,
86206ca24e9SCodrin Ciubotariu 					      &mchp_spdiftx_component,
86306ca24e9SCodrin Ciubotariu 					      &mchp_spdiftx_dai, 1);
8644bf54ca6SClaudiu Beznea 	if (err) {
86506ca24e9SCodrin Ciubotariu 		dev_err(&pdev->dev, "failed to register component: %d\n", err);
8664bf54ca6SClaudiu Beznea 		goto pm_runtime_suspend;
8674bf54ca6SClaudiu Beznea 	}
8684bf54ca6SClaudiu Beznea 
8694bf54ca6SClaudiu Beznea 	return 0;
8704bf54ca6SClaudiu Beznea 
8714bf54ca6SClaudiu Beznea pm_runtime_suspend:
8724bf54ca6SClaudiu Beznea 	if (!pm_runtime_status_suspended(dev->dev))
8734bf54ca6SClaudiu Beznea 		mchp_spdiftx_runtime_suspend(dev->dev);
8744bf54ca6SClaudiu Beznea 	pm_runtime_disable(dev->dev);
87506ca24e9SCodrin Ciubotariu 
876d346a4adSClaudiu Beznea 	return err;
87706ca24e9SCodrin Ciubotariu }
87806ca24e9SCodrin Ciubotariu 
mchp_spdiftx_remove(struct platform_device * pdev)879c1d51c27SUwe Kleine-König static void mchp_spdiftx_remove(struct platform_device *pdev)
8804bf54ca6SClaudiu Beznea {
8814bf54ca6SClaudiu Beznea 	struct mchp_spdiftx_dev *dev = platform_get_drvdata(pdev);
8824bf54ca6SClaudiu Beznea 
8834bf54ca6SClaudiu Beznea 	if (!pm_runtime_status_suspended(dev->dev))
8844bf54ca6SClaudiu Beznea 		mchp_spdiftx_runtime_suspend(dev->dev);
8854bf54ca6SClaudiu Beznea 
8864bf54ca6SClaudiu Beznea 	pm_runtime_disable(dev->dev);
8874bf54ca6SClaudiu Beznea }
8884bf54ca6SClaudiu Beznea 
88906ca24e9SCodrin Ciubotariu static struct platform_driver mchp_spdiftx_driver = {
89006ca24e9SCodrin Ciubotariu 	.probe	= mchp_spdiftx_probe,
891c1d51c27SUwe Kleine-König 	.remove_new = mchp_spdiftx_remove,
89206ca24e9SCodrin Ciubotariu 	.driver	= {
89306ca24e9SCodrin Ciubotariu 		.name	= "mchp_spdiftx",
89467ed7812SRuan Jinjie 		.of_match_table = mchp_spdiftx_dt_ids,
8954bf54ca6SClaudiu Beznea 		.pm = pm_ptr(&mchp_spdiftx_pm_ops)
89606ca24e9SCodrin Ciubotariu 	},
89706ca24e9SCodrin Ciubotariu };
89806ca24e9SCodrin Ciubotariu 
89906ca24e9SCodrin Ciubotariu module_platform_driver(mchp_spdiftx_driver);
90006ca24e9SCodrin Ciubotariu 
90106ca24e9SCodrin Ciubotariu MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
90206ca24e9SCodrin Ciubotariu MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver");
90306ca24e9SCodrin Ciubotariu MODULE_LICENSE("GPL v2");
904