162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Driver for Microchip S/PDIF TX Controller 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitfield.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <sound/asoundef.h> 1762306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 1862306a36Sopenharmony_ci#include <sound/pcm_params.h> 1962306a36Sopenharmony_ci#include <sound/soc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * ---- S/PDIF Transmitter Controller Register map ---- 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define SPDIFTX_CR 0x00 /* Control Register */ 2562306a36Sopenharmony_ci#define SPDIFTX_MR 0x04 /* Mode Register */ 2662306a36Sopenharmony_ci#define SPDIFTX_CDR 0x0C /* Common Data Register */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */ 2962306a36Sopenharmony_ci#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */ 3062306a36Sopenharmony_ci#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */ 3162306a36Sopenharmony_ci#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */ 3462306a36Sopenharmony_ci#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SPDIFTX_VERSION 0xF0 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * ---- Control Register (Write-only) ---- 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */ 4262306a36Sopenharmony_ci#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * ---- Mode Register (Read/Write) ---- 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci/* Transmit Enable */ 4862306a36Sopenharmony_ci#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0) 4962306a36Sopenharmony_ci#define SPDIFTX_MR_TXEN_DISABLE (0 << 0) 5062306a36Sopenharmony_ci#define SPDIFTX_MR_TXEN_ENABLE (1 << 0) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Multichannel Transfer */ 5362306a36Sopenharmony_ci#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1) 5462306a36Sopenharmony_ci#define SPDIFTX_MR_MULTICH_MONO (0 << 1) 5562306a36Sopenharmony_ci#define SPDIFTX_MR_MULTICH_DUAL (1 << 1) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Data Word Endian Mode */ 5862306a36Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2) 5962306a36Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2) 6062306a36Sopenharmony_ci#define SPDIFTX_MR_ENDIAN_BIG (1 << 2) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Data Justification */ 6362306a36Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3) 6462306a36Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3) 6562306a36Sopenharmony_ci#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Common Audio Register Transfer Mode */ 6862306a36Sopenharmony_ci#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4) 6962306a36Sopenharmony_ci#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4) 7062306a36Sopenharmony_ci#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4) 7162306a36Sopenharmony_ci#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Valid Bits per Sample */ 7462306a36Sopenharmony_ci#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Chunk Size */ 7762306a36Sopenharmony_ci#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Validity Bits for Channels 1 and 2 */ 8062306a36Sopenharmony_ci#define SPDIFTX_MR_VALID1 BIT(24) 8162306a36Sopenharmony_ci#define SPDIFTX_MR_VALID2 BIT(25) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Disable Null Frame on underrun */ 8462306a36Sopenharmony_ci#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27) 8562306a36Sopenharmony_ci#define SPDIFTX_MR_DNFR_INVALID (0 << 27) 8662306a36Sopenharmony_ci#define SPDIFTX_MR_DNFR_VALID (1 << 27) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Bytes per Sample */ 8962306a36Sopenharmony_ci#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci#define SPDIFTX_IR_TXRDY BIT(0) 9562306a36Sopenharmony_ci#define SPDIFTX_IR_TXEMPTY BIT(1) 9662306a36Sopenharmony_ci#define SPDIFTX_IR_TXFULL BIT(2) 9762306a36Sopenharmony_ci#define SPDIFTX_IR_TXCHUNK BIT(3) 9862306a36Sopenharmony_ci#define SPDIFTX_IR_TXUDR BIT(4) 9962306a36Sopenharmony_ci#define SPDIFTX_IR_TXOVR BIT(5) 10062306a36Sopenharmony_ci#define SPDIFTX_IR_CSRDY BIT(6) 10162306a36Sopenharmony_ci#define SPDIFTX_IR_UDRDY BIT(7) 10262306a36Sopenharmony_ci#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8) 10362306a36Sopenharmony_ci#define SPDIFTX_IR_SECE BIT(10) 10462306a36Sopenharmony_ci#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11) 10562306a36Sopenharmony_ci#define SPDIFTX_IR_BEND BIT(13) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci switch (reg) { 11062306a36Sopenharmony_ci case SPDIFTX_MR: 11162306a36Sopenharmony_ci case SPDIFTX_IMR: 11262306a36Sopenharmony_ci case SPDIFTX_ISR: 11362306a36Sopenharmony_ci case SPDIFTX_CH1UD(0): 11462306a36Sopenharmony_ci case SPDIFTX_CH1UD(1): 11562306a36Sopenharmony_ci case SPDIFTX_CH1UD(2): 11662306a36Sopenharmony_ci case SPDIFTX_CH1UD(3): 11762306a36Sopenharmony_ci case SPDIFTX_CH1UD(4): 11862306a36Sopenharmony_ci case SPDIFTX_CH1UD(5): 11962306a36Sopenharmony_ci case SPDIFTX_CH1S(0): 12062306a36Sopenharmony_ci case SPDIFTX_CH1S(1): 12162306a36Sopenharmony_ci case SPDIFTX_CH1S(2): 12262306a36Sopenharmony_ci case SPDIFTX_CH1S(3): 12362306a36Sopenharmony_ci case SPDIFTX_CH1S(4): 12462306a36Sopenharmony_ci case SPDIFTX_CH1S(5): 12562306a36Sopenharmony_ci return true; 12662306a36Sopenharmony_ci default: 12762306a36Sopenharmony_ci return false; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci switch (reg) { 13462306a36Sopenharmony_ci case SPDIFTX_CR: 13562306a36Sopenharmony_ci case SPDIFTX_MR: 13662306a36Sopenharmony_ci case SPDIFTX_CDR: 13762306a36Sopenharmony_ci case SPDIFTX_IER: 13862306a36Sopenharmony_ci case SPDIFTX_IDR: 13962306a36Sopenharmony_ci case SPDIFTX_CH1UD(0): 14062306a36Sopenharmony_ci case SPDIFTX_CH1UD(1): 14162306a36Sopenharmony_ci case SPDIFTX_CH1UD(2): 14262306a36Sopenharmony_ci case SPDIFTX_CH1UD(3): 14362306a36Sopenharmony_ci case SPDIFTX_CH1UD(4): 14462306a36Sopenharmony_ci case SPDIFTX_CH1UD(5): 14562306a36Sopenharmony_ci case SPDIFTX_CH1S(0): 14662306a36Sopenharmony_ci case SPDIFTX_CH1S(1): 14762306a36Sopenharmony_ci case SPDIFTX_CH1S(2): 14862306a36Sopenharmony_ci case SPDIFTX_CH1S(3): 14962306a36Sopenharmony_ci case SPDIFTX_CH1S(4): 15062306a36Sopenharmony_ci case SPDIFTX_CH1S(5): 15162306a36Sopenharmony_ci return true; 15262306a36Sopenharmony_ci default: 15362306a36Sopenharmony_ci return false; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci switch (reg) { 16062306a36Sopenharmony_ci case SPDIFTX_CDR: 16162306a36Sopenharmony_ci case SPDIFTX_ISR: 16262306a36Sopenharmony_ci return true; 16362306a36Sopenharmony_ci default: 16462306a36Sopenharmony_ci return false; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const struct regmap_config mchp_spdiftx_regmap_config = { 16962306a36Sopenharmony_ci .reg_bits = 32, 17062306a36Sopenharmony_ci .reg_stride = 4, 17162306a36Sopenharmony_ci .val_bits = 32, 17262306a36Sopenharmony_ci .max_register = SPDIFTX_VERSION, 17362306a36Sopenharmony_ci .readable_reg = mchp_spdiftx_readable_reg, 17462306a36Sopenharmony_ci .writeable_reg = mchp_spdiftx_writeable_reg, 17562306a36Sopenharmony_ci .precious_reg = mchp_spdiftx_precious_reg, 17662306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define SPDIFTX_GCLK_RATIO 128 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci#define SPDIFTX_CS_BITS 192 18262306a36Sopenharmony_ci#define SPDIFTX_UD_BITS 192 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistruct mchp_spdiftx_mixer_control { 18562306a36Sopenharmony_ci unsigned char ch_stat[SPDIFTX_CS_BITS / 8]; 18662306a36Sopenharmony_ci unsigned char user_data[SPDIFTX_UD_BITS / 8]; 18762306a36Sopenharmony_ci spinlock_t lock; /* exclusive access to control data */ 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistruct mchp_spdiftx_dev { 19162306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control control; 19262306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data playback; 19362306a36Sopenharmony_ci struct device *dev; 19462306a36Sopenharmony_ci struct regmap *regmap; 19562306a36Sopenharmony_ci struct clk *pclk; 19662306a36Sopenharmony_ci struct clk *gclk; 19762306a36Sopenharmony_ci unsigned int fmt; 19862306a36Sopenharmony_ci unsigned int suspend_irq; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci u32 mr; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_MR, &mr); 20662306a36Sopenharmony_ci return !!(mr & SPDIFTX_MR_TXEN_ENABLE); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 21262306a36Sopenharmony_ci u32 val; 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) { 21662306a36Sopenharmony_ci val = (ctrl->ch_stat[(i * 4) + 0] << 0) | 21762306a36Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 1] << 8) | 21862306a36Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 2] << 16) | 21962306a36Sopenharmony_ci (ctrl->ch_stat[(i * 4) + 3] << 24); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CH1S(i), val); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 22862306a36Sopenharmony_ci u32 val; 22962306a36Sopenharmony_ci int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) { 23262306a36Sopenharmony_ci val = (ctrl->user_data[(i * 4) + 0] << 0) | 23362306a36Sopenharmony_ci (ctrl->user_data[(i * 4) + 1] << 8) | 23462306a36Sopenharmony_ci (ctrl->user_data[(i * 4) + 2] << 16) | 23562306a36Sopenharmony_ci (ctrl->user_data[(i * 4) + 3] << 24); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = dev_id; 24462306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 24562306a36Sopenharmony_ci u32 sr, imr, pending, idr = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_ISR, &sr); 24862306a36Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_IMR, &imr); 24962306a36Sopenharmony_ci pending = sr & imr; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!pending) 25262306a36Sopenharmony_ci return IRQ_NONE; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (pending & SPDIFTX_IR_TXUDR) { 25562306a36Sopenharmony_ci dev_warn(dev->dev, "underflow detected\n"); 25662306a36Sopenharmony_ci idr |= SPDIFTX_IR_TXUDR; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (pending & SPDIFTX_IR_TXOVR) { 26062306a36Sopenharmony_ci dev_warn(dev->dev, "overflow detected\n"); 26162306a36Sopenharmony_ci idr |= SPDIFTX_IR_TXOVR; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (pending & SPDIFTX_IR_UDRDY) { 26562306a36Sopenharmony_ci spin_lock(&ctrl->lock); 26662306a36Sopenharmony_ci mchp_spdiftx_user_data_write(dev); 26762306a36Sopenharmony_ci spin_unlock(&ctrl->lock); 26862306a36Sopenharmony_ci idr |= SPDIFTX_IR_UDRDY; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (pending & SPDIFTX_IR_CSRDY) { 27262306a36Sopenharmony_ci spin_lock(&ctrl->lock); 27362306a36Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 27462306a36Sopenharmony_ci spin_unlock(&ctrl->lock); 27562306a36Sopenharmony_ci idr |= SPDIFTX_IR_CSRDY; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, idr); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return IRQ_HANDLED; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream, 28462306a36Sopenharmony_ci struct snd_soc_dai *dai) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Software reset the IP */ 28962306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_CR, 29062306a36Sopenharmony_ci SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream, 29662306a36Sopenharmony_ci struct snd_soc_dai *dai) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Disable interrupts */ 30162306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd, 30562306a36Sopenharmony_ci struct snd_soc_dai *dai) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 30862306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* do not start/stop while channel status or user data is updated */ 31262306a36Sopenharmony_ci spin_lock(&ctrl->lock); 31362306a36Sopenharmony_ci switch (cmd) { 31462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 31562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 31662306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, dev->suspend_irq | 31762306a36Sopenharmony_ci SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); 31862306a36Sopenharmony_ci dev->suspend_irq = 0; 31962306a36Sopenharmony_ci fallthrough; 32062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 32162306a36Sopenharmony_ci ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK, 32262306a36Sopenharmony_ci SPDIFTX_MR_TXEN_ENABLE); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 32562306a36Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_IMR, &dev->suspend_irq); 32662306a36Sopenharmony_ci fallthrough; 32762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 32862306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IDR, dev->suspend_irq | 32962306a36Sopenharmony_ci SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); 33062306a36Sopenharmony_ci fallthrough; 33162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 33262306a36Sopenharmony_ci ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK, 33362306a36Sopenharmony_ci SPDIFTX_MR_TXEN_DISABLE); 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci default: 33662306a36Sopenharmony_ci ret = -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci spin_unlock(&ctrl->lock); 33962306a36Sopenharmony_ci if (ret) 34062306a36Sopenharmony_ci dev_err(dev->dev, "unable to start/stop TX: %d\n", ret); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream, 34662306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 34762306a36Sopenharmony_ci struct snd_soc_dai *dai) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci unsigned long flags; 35062306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 35162306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 35262306a36Sopenharmony_ci u32 mr; 35362306a36Sopenharmony_ci unsigned int bps = params_physical_width(params) / 8; 35462306a36Sopenharmony_ci unsigned char aes3; 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", 35862306a36Sopenharmony_ci __func__, params_rate(params), params_format(params), 35962306a36Sopenharmony_ci params_width(params), params_channels(params)); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 36262306a36Sopenharmony_ci dev_err(dev->dev, "Capture is not supported\n"); 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci regmap_read(dev->regmap, SPDIFTX_MR, &mr); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (mr & SPDIFTX_MR_TXEN_ENABLE) { 36962306a36Sopenharmony_ci dev_err(dev->dev, "PCM already running\n"); 37062306a36Sopenharmony_ci return -EBUSY; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Defaults: Toggle mode, justify to LSB, chunksize 1 */ 37462306a36Sopenharmony_ci mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB; 37562306a36Sopenharmony_ci dev->playback.maxburst = 1; 37662306a36Sopenharmony_ci switch (params_channels(params)) { 37762306a36Sopenharmony_ci case 1: 37862306a36Sopenharmony_ci mr |= SPDIFTX_MR_MULTICH_MONO; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case 2: 38162306a36Sopenharmony_ci mr |= SPDIFTX_MR_MULTICH_DUAL; 38262306a36Sopenharmony_ci if (bps > 2) 38362306a36Sopenharmony_ci dev->playback.maxburst = 2; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci dev_err(dev->dev, "unsupported number of channels: %d\n", 38762306a36Sopenharmony_ci params_channels(params)); 38862306a36Sopenharmony_ci return -EINVAL; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, dev->playback.maxburst); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci switch (params_format(params)) { 39362306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 39462306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 8); 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 39762306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 39862306a36Sopenharmony_ci fallthrough; 39962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 40062306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 16); 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S18_3BE: 40362306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 40462306a36Sopenharmony_ci fallthrough; 40562306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S18_3LE: 40662306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 18); 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3BE: 40962306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 41062306a36Sopenharmony_ci fallthrough; 41162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 41262306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 20); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3BE: 41562306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 41662306a36Sopenharmony_ci fallthrough; 41762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 41862306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_BE: 42162306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 42262306a36Sopenharmony_ci fallthrough; 42362306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 42462306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_BE: 42762306a36Sopenharmony_ci mr |= SPDIFTX_MR_ENDIAN_BIG; 42862306a36Sopenharmony_ci fallthrough; 42962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 43062306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 32); 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci default: 43362306a36Sopenharmony_ci dev_err(dev->dev, "unsupported PCM format: %d\n", 43462306a36Sopenharmony_ci params_format(params)); 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mr |= FIELD_PREP(SPDIFTX_MR_BPS_MASK, bps - 1); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (params_rate(params)) { 44162306a36Sopenharmony_ci case 22050: 44262306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_22050; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case 24000: 44562306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_24000; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case 32000: 44862306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_32000; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case 44100: 45162306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_44100; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case 48000: 45462306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_48000; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case 88200: 45762306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_88200; 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case 96000: 46062306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_96000; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case 176400: 46362306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_176400; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case 192000: 46662306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_192000; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case 8000: 46962306a36Sopenharmony_ci case 11025: 47062306a36Sopenharmony_ci case 16000: 47162306a36Sopenharmony_ci case 64000: 47262306a36Sopenharmony_ci aes3 = IEC958_AES3_CON_FS_NOTID; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci default: 47562306a36Sopenharmony_ci dev_err(dev->dev, "unsupported sample frequency: %u\n", 47662306a36Sopenharmony_ci params_rate(params)); 47762306a36Sopenharmony_ci return -EINVAL; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 48062306a36Sopenharmony_ci ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS; 48162306a36Sopenharmony_ci ctrl->ch_stat[3] |= aes3; 48262306a36Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 48362306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* GCLK is enabled by runtime PM. */ 48662306a36Sopenharmony_ci clk_disable_unprepare(dev->gclk); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = clk_set_rate(dev->gclk, params_rate(params) * 48962306a36Sopenharmony_ci SPDIFTX_GCLK_RATIO); 49062306a36Sopenharmony_ci if (ret) { 49162306a36Sopenharmony_ci dev_err(dev->dev, 49262306a36Sopenharmony_ci "unable to change gclk rate to: rate %u * ratio %u\n", 49362306a36Sopenharmony_ci params_rate(params), SPDIFTX_GCLK_RATIO); 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci ret = clk_prepare_enable(dev->gclk); 49762306a36Sopenharmony_ci if (ret) { 49862306a36Sopenharmony_ci dev_err(dev->dev, "unable to enable gclk: %d\n", ret); 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__, 50362306a36Sopenharmony_ci params_rate(params) * SPDIFTX_GCLK_RATIO); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_MR, mr); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, 51162306a36Sopenharmony_ci struct snd_soc_dai *dai) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return regmap_write(dev->regmap, SPDIFTX_CR, 51662306a36Sopenharmony_ci SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 52262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 52362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_BE | \ 52462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3LE | \ 52562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3BE | \ 52662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | \ 52762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3BE | \ 52862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | \ 52962306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3BE | \ 53062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 53162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE | \ 53262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | \ 53362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_BE \ 53462306a36Sopenharmony_ci ) 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int mchp_spdiftx_info(struct snd_kcontrol *kcontrol, 53762306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 54062306a36Sopenharmony_ci uinfo->count = 1; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol, 54662306a36Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci unsigned long flags; 54962306a36Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 55062306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 55162306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 55462306a36Sopenharmony_ci memcpy(uvalue->value.iec958.status, ctrl->ch_stat, 55562306a36Sopenharmony_ci sizeof(ctrl->ch_stat)); 55662306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol, 56262306a36Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci unsigned long flags; 56562306a36Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 56662306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 56762306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 56862306a36Sopenharmony_ci int changed = 0; 56962306a36Sopenharmony_ci int i; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 57262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) { 57362306a36Sopenharmony_ci if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i]) 57462306a36Sopenharmony_ci changed = 1; 57562306a36Sopenharmony_ci ctrl->ch_stat[i] = uvalue->value.iec958.status[i]; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (changed) { 57962306a36Sopenharmony_ci /* don't enable IP while we copy the channel status */ 58062306a36Sopenharmony_ci if (mchp_spdiftx_is_running(dev)) { 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * if SPDIF is running, wait for interrupt to write 58362306a36Sopenharmony_ci * channel status 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, 58662306a36Sopenharmony_ci SPDIFTX_IR_CSRDY); 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci mchp_spdiftx_channel_status_write(dev); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return changed; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol, 59762306a36Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci memset(uvalue->value.iec958.status, 0xff, 60062306a36Sopenharmony_ci sizeof(uvalue->value.iec958.status)); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol, 60662306a36Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 60962306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 61062306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 61162306a36Sopenharmony_ci unsigned long flags; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 61462306a36Sopenharmony_ci memcpy(uvalue->value.iec958.subcode, ctrl->user_data, 61562306a36Sopenharmony_ci sizeof(ctrl->user_data)); 61662306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol, 62262306a36Sopenharmony_ci struct snd_ctl_elem_value *uvalue) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci unsigned long flags; 62562306a36Sopenharmony_ci struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); 62662306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 62762306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl = &dev->control; 62862306a36Sopenharmony_ci int changed = 0; 62962306a36Sopenharmony_ci int i; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 63262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) { 63362306a36Sopenharmony_ci if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i]) 63462306a36Sopenharmony_ci changed = 1; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ctrl->user_data[i] = uvalue->value.iec958.subcode[i]; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci if (changed) { 63962306a36Sopenharmony_ci if (mchp_spdiftx_is_running(dev)) { 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * if SPDIF is running, wait for interrupt to write 64262306a36Sopenharmony_ci * user data 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci regmap_write(dev->regmap, SPDIFTX_IER, 64562306a36Sopenharmony_ci SPDIFTX_IR_UDRDY); 64662306a36Sopenharmony_ci } else { 64762306a36Sopenharmony_ci mchp_spdiftx_user_data_write(dev); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return changed; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic struct snd_kcontrol_new mchp_spdiftx_ctrls[] = { 65662306a36Sopenharmony_ci /* Channel status controller */ 65762306a36Sopenharmony_ci { 65862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 65962306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 66062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 66162306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE, 66262306a36Sopenharmony_ci .info = mchp_spdiftx_info, 66362306a36Sopenharmony_ci .get = mchp_spdiftx_cs_get, 66462306a36Sopenharmony_ci .put = mchp_spdiftx_cs_put, 66562306a36Sopenharmony_ci }, 66662306a36Sopenharmony_ci { 66762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 66862306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), 66962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 67062306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE, 67162306a36Sopenharmony_ci .info = mchp_spdiftx_info, 67262306a36Sopenharmony_ci .get = mchp_spdiftx_cs_mask, 67362306a36Sopenharmony_ci }, 67462306a36Sopenharmony_ci /* User bits controller */ 67562306a36Sopenharmony_ci { 67662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 67762306a36Sopenharmony_ci .name = "IEC958 Subcode Playback Default", 67862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 67962306a36Sopenharmony_ci .info = mchp_spdiftx_info, 68062306a36Sopenharmony_ci .get = mchp_spdiftx_subcode_get, 68162306a36Sopenharmony_ci .put = mchp_spdiftx_subcode_put, 68262306a36Sopenharmony_ci }, 68362306a36Sopenharmony_ci}; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &dev->playback, NULL); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* Add controls */ 69262306a36Sopenharmony_ci snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls, 69362306a36Sopenharmony_ci ARRAY_SIZE(mchp_spdiftx_ctrls)); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { 69962306a36Sopenharmony_ci .probe = mchp_spdiftx_dai_probe, 70062306a36Sopenharmony_ci .startup = mchp_spdiftx_dai_startup, 70162306a36Sopenharmony_ci .shutdown = mchp_spdiftx_dai_shutdown, 70262306a36Sopenharmony_ci .trigger = mchp_spdiftx_trigger, 70362306a36Sopenharmony_ci .hw_params = mchp_spdiftx_hw_params, 70462306a36Sopenharmony_ci .hw_free = mchp_spdiftx_hw_free, 70562306a36Sopenharmony_ci}; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic struct snd_soc_dai_driver mchp_spdiftx_dai = { 70862306a36Sopenharmony_ci .name = "mchp-spdiftx", 70962306a36Sopenharmony_ci .playback = { 71062306a36Sopenharmony_ci .stream_name = "S/PDIF Playback", 71162306a36Sopenharmony_ci .channels_min = 1, 71262306a36Sopenharmony_ci .channels_max = 2, 71362306a36Sopenharmony_ci .rates = MCHP_SPDIFTX_RATES, 71462306a36Sopenharmony_ci .formats = MCHP_SPDIFTX_FORMATS, 71562306a36Sopenharmony_ci }, 71662306a36Sopenharmony_ci .ops = &mchp_spdiftx_dai_ops, 71762306a36Sopenharmony_ci}; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic const struct snd_soc_component_driver mchp_spdiftx_component = { 72062306a36Sopenharmony_ci .name = "mchp-spdiftx", 72162306a36Sopenharmony_ci .legacy_dai_naming = 1, 72262306a36Sopenharmony_ci}; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic const struct of_device_id mchp_spdiftx_dt_ids[] = { 72562306a36Sopenharmony_ci { 72662306a36Sopenharmony_ci .compatible = "microchip,sama7g5-spdiftx", 72762306a36Sopenharmony_ci }, 72862306a36Sopenharmony_ci { /* sentinel */ } 72962306a36Sopenharmony_ci}; 73062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int mchp_spdiftx_runtime_suspend(struct device *dev) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci regcache_cache_only(spdiftx->regmap, true); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci clk_disable_unprepare(spdiftx->gclk); 73962306a36Sopenharmony_ci clk_disable_unprepare(spdiftx->pclk); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int mchp_spdiftx_runtime_resume(struct device *dev) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev); 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = clk_prepare_enable(spdiftx->pclk); 75062306a36Sopenharmony_ci if (ret) { 75162306a36Sopenharmony_ci dev_err(spdiftx->dev, 75262306a36Sopenharmony_ci "failed to enable the peripheral clock: %d\n", ret); 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci ret = clk_prepare_enable(spdiftx->gclk); 75662306a36Sopenharmony_ci if (ret) { 75762306a36Sopenharmony_ci dev_err(spdiftx->dev, 75862306a36Sopenharmony_ci "failed to enable generic clock: %d\n", ret); 75962306a36Sopenharmony_ci goto disable_pclk; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci regcache_cache_only(spdiftx->regmap, false); 76362306a36Sopenharmony_ci regcache_mark_dirty(spdiftx->regmap); 76462306a36Sopenharmony_ci ret = regcache_sync(spdiftx->regmap); 76562306a36Sopenharmony_ci if (ret) { 76662306a36Sopenharmony_ci regcache_cache_only(spdiftx->regmap, true); 76762306a36Sopenharmony_ci clk_disable_unprepare(spdiftx->gclk); 76862306a36Sopenharmony_cidisable_pclk: 76962306a36Sopenharmony_ci clk_disable_unprepare(spdiftx->pclk); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic const struct dev_pm_ops mchp_spdiftx_pm_ops = { 77662306a36Sopenharmony_ci SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) 77762306a36Sopenharmony_ci RUNTIME_PM_OPS(mchp_spdiftx_runtime_suspend, mchp_spdiftx_runtime_resume, 77862306a36Sopenharmony_ci NULL) 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int mchp_spdiftx_probe(struct platform_device *pdev) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev; 78462306a36Sopenharmony_ci struct resource *mem; 78562306a36Sopenharmony_ci struct regmap *regmap; 78662306a36Sopenharmony_ci void __iomem *base; 78762306a36Sopenharmony_ci struct mchp_spdiftx_mixer_control *ctrl; 78862306a36Sopenharmony_ci int irq; 78962306a36Sopenharmony_ci int err; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Get memory for driver data. */ 79262306a36Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 79362306a36Sopenharmony_ci if (!dev) 79462306a36Sopenharmony_ci return -ENOMEM; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Map I/O registers. */ 79762306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 79862306a36Sopenharmony_ci if (IS_ERR(base)) 79962306a36Sopenharmony_ci return PTR_ERR(base); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci regmap = devm_regmap_init_mmio(&pdev->dev, base, 80262306a36Sopenharmony_ci &mchp_spdiftx_regmap_config); 80362306a36Sopenharmony_ci if (IS_ERR(regmap)) 80462306a36Sopenharmony_ci return PTR_ERR(regmap); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Request IRQ */ 80762306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 80862306a36Sopenharmony_ci if (irq < 0) 80962306a36Sopenharmony_ci return irq; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0, 81262306a36Sopenharmony_ci dev_name(&pdev->dev), dev); 81362306a36Sopenharmony_ci if (err) 81462306a36Sopenharmony_ci return err; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Get the peripheral clock */ 81762306a36Sopenharmony_ci dev->pclk = devm_clk_get(&pdev->dev, "pclk"); 81862306a36Sopenharmony_ci if (IS_ERR(dev->pclk)) { 81962306a36Sopenharmony_ci err = PTR_ERR(dev->pclk); 82062306a36Sopenharmony_ci dev_err(&pdev->dev, 82162306a36Sopenharmony_ci "failed to get the peripheral clock: %d\n", err); 82262306a36Sopenharmony_ci return err; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Get the generic clock */ 82662306a36Sopenharmony_ci dev->gclk = devm_clk_get(&pdev->dev, "gclk"); 82762306a36Sopenharmony_ci if (IS_ERR(dev->gclk)) { 82862306a36Sopenharmony_ci err = PTR_ERR(dev->gclk); 82962306a36Sopenharmony_ci dev_err(&pdev->dev, 83062306a36Sopenharmony_ci "failed to get the PMC generic clock: %d\n", err); 83162306a36Sopenharmony_ci return err; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ctrl = &dev->control; 83562306a36Sopenharmony_ci spin_lock_init(&ctrl->lock); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Init channel status */ 83862306a36Sopenharmony_ci ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT | 83962306a36Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_NONE; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci dev->dev = &pdev->dev; 84262306a36Sopenharmony_ci dev->regmap = regmap; 84362306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci pm_runtime_enable(dev->dev); 84662306a36Sopenharmony_ci if (!pm_runtime_enabled(dev->dev)) { 84762306a36Sopenharmony_ci err = mchp_spdiftx_runtime_resume(dev->dev); 84862306a36Sopenharmony_ci if (err) 84962306a36Sopenharmony_ci return err; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR; 85362306a36Sopenharmony_ci dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 85662306a36Sopenharmony_ci if (err) { 85762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register PMC: %d\n", err); 85862306a36Sopenharmony_ci goto pm_runtime_suspend; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci err = devm_snd_soc_register_component(&pdev->dev, 86262306a36Sopenharmony_ci &mchp_spdiftx_component, 86362306a36Sopenharmony_ci &mchp_spdiftx_dai, 1); 86462306a36Sopenharmony_ci if (err) { 86562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register component: %d\n", err); 86662306a36Sopenharmony_ci goto pm_runtime_suspend; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cipm_runtime_suspend: 87262306a36Sopenharmony_ci if (!pm_runtime_status_suspended(dev->dev)) 87362306a36Sopenharmony_ci mchp_spdiftx_runtime_suspend(dev->dev); 87462306a36Sopenharmony_ci pm_runtime_disable(dev->dev); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return err; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void mchp_spdiftx_remove(struct platform_device *pdev) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct mchp_spdiftx_dev *dev = platform_get_drvdata(pdev); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (!pm_runtime_status_suspended(dev->dev)) 88462306a36Sopenharmony_ci mchp_spdiftx_runtime_suspend(dev->dev); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci pm_runtime_disable(dev->dev); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic struct platform_driver mchp_spdiftx_driver = { 89062306a36Sopenharmony_ci .probe = mchp_spdiftx_probe, 89162306a36Sopenharmony_ci .remove_new = mchp_spdiftx_remove, 89262306a36Sopenharmony_ci .driver = { 89362306a36Sopenharmony_ci .name = "mchp_spdiftx", 89462306a36Sopenharmony_ci .of_match_table = mchp_spdiftx_dt_ids, 89562306a36Sopenharmony_ci .pm = pm_ptr(&mchp_spdiftx_pm_ops) 89662306a36Sopenharmony_ci }, 89762306a36Sopenharmony_ci}; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cimodule_platform_driver(mchp_spdiftx_driver); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ciMODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>"); 90262306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver"); 90362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 904