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