18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Driver for Microchip S/PDIF TX Controller 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 158c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * ---- S/PDIF Transmitter Controller Register map ---- 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#define SPDIFTX_CR 0x00 /* Control Register */ 238c2ecf20Sopenharmony_ci#define SPDIFTX_MR 0x04 /* Mode Register */ 248c2ecf20Sopenharmony_ci#define SPDIFTX_CDR 0x0C /* Common Data Register */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */ 278c2ecf20Sopenharmony_ci#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */ 288c2ecf20Sopenharmony_ci#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */ 298c2ecf20Sopenharmony_ci#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */ 328c2ecf20Sopenharmony_ci#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define SPDIFTX_VERSION 0xF0 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * ---- Control Register (Write-only) ---- 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */ 408c2ecf20Sopenharmony_ci#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * ---- Mode Register (Read/Write) ---- 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci/* Transmit Enable */ 468c2ecf20Sopenharmony_ci#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0) 478c2ecf20Sopenharmony_ci#define SPDIFTX_MR_TXEN_DISABLE (0 << 0) 488c2ecf20Sopenharmony_ci#define SPDIFTX_MR_TXEN_ENABLE (1 << 0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Multichannel Transfer */ 518c2ecf20Sopenharmony_ci#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1) 528c2ecf20Sopenharmony_ci#define SPDIFTX_MR_MULTICH_MONO (0 << 1) 538c2ecf20Sopenharmony_ci#define SPDIFTX_MR_MULTICH_DUAL (1 << 1) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Data Word Endian Mode */ 568c2ecf20Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2) 578c2ecf20Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2) 588c2ecf20Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_BIG (1 << 2) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Data Justification */ 618c2ecf20Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3) 628c2ecf20Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3) 638c2ecf20Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Common Audio Register Transfer Mode */ 668c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4) 678c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4) 688c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4) 698c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Valid Bits per Sample */ 728c2ecf20Sopenharmony_ci#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) 738c2ecf20Sopenharmony_ci#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Chunk Size */ 768c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) 778c2ecf20Sopenharmony_ci#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Validity Bits for Channels 1 and 2 */ 808c2ecf20Sopenharmony_ci#define SPDIFTX_MR_VALID1 BIT(24) 818c2ecf20Sopenharmony_ci#define SPDIFTX_MR_VALID2 BIT(25) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* Disable Null Frame on underrun */ 848c2ecf20Sopenharmony_ci#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27) 858c2ecf20Sopenharmony_ci#define SPDIFTX_MR_DNFR_INVALID (0 << 27) 868c2ecf20Sopenharmony_ci#define SPDIFTX_MR_DNFR_VALID (1 << 27) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Bytes per Sample */ 898c2ecf20Sopenharmony_ci#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) 908c2ecf20Sopenharmony_ci#define SPDIFTX_MR_BPS(bytes) \ 918c2ecf20Sopenharmony_ci ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXRDY BIT(0) 978c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXEMPTY BIT(1) 988c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXFULL BIT(2) 998c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXCHUNK BIT(3) 1008c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXUDR BIT(4) 1018c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXOVR BIT(5) 1028c2ecf20Sopenharmony_ci#define SPDIFTX_IR_CSRDY BIT(6) 1038c2ecf20Sopenharmony_ci#define SPDIFTX_IR_UDRDY BIT(7) 1048c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8) 1058c2ecf20Sopenharmony_ci#define SPDIFTX_IR_SECE BIT(10) 1068c2ecf20Sopenharmony_ci#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11) 1078c2ecf20Sopenharmony_ci#define SPDIFTX_IR_BEND BIT(13) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci switch (reg) { 1128c2ecf20Sopenharmony_ci case SPDIFTX_MR: 1138c2ecf20Sopenharmony_ci case SPDIFTX_IMR: 1148c2ecf20Sopenharmony_ci case SPDIFTX_ISR: 1158c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(0): 1168c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(1): 1178c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(2): 1188c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(3): 1198c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(4): 1208c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(5): 1218c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(0): 1228c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(1): 1238c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(2): 1248c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(3): 1258c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(4): 1268c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(5): 1278c2ecf20Sopenharmony_ci return true; 1288c2ecf20Sopenharmony_ci default: 1298c2ecf20Sopenharmony_ci return false; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci switch (reg) { 1368c2ecf20Sopenharmony_ci case SPDIFTX_CR: 1378c2ecf20Sopenharmony_ci case SPDIFTX_MR: 1388c2ecf20Sopenharmony_ci case SPDIFTX_CDR: 1398c2ecf20Sopenharmony_ci case SPDIFTX_IER: 1408c2ecf20Sopenharmony_ci case SPDIFTX_IDR: 1418c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(0): 1428c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(1): 1438c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(2): 1448c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(3): 1458c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(4): 1468c2ecf20Sopenharmony_ci case SPDIFTX_CH1UD(5): 1478c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(0): 1488c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(1): 1498c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(2): 1508c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(3): 1518c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(4): 1528c2ecf20Sopenharmony_ci case SPDIFTX_CH1S(5): 1538c2ecf20Sopenharmony_ci return true; 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci return false; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci switch (reg) { 1628c2ecf20Sopenharmony_ci case SPDIFTX_CDR: 1638c2ecf20Sopenharmony_ci case SPDIFTX_ISR: 1648c2ecf20Sopenharmony_ci return true; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci return false; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct regmap_config mchp_spdiftx_regmap_config = { 1718c2ecf20Sopenharmony_ci .reg_bits = 32, 1728c2ecf20Sopenharmony_ci .reg_stride = 4, 1738c2ecf20Sopenharmony_ci .val_bits = 32, 1748c2ecf20Sopenharmony_ci .max_register = SPDIFTX_VERSION, 1758c2ecf20Sopenharmony_ci .readable_reg = mchp_spdiftx_readable_reg, 1768c2ecf20Sopenharmony_ci .writeable_reg = mchp_spdiftx_writeable_reg, 1778c2ecf20Sopenharmony_ci .precious_reg = mchp_spdiftx_precious_reg, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define SPDIFTX_GCLK_RATIO 128 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define SPDIFTX_CS_BITS 192 1838c2ecf20Sopenharmony_ci#define SPDIFTX_UD_BITS 192 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistruct mchp_spdiftx_mixer_control { 1868c2ecf20Sopenharmony_ci unsigned char ch_stat[SPDIFTX_CS_BITS / 8]; 1878c2ecf20Sopenharmony_ci unsigned char user_data[SPDIFTX_UD_BITS / 8]; 1888c2ecf20Sopenharmony_ci spinlock_t lock; /* exclusive access to control data */ 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct mchp_spdiftx_dev { 1928c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control control; 1938c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data playback; 1948c2ecf20Sopenharmony_ci struct device *dev; 1958c2ecf20Sopenharmony_ci struct regmap *regmap; 1968c2ecf20Sopenharmony_ci struct clk *pclk; 1978c2ecf20Sopenharmony_ci struct clk *gclk; 1988c2ecf20Sopenharmony_ci unsigned int fmt; 1998c2ecf20Sopenharmony_ci unsigned int gclk_enabled:1; 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u32 mr; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_MR, &mr); 2078c2ecf20Sopenharmony_ci return !!(mr & SPDIFTX_MR_TXEN_ENABLE); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 2138c2ecf20Sopenharmony_ci u32 val; 2148c2ecf20Sopenharmony_ci int i; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) { 2178c2ecf20Sopenharmony_ci val = (ctrl->ch_stat[(i * 4) + 0] << 0) | 2188c2ecf20Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 1] << 8) | 2198c2ecf20Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 2] << 16) | 2208c2ecf20Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 3] << 24); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CH1S(i), val); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 2298c2ecf20Sopenharmony_ci u32 val; 2308c2ecf20Sopenharmony_ci int i; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) { 2338c2ecf20Sopenharmony_ci val = (ctrl->user_data[(i * 4) + 0] << 0) | 2348c2ecf20Sopenharmony_ci (ctrl->user_data[(i * 4) + 1] << 8) | 2358c2ecf20Sopenharmony_ci (ctrl->user_data[(i * 4) + 2] << 16) | 2368c2ecf20Sopenharmony_ci (ctrl->user_data[(i * 4) + 3] << 24); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = dev_id; 2458c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 2468c2ecf20Sopenharmony_ci u32 sr, imr, pending, idr = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_ISR, &sr); 2498c2ecf20Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_IMR, &imr); 2508c2ecf20Sopenharmony_ci pending = sr & imr; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!pending) 2538c2ecf20Sopenharmony_ci return IRQ_NONE; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (pending & SPDIFTX_IR_TXUDR) { 2568c2ecf20Sopenharmony_ci dev_warn(dev->dev, "underflow detected\n"); 2578c2ecf20Sopenharmony_ci idr |= SPDIFTX_IR_TXUDR; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (pending & SPDIFTX_IR_TXOVR) { 2618c2ecf20Sopenharmony_ci dev_warn(dev->dev, "overflow detected\n"); 2628c2ecf20Sopenharmony_ci idr |= SPDIFTX_IR_TXOVR; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (pending & SPDIFTX_IR_UDRDY) { 2668c2ecf20Sopenharmony_ci spin_lock(&ctrl->lock); 2678c2ecf20Sopenharmony_ci mchp_spdiftx_user_data_write(dev); 2688c2ecf20Sopenharmony_ci spin_unlock(&ctrl->lock); 2698c2ecf20Sopenharmony_ci idr |= SPDIFTX_IR_UDRDY; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (pending & SPDIFTX_IR_CSRDY) { 2738c2ecf20Sopenharmony_ci spin_lock(&ctrl->lock); 2748c2ecf20Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 2758c2ecf20Sopenharmony_ci spin_unlock(&ctrl->lock); 2768c2ecf20Sopenharmony_ci idr |= SPDIFTX_IR_CSRDY; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, idr); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream, 2858c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Software reset the IP */ 2908c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CR, 2918c2ecf20Sopenharmony_ci SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream, 2978c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Disable interrupts */ 3028c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd, 3068c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 3098c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 3108c2ecf20Sopenharmony_ci u32 mr; 3118c2ecf20Sopenharmony_ci int running; 3128c2ecf20Sopenharmony_ci int ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* do not start/stop while channel status or user data is updated */ 3158c2ecf20Sopenharmony_ci spin_lock(&ctrl->lock); 3168c2ecf20Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_MR, &mr); 3178c2ecf20Sopenharmony_ci running = !!(mr & SPDIFTX_MR_TXEN_ENABLE); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci switch (cmd) { 3208c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3218c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3228c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3238c2ecf20Sopenharmony_ci if (!running) { 3248c2ecf20Sopenharmony_ci mr &= ~SPDIFTX_MR_TXEN_MASK; 3258c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_TXEN_ENABLE; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3298c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3308c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3318c2ecf20Sopenharmony_ci if (running) { 3328c2ecf20Sopenharmony_ci mr &= ~SPDIFTX_MR_TXEN_MASK; 3338c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_TXEN_DISABLE; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci default: 3378c2ecf20Sopenharmony_ci spin_unlock(&ctrl->lock); 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, SPDIFTX_MR, mr); 3428c2ecf20Sopenharmony_ci spin_unlock(&ctrl->lock); 3438c2ecf20Sopenharmony_ci if (ret) { 3448c2ecf20Sopenharmony_ci dev_err(dev->dev, "unable to disable TX: %d\n", ret); 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream, 3528c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 3538c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci unsigned long flags; 3568c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 3578c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 3588c2ecf20Sopenharmony_ci u32 mr; 3598c2ecf20Sopenharmony_ci unsigned int bps = params_physical_width(params) / 8; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", 3638c2ecf20Sopenharmony_ci __func__, params_rate(params), params_format(params), 3648c2ecf20Sopenharmony_ci params_width(params), params_channels(params)); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 3678c2ecf20Sopenharmony_ci dev_err(dev->dev, "Capture is not supported\n"); 3688c2ecf20Sopenharmony_ci return -EINVAL; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_MR, &mr); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (mr & SPDIFTX_MR_TXEN_ENABLE) { 3748c2ecf20Sopenharmony_ci dev_err(dev->dev, "PCM already running\n"); 3758c2ecf20Sopenharmony_ci return -EBUSY; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Defaults: Toggle mode, justify to LSB, chunksize 1 */ 3798c2ecf20Sopenharmony_ci mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB; 3808c2ecf20Sopenharmony_ci dev->playback.maxburst = 1; 3818c2ecf20Sopenharmony_ci switch (params_channels(params)) { 3828c2ecf20Sopenharmony_ci case 1: 3838c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_MULTICH_MONO; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case 2: 3868c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_MULTICH_DUAL; 3878c2ecf20Sopenharmony_ci if (bps > 2) 3888c2ecf20Sopenharmony_ci dev->playback.maxburst = 2; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci default: 3918c2ecf20Sopenharmony_ci dev_err(dev->dev, "unsupported number of channels: %d\n", 3928c2ecf20Sopenharmony_ci params_channels(params)); 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_CHUNK(dev->playback.maxburst); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci switch (params_format(params)) { 3988c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 3998c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(8); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 4028c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4038c2ecf20Sopenharmony_ci fallthrough; 4048c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 4058c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(16); 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S18_3BE: 4088c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4098c2ecf20Sopenharmony_ci fallthrough; 4108c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S18_3LE: 4118c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(18); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3BE: 4148c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4158c2ecf20Sopenharmony_ci fallthrough; 4168c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 4178c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(20); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3BE: 4208c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4218c2ecf20Sopenharmony_ci fallthrough; 4228c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 4238c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(24); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_BE: 4268c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4278c2ecf20Sopenharmony_ci fallthrough; 4288c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 4298c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(24); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_BE: 4328c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 4338c2ecf20Sopenharmony_ci fallthrough; 4348c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 4358c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_VBPS(32); 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci default: 4388c2ecf20Sopenharmony_ci dev_err(dev->dev, "unsupported PCM format: %d\n", 4398c2ecf20Sopenharmony_ci params_format(params)); 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci mr |= SPDIFTX_MR_BPS(bps); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 4468c2ecf20Sopenharmony_ci ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS; 4478c2ecf20Sopenharmony_ci switch (params_rate(params)) { 4488c2ecf20Sopenharmony_ci case 22050: 4498c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_22050; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci case 24000: 4528c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_24000; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case 32000: 4558c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_32000; 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci case 44100: 4588c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_44100; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case 48000: 4618c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_48000; 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci case 88200: 4648c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_88200; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci case 96000: 4678c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_96000; 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case 176400: 4708c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_176400; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case 192000: 4738c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_192000; 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci case 8000: 4768c2ecf20Sopenharmony_ci case 11025: 4778c2ecf20Sopenharmony_ci case 16000: 4788c2ecf20Sopenharmony_ci case 64000: 4798c2ecf20Sopenharmony_ci ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_NOTID; 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci default: 4828c2ecf20Sopenharmony_ci dev_err(dev->dev, "unsupported sample frequency: %u\n", 4838c2ecf20Sopenharmony_ci params_rate(params)); 4848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 4858c2ecf20Sopenharmony_ci return -EINVAL; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 4888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (dev->gclk_enabled) { 4918c2ecf20Sopenharmony_ci clk_disable_unprepare(dev->gclk); 4928c2ecf20Sopenharmony_ci dev->gclk_enabled = 0; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci ret = clk_set_rate(dev->gclk, params_rate(params) * 4958c2ecf20Sopenharmony_ci SPDIFTX_GCLK_RATIO); 4968c2ecf20Sopenharmony_ci if (ret) { 4978c2ecf20Sopenharmony_ci dev_err(dev->dev, 4988c2ecf20Sopenharmony_ci "unable to change gclk rate to: rate %u * ratio %u\n", 4998c2ecf20Sopenharmony_ci params_rate(params), SPDIFTX_GCLK_RATIO); 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dev->gclk); 5038c2ecf20Sopenharmony_ci if (ret) { 5048c2ecf20Sopenharmony_ci dev_err(dev->dev, "unable to enable gclk: %d\n", ret); 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci dev->gclk_enabled = 1; 5088c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__, 5098c2ecf20Sopenharmony_ci params_rate(params) * SPDIFTX_GCLK_RATIO); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Enable interrupts */ 5128c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, 5138c2ecf20Sopenharmony_ci SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_MR, mr); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, 5218c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, 5268c2ecf20Sopenharmony_ci SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); 5278c2ecf20Sopenharmony_ci if (dev->gclk_enabled) { 5288c2ecf20Sopenharmony_ci clk_disable_unprepare(dev->gclk); 5298c2ecf20Sopenharmony_ci dev->gclk_enabled = 0; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return regmap_write(dev->regmap, SPDIFTX_CR, 5338c2ecf20Sopenharmony_ci SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { 5378c2ecf20Sopenharmony_ci .startup = mchp_spdiftx_dai_startup, 5388c2ecf20Sopenharmony_ci .shutdown = mchp_spdiftx_dai_shutdown, 5398c2ecf20Sopenharmony_ci .trigger = mchp_spdiftx_trigger, 5408c2ecf20Sopenharmony_ci .hw_params = mchp_spdiftx_hw_params, 5418c2ecf20Sopenharmony_ci .hw_free = mchp_spdiftx_hw_free, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 5478c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 5488c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_BE | \ 5498c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3LE | \ 5508c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3BE | \ 5518c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | \ 5528c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3BE | \ 5538c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | \ 5548c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3BE | \ 5558c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 5568c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE | \ 5578c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | \ 5588c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_BE \ 5598c2ecf20Sopenharmony_ci ) 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int mchp_spdiftx_info(struct snd_kcontrol *kcontrol, 5628c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 5658c2ecf20Sopenharmony_ci uinfo->count = 1; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol, 5718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci unsigned long flags; 5748c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 5758c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 5768c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 5798c2ecf20Sopenharmony_ci memcpy(uvalue->value.iec958.status, ctrl->ch_stat, 5808c2ecf20Sopenharmony_ci sizeof(ctrl->ch_stat)); 5818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol, 5878c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci unsigned long flags; 5908c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 5918c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 5928c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 5938c2ecf20Sopenharmony_ci int changed = 0; 5948c2ecf20Sopenharmony_ci int i; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 5978c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) { 5988c2ecf20Sopenharmony_ci if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i]) 5998c2ecf20Sopenharmony_ci changed = 1; 6008c2ecf20Sopenharmony_ci ctrl->ch_stat[i] = uvalue->value.iec958.status[i]; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (changed) { 6048c2ecf20Sopenharmony_ci /* don't enable IP while we copy the channel status */ 6058c2ecf20Sopenharmony_ci if (mchp_spdiftx_is_running(dev)) { 6068c2ecf20Sopenharmony_ci /* 6078c2ecf20Sopenharmony_ci * if SPDIF is running, wait for interrupt to write 6088c2ecf20Sopenharmony_ci * channel status 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, 6118c2ecf20Sopenharmony_ci SPDIFTX_IR_CSRDY); 6128c2ecf20Sopenharmony_ci } else { 6138c2ecf20Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return changed; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol, 6228c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci memset(uvalue->value.iec958.status, 0xff, 6258c2ecf20Sopenharmony_ci sizeof(uvalue->value.iec958.status)); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol, 6318c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 6348c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 6358c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 6368c2ecf20Sopenharmony_ci unsigned long flags; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 6398c2ecf20Sopenharmony_ci memcpy(uvalue->value.iec958.subcode, ctrl->user_data, 6408c2ecf20Sopenharmony_ci sizeof(ctrl->user_data)); 6418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol, 6478c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci unsigned long flags; 6508c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 6518c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 6528c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 6538c2ecf20Sopenharmony_ci int changed = 0; 6548c2ecf20Sopenharmony_ci int i; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 6578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) { 6588c2ecf20Sopenharmony_ci if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i]) 6598c2ecf20Sopenharmony_ci changed = 1; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ctrl->user_data[i] = uvalue->value.iec958.subcode[i]; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci if (changed) { 6648c2ecf20Sopenharmony_ci if (mchp_spdiftx_is_running(dev)) { 6658c2ecf20Sopenharmony_ci /* 6668c2ecf20Sopenharmony_ci * if SPDIF is running, wait for interrupt to write 6678c2ecf20Sopenharmony_ci * user data 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, 6708c2ecf20Sopenharmony_ci SPDIFTX_IR_UDRDY); 6718c2ecf20Sopenharmony_ci } else { 6728c2ecf20Sopenharmony_ci mchp_spdiftx_user_data_write(dev); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return changed; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new mchp_spdiftx_ctrls[] = { 6818c2ecf20Sopenharmony_ci /* Channel status controller */ 6828c2ecf20Sopenharmony_ci { 6838c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 6848c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 6858c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 6868c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE, 6878c2ecf20Sopenharmony_ci .info = mchp_spdiftx_info, 6888c2ecf20Sopenharmony_ci .get = mchp_spdiftx_cs_get, 6898c2ecf20Sopenharmony_ci .put = mchp_spdiftx_cs_put, 6908c2ecf20Sopenharmony_ci }, 6918c2ecf20Sopenharmony_ci { 6928c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 6938c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), 6948c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 6958c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE, 6968c2ecf20Sopenharmony_ci .info = mchp_spdiftx_info, 6978c2ecf20Sopenharmony_ci .get = mchp_spdiftx_cs_mask, 6988c2ecf20Sopenharmony_ci }, 6998c2ecf20Sopenharmony_ci /* User bits controller */ 7008c2ecf20Sopenharmony_ci { 7018c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 7028c2ecf20Sopenharmony_ci .name = "IEC958 Subcode Playback Default", 7038c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 7048c2ecf20Sopenharmony_ci .info = mchp_spdiftx_info, 7058c2ecf20Sopenharmony_ci .get = mchp_spdiftx_subcode_get, 7068c2ecf20Sopenharmony_ci .put = mchp_spdiftx_subcode_put, 7078c2ecf20Sopenharmony_ci }, 7088c2ecf20Sopenharmony_ci}; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 7138c2ecf20Sopenharmony_ci int ret; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &dev->playback, NULL); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dev->pclk); 7188c2ecf20Sopenharmony_ci if (ret) { 7198c2ecf20Sopenharmony_ci dev_err(dev->dev, 7208c2ecf20Sopenharmony_ci "failed to enable the peripheral clock: %d\n", ret); 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Add controls */ 7258c2ecf20Sopenharmony_ci snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls, 7268c2ecf20Sopenharmony_ci ARRAY_SIZE(mchp_spdiftx_ctrls)); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return 0; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int mchp_spdiftx_dai_remove(struct snd_soc_dai *dai) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci clk_disable_unprepare(dev->pclk); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver mchp_spdiftx_dai = { 7418c2ecf20Sopenharmony_ci .name = "mchp-spdiftx", 7428c2ecf20Sopenharmony_ci .probe = mchp_spdiftx_dai_probe, 7438c2ecf20Sopenharmony_ci .remove = mchp_spdiftx_dai_remove, 7448c2ecf20Sopenharmony_ci .playback = { 7458c2ecf20Sopenharmony_ci .stream_name = "S/PDIF Playback", 7468c2ecf20Sopenharmony_ci .channels_min = 1, 7478c2ecf20Sopenharmony_ci .channels_max = 2, 7488c2ecf20Sopenharmony_ci .rates = MCHP_SPDIFTX_RATES, 7498c2ecf20Sopenharmony_ci .formats = MCHP_SPDIFTX_FORMATS, 7508c2ecf20Sopenharmony_ci }, 7518c2ecf20Sopenharmony_ci .ops = &mchp_spdiftx_dai_ops, 7528c2ecf20Sopenharmony_ci}; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver mchp_spdiftx_component = { 7558c2ecf20Sopenharmony_ci .name = "mchp-spdiftx", 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic const struct of_device_id mchp_spdiftx_dt_ids[] = { 7598c2ecf20Sopenharmony_ci { 7608c2ecf20Sopenharmony_ci .compatible = "microchip,sama7g5-spdiftx", 7618c2ecf20Sopenharmony_ci }, 7628c2ecf20Sopenharmony_ci { /* sentinel */ } 7638c2ecf20Sopenharmony_ci}; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids); 7668c2ecf20Sopenharmony_cistatic int mchp_spdiftx_probe(struct platform_device *pdev) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct mchp_spdiftx_dev *dev; 7698c2ecf20Sopenharmony_ci struct resource *mem; 7708c2ecf20Sopenharmony_ci struct regmap *regmap; 7718c2ecf20Sopenharmony_ci void __iomem *base; 7728c2ecf20Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl; 7738c2ecf20Sopenharmony_ci int irq; 7748c2ecf20Sopenharmony_ci int err; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* Get memory for driver data. */ 7778c2ecf20Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 7788c2ecf20Sopenharmony_ci if (!dev) 7798c2ecf20Sopenharmony_ci return -ENOMEM; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Map I/O registers. */ 7828c2ecf20Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 7838c2ecf20Sopenharmony_ci if (IS_ERR(base)) 7848c2ecf20Sopenharmony_ci return PTR_ERR(base); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci regmap = devm_regmap_init_mmio(&pdev->dev, base, 7878c2ecf20Sopenharmony_ci &mchp_spdiftx_regmap_config); 7888c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) 7898c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Request IRQ */ 7928c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 7938c2ecf20Sopenharmony_ci if (irq < 0) 7948c2ecf20Sopenharmony_ci return irq; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0, 7978c2ecf20Sopenharmony_ci dev_name(&pdev->dev), dev); 7988c2ecf20Sopenharmony_ci if (err) 7998c2ecf20Sopenharmony_ci return err; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* Get the peripheral clock */ 8028c2ecf20Sopenharmony_ci dev->pclk = devm_clk_get(&pdev->dev, "pclk"); 8038c2ecf20Sopenharmony_ci if (IS_ERR(dev->pclk)) { 8048c2ecf20Sopenharmony_ci err = PTR_ERR(dev->pclk); 8058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8068c2ecf20Sopenharmony_ci "failed to get the peripheral clock: %d\n", err); 8078c2ecf20Sopenharmony_ci return err; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* Get the generic clock */ 8118c2ecf20Sopenharmony_ci dev->gclk = devm_clk_get(&pdev->dev, "gclk"); 8128c2ecf20Sopenharmony_ci if (IS_ERR(dev->gclk)) { 8138c2ecf20Sopenharmony_ci err = PTR_ERR(dev->gclk); 8148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8158c2ecf20Sopenharmony_ci "failed to get the PMC generic clock: %d\n", err); 8168c2ecf20Sopenharmony_ci return err; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci ctrl = &dev->control; 8208c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->lock); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Init channel status */ 8238c2ecf20Sopenharmony_ci ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT | 8248c2ecf20Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_NONE; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci dev->dev = &pdev->dev; 8278c2ecf20Sopenharmony_ci dev->regmap = regmap; 8288c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR; 8318c2ecf20Sopenharmony_ci dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 8348c2ecf20Sopenharmony_ci if (err) { 8358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register PMC: %d\n", err); 8368c2ecf20Sopenharmony_ci return err; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci err = devm_snd_soc_register_component(&pdev->dev, 8408c2ecf20Sopenharmony_ci &mchp_spdiftx_component, 8418c2ecf20Sopenharmony_ci &mchp_spdiftx_dai, 1); 8428c2ecf20Sopenharmony_ci if (err) { 8438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register component: %d\n", err); 8448c2ecf20Sopenharmony_ci return err; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return 0; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic struct platform_driver mchp_spdiftx_driver = { 8518c2ecf20Sopenharmony_ci .probe = mchp_spdiftx_probe, 8528c2ecf20Sopenharmony_ci .driver = { 8538c2ecf20Sopenharmony_ci .name = "mchp_spdiftx", 8548c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids), 8558c2ecf20Sopenharmony_ci }, 8568c2ecf20Sopenharmony_ci}; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cimodule_platform_driver(mchp_spdiftx_driver); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>"); 8618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver"); 8628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 863