18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * DesignWare HDMI audio driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Written and tested against the Designware HDMI Tx found in iMX6.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <drm/bridge/dw_hdmi.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
158c2ecf20Sopenharmony_ci#include <sound/core.h>
168c2ecf20Sopenharmony_ci#include <sound/initval.h>
178c2ecf20Sopenharmony_ci#include <sound/pcm.h>
188c2ecf20Sopenharmony_ci#include <sound/pcm_drm_eld.h>
198c2ecf20Sopenharmony_ci#include <sound/pcm_iec958.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "dw-hdmi-audio.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DRIVER_NAME "dw-hdmi-ahb-audio"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Provide some bits rather than bit offsets */
268c2ecf20Sopenharmony_cienum {
278c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
288c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
298c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_START_START = BIT(0),
308c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_STOP_STOP = BIT(0),
318c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
328c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
338c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
348c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
358c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
368c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
378c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
388c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
398c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
408c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
418c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
428c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
438c2ecf20Sopenharmony_ci		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
448c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
458c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
468c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
478c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
488c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
498c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
508c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0_ALL =
518c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
528c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_LOST |
538c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
548c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_DONE |
558c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
568c2ecf20Sopenharmony_ci		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
578c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
588c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
598c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_INCR4 = 0,
608c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
618c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_MASK_DONE = BIT(7),
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	HDMI_REVISION_ID = 0x0001,
648c2ecf20Sopenharmony_ci	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
658c2ecf20Sopenharmony_ci	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
668c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF0 = 0x3600,
678c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_START = 0x3601,
688c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_STOP = 0x3602,
698c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_THRSLD = 0x3603,
708c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_STRADDR0 = 0x3604,
718c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_STPADDR0 = 0x3608,
728c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_MASK = 0x3614,
738c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_POL = 0x3615,
748c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_CONF1 = 0x3616,
758c2ecf20Sopenharmony_ci	HDMI_AHB_DMA_BUFFPOL = 0x361a,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistruct dw_hdmi_channel_conf {
798c2ecf20Sopenharmony_ci	u8 conf1;
808c2ecf20Sopenharmony_ci	u8 ca;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * The default mapping of ALSA channels to HDMI channels and speaker
858c2ecf20Sopenharmony_ci * allocation bits.  Note that we can't do channel remapping here -
868c2ecf20Sopenharmony_ci * channels must be in the same order.
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci * Mappings for alsa-lib pcm/surround*.conf files:
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1
918c2ecf20Sopenharmony_ci * Channels	2	4	6	6	6	8
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci *				Number of ALSA channels
968c2ecf20Sopenharmony_ci * ALSA Channel	2	3	4	5	6	7	8
978c2ecf20Sopenharmony_ci * 0		FL:0	=	=	=	=	=	=
988c2ecf20Sopenharmony_ci * 1		FR:1	=	=	=	=	=	=
998c2ecf20Sopenharmony_ci * 2			FC:3	RL:4	LFE:2	=	=	=
1008c2ecf20Sopenharmony_ci * 3				RR:5	RL:4	FC:3	=	=
1018c2ecf20Sopenharmony_ci * 4					RR:5	RL:4	=	=
1028c2ecf20Sopenharmony_ci * 5						RR:5	=	=
1038c2ecf20Sopenharmony_ci * 6							RC:6	=
1048c2ecf20Sopenharmony_ci * 7							RLC/FRC	RLC/FRC
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
1078c2ecf20Sopenharmony_ci	{ 0x03, 0x00 },	/* FL,FR */
1088c2ecf20Sopenharmony_ci	{ 0x0b, 0x02 },	/* FL,FR,FC */
1098c2ecf20Sopenharmony_ci	{ 0x33, 0x08 },	/* FL,FR,RL,RR */
1108c2ecf20Sopenharmony_ci	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */
1118c2ecf20Sopenharmony_ci	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */
1128c2ecf20Sopenharmony_ci	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */
1138c2ecf20Sopenharmony_ci	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct snd_dw_hdmi {
1178c2ecf20Sopenharmony_ci	struct snd_card *card;
1188c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
1198c2ecf20Sopenharmony_ci	spinlock_t lock;
1208c2ecf20Sopenharmony_ci	struct dw_hdmi_audio_data data;
1218c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
1228c2ecf20Sopenharmony_ci	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
1238c2ecf20Sopenharmony_ci	void *buf_src;
1248c2ecf20Sopenharmony_ci	void *buf_dst;
1258c2ecf20Sopenharmony_ci	dma_addr_t buf_addr;
1268c2ecf20Sopenharmony_ci	unsigned buf_offset;
1278c2ecf20Sopenharmony_ci	unsigned buf_period;
1288c2ecf20Sopenharmony_ci	unsigned buf_size;
1298c2ecf20Sopenharmony_ci	unsigned channels;
1308c2ecf20Sopenharmony_ci	u8 revision;
1318c2ecf20Sopenharmony_ci	u8 iec_offset;
1328c2ecf20Sopenharmony_ci	u8 cs[192][8];
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void dw_hdmi_writel(u32 val, void __iomem *ptr)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	writeb_relaxed(val, ptr);
1388c2ecf20Sopenharmony_ci	writeb_relaxed(val >> 8, ptr + 1);
1398c2ecf20Sopenharmony_ci	writeb_relaxed(val >> 16, ptr + 2);
1408c2ecf20Sopenharmony_ci	writeb_relaxed(val >> 24, ptr + 3);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/*
1448c2ecf20Sopenharmony_ci * Convert to hardware format: The userspace buffer contains IEC958 samples,
1458c2ecf20Sopenharmony_ci * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
1468c2ecf20Sopenharmony_ci * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
1478c2ecf20Sopenharmony_ci * samples in 23..0.
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
1508c2ecf20Sopenharmony_ci *
1518c2ecf20Sopenharmony_ci * Ideally, we could do with having the data properly formatted in userspace.
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistatic void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
1548c2ecf20Sopenharmony_ci	size_t offset, size_t bytes)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	u32 *src = dw->buf_src + offset;
1578c2ecf20Sopenharmony_ci	u32 *dst = dw->buf_dst + offset;
1588c2ecf20Sopenharmony_ci	u32 *end = dw->buf_src + offset + bytes;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	do {
1618c2ecf20Sopenharmony_ci		u32 b, sample = *src++;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		b = (sample & 8) << (28 - 3);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		sample >>= 4;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		*dst++ = sample | b;
1688c2ecf20Sopenharmony_ci	} while (src < end);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic u32 parity(u32 sample)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	sample ^= sample >> 16;
1748c2ecf20Sopenharmony_ci	sample ^= sample >> 8;
1758c2ecf20Sopenharmony_ci	sample ^= sample >> 4;
1768c2ecf20Sopenharmony_ci	sample ^= sample >> 2;
1778c2ecf20Sopenharmony_ci	sample ^= sample >> 1;
1788c2ecf20Sopenharmony_ci	return (sample & 1) << 27;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
1828c2ecf20Sopenharmony_ci	size_t offset, size_t bytes)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	u32 *src = dw->buf_src + offset;
1858c2ecf20Sopenharmony_ci	u32 *dst = dw->buf_dst + offset;
1868c2ecf20Sopenharmony_ci	u32 *end = dw->buf_src + offset + bytes;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	do {
1898c2ecf20Sopenharmony_ci		unsigned i;
1908c2ecf20Sopenharmony_ci		u8 *cs;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		cs = dw->cs[dw->iec_offset++];
1938c2ecf20Sopenharmony_ci		if (dw->iec_offset >= 192)
1948c2ecf20Sopenharmony_ci			dw->iec_offset = 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		i = dw->channels;
1978c2ecf20Sopenharmony_ci		do {
1988c2ecf20Sopenharmony_ci			u32 sample = *src++;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci			sample &= ~0xff000000;
2018c2ecf20Sopenharmony_ci			sample |= *cs++ << 24;
2028c2ecf20Sopenharmony_ci			sample |= parity(sample & ~0xf8000000);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci			*dst++ = sample;
2058c2ecf20Sopenharmony_ci		} while (--i);
2068c2ecf20Sopenharmony_ci	} while (src < end);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
2108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	u8 cs[4];
2138c2ecf20Sopenharmony_ci	unsigned ch, i, j;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	memset(dw->cs, 0, sizeof(dw->cs));
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	for (ch = 0; ch < 8; ch++) {
2208c2ecf20Sopenharmony_ci		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
2218c2ecf20Sopenharmony_ci		cs[2] |= (ch + 1) << 4;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cs); i++) {
2248c2ecf20Sopenharmony_ci			unsigned c = cs[i];
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci			for (j = 0; j < 8; j++, c >>= 1)
2278c2ecf20Sopenharmony_ci				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	dw->cs[0][0] |= BIT(4);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	void __iomem *base = dw->data.base;
2368c2ecf20Sopenharmony_ci	unsigned offset = dw->buf_offset;
2378c2ecf20Sopenharmony_ci	unsigned period = dw->buf_period;
2388c2ecf20Sopenharmony_ci	u32 start, stop;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dw->reformat(dw, offset, period);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Clear all irqs before enabling irqs and starting DMA */
2438c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
2448c2ecf20Sopenharmony_ci		       base + HDMI_IH_AHBDMAAUD_STAT0);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	start = dw->buf_addr + offset;
2478c2ecf20Sopenharmony_ci	stop = start + period - 1;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Setup the hardware start/stop addresses */
2508c2ecf20Sopenharmony_ci	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
2518c2ecf20Sopenharmony_ci	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
2548c2ecf20Sopenharmony_ci	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	offset += period;
2578c2ecf20Sopenharmony_ci	if (offset >= dw->buf_size)
2588c2ecf20Sopenharmony_ci		offset = 0;
2598c2ecf20Sopenharmony_ci	dw->buf_offset = offset;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	/* Disable interrupts before disabling DMA */
2658c2ecf20Sopenharmony_ci	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
2668c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = data;
2728c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
2738c2ecf20Sopenharmony_ci	unsigned stat;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
2768c2ecf20Sopenharmony_ci	if (!stat)
2778c2ecf20Sopenharmony_ci		return IRQ_NONE;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	substream = dw->substream;
2828c2ecf20Sopenharmony_ci	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
2838c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		spin_lock(&dw->lock);
2868c2ecf20Sopenharmony_ci		if (dw->substream)
2878c2ecf20Sopenharmony_ci			dw_hdmi_start_dma(dw);
2888c2ecf20Sopenharmony_ci		spin_unlock(&dw->lock);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware dw_hdmi_hw = {
2958c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_INTERLEAVED |
2968c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
2978c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP |
2988c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
2998c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
3008c2ecf20Sopenharmony_ci		   SNDRV_PCM_FMTBIT_S24_LE,
3018c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_32000 |
3028c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_44100 |
3038c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_48000 |
3048c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_88200 |
3058c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_96000 |
3068c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_176400 |
3078c2ecf20Sopenharmony_ci		 SNDRV_PCM_RATE_192000,
3088c2ecf20Sopenharmony_ci	.channels_min = 2,
3098c2ecf20Sopenharmony_ci	.channels_max = 8,
3108c2ecf20Sopenharmony_ci	.buffer_bytes_max = 1024 * 1024,
3118c2ecf20Sopenharmony_ci	.period_bytes_min = 256,
3128c2ecf20Sopenharmony_ci	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
3138c2ecf20Sopenharmony_ci	.periods_min = 2,
3148c2ecf20Sopenharmony_ci	.periods_max = 16,
3158c2ecf20Sopenharmony_ci	.fifo_size = 0,
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int dw_hdmi_open(struct snd_pcm_substream *substream)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3218c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = substream->private_data;
3228c2ecf20Sopenharmony_ci	void __iomem *base = dw->data.base;
3238c2ecf20Sopenharmony_ci	u8 *eld;
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	runtime->hw = dw_hdmi_hw;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	eld = dw->data.get_eld(dw->data.hdmi);
3298c2ecf20Sopenharmony_ci	if (eld) {
3308c2ecf20Sopenharmony_ci		ret = snd_pcm_hw_constraint_eld(runtime, eld);
3318c2ecf20Sopenharmony_ci		if (ret < 0)
3328c2ecf20Sopenharmony_ci			return ret;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ret = snd_pcm_limit_hw_rates(runtime);
3368c2ecf20Sopenharmony_ci	if (ret < 0)
3378c2ecf20Sopenharmony_ci		return ret;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(runtime,
3408c2ecf20Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
3418c2ecf20Sopenharmony_ci	if (ret < 0)
3428c2ecf20Sopenharmony_ci		return ret;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Limit the buffer size to the size of the preallocated buffer */
3458c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_minmax(runtime,
3468c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
3478c2ecf20Sopenharmony_ci					   0, substream->dma_buffer.bytes);
3488c2ecf20Sopenharmony_ci	if (ret < 0)
3498c2ecf20Sopenharmony_ci		return ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Clear FIFO */
3528c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
3538c2ecf20Sopenharmony_ci		       base + HDMI_AHB_DMA_CONF0);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Configure interrupt polarities */
3568c2ecf20Sopenharmony_ci	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
3578c2ecf20Sopenharmony_ci	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Keep interrupts masked, and clear any pending */
3608c2ecf20Sopenharmony_ci	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
3618c2ecf20Sopenharmony_ci	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
3648c2ecf20Sopenharmony_ci			  "dw-hdmi-audio", dw);
3658c2ecf20Sopenharmony_ci	if (ret)
3668c2ecf20Sopenharmony_ci		return ret;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Un-mute done interrupt */
3698c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
3708c2ecf20Sopenharmony_ci		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
3718c2ecf20Sopenharmony_ci		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int dw_hdmi_close(struct snd_pcm_substream *substream)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = substream->private_data;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Mute all interrupts */
3818c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
3828c2ecf20Sopenharmony_ci		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	free_irq(dw->data.irq, dw);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	return snd_pcm_lib_free_vmalloc_buffer(substream);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
3958c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	/* Allocate the PCM runtime buffer, which is exposed to userspace. */
3988c2ecf20Sopenharmony_ci	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
3998c2ecf20Sopenharmony_ci						params_buffer_bytes(params));
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic int dw_hdmi_prepare(struct snd_pcm_substream *substream)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4058c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = substream->private_data;
4068c2ecf20Sopenharmony_ci	u8 threshold, conf0, conf1, ca;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
4098c2ecf20Sopenharmony_ci	switch (dw->revision) {
4108c2ecf20Sopenharmony_ci	case 0x0a:
4118c2ecf20Sopenharmony_ci		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
4128c2ecf20Sopenharmony_ci			HDMI_AHB_DMA_CONF0_INCR4;
4138c2ecf20Sopenharmony_ci		if (runtime->channels == 2)
4148c2ecf20Sopenharmony_ci			threshold = 126;
4158c2ecf20Sopenharmony_ci		else
4168c2ecf20Sopenharmony_ci			threshold = 124;
4178c2ecf20Sopenharmony_ci		break;
4188c2ecf20Sopenharmony_ci	case 0x1a:
4198c2ecf20Sopenharmony_ci		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
4208c2ecf20Sopenharmony_ci			HDMI_AHB_DMA_CONF0_INCR8;
4218c2ecf20Sopenharmony_ci		threshold = 128;
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	default:
4248c2ecf20Sopenharmony_ci		/* NOTREACHED */
4258c2ecf20Sopenharmony_ci		return -EINVAL;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* Minimum number of bytes in the fifo. */
4318c2ecf20Sopenharmony_ci	runtime->hw.fifo_size = threshold * 32;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
4348c2ecf20Sopenharmony_ci	conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
4358c2ecf20Sopenharmony_ci	ca = default_hdmi_channel_config[runtime->channels - 2].ca;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
4388c2ecf20Sopenharmony_ci	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
4398c2ecf20Sopenharmony_ci	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	dw_hdmi_set_channel_count(dw->data.hdmi, runtime->channels);
4428c2ecf20Sopenharmony_ci	dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	switch (runtime->format) {
4458c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
4468c2ecf20Sopenharmony_ci		dw->reformat = dw_hdmi_reformat_iec958;
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
4498c2ecf20Sopenharmony_ci		dw_hdmi_create_cs(dw, runtime);
4508c2ecf20Sopenharmony_ci		dw->reformat = dw_hdmi_reformat_s24;
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci	dw->iec_offset = 0;
4548c2ecf20Sopenharmony_ci	dw->channels = runtime->channels;
4558c2ecf20Sopenharmony_ci	dw->buf_src  = runtime->dma_area;
4568c2ecf20Sopenharmony_ci	dw->buf_dst  = substream->dma_buffer.area;
4578c2ecf20Sopenharmony_ci	dw->buf_addr = substream->dma_buffer.addr;
4588c2ecf20Sopenharmony_ci	dw->buf_period = snd_pcm_lib_period_bytes(substream);
4598c2ecf20Sopenharmony_ci	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = substream->private_data;
4678c2ecf20Sopenharmony_ci	unsigned long flags;
4688c2ecf20Sopenharmony_ci	int ret = 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	switch (cmd) {
4718c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4728c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dw->lock, flags);
4738c2ecf20Sopenharmony_ci		dw->buf_offset = 0;
4748c2ecf20Sopenharmony_ci		dw->substream = substream;
4758c2ecf20Sopenharmony_ci		dw_hdmi_start_dma(dw);
4768c2ecf20Sopenharmony_ci		dw_hdmi_audio_enable(dw->data.hdmi);
4778c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dw->lock, flags);
4788c2ecf20Sopenharmony_ci		substream->runtime->delay = substream->runtime->period_size;
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4828c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dw->lock, flags);
4838c2ecf20Sopenharmony_ci		dw->substream = NULL;
4848c2ecf20Sopenharmony_ci		dw_hdmi_stop_dma(dw);
4858c2ecf20Sopenharmony_ci		dw_hdmi_audio_disable(dw->data.hdmi);
4868c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dw->lock, flags);
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	default:
4908c2ecf20Sopenharmony_ci		ret = -EINVAL;
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	return ret;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5008c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = substream->private_data;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/*
5038c2ecf20Sopenharmony_ci	 * We are unable to report the exact hardware position as
5048c2ecf20Sopenharmony_ci	 * reading the 32-bit DMA position using 8-bit reads is racy.
5058c2ecf20Sopenharmony_ci	 */
5068c2ecf20Sopenharmony_ci	return bytes_to_frames(runtime, dw->buf_offset);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_dw_hdmi_ops = {
5108c2ecf20Sopenharmony_ci	.open = dw_hdmi_open,
5118c2ecf20Sopenharmony_ci	.close = dw_hdmi_close,
5128c2ecf20Sopenharmony_ci	.ioctl = snd_pcm_lib_ioctl,
5138c2ecf20Sopenharmony_ci	.hw_params = dw_hdmi_hw_params,
5148c2ecf20Sopenharmony_ci	.hw_free = dw_hdmi_hw_free,
5158c2ecf20Sopenharmony_ci	.prepare = dw_hdmi_prepare,
5168c2ecf20Sopenharmony_ci	.trigger = dw_hdmi_trigger,
5178c2ecf20Sopenharmony_ci	.pointer = dw_hdmi_pointer,
5188c2ecf20Sopenharmony_ci	.page = snd_pcm_lib_get_vmalloc_page,
5198c2ecf20Sopenharmony_ci};
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int snd_dw_hdmi_probe(struct platform_device *pdev)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
5248c2ecf20Sopenharmony_ci	struct device *dev = pdev->dev.parent;
5258c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw;
5268c2ecf20Sopenharmony_ci	struct snd_card *card;
5278c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
5288c2ecf20Sopenharmony_ci	unsigned revision;
5298c2ecf20Sopenharmony_ci	int ret;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
5328c2ecf20Sopenharmony_ci		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
5338c2ecf20Sopenharmony_ci	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
5348c2ecf20Sopenharmony_ci	if (revision != 0x0a && revision != 0x1a) {
5358c2ecf20Sopenharmony_ci		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
5368c2ecf20Sopenharmony_ci			revision);
5378c2ecf20Sopenharmony_ci		return -ENXIO;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
5418c2ecf20Sopenharmony_ci			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
5428c2ecf20Sopenharmony_ci	if (ret < 0)
5438c2ecf20Sopenharmony_ci		return ret;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
5468c2ecf20Sopenharmony_ci	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
5478c2ecf20Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
5488c2ecf20Sopenharmony_ci		 "%s rev 0x%02x, irq %d", card->shortname, revision,
5498c2ecf20Sopenharmony_ci		 data->irq);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	dw = card->private_data;
5528c2ecf20Sopenharmony_ci	dw->card = card;
5538c2ecf20Sopenharmony_ci	dw->data = *data;
5548c2ecf20Sopenharmony_ci	dw->revision = revision;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	spin_lock_init(&dw->lock);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
5598c2ecf20Sopenharmony_ci	if (ret < 0)
5608c2ecf20Sopenharmony_ci		goto err;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	dw->pcm = pcm;
5638c2ecf20Sopenharmony_ci	pcm->private_data = dw;
5648c2ecf20Sopenharmony_ci	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
5658c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/*
5688c2ecf20Sopenharmony_ci	 * To support 8-channel 96kHz audio reliably, we need 512k
5698c2ecf20Sopenharmony_ci	 * to satisfy alsa with our restricted period (ERR004323).
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
5728c2ecf20Sopenharmony_ci			dev, 128 * 1024, 1024 * 1024);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ret = snd_card_register(card);
5758c2ecf20Sopenharmony_ci	if (ret < 0)
5768c2ecf20Sopenharmony_ci		goto err;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dw);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cierr:
5838c2ecf20Sopenharmony_ci	snd_card_free(card);
5848c2ecf20Sopenharmony_ci	return ret;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int snd_dw_hdmi_remove(struct platform_device *pdev)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	snd_card_free(dw->card);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
5978c2ecf20Sopenharmony_ci/*
5988c2ecf20Sopenharmony_ci * This code is fine, but requires implementation in the dw_hdmi_trigger()
5998c2ecf20Sopenharmony_ci * method which is currently missing as I have no way to test this.
6008c2ecf20Sopenharmony_ci */
6018c2ecf20Sopenharmony_cistatic int snd_dw_hdmi_suspend(struct device *dev)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int snd_dw_hdmi_resume(struct device *dev)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
6208c2ecf20Sopenharmony_ci			 snd_dw_hdmi_resume);
6218c2ecf20Sopenharmony_ci#define PM_OPS &snd_dw_hdmi_pm
6228c2ecf20Sopenharmony_ci#else
6238c2ecf20Sopenharmony_ci#define PM_OPS NULL
6248c2ecf20Sopenharmony_ci#endif
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic struct platform_driver snd_dw_hdmi_driver = {
6278c2ecf20Sopenharmony_ci	.probe	= snd_dw_hdmi_probe,
6288c2ecf20Sopenharmony_ci	.remove	= snd_dw_hdmi_remove,
6298c2ecf20Sopenharmony_ci	.driver	= {
6308c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
6318c2ecf20Sopenharmony_ci		.pm = PM_OPS,
6328c2ecf20Sopenharmony_ci	},
6338c2ecf20Sopenharmony_ci};
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cimodule_platform_driver(snd_dw_hdmi_driver);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
6388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
6398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
6408c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
641