162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2014-2015 Broadcom Corporation 362306a36Sopenharmony_ci#include <linux/clk.h> 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/of_device.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <sound/core.h> 1162306a36Sopenharmony_ci#include <sound/pcm.h> 1262306a36Sopenharmony_ci#include <sound/pcm_params.h> 1362306a36Sopenharmony_ci#include <sound/soc.h> 1462306a36Sopenharmony_ci#include <sound/soc-dai.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cygnus-ssp.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DEFAULT_VCO 1354750204 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define CAPTURE_FCI_ID_BASE 0x180 2162306a36Sopenharmony_ci#define CYGNUS_SSP_TRISTATE_MASK 0x001fff 2262306a36Sopenharmony_ci#define CYGNUS_PLLCLKSEL_MASK 0xf 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Used with stream_on field to indicate which streams are active */ 2562306a36Sopenharmony_ci#define PLAYBACK_STREAM_MASK BIT(0) 2662306a36Sopenharmony_ci#define CAPTURE_STREAM_MASK BIT(1) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define I2S_STREAM_CFG_MASK 0xff003ff 2962306a36Sopenharmony_ci#define I2S_CAP_STREAM_CFG_MASK 0xf0 3062306a36Sopenharmony_ci#define SPDIF_STREAM_CFG_MASK 0x3ff 3162306a36Sopenharmony_ci#define CH_GRP_STEREO 0x1 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Begin register offset defines */ 3462306a36Sopenharmony_ci#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c 3562306a36Sopenharmony_ci#define AUD_MISC_SEROUT_SPDIF_OE 12 3662306a36Sopenharmony_ci#define AUD_MISC_SEROUT_MCLK_OE 3 3762306a36Sopenharmony_ci#define AUD_MISC_SEROUT_LRCK_OE 2 3862306a36Sopenharmony_ci#define AUD_MISC_SEROUT_SCLK_OE 1 3962306a36Sopenharmony_ci#define AUD_MISC_SEROUT_SDAT_OE 0 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_xxx regs */ 4262306a36Sopenharmony_ci#define BF_DST_CFG0_OFFSET 0x100 4362306a36Sopenharmony_ci#define BF_DST_CFG1_OFFSET 0x104 4462306a36Sopenharmony_ci#define BF_DST_CFG2_OFFSET 0x108 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define BF_DST_CTRL0_OFFSET 0x130 4762306a36Sopenharmony_ci#define BF_DST_CTRL1_OFFSET 0x134 4862306a36Sopenharmony_ci#define BF_DST_CTRL2_OFFSET 0x138 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define BF_SRC_CFG0_OFFSET 0x148 5162306a36Sopenharmony_ci#define BF_SRC_CFG1_OFFSET 0x14c 5262306a36Sopenharmony_ci#define BF_SRC_CFG2_OFFSET 0x150 5362306a36Sopenharmony_ci#define BF_SRC_CFG3_OFFSET 0x154 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define BF_SRC_CTRL0_OFFSET 0x1c0 5662306a36Sopenharmony_ci#define BF_SRC_CTRL1_OFFSET 0x1c4 5762306a36Sopenharmony_ci#define BF_SRC_CTRL2_OFFSET 0x1c8 5862306a36Sopenharmony_ci#define BF_SRC_CTRL3_OFFSET 0x1cc 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define BF_SRC_GRP0_OFFSET 0x1fc 6162306a36Sopenharmony_ci#define BF_SRC_GRP1_OFFSET 0x200 6262306a36Sopenharmony_ci#define BF_SRC_GRP2_OFFSET 0x204 6362306a36Sopenharmony_ci#define BF_SRC_GRP3_OFFSET 0x208 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define BF_SRC_GRP_EN_OFFSET 0x320 6662306a36Sopenharmony_ci#define BF_SRC_GRP_FLOWON_OFFSET 0x324 6762306a36Sopenharmony_ci#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_xxx regs */ 7062306a36Sopenharmony_ci#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00 7162306a36Sopenharmony_ci#define OUT_I2S_0_CFG_OFFSET 0xa04 7262306a36Sopenharmony_ci#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40 7562306a36Sopenharmony_ci#define OUT_I2S_1_CFG_OFFSET 0xa44 7662306a36Sopenharmony_ci#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80 7962306a36Sopenharmony_ci#define OUT_I2S_2_CFG_OFFSET 0xa84 8062306a36Sopenharmony_ci#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */ 8362306a36Sopenharmony_ci#define SPDIF_STREAM_CFG_OFFSET 0xac0 8462306a36Sopenharmony_ci#define SPDIF_CTRL_OFFSET 0xac4 8562306a36Sopenharmony_ci#define SPDIF_FORMAT_CFG_OFFSET 0xad8 8662306a36Sopenharmony_ci#define SPDIF_MCLK_CFG_OFFSET 0xadc 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_xxx regs */ 8962306a36Sopenharmony_ci#define IOP_PLL_0_MACRO_OFFSET 0xb00 9062306a36Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14 9162306a36Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18 9262306a36Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30 9562306a36Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34 9662306a36Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* AUD_FMM_IOP_xxx regs */ 9962306a36Sopenharmony_ci#define IOP_PLL_0_CONTROL_OFFSET 0xb04 10062306a36Sopenharmony_ci#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08 10162306a36Sopenharmony_ci#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20 10262306a36Sopenharmony_ci#define IOP_PLL_0_RESET_OFFSET 0xb5c 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* AUD_FMM_IOP_IN_I2S_xxx regs */ 10562306a36Sopenharmony_ci#define IN_I2S_0_STREAM_CFG_OFFSET 0x00 10662306a36Sopenharmony_ci#define IN_I2S_0_CFG_OFFSET 0x04 10762306a36Sopenharmony_ci#define IN_I2S_1_STREAM_CFG_OFFSET 0x40 10862306a36Sopenharmony_ci#define IN_I2S_1_CFG_OFFSET 0x44 10962306a36Sopenharmony_ci#define IN_I2S_2_STREAM_CFG_OFFSET 0x80 11062306a36Sopenharmony_ci#define IN_I2S_2_CFG_OFFSET 0x84 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* AUD_FMM_IOP_MISC_xxx regs */ 11362306a36Sopenharmony_ci#define IOP_SW_INIT_LOGIC 0x1c0 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* End register offset defines */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */ 11962306a36Sopenharmony_ci#define I2S_OUT_MCLKRATE_SHIFT 16 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */ 12262306a36Sopenharmony_ci#define I2S_OUT_PLLCLKSEL_SHIFT 0 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */ 12562306a36Sopenharmony_ci#define I2S_OUT_STREAM_ENA 31 12662306a36Sopenharmony_ci#define I2S_OUT_STREAM_CFG_GROUP_ID 20 12762306a36Sopenharmony_ci#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* AUD_FMM_IOP_IN_I2S_x_CAP */ 13062306a36Sopenharmony_ci#define I2S_IN_STREAM_CFG_CAP_ENA 31 13162306a36Sopenharmony_ci#define I2S_IN_STREAM_CFG_0_GROUP_ID 4 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */ 13462306a36Sopenharmony_ci#define I2S_OUT_CFGX_CLK_ENA 0 13562306a36Sopenharmony_ci#define I2S_OUT_CFGX_DATA_ENABLE 1 13662306a36Sopenharmony_ci#define I2S_OUT_CFGX_DATA_ALIGNMENT 6 13762306a36Sopenharmony_ci#define I2S_OUT_CFGX_BITS_PER_SLOT 13 13862306a36Sopenharmony_ci#define I2S_OUT_CFGX_VALID_SLOT 14 13962306a36Sopenharmony_ci#define I2S_OUT_CFGX_FSYNC_WIDTH 18 14062306a36Sopenharmony_ci#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26 14162306a36Sopenharmony_ci#define I2S_OUT_CFGX_SLAVE_MODE 30 14262306a36Sopenharmony_ci#define I2S_OUT_CFGX_TDM_MODE 31 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */ 14562306a36Sopenharmony_ci#define BF_SRC_CFGX_SFIFO_ENA 0 14662306a36Sopenharmony_ci#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1 14762306a36Sopenharmony_ci#define BF_SRC_CFGX_SAMPLE_CH_MODE 2 14862306a36Sopenharmony_ci#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5 14962306a36Sopenharmony_ci#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10 15062306a36Sopenharmony_ci#define BF_SRC_CFGX_BIT_RES 20 15162306a36Sopenharmony_ci#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */ 15462306a36Sopenharmony_ci#define BF_DST_CFGX_CAP_ENA 0 15562306a36Sopenharmony_ci#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1 15662306a36Sopenharmony_ci#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2 15762306a36Sopenharmony_ci#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11 15862306a36Sopenharmony_ci#define BF_DST_CFGX_FCI_ID 12 15962306a36Sopenharmony_ci#define BF_DST_CFGX_CAP_MODE 24 16062306a36Sopenharmony_ci#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* AUD_FMM_IOP_OUT_SPDIF_xxx */ 16362306a36Sopenharmony_ci#define SPDIF_0_OUT_DITHER_ENA 3 16462306a36Sopenharmony_ci#define SPDIF_0_OUT_STREAM_ENA 31 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_USER */ 16762306a36Sopenharmony_ci#define IOP_PLL_0_USER_NDIV_FRAC 10 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_ACTIVE */ 17062306a36Sopenharmony_ci#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \ 17462306a36Sopenharmony_ci .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \ 17562306a36Sopenharmony_ci .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \ 17662306a36Sopenharmony_ci .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \ 17762306a36Sopenharmony_ci .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \ 17862306a36Sopenharmony_ci .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \ 17962306a36Sopenharmony_ci .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \ 18062306a36Sopenharmony_ci .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \ 18162306a36Sopenharmony_ci .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \ 18262306a36Sopenharmony_ci .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \ 18362306a36Sopenharmony_ci .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \ 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistruct pll_macro_entry { 18762306a36Sopenharmony_ci u32 mclk; 18862306a36Sopenharmony_ci u32 pll_ch_num; 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* 19262306a36Sopenharmony_ci * PLL has 3 output channels (1x, 2x, and 4x). Below are 19362306a36Sopenharmony_ci * the common MCLK frequencies used by audio driver 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic const struct pll_macro_entry pll_predef_mclk[] = { 19662306a36Sopenharmony_ci { 4096000, 0}, 19762306a36Sopenharmony_ci { 8192000, 1}, 19862306a36Sopenharmony_ci {16384000, 2}, 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci { 5644800, 0}, 20162306a36Sopenharmony_ci {11289600, 1}, 20262306a36Sopenharmony_ci {22579200, 2}, 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci { 6144000, 0}, 20562306a36Sopenharmony_ci {12288000, 1}, 20662306a36Sopenharmony_ci {24576000, 2}, 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci {12288000, 0}, 20962306a36Sopenharmony_ci {24576000, 1}, 21062306a36Sopenharmony_ci {49152000, 2}, 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci {22579200, 0}, 21362306a36Sopenharmony_ci {45158400, 1}, 21462306a36Sopenharmony_ci {90316800, 2}, 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci {24576000, 0}, 21762306a36Sopenharmony_ci {49152000, 1}, 21862306a36Sopenharmony_ci {98304000, 2}, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#define CYGNUS_RATE_MIN 8000 22262306a36Sopenharmony_ci#define CYGNUS_RATE_MAX 384000 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* List of valid frame sizes for tdm mode */ 22562306a36Sopenharmony_cistatic const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const unsigned int cygnus_rates[] = { 22862306a36Sopenharmony_ci 8000, 11025, 16000, 22050, 32000, 44100, 48000, 22962306a36Sopenharmony_ci 88200, 96000, 176400, 192000, 352800, 384000 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = { 23362306a36Sopenharmony_ci .count = ARRAY_SIZE(cygnus_rates), 23462306a36Sopenharmony_ci .list = cygnus_rates, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return &cygaud->portinfo[dai->id]; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int audio_ssp_init_portregs(struct cygnus_aio_port *aio) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci u32 value, fci_id; 24762306a36Sopenharmony_ci int status = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci switch (aio->port_type) { 25062306a36Sopenharmony_ci case PORT_TDM: 25162306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 25262306a36Sopenharmony_ci value &= ~I2S_STREAM_CFG_MASK; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Set Group ID */ 25562306a36Sopenharmony_ci writel(aio->portnum, 25662306a36Sopenharmony_ci aio->cygaud->audio + aio->regs.bf_sourcech_grp); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */ 25962306a36Sopenharmony_ci value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID; 26062306a36Sopenharmony_ci value |= aio->portnum; /* FCI ID is the port num */ 26162306a36Sopenharmony_ci value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING; 26262306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */ 26562306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 26662306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); 26762306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); 26862306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); 26962306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */ 27262306a36Sopenharmony_ci value = readl(aio->cygaud->i2s_in + 27362306a36Sopenharmony_ci aio->regs.i2s_cap_stream_cfg); 27462306a36Sopenharmony_ci value &= ~I2S_CAP_STREAM_CFG_MASK; 27562306a36Sopenharmony_ci value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID; 27662306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + 27762306a36Sopenharmony_ci aio->regs.i2s_cap_stream_cfg); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */ 28062306a36Sopenharmony_ci fci_id = CAPTURE_FCI_ID_BASE + aio->portnum; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 28362306a36Sopenharmony_ci value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE); 28462306a36Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL); 28562306a36Sopenharmony_ci value |= (fci_id << BF_DST_CFGX_FCI_ID); 28662306a36Sopenharmony_ci value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID); 28762306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Enable the transmit pin for this port */ 29062306a36Sopenharmony_ci value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 29162306a36Sopenharmony_ci value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE); 29262306a36Sopenharmony_ci writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case PORT_SPDIF: 29562306a36Sopenharmony_ci writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET); 29862306a36Sopenharmony_ci value |= BIT(SPDIF_0_OUT_DITHER_ENA); 29962306a36Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Enable and set the FCI ID for the SPDIF channel */ 30262306a36Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); 30362306a36Sopenharmony_ci value &= ~SPDIF_STREAM_CFG_MASK; 30462306a36Sopenharmony_ci value |= aio->portnum; /* FCI ID is the port num */ 30562306a36Sopenharmony_ci value |= BIT(SPDIF_0_OUT_STREAM_ENA); 30662306a36Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 30962306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); 31062306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); 31162306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); 31262306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Enable the spdif output pin */ 31562306a36Sopenharmony_ci value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 31662306a36Sopenharmony_ci value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE); 31762306a36Sopenharmony_ci writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci default: 32062306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "Port not supported\n"); 32162306a36Sopenharmony_ci status = -EINVAL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return status; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void audio_ssp_in_enable(struct cygnus_aio_port *aio) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci u32 value; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 33262306a36Sopenharmony_ci value |= BIT(BF_DST_CFGX_CAP_ENA); 33362306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 33862306a36Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_CLK_ENA); 33962306a36Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); 34062306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 34362306a36Sopenharmony_ci value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA); 34462306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci aio->streams_on |= CAPTURE_STREAM_MASK; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void audio_ssp_in_disable(struct cygnus_aio_port *aio) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci u32 value; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 35462306a36Sopenharmony_ci value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA); 35562306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci aio->streams_on &= ~CAPTURE_STREAM_MASK; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* If both playback and capture are off */ 36062306a36Sopenharmony_ci if (!aio->streams_on) { 36162306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 36262306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); 36362306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); 36462306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 37062306a36Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_CAP_ENA); 37162306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int audio_ssp_out_enable(struct cygnus_aio_port *aio) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci u32 value; 37762306a36Sopenharmony_ci int status = 0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (aio->port_type) { 38062306a36Sopenharmony_ci case PORT_TDM: 38162306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 38262306a36Sopenharmony_ci value |= BIT(I2S_OUT_STREAM_ENA); 38362306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 38862306a36Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_CLK_ENA); 38962306a36Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); 39062306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 39362306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_ENA); 39462306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci aio->streams_on |= PLAYBACK_STREAM_MASK; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case PORT_SPDIF: 39962306a36Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 40062306a36Sopenharmony_ci value |= 0x3; 40162306a36Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 40662306a36Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_ENA); 40762306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci default: 41062306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 41162306a36Sopenharmony_ci "Port not supported %d\n", aio->portnum); 41262306a36Sopenharmony_ci status = -EINVAL; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return status; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int audio_ssp_out_disable(struct cygnus_aio_port *aio) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u32 value; 42162306a36Sopenharmony_ci int status = 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci switch (aio->port_type) { 42462306a36Sopenharmony_ci case PORT_TDM: 42562306a36Sopenharmony_ci aio->streams_on &= ~PLAYBACK_STREAM_MASK; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* If both playback and capture are off */ 42862306a36Sopenharmony_ci if (!aio->streams_on) { 42962306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 43062306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); 43162306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); 43262306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* set group_sync_dis = 1 */ 43662306a36Sopenharmony_ci value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 43762306a36Sopenharmony_ci value |= BIT(aio->portnum); 43862306a36Sopenharmony_ci writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 44362306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); 44462306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* set group_sync_dis = 0 */ 44762306a36Sopenharmony_ci value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 44862306a36Sopenharmony_ci value &= ~BIT(aio->portnum); 44962306a36Sopenharmony_ci writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 45262306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_STREAM_ENA); 45362306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* IOP SW INIT on OUT_I2S_x */ 45662306a36Sopenharmony_ci value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 45762306a36Sopenharmony_ci value |= BIT(aio->portnum); 45862306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 45962306a36Sopenharmony_ci value &= ~BIT(aio->portnum); 46062306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case PORT_SPDIF: 46362306a36Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 46462306a36Sopenharmony_ci value &= ~0x3; 46562306a36Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 46662306a36Sopenharmony_ci writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 46962306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); 47062306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci default: 47362306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 47462306a36Sopenharmony_ci "Port not supported %d\n", aio->portnum); 47562306a36Sopenharmony_ci status = -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return status; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk, 48262306a36Sopenharmony_ci struct cygnus_aio_port *aio) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int i = 0, error; 48562306a36Sopenharmony_ci bool found = false; 48662306a36Sopenharmony_ci const struct pll_macro_entry *p_entry; 48762306a36Sopenharmony_ci struct clk *ch_clk; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) { 49062306a36Sopenharmony_ci p_entry = &pll_predef_mclk[i]; 49162306a36Sopenharmony_ci if (p_entry->mclk == mclk) { 49262306a36Sopenharmony_ci found = true; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci if (!found) { 49762306a36Sopenharmony_ci dev_err(cygaud->dev, 49862306a36Sopenharmony_ci "%s No valid mclk freq (%u) found!\n", __func__, mclk); 49962306a36Sopenharmony_ci return -EINVAL; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ch_clk = cygaud->audio_clk[p_entry->pll_ch_num]; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) { 50562306a36Sopenharmony_ci error = clk_prepare_enable(ch_clk); 50662306a36Sopenharmony_ci if (error) { 50762306a36Sopenharmony_ci dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", 50862306a36Sopenharmony_ci __func__, error); 50962306a36Sopenharmony_ci return error; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci aio->clk_trace.cap_clk_en = true; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) { 51562306a36Sopenharmony_ci error = clk_prepare_enable(ch_clk); 51662306a36Sopenharmony_ci if (error) { 51762306a36Sopenharmony_ci dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", 51862306a36Sopenharmony_ci __func__, error); 51962306a36Sopenharmony_ci return error; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci aio->clk_trace.play_clk_en = true; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci error = clk_set_rate(ch_clk, mclk); 52562306a36Sopenharmony_ci if (error) { 52662306a36Sopenharmony_ci dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n", 52762306a36Sopenharmony_ci __func__, error); 52862306a36Sopenharmony_ci return error; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return p_entry->pll_ch_num; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci u32 value; 53762306a36Sopenharmony_ci u32 mask = 0xf; 53862306a36Sopenharmony_ci u32 sclk; 53962306a36Sopenharmony_ci u32 mclk_rate; 54062306a36Sopenharmony_ci unsigned int bit_rate; 54162306a36Sopenharmony_ci unsigned int ratio; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci bit_rate = aio->bit_per_frame * aio->lrclk; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * Check if the bit clock can be generated from the given MCLK. 54762306a36Sopenharmony_ci * MCLK must be a perfect multiple of bit clock and must be one of the 54862306a36Sopenharmony_ci * following values... (2,4,6,8,10,12,14) 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci if ((aio->mclk % bit_rate) != 0) 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci ratio = aio->mclk / bit_rate; 55462306a36Sopenharmony_ci switch (ratio) { 55562306a36Sopenharmony_ci case 2: 55662306a36Sopenharmony_ci case 4: 55762306a36Sopenharmony_ci case 6: 55862306a36Sopenharmony_ci case 8: 55962306a36Sopenharmony_ci case 10: 56062306a36Sopenharmony_ci case 12: 56162306a36Sopenharmony_ci case 14: 56262306a36Sopenharmony_ci mclk_rate = ratio / 2; 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci default: 56662306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 56762306a36Sopenharmony_ci "Invalid combination of MCLK and BCLK\n"); 56862306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n", 56962306a36Sopenharmony_ci aio->lrclk, aio->bit_per_frame, aio->mclk); 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Set sclk rate */ 57462306a36Sopenharmony_ci switch (aio->port_type) { 57562306a36Sopenharmony_ci case PORT_TDM: 57662306a36Sopenharmony_ci sclk = aio->bit_per_frame; 57762306a36Sopenharmony_ci if (sclk == 512) 57862306a36Sopenharmony_ci sclk = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* sclks_per_1fs_div = sclk cycles/32 */ 58162306a36Sopenharmony_ci sclk /= 32; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Set number of bitclks per frame */ 58462306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 58562306a36Sopenharmony_ci value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32); 58662306a36Sopenharmony_ci value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32; 58762306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 58862306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, 58962306a36Sopenharmony_ci "SCLKS_PER_1FS_DIV32 = 0x%x\n", value); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case PORT_SPDIF: 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci default: 59462306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "Unknown port type\n"); 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Set MCLK_RATE ssp port (spdif and ssp are the same) */ 59962306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 60062306a36Sopenharmony_ci value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT); 60162306a36Sopenharmony_ci value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT); 60262306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value); 60562306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n", 60662306a36Sopenharmony_ci aio->bit_per_frame, aio->mclk, aio->lrclk); 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int cygnus_ssp_hw_params(struct snd_pcm_substream *substream, 61162306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 61262306a36Sopenharmony_ci struct snd_soc_dai *dai) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 61562306a36Sopenharmony_ci int rate, bitres; 61662306a36Sopenharmony_ci u32 value; 61762306a36Sopenharmony_ci u32 mask = 0x1f; 61862306a36Sopenharmony_ci int ret = 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum); 62162306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "params_channels %d\n", 62262306a36Sopenharmony_ci params_channels(params)); 62362306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params)); 62462306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params)); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci rate = params_rate(params); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci switch (aio->mode) { 62962306a36Sopenharmony_ci case CYGNUS_SSPMODE_TDM: 63062306a36Sopenharmony_ci if ((rate == 192000) && (params_channels(params) > 4)) { 63162306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n", 63262306a36Sopenharmony_ci params_channels(params), rate); 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case CYGNUS_SSPMODE_I2S: 63762306a36Sopenharmony_ci aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */ 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 64162306a36Sopenharmony_ci "%s port running in unknown mode\n", __func__); 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 64662306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 64762306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE); 64862306a36Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); 64962306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci switch (params_format(params)) { 65262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 65362306a36Sopenharmony_ci bitres = 16; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 65762306a36Sopenharmony_ci /* 32 bit mode is coded as 0 */ 65862306a36Sopenharmony_ci bitres = 0; 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci default: 66262306a36Sopenharmony_ci return -EINVAL; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 66662306a36Sopenharmony_ci value &= ~(mask << BF_SRC_CFGX_BIT_RES); 66762306a36Sopenharmony_ci value |= (bitres << BF_SRC_CFGX_BIT_RES); 66862306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci } else { 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci switch (params_format(params)) { 67362306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 67462306a36Sopenharmony_ci value = readl(aio->cygaud->audio + 67562306a36Sopenharmony_ci aio->regs.bf_destch_cfg); 67662306a36Sopenharmony_ci value |= BIT(BF_DST_CFGX_CAP_MODE); 67762306a36Sopenharmony_ci writel(value, aio->cygaud->audio + 67862306a36Sopenharmony_ci aio->regs.bf_destch_cfg); 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 68262306a36Sopenharmony_ci value = readl(aio->cygaud->audio + 68362306a36Sopenharmony_ci aio->regs.bf_destch_cfg); 68462306a36Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_CAP_MODE); 68562306a36Sopenharmony_ci writel(value, aio->cygaud->audio + 68662306a36Sopenharmony_ci aio->regs.bf_destch_cfg); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci aio->lrclk = rate; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (!aio->is_slave) 69762306a36Sopenharmony_ci ret = cygnus_ssp_set_clocks(aio); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/* 70362306a36Sopenharmony_ci * This function sets the mclk frequency for pll clock 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai, 70662306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci int sel; 70962306a36Sopenharmony_ci u32 value; 71062306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 71162306a36Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, 71462306a36Sopenharmony_ci "%s Enter port = %d\n", __func__, aio->portnum); 71562306a36Sopenharmony_ci sel = pll_configure_mclk(cygaud, freq, aio); 71662306a36Sopenharmony_ci if (sel < 0) { 71762306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 71862306a36Sopenharmony_ci "%s Setting mclk failed.\n", __func__); 71962306a36Sopenharmony_ci return -EINVAL; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci aio->mclk = freq; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel); 72562306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 72662306a36Sopenharmony_ci value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT); 72762306a36Sopenharmony_ci value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT); 72862306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int cygnus_ssp_startup(struct snd_pcm_substream *substream, 73462306a36Sopenharmony_ci struct snd_soc_dai *dai) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, aio); 73962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 74062306a36Sopenharmony_ci aio->clk_trace.play_en = true; 74162306a36Sopenharmony_ci else 74262306a36Sopenharmony_ci aio->clk_trace.cap_en = true; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci substream->runtime->hw.rate_min = CYGNUS_RATE_MIN; 74562306a36Sopenharmony_ci substream->runtime->hw.rate_max = CYGNUS_RATE_MAX; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci snd_pcm_hw_constraint_list(substream->runtime, 0, 74862306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint); 74962306a36Sopenharmony_ci return 0; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void cygnus_ssp_shutdown(struct snd_pcm_substream *substream, 75362306a36Sopenharmony_ci struct snd_soc_dai *dai) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 75862306a36Sopenharmony_ci aio->clk_trace.play_en = false; 75962306a36Sopenharmony_ci else 76062306a36Sopenharmony_ci aio->clk_trace.cap_en = false; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (!aio->is_slave) { 76362306a36Sopenharmony_ci u32 val; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 76662306a36Sopenharmony_ci val &= CYGNUS_PLLCLKSEL_MASK; 76762306a36Sopenharmony_ci if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { 76862306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", 76962306a36Sopenharmony_ci val); 77062306a36Sopenharmony_ci return; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 77462306a36Sopenharmony_ci if (aio->clk_trace.play_clk_en) { 77562306a36Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 77662306a36Sopenharmony_ci audio_clk[val]); 77762306a36Sopenharmony_ci aio->clk_trace.play_clk_en = false; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } else { 78062306a36Sopenharmony_ci if (aio->clk_trace.cap_clk_en) { 78162306a36Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 78262306a36Sopenharmony_ci audio_clk[val]); 78362306a36Sopenharmony_ci aio->clk_trace.cap_clk_en = false; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/* 79062306a36Sopenharmony_ci * Bit Update Notes 79162306a36Sopenharmony_ci * 31 Yes TDM Mode (1 = TDM, 0 = i2s) 79262306a36Sopenharmony_ci * 30 Yes Slave Mode (1 = Slave, 0 = Master) 79362306a36Sopenharmony_ci * 29:26 No Sclks per frame 79462306a36Sopenharmony_ci * 25:18 Yes FS Width 79562306a36Sopenharmony_ci * 17:14 No Valid Slots 79662306a36Sopenharmony_ci * 13 No Bits (1 = 16 bits, 0 = 32 bits) 79762306a36Sopenharmony_ci * 12:08 No Bits per samp 79862306a36Sopenharmony_ci * 07 Yes Justifcation (1 = LSB, 0 = MSB) 79962306a36Sopenharmony_ci * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay 80062306a36Sopenharmony_ci * 05 Yes SCLK polarity (1 = Rising, 0 = Falling) 80162306a36Sopenharmony_ci * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left) 80262306a36Sopenharmony_ci * 03:02 Yes Reserved - write as zero 80362306a36Sopenharmony_ci * 01 No Data Enable 80462306a36Sopenharmony_ci * 00 No CLK Enable 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci/* Input cfg is same as output, but the FS width is not a valid field */ 80962306a36Sopenharmony_ci#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000) 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciint cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if ((len > 0) && (len < 256)) { 81662306a36Sopenharmony_ci aio->fsync_width = len; 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci } else { 81962306a36Sopenharmony_ci return -EINVAL; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 82762306a36Sopenharmony_ci u32 ssp_curcfg; 82862306a36Sopenharmony_ci u32 ssp_newcfg; 82962306a36Sopenharmony_ci u32 ssp_outcfg; 83062306a36Sopenharmony_ci u32 ssp_incfg; 83162306a36Sopenharmony_ci u32 val; 83262306a36Sopenharmony_ci u32 mask; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (aio->port_type == PORT_SPDIF) 83762306a36Sopenharmony_ci return -EINVAL; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ssp_newcfg = 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 84262306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 84362306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE); 84462306a36Sopenharmony_ci aio->is_slave = 1; 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 84762306a36Sopenharmony_ci ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE); 84862306a36Sopenharmony_ci aio->is_slave = 0; 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci default: 85162306a36Sopenharmony_ci return -EINVAL; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 85562306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 85662306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); 85762306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); 85862306a36Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_I2S; 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 86262306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 86362306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* DSP_A = data after FS, DSP_B = data during FS */ 86662306a36Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) 86762306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if ((aio->fsync_width > 0) && (aio->fsync_width < 256)) 87062306a36Sopenharmony_ci ssp_newcfg |= 87162306a36Sopenharmony_ci (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH); 87262306a36Sopenharmony_ci else 87362306a36Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_TDM; 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci default: 87962306a36Sopenharmony_ci return -EINVAL; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* 88362306a36Sopenharmony_ci * SSP out cfg. 88462306a36Sopenharmony_ci * Retain bits we do not want to update, then OR in new bits 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 88762306a36Sopenharmony_ci ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg; 88862306a36Sopenharmony_ci writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* 89162306a36Sopenharmony_ci * SSP in cfg. 89262306a36Sopenharmony_ci * Retain bits we do not want to update, then OR in new bits 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 89562306a36Sopenharmony_ci ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg; 89662306a36Sopenharmony_ci writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* 90162306a36Sopenharmony_ci * Configure the word clk and bit clk as output or tristate 90262306a36Sopenharmony_ci * Each port has 4 bits for controlling its pins. 90362306a36Sopenharmony_ci * Shift the mask based upon port number. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci mask = BIT(AUD_MISC_SEROUT_LRCK_OE) 90662306a36Sopenharmony_ci | BIT(AUD_MISC_SEROUT_SCLK_OE) 90762306a36Sopenharmony_ci | BIT(AUD_MISC_SEROUT_MCLK_OE); 90862306a36Sopenharmony_ci mask = mask << (aio->portnum * 4); 90962306a36Sopenharmony_ci if (aio->is_slave) 91062306a36Sopenharmony_ci /* Set bit for tri-state */ 91162306a36Sopenharmony_ci val |= mask; 91262306a36Sopenharmony_ci else 91362306a36Sopenharmony_ci /* Clear bit for drive */ 91462306a36Sopenharmony_ci val &= ~mask; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val); 91762306a36Sopenharmony_ci writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd, 92362306a36Sopenharmony_ci struct snd_soc_dai *dai) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 92662306a36Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, 92962306a36Sopenharmony_ci "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci switch (cmd) { 93262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 93362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 93462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 93562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 93662306a36Sopenharmony_ci audio_ssp_out_enable(aio); 93762306a36Sopenharmony_ci else 93862306a36Sopenharmony_ci audio_ssp_in_enable(aio); 93962306a36Sopenharmony_ci cygaud->active_ports++; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 94462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 94562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 94662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 94762306a36Sopenharmony_ci audio_ssp_out_disable(aio); 94862306a36Sopenharmony_ci else 94962306a36Sopenharmony_ci audio_ssp_in_disable(aio); 95062306a36Sopenharmony_ci cygaud->active_ports--; 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci default: 95462306a36Sopenharmony_ci return -EINVAL; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, 96162306a36Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 96462306a36Sopenharmony_ci u32 value; 96562306a36Sopenharmony_ci int bits_per_slot = 0; /* default to 32-bits per slot */ 96662306a36Sopenharmony_ci int frame_bits; 96762306a36Sopenharmony_ci unsigned int active_slots; 96862306a36Sopenharmony_ci bool found = false; 96962306a36Sopenharmony_ci int i; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (tx_mask != rx_mask) { 97262306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 97362306a36Sopenharmony_ci "%s tx_mask must equal rx_mask\n", __func__); 97462306a36Sopenharmony_ci return -EINVAL; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci active_slots = hweight32(tx_mask); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (active_slots > 16) 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Slot value must be even */ 98362306a36Sopenharmony_ci if (active_slots % 2) 98462306a36Sopenharmony_ci return -EINVAL; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* We encode 16 slots as 0 in the reg */ 98762306a36Sopenharmony_ci if (active_slots == 16) 98862306a36Sopenharmony_ci active_slots = 0; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Slot Width is either 16 or 32 */ 99162306a36Sopenharmony_ci switch (slot_width) { 99262306a36Sopenharmony_ci case 16: 99362306a36Sopenharmony_ci bits_per_slot = 1; 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci case 32: 99662306a36Sopenharmony_ci bits_per_slot = 0; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci default: 99962306a36Sopenharmony_ci bits_per_slot = 0; 100062306a36Sopenharmony_ci dev_warn(aio->cygaud->dev, 100162306a36Sopenharmony_ci "%s Defaulting Slot Width to 32\n", __func__); 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci frame_bits = slots * slot_width; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) { 100762306a36Sopenharmony_ci if (ssp_valid_tdm_framesize[i] == frame_bits) { 100862306a36Sopenharmony_ci found = true; 100962306a36Sopenharmony_ci break; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (!found) { 101462306a36Sopenharmony_ci dev_err(aio->cygaud->dev, 101562306a36Sopenharmony_ci "%s In TDM mode, frame bits INVALID (%d)\n", 101662306a36Sopenharmony_ci __func__, frame_bits); 101762306a36Sopenharmony_ci return -EINVAL; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci aio->bit_per_frame = frame_bits; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n", 102362306a36Sopenharmony_ci __func__, active_slots, frame_bits); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* Set capture side of ssp port */ 102662306a36Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 102762306a36Sopenharmony_ci value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); 102862306a36Sopenharmony_ci value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); 102962306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); 103062306a36Sopenharmony_ci value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); 103162306a36Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Set playback side of ssp port */ 103462306a36Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 103562306a36Sopenharmony_ci value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); 103662306a36Sopenharmony_ci value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); 103762306a36Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); 103862306a36Sopenharmony_ci value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); 103962306a36Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 104562306a36Sopenharmony_cistatic int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (!aio->is_slave) { 105362306a36Sopenharmony_ci u32 val; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 105662306a36Sopenharmony_ci val &= CYGNUS_PLLCLKSEL_MASK; 105762306a36Sopenharmony_ci if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { 105862306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", 105962306a36Sopenharmony_ci val); 106062306a36Sopenharmony_ci return -EINVAL; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (aio->clk_trace.cap_clk_en) 106462306a36Sopenharmony_ci clk_disable_unprepare(aio->cygaud->audio_clk[val]); 106562306a36Sopenharmony_ci if (aio->clk_trace.play_clk_en) 106662306a36Sopenharmony_ci clk_disable_unprepare(aio->cygaud->audio_clk[val]); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci aio->pll_clk_num = val; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int cygnus_ssp_suspend(struct snd_soc_component *component) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct snd_soc_dai *dai; 107762306a36Sopenharmony_ci int ret = 0; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci for_each_component_dais(component, dai) 108062306a36Sopenharmony_ci ret |= __cygnus_ssp_suspend(dai); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci return ret; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 108862306a36Sopenharmony_ci int error; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (!aio->is_slave) { 109462306a36Sopenharmony_ci if (aio->clk_trace.cap_clk_en) { 109562306a36Sopenharmony_ci error = clk_prepare_enable(aio->cygaud-> 109662306a36Sopenharmony_ci audio_clk[aio->pll_clk_num]); 109762306a36Sopenharmony_ci if (error) { 109862306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", 109962306a36Sopenharmony_ci __func__); 110062306a36Sopenharmony_ci return -EINVAL; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci if (aio->clk_trace.play_clk_en) { 110462306a36Sopenharmony_ci error = clk_prepare_enable(aio->cygaud-> 110562306a36Sopenharmony_ci audio_clk[aio->pll_clk_num]); 110662306a36Sopenharmony_ci if (error) { 110762306a36Sopenharmony_ci if (aio->clk_trace.cap_clk_en) 110862306a36Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 110962306a36Sopenharmony_ci audio_clk[aio->pll_clk_num]); 111062306a36Sopenharmony_ci dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", 111162306a36Sopenharmony_ci __func__); 111262306a36Sopenharmony_ci return -EINVAL; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic int cygnus_ssp_resume(struct snd_soc_component *component) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct snd_soc_dai *dai; 112362306a36Sopenharmony_ci int ret = 0; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci for_each_component_dais(component, dai) 112662306a36Sopenharmony_ci ret |= __cygnus_ssp_resume(dai); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci return ret; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci#else 113262306a36Sopenharmony_ci#define cygnus_ssp_suspend NULL 113362306a36Sopenharmony_ci#define cygnus_ssp_resume NULL 113462306a36Sopenharmony_ci#endif 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { 113762306a36Sopenharmony_ci .startup = cygnus_ssp_startup, 113862306a36Sopenharmony_ci .shutdown = cygnus_ssp_shutdown, 113962306a36Sopenharmony_ci .trigger = cygnus_ssp_trigger, 114062306a36Sopenharmony_ci .hw_params = cygnus_ssp_hw_params, 114162306a36Sopenharmony_ci .set_fmt = cygnus_ssp_set_fmt, 114262306a36Sopenharmony_ci .set_sysclk = cygnus_ssp_set_sysclk, 114362306a36Sopenharmony_ci .set_tdm_slot = cygnus_set_dai_tdm_slot, 114462306a36Sopenharmony_ci}; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { 114762306a36Sopenharmony_ci .startup = cygnus_ssp_startup, 114862306a36Sopenharmony_ci .shutdown = cygnus_ssp_shutdown, 114962306a36Sopenharmony_ci .trigger = cygnus_ssp_trigger, 115062306a36Sopenharmony_ci .hw_params = cygnus_ssp_hw_params, 115162306a36Sopenharmony_ci .set_sysclk = cygnus_ssp_set_sysclk, 115262306a36Sopenharmony_ci}; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci#define INIT_CPU_DAI(num) { \ 115562306a36Sopenharmony_ci .name = "cygnus-ssp" #num, \ 115662306a36Sopenharmony_ci .playback = { \ 115762306a36Sopenharmony_ci .channels_min = 2, \ 115862306a36Sopenharmony_ci .channels_max = 16, \ 115962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, \ 116062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 116162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, \ 116262306a36Sopenharmony_ci }, \ 116362306a36Sopenharmony_ci .capture = { \ 116462306a36Sopenharmony_ci .channels_min = 2, \ 116562306a36Sopenharmony_ci .channels_max = 16, \ 116662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, \ 116762306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 116862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, \ 116962306a36Sopenharmony_ci }, \ 117062306a36Sopenharmony_ci .ops = &cygnus_ssp_dai_ops, \ 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = { 117462306a36Sopenharmony_ci INIT_CPU_DAI(0), 117562306a36Sopenharmony_ci INIT_CPU_DAI(1), 117662306a36Sopenharmony_ci INIT_CPU_DAI(2), 117762306a36Sopenharmony_ci}; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic const struct snd_soc_dai_driver cygnus_spdif_dai_info = { 118062306a36Sopenharmony_ci .name = "cygnus-spdif", 118162306a36Sopenharmony_ci .playback = { 118262306a36Sopenharmony_ci .channels_min = 2, 118362306a36Sopenharmony_ci .channels_max = 2, 118462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 118562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 118662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 118762306a36Sopenharmony_ci }, 118862306a36Sopenharmony_ci .ops = &cygnus_spdif_dai_ops, 118962306a36Sopenharmony_ci}; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS]; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic const struct snd_soc_component_driver cygnus_ssp_component = { 119462306a36Sopenharmony_ci .name = "cygnus-audio", 119562306a36Sopenharmony_ci .suspend = cygnus_ssp_suspend, 119662306a36Sopenharmony_ci .resume = cygnus_ssp_resume, 119762306a36Sopenharmony_ci .legacy_dai_naming = 1, 119862306a36Sopenharmony_ci}; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci/* 120162306a36Sopenharmony_ci * Return < 0 if error 120262306a36Sopenharmony_ci * Return 0 if disabled 120362306a36Sopenharmony_ci * Return 1 if enabled and node is parsed successfully 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_cistatic int parse_ssp_child_node(struct platform_device *pdev, 120662306a36Sopenharmony_ci struct device_node *dn, 120762306a36Sopenharmony_ci struct cygnus_audio *cygaud, 120862306a36Sopenharmony_ci struct snd_soc_dai_driver *p_dai) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct cygnus_aio_port *aio; 121162306a36Sopenharmony_ci struct cygnus_ssp_regs ssp_regs[3]; 121262306a36Sopenharmony_ci u32 rawval; 121362306a36Sopenharmony_ci int portnum = -1; 121462306a36Sopenharmony_ci enum cygnus_audio_port_type port_type; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (of_property_read_u32(dn, "reg", &rawval)) { 121762306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing reg property\n"); 121862306a36Sopenharmony_ci return -EINVAL; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci portnum = rawval; 122262306a36Sopenharmony_ci switch (rawval) { 122362306a36Sopenharmony_ci case 0: 122462306a36Sopenharmony_ci ssp_regs[0] = INIT_SSP_REGS(0); 122562306a36Sopenharmony_ci port_type = PORT_TDM; 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci case 1: 122862306a36Sopenharmony_ci ssp_regs[1] = INIT_SSP_REGS(1); 122962306a36Sopenharmony_ci port_type = PORT_TDM; 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci case 2: 123262306a36Sopenharmony_ci ssp_regs[2] = INIT_SSP_REGS(2); 123362306a36Sopenharmony_ci port_type = PORT_TDM; 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci case 3: 123662306a36Sopenharmony_ci port_type = PORT_SPDIF; 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci default: 123962306a36Sopenharmony_ci dev_err(&pdev->dev, "Bad value for reg %u\n", rawval); 124062306a36Sopenharmony_ci return -EINVAL; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci aio = &cygaud->portinfo[portnum]; 124462306a36Sopenharmony_ci aio->cygaud = cygaud; 124562306a36Sopenharmony_ci aio->portnum = portnum; 124662306a36Sopenharmony_ci aio->port_type = port_type; 124762306a36Sopenharmony_ci aio->fsync_width = -1; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci switch (port_type) { 125062306a36Sopenharmony_ci case PORT_TDM: 125162306a36Sopenharmony_ci aio->regs = ssp_regs[portnum]; 125262306a36Sopenharmony_ci *p_dai = cygnus_ssp_dai_info[portnum]; 125362306a36Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_UNKNOWN; 125462306a36Sopenharmony_ci break; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci case PORT_SPDIF: 125762306a36Sopenharmony_ci aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET; 125862306a36Sopenharmony_ci aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET; 125962306a36Sopenharmony_ci aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET; 126062306a36Sopenharmony_ci aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET; 126162306a36Sopenharmony_ci *p_dai = cygnus_spdif_dai_info; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* For the purposes of this code SPDIF can be I2S mode */ 126462306a36Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_I2S; 126562306a36Sopenharmony_ci break; 126662306a36Sopenharmony_ci default: 126762306a36Sopenharmony_ci dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type); 126862306a36Sopenharmony_ci return -EINVAL; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum); 127262306a36Sopenharmony_ci aio->streams_on = 0; 127362306a36Sopenharmony_ci aio->cygaud->dev = &pdev->dev; 127462306a36Sopenharmony_ci aio->clk_trace.play_en = false; 127562306a36Sopenharmony_ci aio->clk_trace.cap_en = false; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci audio_ssp_init_portregs(aio); 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic int audio_clk_init(struct platform_device *pdev, 128262306a36Sopenharmony_ci struct cygnus_audio *cygaud) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci int i; 128562306a36Sopenharmony_ci char clk_name[PROP_LEN_MAX]; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) { 128862306a36Sopenharmony_ci snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name); 129162306a36Sopenharmony_ci if (IS_ERR(cygaud->audio_clk[i])) 129262306a36Sopenharmony_ci return PTR_ERR(cygaud->audio_clk[i]); 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return 0; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic int cygnus_ssp_probe(struct platform_device *pdev) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 130162306a36Sopenharmony_ci struct device_node *child_node; 130262306a36Sopenharmony_ci struct cygnus_audio *cygaud; 130362306a36Sopenharmony_ci int err; 130462306a36Sopenharmony_ci int node_count; 130562306a36Sopenharmony_ci int active_port_count; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL); 130862306a36Sopenharmony_ci if (!cygaud) 130962306a36Sopenharmony_ci return -ENOMEM; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci dev_set_drvdata(dev, cygaud); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud"); 131462306a36Sopenharmony_ci if (IS_ERR(cygaud->audio)) 131562306a36Sopenharmony_ci return PTR_ERR(cygaud->audio); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in"); 131862306a36Sopenharmony_ci if (IS_ERR(cygaud->i2s_in)) 131962306a36Sopenharmony_ci return PTR_ERR(cygaud->i2s_in); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* Tri-state all controlable pins until we know that we need them */ 132262306a36Sopenharmony_ci writel(CYGNUS_SSP_TRISTATE_MASK, 132362306a36Sopenharmony_ci cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci node_count = of_get_child_count(pdev->dev.of_node); 132662306a36Sopenharmony_ci if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) { 132762306a36Sopenharmony_ci dev_err(dev, "child nodes is %d. Must be between 1 and %d\n", 132862306a36Sopenharmony_ci node_count, CYGNUS_MAX_PORTS); 132962306a36Sopenharmony_ci return -EINVAL; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci active_port_count = 0; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci for_each_available_child_of_node(pdev->dev.of_node, child_node) { 133562306a36Sopenharmony_ci err = parse_ssp_child_node(pdev, child_node, cygaud, 133662306a36Sopenharmony_ci &cygnus_ssp_dai[active_port_count]); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* negative is err, 0 is active and good, 1 is disabled */ 133962306a36Sopenharmony_ci if (err < 0) { 134062306a36Sopenharmony_ci of_node_put(child_node); 134162306a36Sopenharmony_ci return err; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci else if (!err) { 134462306a36Sopenharmony_ci dev_dbg(dev, "Activating DAI: %s\n", 134562306a36Sopenharmony_ci cygnus_ssp_dai[active_port_count].name); 134662306a36Sopenharmony_ci active_port_count++; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci cygaud->dev = dev; 135162306a36Sopenharmony_ci cygaud->active_ports = 0; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci dev_dbg(dev, "Registering %d DAIs\n", active_port_count); 135462306a36Sopenharmony_ci err = devm_snd_soc_register_component(dev, &cygnus_ssp_component, 135562306a36Sopenharmony_ci cygnus_ssp_dai, active_port_count); 135662306a36Sopenharmony_ci if (err) { 135762306a36Sopenharmony_ci dev_err(dev, "snd_soc_register_dai failed\n"); 135862306a36Sopenharmony_ci return err; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci cygaud->irq_num = platform_get_irq(pdev, 0); 136262306a36Sopenharmony_ci if (cygaud->irq_num <= 0) 136362306a36Sopenharmony_ci return cygaud->irq_num; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci err = audio_clk_init(pdev, cygaud); 136662306a36Sopenharmony_ci if (err) { 136762306a36Sopenharmony_ci dev_err(dev, "audio clock initialization failed\n"); 136862306a36Sopenharmony_ci return err; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci err = cygnus_soc_platform_register(dev, cygaud); 137262306a36Sopenharmony_ci if (err) { 137362306a36Sopenharmony_ci dev_err(dev, "platform reg error %d\n", err); 137462306a36Sopenharmony_ci return err; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci return 0; 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic void cygnus_ssp_remove(struct platform_device *pdev) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci cygnus_soc_platform_unregister(&pdev->dev); 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic const struct of_device_id cygnus_ssp_of_match[] = { 138662306a36Sopenharmony_ci { .compatible = "brcm,cygnus-audio" }, 138762306a36Sopenharmony_ci {}, 138862306a36Sopenharmony_ci}; 138962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cygnus_ssp_of_match); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_cistatic struct platform_driver cygnus_ssp_driver = { 139262306a36Sopenharmony_ci .probe = cygnus_ssp_probe, 139362306a36Sopenharmony_ci .remove_new = cygnus_ssp_remove, 139462306a36Sopenharmony_ci .driver = { 139562306a36Sopenharmony_ci .name = "cygnus-ssp", 139662306a36Sopenharmony_ci .of_match_table = cygnus_ssp_of_match, 139762306a36Sopenharmony_ci }, 139862306a36Sopenharmony_ci}; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cimodule_platform_driver(cygnus_ssp_driver); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ciMODULE_ALIAS("platform:cygnus-ssp"); 140362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 140462306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 140562306a36Sopenharmony_ciMODULE_DESCRIPTION("Cygnus ASoC SSP Interface"); 1406