18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 238c2ecf20Sopenharmony_ci#include <sound/soc.h> 248c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "cygnus-ssp.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DEFAULT_VCO 1354750204 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CAPTURE_FCI_ID_BASE 0x180 318c2ecf20Sopenharmony_ci#define CYGNUS_SSP_TRISTATE_MASK 0x001fff 328c2ecf20Sopenharmony_ci#define CYGNUS_PLLCLKSEL_MASK 0xf 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Used with stream_on field to indicate which streams are active */ 358c2ecf20Sopenharmony_ci#define PLAYBACK_STREAM_MASK BIT(0) 368c2ecf20Sopenharmony_ci#define CAPTURE_STREAM_MASK BIT(1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define I2S_STREAM_CFG_MASK 0xff003ff 398c2ecf20Sopenharmony_ci#define I2S_CAP_STREAM_CFG_MASK 0xf0 408c2ecf20Sopenharmony_ci#define SPDIF_STREAM_CFG_MASK 0x3ff 418c2ecf20Sopenharmony_ci#define CH_GRP_STEREO 0x1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Begin register offset defines */ 448c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c 458c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_SPDIF_OE 12 468c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_MCLK_OE 3 478c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_LRCK_OE 2 488c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_SCLK_OE 1 498c2ecf20Sopenharmony_ci#define AUD_MISC_SEROUT_SDAT_OE 0 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_xxx regs */ 528c2ecf20Sopenharmony_ci#define BF_DST_CFG0_OFFSET 0x100 538c2ecf20Sopenharmony_ci#define BF_DST_CFG1_OFFSET 0x104 548c2ecf20Sopenharmony_ci#define BF_DST_CFG2_OFFSET 0x108 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define BF_DST_CTRL0_OFFSET 0x130 578c2ecf20Sopenharmony_ci#define BF_DST_CTRL1_OFFSET 0x134 588c2ecf20Sopenharmony_ci#define BF_DST_CTRL2_OFFSET 0x138 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define BF_SRC_CFG0_OFFSET 0x148 618c2ecf20Sopenharmony_ci#define BF_SRC_CFG1_OFFSET 0x14c 628c2ecf20Sopenharmony_ci#define BF_SRC_CFG2_OFFSET 0x150 638c2ecf20Sopenharmony_ci#define BF_SRC_CFG3_OFFSET 0x154 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define BF_SRC_CTRL0_OFFSET 0x1c0 668c2ecf20Sopenharmony_ci#define BF_SRC_CTRL1_OFFSET 0x1c4 678c2ecf20Sopenharmony_ci#define BF_SRC_CTRL2_OFFSET 0x1c8 688c2ecf20Sopenharmony_ci#define BF_SRC_CTRL3_OFFSET 0x1cc 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define BF_SRC_GRP0_OFFSET 0x1fc 718c2ecf20Sopenharmony_ci#define BF_SRC_GRP1_OFFSET 0x200 728c2ecf20Sopenharmony_ci#define BF_SRC_GRP2_OFFSET 0x204 738c2ecf20Sopenharmony_ci#define BF_SRC_GRP3_OFFSET 0x208 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define BF_SRC_GRP_EN_OFFSET 0x320 768c2ecf20Sopenharmony_ci#define BF_SRC_GRP_FLOWON_OFFSET 0x324 778c2ecf20Sopenharmony_ci#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_xxx regs */ 808c2ecf20Sopenharmony_ci#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00 818c2ecf20Sopenharmony_ci#define OUT_I2S_0_CFG_OFFSET 0xa04 828c2ecf20Sopenharmony_ci#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40 858c2ecf20Sopenharmony_ci#define OUT_I2S_1_CFG_OFFSET 0xa44 868c2ecf20Sopenharmony_ci#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80 898c2ecf20Sopenharmony_ci#define OUT_I2S_2_CFG_OFFSET 0xa84 908c2ecf20Sopenharmony_ci#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */ 938c2ecf20Sopenharmony_ci#define SPDIF_STREAM_CFG_OFFSET 0xac0 948c2ecf20Sopenharmony_ci#define SPDIF_CTRL_OFFSET 0xac4 958c2ecf20Sopenharmony_ci#define SPDIF_FORMAT_CFG_OFFSET 0xad8 968c2ecf20Sopenharmony_ci#define SPDIF_MCLK_CFG_OFFSET 0xadc 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_xxx regs */ 998c2ecf20Sopenharmony_ci#define IOP_PLL_0_MACRO_OFFSET 0xb00 1008c2ecf20Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14 1018c2ecf20Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18 1028c2ecf20Sopenharmony_ci#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30 1058c2ecf20Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34 1068c2ecf20Sopenharmony_ci#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_xxx regs */ 1098c2ecf20Sopenharmony_ci#define IOP_PLL_0_CONTROL_OFFSET 0xb04 1108c2ecf20Sopenharmony_ci#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08 1118c2ecf20Sopenharmony_ci#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20 1128c2ecf20Sopenharmony_ci#define IOP_PLL_0_RESET_OFFSET 0xb5c 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_IN_I2S_xxx regs */ 1158c2ecf20Sopenharmony_ci#define IN_I2S_0_STREAM_CFG_OFFSET 0x00 1168c2ecf20Sopenharmony_ci#define IN_I2S_0_CFG_OFFSET 0x04 1178c2ecf20Sopenharmony_ci#define IN_I2S_1_STREAM_CFG_OFFSET 0x40 1188c2ecf20Sopenharmony_ci#define IN_I2S_1_CFG_OFFSET 0x44 1198c2ecf20Sopenharmony_ci#define IN_I2S_2_STREAM_CFG_OFFSET 0x80 1208c2ecf20Sopenharmony_ci#define IN_I2S_2_CFG_OFFSET 0x84 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_MISC_xxx regs */ 1238c2ecf20Sopenharmony_ci#define IOP_SW_INIT_LOGIC 0x1c0 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* End register offset defines */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */ 1298c2ecf20Sopenharmony_ci#define I2S_OUT_MCLKRATE_SHIFT 16 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */ 1328c2ecf20Sopenharmony_ci#define I2S_OUT_PLLCLKSEL_SHIFT 0 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */ 1358c2ecf20Sopenharmony_ci#define I2S_OUT_STREAM_ENA 31 1368c2ecf20Sopenharmony_ci#define I2S_OUT_STREAM_CFG_GROUP_ID 20 1378c2ecf20Sopenharmony_ci#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_IN_I2S_x_CAP */ 1408c2ecf20Sopenharmony_ci#define I2S_IN_STREAM_CFG_CAP_ENA 31 1418c2ecf20Sopenharmony_ci#define I2S_IN_STREAM_CFG_0_GROUP_ID 4 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */ 1448c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_CLK_ENA 0 1458c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_DATA_ENABLE 1 1468c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_DATA_ALIGNMENT 6 1478c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_BITS_PER_SLOT 13 1488c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_VALID_SLOT 14 1498c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_FSYNC_WIDTH 18 1508c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26 1518c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_SLAVE_MODE 30 1528c2ecf20Sopenharmony_ci#define I2S_OUT_CFGX_TDM_MODE 31 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */ 1558c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_SFIFO_ENA 0 1568c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1 1578c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_SAMPLE_CH_MODE 2 1588c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5 1598c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10 1608c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_BIT_RES 20 1618c2ecf20Sopenharmony_ci#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */ 1648c2ecf20Sopenharmony_ci#define BF_DST_CFGX_CAP_ENA 0 1658c2ecf20Sopenharmony_ci#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1 1668c2ecf20Sopenharmony_ci#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2 1678c2ecf20Sopenharmony_ci#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11 1688c2ecf20Sopenharmony_ci#define BF_DST_CFGX_FCI_ID 12 1698c2ecf20Sopenharmony_ci#define BF_DST_CFGX_CAP_MODE 24 1708c2ecf20Sopenharmony_ci#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_OUT_SPDIF_xxx */ 1738c2ecf20Sopenharmony_ci#define SPDIF_0_OUT_DITHER_ENA 3 1748c2ecf20Sopenharmony_ci#define SPDIF_0_OUT_STREAM_ENA 31 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_USER */ 1778c2ecf20Sopenharmony_ci#define IOP_PLL_0_USER_NDIV_FRAC 10 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* AUD_FMM_IOP_PLL_0_ACTIVE */ 1808c2ecf20Sopenharmony_ci#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \ 1848c2ecf20Sopenharmony_ci .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \ 1858c2ecf20Sopenharmony_ci .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \ 1868c2ecf20Sopenharmony_ci .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \ 1878c2ecf20Sopenharmony_ci .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \ 1888c2ecf20Sopenharmony_ci .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \ 1898c2ecf20Sopenharmony_ci .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \ 1908c2ecf20Sopenharmony_ci .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \ 1918c2ecf20Sopenharmony_ci .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \ 1928c2ecf20Sopenharmony_ci .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \ 1938c2ecf20Sopenharmony_ci .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \ 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct pll_macro_entry { 1978c2ecf20Sopenharmony_ci u32 mclk; 1988c2ecf20Sopenharmony_ci u32 pll_ch_num; 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * PLL has 3 output channels (1x, 2x, and 4x). Below are 2038c2ecf20Sopenharmony_ci * the common MCLK frequencies used by audio driver 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic const struct pll_macro_entry pll_predef_mclk[] = { 2068c2ecf20Sopenharmony_ci { 4096000, 0}, 2078c2ecf20Sopenharmony_ci { 8192000, 1}, 2088c2ecf20Sopenharmony_ci {16384000, 2}, 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci { 5644800, 0}, 2118c2ecf20Sopenharmony_ci {11289600, 1}, 2128c2ecf20Sopenharmony_ci {22579200, 2}, 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci { 6144000, 0}, 2158c2ecf20Sopenharmony_ci {12288000, 1}, 2168c2ecf20Sopenharmony_ci {24576000, 2}, 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci {12288000, 0}, 2198c2ecf20Sopenharmony_ci {24576000, 1}, 2208c2ecf20Sopenharmony_ci {49152000, 2}, 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci {22579200, 0}, 2238c2ecf20Sopenharmony_ci {45158400, 1}, 2248c2ecf20Sopenharmony_ci {90316800, 2}, 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci {24576000, 0}, 2278c2ecf20Sopenharmony_ci {49152000, 1}, 2288c2ecf20Sopenharmony_ci {98304000, 2}, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define CYGNUS_RATE_MIN 8000 2328c2ecf20Sopenharmony_ci#define CYGNUS_RATE_MAX 384000 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* List of valid frame sizes for tdm mode */ 2358c2ecf20Sopenharmony_cistatic const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const unsigned int cygnus_rates[] = { 2388c2ecf20Sopenharmony_ci 8000, 11025, 16000, 22050, 32000, 44100, 48000, 2398c2ecf20Sopenharmony_ci 88200, 96000, 176400, 192000, 352800, 384000 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = { 2438c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(cygnus_rates), 2448c2ecf20Sopenharmony_ci .list = cygnus_rates, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return &cygaud->portinfo[dai->id]; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int audio_ssp_init_portregs(struct cygnus_aio_port *aio) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci u32 value, fci_id; 2578c2ecf20Sopenharmony_ci int status = 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci switch (aio->port_type) { 2608c2ecf20Sopenharmony_ci case PORT_TDM: 2618c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 2628c2ecf20Sopenharmony_ci value &= ~I2S_STREAM_CFG_MASK; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Set Group ID */ 2658c2ecf20Sopenharmony_ci writel(aio->portnum, 2668c2ecf20Sopenharmony_ci aio->cygaud->audio + aio->regs.bf_sourcech_grp); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */ 2698c2ecf20Sopenharmony_ci value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID; 2708c2ecf20Sopenharmony_ci value |= aio->portnum; /* FCI ID is the port num */ 2718c2ecf20Sopenharmony_ci value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING; 2728c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */ 2758c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 2768c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); 2778c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); 2788c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); 2798c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */ 2828c2ecf20Sopenharmony_ci value = readl(aio->cygaud->i2s_in + 2838c2ecf20Sopenharmony_ci aio->regs.i2s_cap_stream_cfg); 2848c2ecf20Sopenharmony_ci value &= ~I2S_CAP_STREAM_CFG_MASK; 2858c2ecf20Sopenharmony_ci value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID; 2868c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + 2878c2ecf20Sopenharmony_ci aio->regs.i2s_cap_stream_cfg); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */ 2908c2ecf20Sopenharmony_ci fci_id = CAPTURE_FCI_ID_BASE + aio->portnum; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 2938c2ecf20Sopenharmony_ci value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE); 2948c2ecf20Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL); 2958c2ecf20Sopenharmony_ci value |= (fci_id << BF_DST_CFGX_FCI_ID); 2968c2ecf20Sopenharmony_ci value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID); 2978c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Enable the transmit pin for this port */ 3008c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 3018c2ecf20Sopenharmony_ci value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE); 3028c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case PORT_SPDIF: 3058c2ecf20Sopenharmony_ci writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET); 3088c2ecf20Sopenharmony_ci value |= BIT(SPDIF_0_OUT_DITHER_ENA); 3098c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Enable and set the FCI ID for the SPDIF channel */ 3128c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); 3138c2ecf20Sopenharmony_ci value &= ~SPDIF_STREAM_CFG_MASK; 3148c2ecf20Sopenharmony_ci value |= aio->portnum; /* FCI ID is the port num */ 3158c2ecf20Sopenharmony_ci value |= BIT(SPDIF_0_OUT_STREAM_ENA); 3168c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 3198c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); 3208c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); 3218c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); 3228c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Enable the spdif output pin */ 3258c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 3268c2ecf20Sopenharmony_ci value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE); 3278c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci default: 3308c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "Port not supported\n"); 3318c2ecf20Sopenharmony_ci status = -EINVAL; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return status; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void audio_ssp_in_enable(struct cygnus_aio_port *aio) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci u32 value; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 3428c2ecf20Sopenharmony_ci value |= BIT(BF_DST_CFGX_CAP_ENA); 3438c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 3488c2ecf20Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_CLK_ENA); 3498c2ecf20Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); 3508c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 3538c2ecf20Sopenharmony_ci value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA); 3548c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci aio->streams_on |= CAPTURE_STREAM_MASK; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void audio_ssp_in_disable(struct cygnus_aio_port *aio) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci u32 value; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 3648c2ecf20Sopenharmony_ci value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA); 3658c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci aio->streams_on &= ~CAPTURE_STREAM_MASK; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* If both playback and capture are off */ 3708c2ecf20Sopenharmony_ci if (!aio->streams_on) { 3718c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 3728c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); 3738c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); 3748c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); 3808c2ecf20Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_CAP_ENA); 3818c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int audio_ssp_out_enable(struct cygnus_aio_port *aio) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u32 value; 3878c2ecf20Sopenharmony_ci int status = 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci switch (aio->port_type) { 3908c2ecf20Sopenharmony_ci case PORT_TDM: 3918c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 3928c2ecf20Sopenharmony_ci value |= BIT(I2S_OUT_STREAM_ENA); 3938c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 3988c2ecf20Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_CLK_ENA); 3998c2ecf20Sopenharmony_ci value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); 4008c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4038c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_ENA); 4048c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci aio->streams_on |= PLAYBACK_STREAM_MASK; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case PORT_SPDIF: 4098c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 4108c2ecf20Sopenharmony_ci value |= 0x3; 4118c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4168c2ecf20Sopenharmony_ci value |= BIT(BF_SRC_CFGX_SFIFO_ENA); 4178c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 4218c2ecf20Sopenharmony_ci "Port not supported %d\n", aio->portnum); 4228c2ecf20Sopenharmony_ci status = -EINVAL; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return status; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int audio_ssp_out_disable(struct cygnus_aio_port *aio) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci u32 value; 4318c2ecf20Sopenharmony_ci int status = 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci switch (aio->port_type) { 4348c2ecf20Sopenharmony_ci case PORT_TDM: 4358c2ecf20Sopenharmony_ci aio->streams_on &= ~PLAYBACK_STREAM_MASK; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* If both playback and capture are off */ 4388c2ecf20Sopenharmony_ci if (!aio->streams_on) { 4398c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 4408c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); 4418c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); 4428c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* set group_sync_dis = 1 */ 4468c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 4478c2ecf20Sopenharmony_ci value |= BIT(aio->portnum); 4488c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4538c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); 4548c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* set group_sync_dis = 0 */ 4578c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 4588c2ecf20Sopenharmony_ci value &= ~BIT(aio->portnum); 4598c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); 4628c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_STREAM_ENA); 4638c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* IOP SW INIT on OUT_I2S_x */ 4668c2ecf20Sopenharmony_ci value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 4678c2ecf20Sopenharmony_ci value |= BIT(aio->portnum); 4688c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 4698c2ecf20Sopenharmony_ci value &= ~BIT(aio->portnum); 4708c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case PORT_SPDIF: 4738c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 4748c2ecf20Sopenharmony_ci value &= ~0x3; 4758c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); 4768c2ecf20Sopenharmony_ci writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4798c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); 4808c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 4848c2ecf20Sopenharmony_ci "Port not supported %d\n", aio->portnum); 4858c2ecf20Sopenharmony_ci status = -EINVAL; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return status; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk, 4928c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci int i = 0, error; 4958c2ecf20Sopenharmony_ci bool found = false; 4968c2ecf20Sopenharmony_ci const struct pll_macro_entry *p_entry; 4978c2ecf20Sopenharmony_ci struct clk *ch_clk; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) { 5008c2ecf20Sopenharmony_ci p_entry = &pll_predef_mclk[i]; 5018c2ecf20Sopenharmony_ci if (p_entry->mclk == mclk) { 5028c2ecf20Sopenharmony_ci found = true; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci if (!found) { 5078c2ecf20Sopenharmony_ci dev_err(cygaud->dev, 5088c2ecf20Sopenharmony_ci "%s No valid mclk freq (%u) found!\n", __func__, mclk); 5098c2ecf20Sopenharmony_ci return -EINVAL; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ch_clk = cygaud->audio_clk[p_entry->pll_ch_num]; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) { 5158c2ecf20Sopenharmony_ci error = clk_prepare_enable(ch_clk); 5168c2ecf20Sopenharmony_ci if (error) { 5178c2ecf20Sopenharmony_ci dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", 5188c2ecf20Sopenharmony_ci __func__, error); 5198c2ecf20Sopenharmony_ci return error; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci aio->clk_trace.cap_clk_en = true; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) { 5258c2ecf20Sopenharmony_ci error = clk_prepare_enable(ch_clk); 5268c2ecf20Sopenharmony_ci if (error) { 5278c2ecf20Sopenharmony_ci dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", 5288c2ecf20Sopenharmony_ci __func__, error); 5298c2ecf20Sopenharmony_ci return error; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci aio->clk_trace.play_clk_en = true; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci error = clk_set_rate(ch_clk, mclk); 5358c2ecf20Sopenharmony_ci if (error) { 5368c2ecf20Sopenharmony_ci dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n", 5378c2ecf20Sopenharmony_ci __func__, error); 5388c2ecf20Sopenharmony_ci return error; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return p_entry->pll_ch_num; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci u32 value; 5478c2ecf20Sopenharmony_ci u32 mask = 0xf; 5488c2ecf20Sopenharmony_ci u32 sclk; 5498c2ecf20Sopenharmony_ci u32 mclk_rate; 5508c2ecf20Sopenharmony_ci unsigned int bit_rate; 5518c2ecf20Sopenharmony_ci unsigned int ratio; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci bit_rate = aio->bit_per_frame * aio->lrclk; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Check if the bit clock can be generated from the given MCLK. 5578c2ecf20Sopenharmony_ci * MCLK must be a perfect multiple of bit clock and must be one of the 5588c2ecf20Sopenharmony_ci * following values... (2,4,6,8,10,12,14) 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci if ((aio->mclk % bit_rate) != 0) 5618c2ecf20Sopenharmony_ci return -EINVAL; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ratio = aio->mclk / bit_rate; 5648c2ecf20Sopenharmony_ci switch (ratio) { 5658c2ecf20Sopenharmony_ci case 2: 5668c2ecf20Sopenharmony_ci case 4: 5678c2ecf20Sopenharmony_ci case 6: 5688c2ecf20Sopenharmony_ci case 8: 5698c2ecf20Sopenharmony_ci case 10: 5708c2ecf20Sopenharmony_ci case 12: 5718c2ecf20Sopenharmony_ci case 14: 5728c2ecf20Sopenharmony_ci mclk_rate = ratio / 2; 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci default: 5768c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 5778c2ecf20Sopenharmony_ci "Invalid combination of MCLK and BCLK\n"); 5788c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n", 5798c2ecf20Sopenharmony_ci aio->lrclk, aio->bit_per_frame, aio->mclk); 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Set sclk rate */ 5848c2ecf20Sopenharmony_ci switch (aio->port_type) { 5858c2ecf20Sopenharmony_ci case PORT_TDM: 5868c2ecf20Sopenharmony_ci sclk = aio->bit_per_frame; 5878c2ecf20Sopenharmony_ci if (sclk == 512) 5888c2ecf20Sopenharmony_ci sclk = 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* sclks_per_1fs_div = sclk cycles/32 */ 5918c2ecf20Sopenharmony_ci sclk /= 32; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Set number of bitclks per frame */ 5948c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 5958c2ecf20Sopenharmony_ci value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32); 5968c2ecf20Sopenharmony_ci value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32; 5978c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 5988c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, 5998c2ecf20Sopenharmony_ci "SCLKS_PER_1FS_DIV32 = 0x%x\n", value); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci case PORT_SPDIF: 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci default: 6048c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "Unknown port type\n"); 6058c2ecf20Sopenharmony_ci return -EINVAL; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Set MCLK_RATE ssp port (spdif and ssp are the same) */ 6098c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 6108c2ecf20Sopenharmony_ci value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT); 6118c2ecf20Sopenharmony_ci value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT); 6128c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value); 6158c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n", 6168c2ecf20Sopenharmony_ci aio->bit_per_frame, aio->mclk, aio->lrclk); 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int cygnus_ssp_hw_params(struct snd_pcm_substream *substream, 6218c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 6228c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 6258c2ecf20Sopenharmony_ci int rate, bitres; 6268c2ecf20Sopenharmony_ci u32 value; 6278c2ecf20Sopenharmony_ci u32 mask = 0x1f; 6288c2ecf20Sopenharmony_ci int ret = 0; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum); 6318c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "params_channels %d\n", 6328c2ecf20Sopenharmony_ci params_channels(params)); 6338c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params)); 6348c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params)); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci rate = params_rate(params); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci switch (aio->mode) { 6398c2ecf20Sopenharmony_ci case CYGNUS_SSPMODE_TDM: 6408c2ecf20Sopenharmony_ci if ((rate == 192000) && (params_channels(params) > 4)) { 6418c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n", 6428c2ecf20Sopenharmony_ci params_channels(params), rate); 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci case CYGNUS_SSPMODE_I2S: 6478c2ecf20Sopenharmony_ci aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */ 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci default: 6508c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 6518c2ecf20Sopenharmony_ci "%s port running in unknown mode\n", __func__); 6528c2ecf20Sopenharmony_ci return -EINVAL; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 6568c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 6578c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE); 6588c2ecf20Sopenharmony_ci value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); 6598c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci switch (params_format(params)) { 6628c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 6638c2ecf20Sopenharmony_ci bitres = 16; 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 6678c2ecf20Sopenharmony_ci /* 32 bit mode is coded as 0 */ 6688c2ecf20Sopenharmony_ci bitres = 0; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci default: 6728c2ecf20Sopenharmony_ci return -EINVAL; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 6768c2ecf20Sopenharmony_ci value &= ~(mask << BF_SRC_CFGX_BIT_RES); 6778c2ecf20Sopenharmony_ci value |= (bitres << BF_SRC_CFGX_BIT_RES); 6788c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci } else { 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci switch (params_format(params)) { 6838c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 6848c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + 6858c2ecf20Sopenharmony_ci aio->regs.bf_destch_cfg); 6868c2ecf20Sopenharmony_ci value |= BIT(BF_DST_CFGX_CAP_MODE); 6878c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + 6888c2ecf20Sopenharmony_ci aio->regs.bf_destch_cfg); 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 6928c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + 6938c2ecf20Sopenharmony_ci aio->regs.bf_destch_cfg); 6948c2ecf20Sopenharmony_ci value &= ~BIT(BF_DST_CFGX_CAP_MODE); 6958c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + 6968c2ecf20Sopenharmony_ci aio->regs.bf_destch_cfg); 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci default: 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci aio->lrclk = rate; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (!aio->is_slave) 7078c2ecf20Sopenharmony_ci ret = cygnus_ssp_set_clocks(aio); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return ret; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* 7138c2ecf20Sopenharmony_ci * This function sets the mclk frequency for pll clock 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_cistatic int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai, 7168c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int sel; 7198c2ecf20Sopenharmony_ci u32 value; 7208c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 7218c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, 7248c2ecf20Sopenharmony_ci "%s Enter port = %d\n", __func__, aio->portnum); 7258c2ecf20Sopenharmony_ci sel = pll_configure_mclk(cygaud, freq, aio); 7268c2ecf20Sopenharmony_ci if (sel < 0) { 7278c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 7288c2ecf20Sopenharmony_ci "%s Setting mclk failed.\n", __func__); 7298c2ecf20Sopenharmony_ci return -EINVAL; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci aio->mclk = freq; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel); 7358c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 7368c2ecf20Sopenharmony_ci value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT); 7378c2ecf20Sopenharmony_ci value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT); 7388c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return 0; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic int cygnus_ssp_startup(struct snd_pcm_substream *substream, 7448c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, aio); 7498c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7508c2ecf20Sopenharmony_ci aio->clk_trace.play_en = true; 7518c2ecf20Sopenharmony_ci else 7528c2ecf20Sopenharmony_ci aio->clk_trace.cap_en = true; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci substream->runtime->hw.rate_min = CYGNUS_RATE_MIN; 7558c2ecf20Sopenharmony_ci substream->runtime->hw.rate_max = CYGNUS_RATE_MAX; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(substream->runtime, 0, 7588c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint); 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void cygnus_ssp_shutdown(struct snd_pcm_substream *substream, 7638c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7688c2ecf20Sopenharmony_ci aio->clk_trace.play_en = false; 7698c2ecf20Sopenharmony_ci else 7708c2ecf20Sopenharmony_ci aio->clk_trace.cap_en = false; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!aio->is_slave) { 7738c2ecf20Sopenharmony_ci u32 val; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 7768c2ecf20Sopenharmony_ci val &= CYGNUS_PLLCLKSEL_MASK; 7778c2ecf20Sopenharmony_ci if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { 7788c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", 7798c2ecf20Sopenharmony_ci val); 7808c2ecf20Sopenharmony_ci return; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 7848c2ecf20Sopenharmony_ci if (aio->clk_trace.play_clk_en) { 7858c2ecf20Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 7868c2ecf20Sopenharmony_ci audio_clk[val]); 7878c2ecf20Sopenharmony_ci aio->clk_trace.play_clk_en = false; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci if (aio->clk_trace.cap_clk_en) { 7918c2ecf20Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 7928c2ecf20Sopenharmony_ci audio_clk[val]); 7938c2ecf20Sopenharmony_ci aio->clk_trace.cap_clk_en = false; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci/* 8008c2ecf20Sopenharmony_ci * Bit Update Notes 8018c2ecf20Sopenharmony_ci * 31 Yes TDM Mode (1 = TDM, 0 = i2s) 8028c2ecf20Sopenharmony_ci * 30 Yes Slave Mode (1 = Slave, 0 = Master) 8038c2ecf20Sopenharmony_ci * 29:26 No Sclks per frame 8048c2ecf20Sopenharmony_ci * 25:18 Yes FS Width 8058c2ecf20Sopenharmony_ci * 17:14 No Valid Slots 8068c2ecf20Sopenharmony_ci * 13 No Bits (1 = 16 bits, 0 = 32 bits) 8078c2ecf20Sopenharmony_ci * 12:08 No Bits per samp 8088c2ecf20Sopenharmony_ci * 07 Yes Justifcation (1 = LSB, 0 = MSB) 8098c2ecf20Sopenharmony_ci * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay 8108c2ecf20Sopenharmony_ci * 05 Yes SCLK polarity (1 = Rising, 0 = Falling) 8118c2ecf20Sopenharmony_ci * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left) 8128c2ecf20Sopenharmony_ci * 03:02 Yes Reserved - write as zero 8138c2ecf20Sopenharmony_ci * 01 No Data Enable 8148c2ecf20Sopenharmony_ci * 00 No CLK Enable 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci/* Input cfg is same as output, but the FS width is not a valid field */ 8198c2ecf20Sopenharmony_ci#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000) 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ciint cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if ((len > 0) && (len < 256)) { 8268c2ecf20Sopenharmony_ci aio->fsync_width = len; 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci } else { 8298c2ecf20Sopenharmony_ci return -EINVAL; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 8378c2ecf20Sopenharmony_ci u32 ssp_curcfg; 8388c2ecf20Sopenharmony_ci u32 ssp_newcfg; 8398c2ecf20Sopenharmony_ci u32 ssp_outcfg; 8408c2ecf20Sopenharmony_ci u32 ssp_incfg; 8418c2ecf20Sopenharmony_ci u32 val; 8428c2ecf20Sopenharmony_ci u32 mask; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (aio->port_type == PORT_SPDIF) 8478c2ecf20Sopenharmony_ci return -EINVAL; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ssp_newcfg = 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 8528c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 8538c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE); 8548c2ecf20Sopenharmony_ci aio->is_slave = 1; 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 8578c2ecf20Sopenharmony_ci ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE); 8588c2ecf20Sopenharmony_ci aio->is_slave = 0; 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci default: 8618c2ecf20Sopenharmony_ci return -EINVAL; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 8658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 8668c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); 8678c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); 8688c2ecf20Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_I2S; 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 8728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 8738c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* DSP_A = data after FS, DSP_B = data during FS */ 8768c2ecf20Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) 8778c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if ((aio->fsync_width > 0) && (aio->fsync_width < 256)) 8808c2ecf20Sopenharmony_ci ssp_newcfg |= 8818c2ecf20Sopenharmony_ci (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH); 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_TDM; 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci default: 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* 8938c2ecf20Sopenharmony_ci * SSP out cfg. 8948c2ecf20Sopenharmony_ci * Retain bits we do not want to update, then OR in new bits 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 8978c2ecf20Sopenharmony_ci ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg; 8988c2ecf20Sopenharmony_ci writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* 9018c2ecf20Sopenharmony_ci * SSP in cfg. 9028c2ecf20Sopenharmony_ci * Retain bits we do not want to update, then OR in new bits 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_ci ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 9058c2ecf20Sopenharmony_ci ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg; 9068c2ecf20Sopenharmony_ci writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * Configure the word clk and bit clk as output or tristate 9128c2ecf20Sopenharmony_ci * Each port has 4 bits for controlling its pins. 9138c2ecf20Sopenharmony_ci * Shift the mask based upon port number. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci mask = BIT(AUD_MISC_SEROUT_LRCK_OE) 9168c2ecf20Sopenharmony_ci | BIT(AUD_MISC_SEROUT_SCLK_OE) 9178c2ecf20Sopenharmony_ci | BIT(AUD_MISC_SEROUT_MCLK_OE); 9188c2ecf20Sopenharmony_ci mask = mask << (aio->portnum * 4); 9198c2ecf20Sopenharmony_ci if (aio->is_slave) 9208c2ecf20Sopenharmony_ci /* Set bit for tri-state */ 9218c2ecf20Sopenharmony_ci val |= mask; 9228c2ecf20Sopenharmony_ci else 9238c2ecf20Sopenharmony_ci /* Clear bit for drive */ 9248c2ecf20Sopenharmony_ci val &= ~mask; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val); 9278c2ecf20Sopenharmony_ci writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return 0; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd, 9338c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); 9368c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, 9398c2ecf20Sopenharmony_ci "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci switch (cmd) { 9428c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 9438c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 9448c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 9458c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 9468c2ecf20Sopenharmony_ci audio_ssp_out_enable(aio); 9478c2ecf20Sopenharmony_ci else 9488c2ecf20Sopenharmony_ci audio_ssp_in_enable(aio); 9498c2ecf20Sopenharmony_ci cygaud->active_ports++; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 9548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 9558c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 9568c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 9578c2ecf20Sopenharmony_ci audio_ssp_out_disable(aio); 9588c2ecf20Sopenharmony_ci else 9598c2ecf20Sopenharmony_ci audio_ssp_in_disable(aio); 9608c2ecf20Sopenharmony_ci cygaud->active_ports--; 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci default: 9648c2ecf20Sopenharmony_ci return -EINVAL; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return 0; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, 9718c2ecf20Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 9748c2ecf20Sopenharmony_ci u32 value; 9758c2ecf20Sopenharmony_ci int bits_per_slot = 0; /* default to 32-bits per slot */ 9768c2ecf20Sopenharmony_ci int frame_bits; 9778c2ecf20Sopenharmony_ci unsigned int active_slots; 9788c2ecf20Sopenharmony_ci bool found = false; 9798c2ecf20Sopenharmony_ci int i; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (tx_mask != rx_mask) { 9828c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 9838c2ecf20Sopenharmony_ci "%s tx_mask must equal rx_mask\n", __func__); 9848c2ecf20Sopenharmony_ci return -EINVAL; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci active_slots = hweight32(tx_mask); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (active_slots > 16) 9908c2ecf20Sopenharmony_ci return -EINVAL; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Slot value must be even */ 9938c2ecf20Sopenharmony_ci if (active_slots % 2) 9948c2ecf20Sopenharmony_ci return -EINVAL; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* We encode 16 slots as 0 in the reg */ 9978c2ecf20Sopenharmony_ci if (active_slots == 16) 9988c2ecf20Sopenharmony_ci active_slots = 0; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* Slot Width is either 16 or 32 */ 10018c2ecf20Sopenharmony_ci switch (slot_width) { 10028c2ecf20Sopenharmony_ci case 16: 10038c2ecf20Sopenharmony_ci bits_per_slot = 1; 10048c2ecf20Sopenharmony_ci break; 10058c2ecf20Sopenharmony_ci case 32: 10068c2ecf20Sopenharmony_ci bits_per_slot = 0; 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci default: 10098c2ecf20Sopenharmony_ci bits_per_slot = 0; 10108c2ecf20Sopenharmony_ci dev_warn(aio->cygaud->dev, 10118c2ecf20Sopenharmony_ci "%s Defaulting Slot Width to 32\n", __func__); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci frame_bits = slots * slot_width; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) { 10178c2ecf20Sopenharmony_ci if (ssp_valid_tdm_framesize[i] == frame_bits) { 10188c2ecf20Sopenharmony_ci found = true; 10198c2ecf20Sopenharmony_ci break; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if (!found) { 10248c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, 10258c2ecf20Sopenharmony_ci "%s In TDM mode, frame bits INVALID (%d)\n", 10268c2ecf20Sopenharmony_ci __func__, frame_bits); 10278c2ecf20Sopenharmony_ci return -EINVAL; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci aio->bit_per_frame = frame_bits; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n", 10338c2ecf20Sopenharmony_ci __func__, active_slots, frame_bits); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* Set capture side of ssp port */ 10368c2ecf20Sopenharmony_ci value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 10378c2ecf20Sopenharmony_ci value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); 10388c2ecf20Sopenharmony_ci value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); 10398c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); 10408c2ecf20Sopenharmony_ci value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); 10418c2ecf20Sopenharmony_ci writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Set playback side of ssp port */ 10448c2ecf20Sopenharmony_ci value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); 10458c2ecf20Sopenharmony_ci value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); 10468c2ecf20Sopenharmony_ci value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); 10478c2ecf20Sopenharmony_ci value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); 10488c2ecf20Sopenharmony_ci value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); 10498c2ecf20Sopenharmony_ci writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return 0; 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 10558c2ecf20Sopenharmony_cistatic int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci if (!aio->is_slave) { 10638c2ecf20Sopenharmony_ci u32 val; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); 10668c2ecf20Sopenharmony_ci val &= CYGNUS_PLLCLKSEL_MASK; 10678c2ecf20Sopenharmony_ci if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { 10688c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", 10698c2ecf20Sopenharmony_ci val); 10708c2ecf20Sopenharmony_ci return -EINVAL; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (aio->clk_trace.cap_clk_en) 10748c2ecf20Sopenharmony_ci clk_disable_unprepare(aio->cygaud->audio_clk[val]); 10758c2ecf20Sopenharmony_ci if (aio->clk_trace.play_clk_en) 10768c2ecf20Sopenharmony_ci clk_disable_unprepare(aio->cygaud->audio_clk[val]); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci aio->pll_clk_num = val; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci return 0; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int cygnus_ssp_suspend(struct snd_soc_component *component) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 10878c2ecf20Sopenharmony_ci int ret = 0; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci for_each_component_dais(component, dai) 10908c2ecf20Sopenharmony_ci ret |= __cygnus_ssp_suspend(dai); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return ret; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); 10988c2ecf20Sopenharmony_ci int error; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) 11018c2ecf20Sopenharmony_ci return 0; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci if (!aio->is_slave) { 11048c2ecf20Sopenharmony_ci if (aio->clk_trace.cap_clk_en) { 11058c2ecf20Sopenharmony_ci error = clk_prepare_enable(aio->cygaud-> 11068c2ecf20Sopenharmony_ci audio_clk[aio->pll_clk_num]); 11078c2ecf20Sopenharmony_ci if (error) { 11088c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", 11098c2ecf20Sopenharmony_ci __func__); 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci if (aio->clk_trace.play_clk_en) { 11148c2ecf20Sopenharmony_ci error = clk_prepare_enable(aio->cygaud-> 11158c2ecf20Sopenharmony_ci audio_clk[aio->pll_clk_num]); 11168c2ecf20Sopenharmony_ci if (error) { 11178c2ecf20Sopenharmony_ci if (aio->clk_trace.cap_clk_en) 11188c2ecf20Sopenharmony_ci clk_disable_unprepare(aio->cygaud-> 11198c2ecf20Sopenharmony_ci audio_clk[aio->pll_clk_num]); 11208c2ecf20Sopenharmony_ci dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", 11218c2ecf20Sopenharmony_ci __func__); 11228c2ecf20Sopenharmony_ci return -EINVAL; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci return 0; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int cygnus_ssp_resume(struct snd_soc_component *component) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 11338c2ecf20Sopenharmony_ci int ret = 0; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci for_each_component_dais(component, dai) 11368c2ecf20Sopenharmony_ci ret |= __cygnus_ssp_resume(dai); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci return ret; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci#else 11428c2ecf20Sopenharmony_ci#define cygnus_ssp_suspend NULL 11438c2ecf20Sopenharmony_ci#define cygnus_ssp_resume NULL 11448c2ecf20Sopenharmony_ci#endif 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { 11478c2ecf20Sopenharmony_ci .startup = cygnus_ssp_startup, 11488c2ecf20Sopenharmony_ci .shutdown = cygnus_ssp_shutdown, 11498c2ecf20Sopenharmony_ci .trigger = cygnus_ssp_trigger, 11508c2ecf20Sopenharmony_ci .hw_params = cygnus_ssp_hw_params, 11518c2ecf20Sopenharmony_ci .set_fmt = cygnus_ssp_set_fmt, 11528c2ecf20Sopenharmony_ci .set_sysclk = cygnus_ssp_set_sysclk, 11538c2ecf20Sopenharmony_ci .set_tdm_slot = cygnus_set_dai_tdm_slot, 11548c2ecf20Sopenharmony_ci}; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { 11578c2ecf20Sopenharmony_ci .startup = cygnus_ssp_startup, 11588c2ecf20Sopenharmony_ci .shutdown = cygnus_ssp_shutdown, 11598c2ecf20Sopenharmony_ci .trigger = cygnus_ssp_trigger, 11608c2ecf20Sopenharmony_ci .hw_params = cygnus_ssp_hw_params, 11618c2ecf20Sopenharmony_ci .set_sysclk = cygnus_ssp_set_sysclk, 11628c2ecf20Sopenharmony_ci}; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci#define INIT_CPU_DAI(num) { \ 11658c2ecf20Sopenharmony_ci .name = "cygnus-ssp" #num, \ 11668c2ecf20Sopenharmony_ci .playback = { \ 11678c2ecf20Sopenharmony_ci .channels_min = 2, \ 11688c2ecf20Sopenharmony_ci .channels_max = 16, \ 11698c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, \ 11708c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 11718c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, \ 11728c2ecf20Sopenharmony_ci }, \ 11738c2ecf20Sopenharmony_ci .capture = { \ 11748c2ecf20Sopenharmony_ci .channels_min = 2, \ 11758c2ecf20Sopenharmony_ci .channels_max = 16, \ 11768c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, \ 11778c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 11788c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, \ 11798c2ecf20Sopenharmony_ci }, \ 11808c2ecf20Sopenharmony_ci .ops = &cygnus_ssp_dai_ops, \ 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = { 11848c2ecf20Sopenharmony_ci INIT_CPU_DAI(0), 11858c2ecf20Sopenharmony_ci INIT_CPU_DAI(1), 11868c2ecf20Sopenharmony_ci INIT_CPU_DAI(2), 11878c2ecf20Sopenharmony_ci}; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_driver cygnus_spdif_dai_info = { 11908c2ecf20Sopenharmony_ci .name = "cygnus-spdif", 11918c2ecf20Sopenharmony_ci .playback = { 11928c2ecf20Sopenharmony_ci .channels_min = 2, 11938c2ecf20Sopenharmony_ci .channels_max = 2, 11948c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 11958c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 11968c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 11978c2ecf20Sopenharmony_ci }, 11988c2ecf20Sopenharmony_ci .ops = &cygnus_spdif_dai_ops, 11998c2ecf20Sopenharmony_ci}; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS]; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver cygnus_ssp_component = { 12048c2ecf20Sopenharmony_ci .name = "cygnus-audio", 12058c2ecf20Sopenharmony_ci .suspend = cygnus_ssp_suspend, 12068c2ecf20Sopenharmony_ci .resume = cygnus_ssp_resume, 12078c2ecf20Sopenharmony_ci}; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci/* 12108c2ecf20Sopenharmony_ci * Return < 0 if error 12118c2ecf20Sopenharmony_ci * Return 0 if disabled 12128c2ecf20Sopenharmony_ci * Return 1 if enabled and node is parsed successfully 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_cistatic int parse_ssp_child_node(struct platform_device *pdev, 12158c2ecf20Sopenharmony_ci struct device_node *dn, 12168c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud, 12178c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *p_dai) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 12208c2ecf20Sopenharmony_ci struct cygnus_ssp_regs ssp_regs[3]; 12218c2ecf20Sopenharmony_ci u32 rawval; 12228c2ecf20Sopenharmony_ci int portnum = -1; 12238c2ecf20Sopenharmony_ci enum cygnus_audio_port_type port_type; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (of_property_read_u32(dn, "reg", &rawval)) { 12268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing reg property\n"); 12278c2ecf20Sopenharmony_ci return -EINVAL; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci portnum = rawval; 12318c2ecf20Sopenharmony_ci switch (rawval) { 12328c2ecf20Sopenharmony_ci case 0: 12338c2ecf20Sopenharmony_ci ssp_regs[0] = INIT_SSP_REGS(0); 12348c2ecf20Sopenharmony_ci port_type = PORT_TDM; 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci case 1: 12378c2ecf20Sopenharmony_ci ssp_regs[1] = INIT_SSP_REGS(1); 12388c2ecf20Sopenharmony_ci port_type = PORT_TDM; 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci case 2: 12418c2ecf20Sopenharmony_ci ssp_regs[2] = INIT_SSP_REGS(2); 12428c2ecf20Sopenharmony_ci port_type = PORT_TDM; 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci case 3: 12458c2ecf20Sopenharmony_ci port_type = PORT_SPDIF; 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci default: 12488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Bad value for reg %u\n", rawval); 12498c2ecf20Sopenharmony_ci return -EINVAL; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci aio = &cygaud->portinfo[portnum]; 12538c2ecf20Sopenharmony_ci aio->cygaud = cygaud; 12548c2ecf20Sopenharmony_ci aio->portnum = portnum; 12558c2ecf20Sopenharmony_ci aio->port_type = port_type; 12568c2ecf20Sopenharmony_ci aio->fsync_width = -1; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci switch (port_type) { 12598c2ecf20Sopenharmony_ci case PORT_TDM: 12608c2ecf20Sopenharmony_ci aio->regs = ssp_regs[portnum]; 12618c2ecf20Sopenharmony_ci *p_dai = cygnus_ssp_dai_info[portnum]; 12628c2ecf20Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_UNKNOWN; 12638c2ecf20Sopenharmony_ci break; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci case PORT_SPDIF: 12668c2ecf20Sopenharmony_ci aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET; 12678c2ecf20Sopenharmony_ci aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET; 12688c2ecf20Sopenharmony_ci aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET; 12698c2ecf20Sopenharmony_ci aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET; 12708c2ecf20Sopenharmony_ci *p_dai = cygnus_spdif_dai_info; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* For the purposes of this code SPDIF can be I2S mode */ 12738c2ecf20Sopenharmony_ci aio->mode = CYGNUS_SSPMODE_I2S; 12748c2ecf20Sopenharmony_ci break; 12758c2ecf20Sopenharmony_ci default: 12768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type); 12778c2ecf20Sopenharmony_ci return -EINVAL; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum); 12818c2ecf20Sopenharmony_ci aio->streams_on = 0; 12828c2ecf20Sopenharmony_ci aio->cygaud->dev = &pdev->dev; 12838c2ecf20Sopenharmony_ci aio->clk_trace.play_en = false; 12848c2ecf20Sopenharmony_ci aio->clk_trace.cap_en = false; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci audio_ssp_init_portregs(aio); 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic int audio_clk_init(struct platform_device *pdev, 12918c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci int i; 12948c2ecf20Sopenharmony_ci char clk_name[PROP_LEN_MAX]; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) { 12978c2ecf20Sopenharmony_ci snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name); 13008c2ecf20Sopenharmony_ci if (IS_ERR(cygaud->audio_clk[i])) 13018c2ecf20Sopenharmony_ci return PTR_ERR(cygaud->audio_clk[i]); 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci return 0; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic int cygnus_ssp_probe(struct platform_device *pdev) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13108c2ecf20Sopenharmony_ci struct device_node *child_node; 13118c2ecf20Sopenharmony_ci struct resource *res; 13128c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud; 13138c2ecf20Sopenharmony_ci int err = -EINVAL; 13148c2ecf20Sopenharmony_ci int node_count; 13158c2ecf20Sopenharmony_ci int active_port_count; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL); 13188c2ecf20Sopenharmony_ci if (!cygaud) 13198c2ecf20Sopenharmony_ci return -ENOMEM; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci dev_set_drvdata(dev, cygaud); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); 13248c2ecf20Sopenharmony_ci cygaud->audio = devm_ioremap_resource(dev, res); 13258c2ecf20Sopenharmony_ci if (IS_ERR(cygaud->audio)) 13268c2ecf20Sopenharmony_ci return PTR_ERR(cygaud->audio); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in"); 13298c2ecf20Sopenharmony_ci cygaud->i2s_in = devm_ioremap_resource(dev, res); 13308c2ecf20Sopenharmony_ci if (IS_ERR(cygaud->i2s_in)) 13318c2ecf20Sopenharmony_ci return PTR_ERR(cygaud->i2s_in); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* Tri-state all controlable pins until we know that we need them */ 13348c2ecf20Sopenharmony_ci writel(CYGNUS_SSP_TRISTATE_MASK, 13358c2ecf20Sopenharmony_ci cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci node_count = of_get_child_count(pdev->dev.of_node); 13388c2ecf20Sopenharmony_ci if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) { 13398c2ecf20Sopenharmony_ci dev_err(dev, "child nodes is %d. Must be between 1 and %d\n", 13408c2ecf20Sopenharmony_ci node_count, CYGNUS_MAX_PORTS); 13418c2ecf20Sopenharmony_ci return -EINVAL; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci active_port_count = 0; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci for_each_available_child_of_node(pdev->dev.of_node, child_node) { 13478c2ecf20Sopenharmony_ci err = parse_ssp_child_node(pdev, child_node, cygaud, 13488c2ecf20Sopenharmony_ci &cygnus_ssp_dai[active_port_count]); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* negative is err, 0 is active and good, 1 is disabled */ 13518c2ecf20Sopenharmony_ci if (err < 0) 13528c2ecf20Sopenharmony_ci return err; 13538c2ecf20Sopenharmony_ci else if (!err) { 13548c2ecf20Sopenharmony_ci dev_dbg(dev, "Activating DAI: %s\n", 13558c2ecf20Sopenharmony_ci cygnus_ssp_dai[active_port_count].name); 13568c2ecf20Sopenharmony_ci active_port_count++; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci cygaud->dev = dev; 13618c2ecf20Sopenharmony_ci cygaud->active_ports = 0; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci dev_dbg(dev, "Registering %d DAIs\n", active_port_count); 13648c2ecf20Sopenharmony_ci err = devm_snd_soc_register_component(dev, &cygnus_ssp_component, 13658c2ecf20Sopenharmony_ci cygnus_ssp_dai, active_port_count); 13668c2ecf20Sopenharmony_ci if (err) { 13678c2ecf20Sopenharmony_ci dev_err(dev, "snd_soc_register_dai failed\n"); 13688c2ecf20Sopenharmony_ci return err; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci cygaud->irq_num = platform_get_irq(pdev, 0); 13728c2ecf20Sopenharmony_ci if (cygaud->irq_num <= 0) 13738c2ecf20Sopenharmony_ci return cygaud->irq_num; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci err = audio_clk_init(pdev, cygaud); 13768c2ecf20Sopenharmony_ci if (err) { 13778c2ecf20Sopenharmony_ci dev_err(dev, "audio clock initialization failed\n"); 13788c2ecf20Sopenharmony_ci return err; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci err = cygnus_soc_platform_register(dev, cygaud); 13828c2ecf20Sopenharmony_ci if (err) { 13838c2ecf20Sopenharmony_ci dev_err(dev, "platform reg error %d\n", err); 13848c2ecf20Sopenharmony_ci return err; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic int cygnus_ssp_remove(struct platform_device *pdev) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci cygnus_soc_platform_unregister(&pdev->dev); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci return 0; 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic const struct of_device_id cygnus_ssp_of_match[] = { 13988c2ecf20Sopenharmony_ci { .compatible = "brcm,cygnus-audio" }, 13998c2ecf20Sopenharmony_ci {}, 14008c2ecf20Sopenharmony_ci}; 14018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cygnus_ssp_of_match); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic struct platform_driver cygnus_ssp_driver = { 14048c2ecf20Sopenharmony_ci .probe = cygnus_ssp_probe, 14058c2ecf20Sopenharmony_ci .remove = cygnus_ssp_remove, 14068c2ecf20Sopenharmony_ci .driver = { 14078c2ecf20Sopenharmony_ci .name = "cygnus-ssp", 14088c2ecf20Sopenharmony_ci .of_match_table = cygnus_ssp_of_match, 14098c2ecf20Sopenharmony_ci }, 14108c2ecf20Sopenharmony_ci}; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cimodule_platform_driver(cygnus_ssp_driver); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:cygnus-ssp"); 14158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 14178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cygnus ASoC SSP Interface"); 1418