1406 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (C) 2014-2015 Broadcom Corporation3#include <linux/clk.h>4#include <linux/delay.h>5#include <linux/init.h>6#include <linux/io.h>7#include <linux/module.h>8#include <linux/of.h>9#include <linux/slab.h>10#include <sound/core.h>11#include <sound/pcm.h>12#include <sound/pcm_params.h>13#include <sound/soc.h>14#include <sound/soc-dai.h>15 16#include "cygnus-ssp.h"17 18#define DEFAULT_VCO 135475020419 20#define CAPTURE_FCI_ID_BASE 0x18021#define CYGNUS_SSP_TRISTATE_MASK 0x001fff22#define CYGNUS_PLLCLKSEL_MASK 0xf23 24/* Used with stream_on field to indicate which streams are active */25#define PLAYBACK_STREAM_MASK BIT(0)26#define CAPTURE_STREAM_MASK BIT(1)27 28#define I2S_STREAM_CFG_MASK 0xff003ff29#define I2S_CAP_STREAM_CFG_MASK 0xf030#define SPDIF_STREAM_CFG_MASK 0x3ff31#define CH_GRP_STEREO 0x132 33/* Begin register offset defines */34#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c35#define AUD_MISC_SEROUT_SPDIF_OE 1236#define AUD_MISC_SEROUT_MCLK_OE 337#define AUD_MISC_SEROUT_LRCK_OE 238#define AUD_MISC_SEROUT_SCLK_OE 139#define AUD_MISC_SEROUT_SDAT_OE 040 41/* AUD_FMM_BF_CTRL_xxx regs */42#define BF_DST_CFG0_OFFSET 0x10043#define BF_DST_CFG1_OFFSET 0x10444#define BF_DST_CFG2_OFFSET 0x10845 46#define BF_DST_CTRL0_OFFSET 0x13047#define BF_DST_CTRL1_OFFSET 0x13448#define BF_DST_CTRL2_OFFSET 0x13849 50#define BF_SRC_CFG0_OFFSET 0x14851#define BF_SRC_CFG1_OFFSET 0x14c52#define BF_SRC_CFG2_OFFSET 0x15053#define BF_SRC_CFG3_OFFSET 0x15454 55#define BF_SRC_CTRL0_OFFSET 0x1c056#define BF_SRC_CTRL1_OFFSET 0x1c457#define BF_SRC_CTRL2_OFFSET 0x1c858#define BF_SRC_CTRL3_OFFSET 0x1cc59 60#define BF_SRC_GRP0_OFFSET 0x1fc61#define BF_SRC_GRP1_OFFSET 0x20062#define BF_SRC_GRP2_OFFSET 0x20463#define BF_SRC_GRP3_OFFSET 0x20864 65#define BF_SRC_GRP_EN_OFFSET 0x32066#define BF_SRC_GRP_FLOWON_OFFSET 0x32467#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x32868 69/* AUD_FMM_IOP_OUT_I2S_xxx regs */70#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa0071#define OUT_I2S_0_CFG_OFFSET 0xa0472#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c73 74#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa4075#define OUT_I2S_1_CFG_OFFSET 0xa4476#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c77 78#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa8079#define OUT_I2S_2_CFG_OFFSET 0xa8480#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c81 82/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */83#define SPDIF_STREAM_CFG_OFFSET 0xac084#define SPDIF_CTRL_OFFSET 0xac485#define SPDIF_FORMAT_CFG_OFFSET 0xad886#define SPDIF_MCLK_CFG_OFFSET 0xadc87 88/* AUD_FMM_IOP_PLL_0_xxx regs */89#define IOP_PLL_0_MACRO_OFFSET 0xb0090#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb1491#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb1892#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c93 94#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb3095#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb3496#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb3897 98/* AUD_FMM_IOP_xxx regs */99#define IOP_PLL_0_CONTROL_OFFSET 0xb04100#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08101#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20102#define IOP_PLL_0_RESET_OFFSET 0xb5c103 104/* AUD_FMM_IOP_IN_I2S_xxx regs */105#define IN_I2S_0_STREAM_CFG_OFFSET 0x00106#define IN_I2S_0_CFG_OFFSET 0x04107#define IN_I2S_1_STREAM_CFG_OFFSET 0x40108#define IN_I2S_1_CFG_OFFSET 0x44109#define IN_I2S_2_STREAM_CFG_OFFSET 0x80110#define IN_I2S_2_CFG_OFFSET 0x84111 112/* AUD_FMM_IOP_MISC_xxx regs */113#define IOP_SW_INIT_LOGIC 0x1c0114 115/* End register offset defines */116 117 118/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */119#define I2S_OUT_MCLKRATE_SHIFT 16120 121/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */122#define I2S_OUT_PLLCLKSEL_SHIFT 0123 124/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */125#define I2S_OUT_STREAM_ENA 31126#define I2S_OUT_STREAM_CFG_GROUP_ID 20127#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24128 129/* AUD_FMM_IOP_IN_I2S_x_CAP */130#define I2S_IN_STREAM_CFG_CAP_ENA 31131#define I2S_IN_STREAM_CFG_0_GROUP_ID 4132 133/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */134#define I2S_OUT_CFGX_CLK_ENA 0135#define I2S_OUT_CFGX_DATA_ENABLE 1136#define I2S_OUT_CFGX_DATA_ALIGNMENT 6137#define I2S_OUT_CFGX_BITS_PER_SLOT 13138#define I2S_OUT_CFGX_VALID_SLOT 14139#define I2S_OUT_CFGX_FSYNC_WIDTH 18140#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26141#define I2S_OUT_CFGX_SLAVE_MODE 30142#define I2S_OUT_CFGX_TDM_MODE 31143 144/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */145#define BF_SRC_CFGX_SFIFO_ENA 0146#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1147#define BF_SRC_CFGX_SAMPLE_CH_MODE 2148#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5149#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10150#define BF_SRC_CFGX_BIT_RES 20151#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31152 153/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */154#define BF_DST_CFGX_CAP_ENA 0155#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1156#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2157#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11158#define BF_DST_CFGX_FCI_ID 12159#define BF_DST_CFGX_CAP_MODE 24160#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31161 162/* AUD_FMM_IOP_OUT_SPDIF_xxx */163#define SPDIF_0_OUT_DITHER_ENA 3164#define SPDIF_0_OUT_STREAM_ENA 31165 166/* AUD_FMM_IOP_PLL_0_USER */167#define IOP_PLL_0_USER_NDIV_FRAC 10168 169/* AUD_FMM_IOP_PLL_0_ACTIVE */170#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10171 172 173#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \174 .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \175 .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \176 .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \177 .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \178 .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \179 .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \180 .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \181 .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \182 .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \183 .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \184}185 186struct pll_macro_entry {187 u32 mclk;188 u32 pll_ch_num;189};190 191/*192 * PLL has 3 output channels (1x, 2x, and 4x). Below are193 * the common MCLK frequencies used by audio driver194 */195static const struct pll_macro_entry pll_predef_mclk[] = {196 { 4096000, 0},197 { 8192000, 1},198 {16384000, 2},199 200 { 5644800, 0},201 {11289600, 1},202 {22579200, 2},203 204 { 6144000, 0},205 {12288000, 1},206 {24576000, 2},207 208 {12288000, 0},209 {24576000, 1},210 {49152000, 2},211 212 {22579200, 0},213 {45158400, 1},214 {90316800, 2},215 216 {24576000, 0},217 {49152000, 1},218 {98304000, 2},219};220 221#define CYGNUS_RATE_MIN 8000222#define CYGNUS_RATE_MAX 384000223 224/* List of valid frame sizes for tdm mode */225static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};226 227static const unsigned int cygnus_rates[] = {228 8000, 11025, 16000, 22050, 32000, 44100, 48000,229 88200, 96000, 176400, 192000, 352800, 384000230};231 232static const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = {233 .count = ARRAY_SIZE(cygnus_rates),234 .list = cygnus_rates,235};236 237static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)238{239 struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);240 241 return &cygaud->portinfo[dai->id];242}243 244static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)245{246 u32 value, fci_id;247 int status = 0;248 249 switch (aio->port_type) {250 case PORT_TDM:251 value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);252 value &= ~I2S_STREAM_CFG_MASK;253 254 /* Set Group ID */255 writel(aio->portnum,256 aio->cygaud->audio + aio->regs.bf_sourcech_grp);257 258 /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */259 value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;260 value |= aio->portnum; /* FCI ID is the port num */261 value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;262 writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);263 264 /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */265 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);266 value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);267 value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);268 value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);269 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);270 271 /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */272 value = readl(aio->cygaud->i2s_in +273 aio->regs.i2s_cap_stream_cfg);274 value &= ~I2S_CAP_STREAM_CFG_MASK;275 value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;276 writel(value, aio->cygaud->i2s_in +277 aio->regs.i2s_cap_stream_cfg);278 279 /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */280 fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;281 282 value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);283 value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);284 value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);285 value |= (fci_id << BF_DST_CFGX_FCI_ID);286 value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);287 writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);288 289 /* Enable the transmit pin for this port */290 value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);291 value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);292 writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);293 break;294 case PORT_SPDIF:295 writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);296 297 value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);298 value |= BIT(SPDIF_0_OUT_DITHER_ENA);299 writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);300 301 /* Enable and set the FCI ID for the SPDIF channel */302 value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);303 value &= ~SPDIF_STREAM_CFG_MASK;304 value |= aio->portnum; /* FCI ID is the port num */305 value |= BIT(SPDIF_0_OUT_STREAM_ENA);306 writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);307 308 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);309 value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);310 value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);311 value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);312 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);313 314 /* Enable the spdif output pin */315 value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);316 value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);317 writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);318 break;319 default:320 dev_err(aio->cygaud->dev, "Port not supported\n");321 status = -EINVAL;322 }323 324 return status;325}326 327static void audio_ssp_in_enable(struct cygnus_aio_port *aio)328{329 u32 value;330 331 value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);332 value |= BIT(BF_DST_CFGX_CAP_ENA);333 writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);334 335 writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);336 337 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);338 value |= BIT(I2S_OUT_CFGX_CLK_ENA);339 value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);340 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);341 342 value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);343 value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);344 writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);345 346 aio->streams_on |= CAPTURE_STREAM_MASK;347}348 349static void audio_ssp_in_disable(struct cygnus_aio_port *aio)350{351 u32 value;352 353 value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);354 value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);355 writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);356 357 aio->streams_on &= ~CAPTURE_STREAM_MASK;358 359 /* If both playback and capture are off */360 if (!aio->streams_on) {361 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);362 value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);363 value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);364 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);365 }366 367 writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);368 369 value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);370 value &= ~BIT(BF_DST_CFGX_CAP_ENA);371 writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);372}373 374static int audio_ssp_out_enable(struct cygnus_aio_port *aio)375{376 u32 value;377 int status = 0;378 379 switch (aio->port_type) {380 case PORT_TDM:381 value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);382 value |= BIT(I2S_OUT_STREAM_ENA);383 writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);384 385 writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);386 387 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);388 value |= BIT(I2S_OUT_CFGX_CLK_ENA);389 value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);390 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);391 392 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);393 value |= BIT(BF_SRC_CFGX_SFIFO_ENA);394 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);395 396 aio->streams_on |= PLAYBACK_STREAM_MASK;397 break;398 case PORT_SPDIF:399 value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);400 value |= 0x3;401 writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);402 403 writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);404 405 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);406 value |= BIT(BF_SRC_CFGX_SFIFO_ENA);407 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);408 break;409 default:410 dev_err(aio->cygaud->dev,411 "Port not supported %d\n", aio->portnum);412 status = -EINVAL;413 }414 415 return status;416}417 418static int audio_ssp_out_disable(struct cygnus_aio_port *aio)419{420 u32 value;421 int status = 0;422 423 switch (aio->port_type) {424 case PORT_TDM:425 aio->streams_on &= ~PLAYBACK_STREAM_MASK;426 427 /* If both playback and capture are off */428 if (!aio->streams_on) {429 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);430 value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);431 value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);432 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);433 }434 435 /* set group_sync_dis = 1 */436 value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);437 value |= BIT(aio->portnum);438 writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);439 440 writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);441 442 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);443 value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);444 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);445 446 /* set group_sync_dis = 0 */447 value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);448 value &= ~BIT(aio->portnum);449 writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);450 451 value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);452 value &= ~BIT(I2S_OUT_STREAM_ENA);453 writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);454 455 /* IOP SW INIT on OUT_I2S_x */456 value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);457 value |= BIT(aio->portnum);458 writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);459 value &= ~BIT(aio->portnum);460 writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);461 break;462 case PORT_SPDIF:463 value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);464 value &= ~0x3;465 writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);466 writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);467 468 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);469 value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);470 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);471 break;472 default:473 dev_err(aio->cygaud->dev,474 "Port not supported %d\n", aio->portnum);475 status = -EINVAL;476 }477 478 return status;479}480 481static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,482 struct cygnus_aio_port *aio)483{484 int i = 0, error;485 bool found = false;486 const struct pll_macro_entry *p_entry;487 struct clk *ch_clk;488 489 for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {490 p_entry = &pll_predef_mclk[i];491 if (p_entry->mclk == mclk) {492 found = true;493 break;494 }495 }496 if (!found) {497 dev_err(cygaud->dev,498 "%s No valid mclk freq (%u) found!\n", __func__, mclk);499 return -EINVAL;500 }501 502 ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];503 504 if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {505 error = clk_prepare_enable(ch_clk);506 if (error) {507 dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",508 __func__, error);509 return error;510 }511 aio->clk_trace.cap_clk_en = true;512 }513 514 if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {515 error = clk_prepare_enable(ch_clk);516 if (error) {517 dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",518 __func__, error);519 return error;520 }521 aio->clk_trace.play_clk_en = true;522 }523 524 error = clk_set_rate(ch_clk, mclk);525 if (error) {526 dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",527 __func__, error);528 return error;529 }530 531 return p_entry->pll_ch_num;532}533 534static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio)535{536 u32 value;537 u32 mask = 0xf;538 u32 sclk;539 u32 mclk_rate;540 unsigned int bit_rate;541 unsigned int ratio;542 543 bit_rate = aio->bit_per_frame * aio->lrclk;544 545 /*546 * Check if the bit clock can be generated from the given MCLK.547 * MCLK must be a perfect multiple of bit clock and must be one of the548 * following values... (2,4,6,8,10,12,14)549 */550 if ((aio->mclk % bit_rate) != 0)551 return -EINVAL;552 553 ratio = aio->mclk / bit_rate;554 switch (ratio) {555 case 2:556 case 4:557 case 6:558 case 8:559 case 10:560 case 12:561 case 14:562 mclk_rate = ratio / 2;563 break;564 565 default:566 dev_err(aio->cygaud->dev,567 "Invalid combination of MCLK and BCLK\n");568 dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",569 aio->lrclk, aio->bit_per_frame, aio->mclk);570 return -EINVAL;571 }572 573 /* Set sclk rate */574 switch (aio->port_type) {575 case PORT_TDM:576 sclk = aio->bit_per_frame;577 if (sclk == 512)578 sclk = 0;579 580 /* sclks_per_1fs_div = sclk cycles/32 */581 sclk /= 32;582 583 /* Set number of bitclks per frame */584 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);585 value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);586 value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;587 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);588 dev_dbg(aio->cygaud->dev,589 "SCLKS_PER_1FS_DIV32 = 0x%x\n", value);590 break;591 case PORT_SPDIF:592 break;593 default:594 dev_err(aio->cygaud->dev, "Unknown port type\n");595 return -EINVAL;596 }597 598 /* Set MCLK_RATE ssp port (spdif and ssp are the same) */599 value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);600 value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);601 value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT);602 writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);603 604 dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);605 dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",606 aio->bit_per_frame, aio->mclk, aio->lrclk);607 return 0;608}609 610static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,611 struct snd_pcm_hw_params *params,612 struct snd_soc_dai *dai)613{614 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);615 int rate, bitres;616 u32 value;617 u32 mask = 0x1f;618 int ret = 0;619 620 dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);621 dev_dbg(aio->cygaud->dev, "params_channels %d\n",622 params_channels(params));623 dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));624 dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));625 626 rate = params_rate(params);627 628 switch (aio->mode) {629 case CYGNUS_SSPMODE_TDM:630 if ((rate == 192000) && (params_channels(params) > 4)) {631 dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",632 params_channels(params), rate);633 return -EINVAL;634 }635 break;636 case CYGNUS_SSPMODE_I2S:637 aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */638 break;639 default:640 dev_err(aio->cygaud->dev,641 "%s port running in unknown mode\n", __func__);642 return -EINVAL;643 }644 645 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {646 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);647 value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);648 value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);649 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);650 651 switch (params_format(params)) {652 case SNDRV_PCM_FORMAT_S16_LE:653 bitres = 16;654 break;655 656 case SNDRV_PCM_FORMAT_S32_LE:657 /* 32 bit mode is coded as 0 */658 bitres = 0;659 break;660 661 default:662 return -EINVAL;663 }664 665 value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);666 value &= ~(mask << BF_SRC_CFGX_BIT_RES);667 value |= (bitres << BF_SRC_CFGX_BIT_RES);668 writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);669 670 } else {671 672 switch (params_format(params)) {673 case SNDRV_PCM_FORMAT_S16_LE:674 value = readl(aio->cygaud->audio +675 aio->regs.bf_destch_cfg);676 value |= BIT(BF_DST_CFGX_CAP_MODE);677 writel(value, aio->cygaud->audio +678 aio->regs.bf_destch_cfg);679 break;680 681 case SNDRV_PCM_FORMAT_S32_LE:682 value = readl(aio->cygaud->audio +683 aio->regs.bf_destch_cfg);684 value &= ~BIT(BF_DST_CFGX_CAP_MODE);685 writel(value, aio->cygaud->audio +686 aio->regs.bf_destch_cfg);687 break;688 689 default:690 return -EINVAL;691 }692 }693 694 aio->lrclk = rate;695 696 if (!aio->is_slave)697 ret = cygnus_ssp_set_clocks(aio);698 699 return ret;700}701 702/*703 * This function sets the mclk frequency for pll clock704 */705static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,706 int clk_id, unsigned int freq, int dir)707{708 int sel;709 u32 value;710 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);711 struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);712 713 dev_dbg(aio->cygaud->dev,714 "%s Enter port = %d\n", __func__, aio->portnum);715 sel = pll_configure_mclk(cygaud, freq, aio);716 if (sel < 0) {717 dev_err(aio->cygaud->dev,718 "%s Setting mclk failed.\n", __func__);719 return -EINVAL;720 }721 722 aio->mclk = freq;723 724 dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);725 value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);726 value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);727 value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);728 writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);729 730 return 0;731}732 733static int cygnus_ssp_startup(struct snd_pcm_substream *substream,734 struct snd_soc_dai *dai)735{736 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);737 738 snd_soc_dai_set_dma_data(dai, substream, aio);739 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)740 aio->clk_trace.play_en = true;741 else742 aio->clk_trace.cap_en = true;743 744 substream->runtime->hw.rate_min = CYGNUS_RATE_MIN;745 substream->runtime->hw.rate_max = CYGNUS_RATE_MAX;746 747 snd_pcm_hw_constraint_list(substream->runtime, 0,748 SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint);749 return 0;750}751 752static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,753 struct snd_soc_dai *dai)754{755 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);756 757 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)758 aio->clk_trace.play_en = false;759 else760 aio->clk_trace.cap_en = false;761 762 if (!aio->is_slave) {763 u32 val;764 765 val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);766 val &= CYGNUS_PLLCLKSEL_MASK;767 if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {768 dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",769 val);770 return;771 }772 773 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {774 if (aio->clk_trace.play_clk_en) {775 clk_disable_unprepare(aio->cygaud->776 audio_clk[val]);777 aio->clk_trace.play_clk_en = false;778 }779 } else {780 if (aio->clk_trace.cap_clk_en) {781 clk_disable_unprepare(aio->cygaud->782 audio_clk[val]);783 aio->clk_trace.cap_clk_en = false;784 }785 }786 }787}788 789/*790 * Bit Update Notes791 * 31 Yes TDM Mode (1 = TDM, 0 = i2s)792 * 30 Yes Slave Mode (1 = Slave, 0 = Master)793 * 29:26 No Sclks per frame794 * 25:18 Yes FS Width795 * 17:14 No Valid Slots796 * 13 No Bits (1 = 16 bits, 0 = 32 bits)797 * 12:08 No Bits per samp798 * 07 Yes Justifcation (1 = LSB, 0 = MSB)799 * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay800 * 05 Yes SCLK polarity (1 = Rising, 0 = Falling)801 * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left)802 * 03:02 Yes Reserved - write as zero803 * 01 No Data Enable804 * 00 No CLK Enable805 */806#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03807 808/* Input cfg is same as output, but the FS width is not a valid field */809#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)810 811int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)812{813 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);814 815 if ((len > 0) && (len < 256)) {816 aio->fsync_width = len;817 return 0;818 } else {819 return -EINVAL;820 }821}822EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width);823 824static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)825{826 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);827 u32 ssp_curcfg;828 u32 ssp_newcfg;829 u32 ssp_outcfg;830 u32 ssp_incfg;831 u32 val;832 u32 mask;833 834 dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt);835 836 if (aio->port_type == PORT_SPDIF)837 return -EINVAL;838 839 ssp_newcfg = 0;840 841 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {842 case SND_SOC_DAIFMT_BC_FC:843 ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);844 aio->is_slave = 1;845 break;846 case SND_SOC_DAIFMT_BP_FP:847 ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);848 aio->is_slave = 0;849 break;850 default:851 return -EINVAL;852 }853 854 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {855 case SND_SOC_DAIFMT_I2S:856 ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);857 ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);858 aio->mode = CYGNUS_SSPMODE_I2S;859 break;860 861 case SND_SOC_DAIFMT_DSP_A:862 case SND_SOC_DAIFMT_DSP_B:863 ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);864 865 /* DSP_A = data after FS, DSP_B = data during FS */866 if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)867 ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);868 869 if ((aio->fsync_width > 0) && (aio->fsync_width < 256))870 ssp_newcfg |=871 (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);872 else873 ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);874 875 aio->mode = CYGNUS_SSPMODE_TDM;876 break;877 878 default:879 return -EINVAL;880 }881 882 /*883 * SSP out cfg.884 * Retain bits we do not want to update, then OR in new bits885 */886 ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);887 ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;888 writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);889 890 /*891 * SSP in cfg.892 * Retain bits we do not want to update, then OR in new bits893 */894 ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);895 ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;896 writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);897 898 val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);899 900 /*901 * Configure the word clk and bit clk as output or tristate902 * Each port has 4 bits for controlling its pins.903 * Shift the mask based upon port number.904 */905 mask = BIT(AUD_MISC_SEROUT_LRCK_OE)906 | BIT(AUD_MISC_SEROUT_SCLK_OE)907 | BIT(AUD_MISC_SEROUT_MCLK_OE);908 mask = mask << (aio->portnum * 4);909 if (aio->is_slave)910 /* Set bit for tri-state */911 val |= mask;912 else913 /* Clear bit for drive */914 val &= ~mask;915 916 dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val);917 writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);918 919 return 0;920}921 922static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,923 struct snd_soc_dai *dai)924{925 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);926 struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);927 928 dev_dbg(aio->cygaud->dev,929 "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);930 931 switch (cmd) {932 case SNDRV_PCM_TRIGGER_START:933 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:934 case SNDRV_PCM_TRIGGER_RESUME:935 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)936 audio_ssp_out_enable(aio);937 else938 audio_ssp_in_enable(aio);939 cygaud->active_ports++;940 941 break;942 943 case SNDRV_PCM_TRIGGER_STOP:944 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:945 case SNDRV_PCM_TRIGGER_SUSPEND:946 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)947 audio_ssp_out_disable(aio);948 else949 audio_ssp_in_disable(aio);950 cygaud->active_ports--;951 break;952 953 default:954 return -EINVAL;955 }956 957 return 0;958}959 960static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,961 unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)962{963 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);964 u32 value;965 int bits_per_slot = 0; /* default to 32-bits per slot */966 int frame_bits;967 unsigned int active_slots;968 bool found = false;969 int i;970 971 if (tx_mask != rx_mask) {972 dev_err(aio->cygaud->dev,973 "%s tx_mask must equal rx_mask\n", __func__);974 return -EINVAL;975 }976 977 active_slots = hweight32(tx_mask);978 979 if (active_slots > 16)980 return -EINVAL;981 982 /* Slot value must be even */983 if (active_slots % 2)984 return -EINVAL;985 986 /* We encode 16 slots as 0 in the reg */987 if (active_slots == 16)988 active_slots = 0;989 990 /* Slot Width is either 16 or 32 */991 switch (slot_width) {992 case 16:993 bits_per_slot = 1;994 break;995 case 32:996 bits_per_slot = 0;997 break;998 default:999 bits_per_slot = 0;1000 dev_warn(aio->cygaud->dev,1001 "%s Defaulting Slot Width to 32\n", __func__);1002 }1003 1004 frame_bits = slots * slot_width;1005 1006 for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {1007 if (ssp_valid_tdm_framesize[i] == frame_bits) {1008 found = true;1009 break;1010 }1011 }1012 1013 if (!found) {1014 dev_err(aio->cygaud->dev,1015 "%s In TDM mode, frame bits INVALID (%d)\n",1016 __func__, frame_bits);1017 return -EINVAL;1018 }1019 1020 aio->bit_per_frame = frame_bits;1021 1022 dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",1023 __func__, active_slots, frame_bits);1024 1025 /* Set capture side of ssp port */1026 value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);1027 value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);1028 value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);1029 value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);1030 value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);1031 writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);1032 1033 /* Set playback side of ssp port */1034 value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);1035 value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);1036 value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);1037 value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);1038 value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);1039 writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);1040 1041 return 0;1042}1043 1044#ifdef CONFIG_PM_SLEEP1045static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)1046{1047 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);1048 1049 if (!snd_soc_dai_active(cpu_dai))1050 return 0;1051 1052 if (!aio->is_slave) {1053 u32 val;1054 1055 val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);1056 val &= CYGNUS_PLLCLKSEL_MASK;1057 if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {1058 dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",1059 val);1060 return -EINVAL;1061 }1062 1063 if (aio->clk_trace.cap_clk_en)1064 clk_disable_unprepare(aio->cygaud->audio_clk[val]);1065 if (aio->clk_trace.play_clk_en)1066 clk_disable_unprepare(aio->cygaud->audio_clk[val]);1067 1068 aio->pll_clk_num = val;1069 }1070 1071 return 0;1072}1073 1074static int cygnus_ssp_suspend(struct snd_soc_component *component)1075{1076 struct snd_soc_dai *dai;1077 int ret = 0;1078 1079 for_each_component_dais(component, dai)1080 ret |= __cygnus_ssp_suspend(dai);1081 1082 return ret;1083}1084 1085static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)1086{1087 struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);1088 int error;1089 1090 if (!snd_soc_dai_active(cpu_dai))1091 return 0;1092 1093 if (!aio->is_slave) {1094 if (aio->clk_trace.cap_clk_en) {1095 error = clk_prepare_enable(aio->cygaud->1096 audio_clk[aio->pll_clk_num]);1097 if (error) {1098 dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",1099 __func__);1100 return -EINVAL;1101 }1102 }1103 if (aio->clk_trace.play_clk_en) {1104 error = clk_prepare_enable(aio->cygaud->1105 audio_clk[aio->pll_clk_num]);1106 if (error) {1107 if (aio->clk_trace.cap_clk_en)1108 clk_disable_unprepare(aio->cygaud->1109 audio_clk[aio->pll_clk_num]);1110 dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",1111 __func__);1112 return -EINVAL;1113 }1114 }1115 }1116 1117 return 0;1118}1119 1120static int cygnus_ssp_resume(struct snd_soc_component *component)1121{1122 struct snd_soc_dai *dai;1123 int ret = 0;1124 1125 for_each_component_dais(component, dai)1126 ret |= __cygnus_ssp_resume(dai);1127 1128 return ret;1129}1130 1131#else1132#define cygnus_ssp_suspend NULL1133#define cygnus_ssp_resume NULL1134#endif1135 1136static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {1137 .startup = cygnus_ssp_startup,1138 .shutdown = cygnus_ssp_shutdown,1139 .trigger = cygnus_ssp_trigger,1140 .hw_params = cygnus_ssp_hw_params,1141 .set_fmt = cygnus_ssp_set_fmt,1142 .set_sysclk = cygnus_ssp_set_sysclk,1143 .set_tdm_slot = cygnus_set_dai_tdm_slot,1144};1145 1146static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {1147 .startup = cygnus_ssp_startup,1148 .shutdown = cygnus_ssp_shutdown,1149 .trigger = cygnus_ssp_trigger,1150 .hw_params = cygnus_ssp_hw_params,1151 .set_sysclk = cygnus_ssp_set_sysclk,1152};1153 1154#define INIT_CPU_DAI(num) { \1155 .name = "cygnus-ssp" #num, \1156 .playback = { \1157 .channels_min = 2, \1158 .channels_max = 16, \1159 .rates = SNDRV_PCM_RATE_KNOT, \1160 .formats = SNDRV_PCM_FMTBIT_S16_LE | \1161 SNDRV_PCM_FMTBIT_S32_LE, \1162 }, \1163 .capture = { \1164 .channels_min = 2, \1165 .channels_max = 16, \1166 .rates = SNDRV_PCM_RATE_KNOT, \1167 .formats = SNDRV_PCM_FMTBIT_S16_LE | \1168 SNDRV_PCM_FMTBIT_S32_LE, \1169 }, \1170 .ops = &cygnus_ssp_dai_ops, \1171}1172 1173static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {1174 INIT_CPU_DAI(0),1175 INIT_CPU_DAI(1),1176 INIT_CPU_DAI(2),1177};1178 1179static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {1180 .name = "cygnus-spdif",1181 .playback = {1182 .channels_min = 2,1183 .channels_max = 2,1184 .rates = SNDRV_PCM_RATE_KNOT,1185 .formats = SNDRV_PCM_FMTBIT_S16_LE |1186 SNDRV_PCM_FMTBIT_S32_LE,1187 },1188 .ops = &cygnus_spdif_dai_ops,1189};1190 1191static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];1192 1193static const struct snd_soc_component_driver cygnus_ssp_component = {1194 .name = "cygnus-audio",1195 .suspend = cygnus_ssp_suspend,1196 .resume = cygnus_ssp_resume,1197 .legacy_dai_naming = 1,1198};1199 1200/*1201 * Return < 0 if error1202 * Return 0 if disabled1203 * Return 1 if enabled and node is parsed successfully1204 */1205static int parse_ssp_child_node(struct platform_device *pdev,1206 struct device_node *dn,1207 struct cygnus_audio *cygaud,1208 struct snd_soc_dai_driver *p_dai)1209{1210 struct cygnus_aio_port *aio;1211 struct cygnus_ssp_regs ssp_regs[3];1212 u32 rawval;1213 int portnum = -1;1214 enum cygnus_audio_port_type port_type;1215 1216 if (of_property_read_u32(dn, "reg", &rawval)) {1217 dev_err(&pdev->dev, "Missing reg property\n");1218 return -EINVAL;1219 }1220 1221 portnum = rawval;1222 switch (rawval) {1223 case 0:1224 ssp_regs[0] = INIT_SSP_REGS(0);1225 port_type = PORT_TDM;1226 break;1227 case 1:1228 ssp_regs[1] = INIT_SSP_REGS(1);1229 port_type = PORT_TDM;1230 break;1231 case 2:1232 ssp_regs[2] = INIT_SSP_REGS(2);1233 port_type = PORT_TDM;1234 break;1235 case 3:1236 port_type = PORT_SPDIF;1237 break;1238 default:1239 dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);1240 return -EINVAL;1241 }1242 1243 aio = &cygaud->portinfo[portnum];1244 aio->cygaud = cygaud;1245 aio->portnum = portnum;1246 aio->port_type = port_type;1247 aio->fsync_width = -1;1248 1249 switch (port_type) {1250 case PORT_TDM:1251 aio->regs = ssp_regs[portnum];1252 *p_dai = cygnus_ssp_dai_info[portnum];1253 aio->mode = CYGNUS_SSPMODE_UNKNOWN;1254 break;1255 1256 case PORT_SPDIF:1257 aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;1258 aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;1259 aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;1260 aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;1261 *p_dai = cygnus_spdif_dai_info;1262 1263 /* For the purposes of this code SPDIF can be I2S mode */1264 aio->mode = CYGNUS_SSPMODE_I2S;1265 break;1266 default:1267 dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);1268 return -EINVAL;1269 }1270 1271 dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);1272 aio->streams_on = 0;1273 aio->cygaud->dev = &pdev->dev;1274 aio->clk_trace.play_en = false;1275 aio->clk_trace.cap_en = false;1276 1277 audio_ssp_init_portregs(aio);1278 return 0;1279}1280 1281static int audio_clk_init(struct platform_device *pdev,1282 struct cygnus_audio *cygaud)1283{1284 int i;1285 char clk_name[PROP_LEN_MAX];1286 1287 for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {1288 snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);1289 1290 cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);1291 if (IS_ERR(cygaud->audio_clk[i]))1292 return PTR_ERR(cygaud->audio_clk[i]);1293 }1294 1295 return 0;1296}1297 1298static int cygnus_ssp_probe(struct platform_device *pdev)1299{1300 struct device *dev = &pdev->dev;1301 struct device_node *child_node;1302 struct cygnus_audio *cygaud;1303 int err;1304 int node_count;1305 int active_port_count;1306 1307 cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);1308 if (!cygaud)1309 return -ENOMEM;1310 1311 dev_set_drvdata(dev, cygaud);1312 1313 cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud");1314 if (IS_ERR(cygaud->audio))1315 return PTR_ERR(cygaud->audio);1316 1317 cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in");1318 if (IS_ERR(cygaud->i2s_in))1319 return PTR_ERR(cygaud->i2s_in);1320 1321 /* Tri-state all controlable pins until we know that we need them */1322 writel(CYGNUS_SSP_TRISTATE_MASK,1323 cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);1324 1325 node_count = of_get_child_count(pdev->dev.of_node);1326 if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {1327 dev_err(dev, "child nodes is %d. Must be between 1 and %d\n",1328 node_count, CYGNUS_MAX_PORTS);1329 return -EINVAL;1330 }1331 1332 active_port_count = 0;1333 1334 for_each_available_child_of_node(pdev->dev.of_node, child_node) {1335 err = parse_ssp_child_node(pdev, child_node, cygaud,1336 &cygnus_ssp_dai[active_port_count]);1337 1338 /* negative is err, 0 is active and good, 1 is disabled */1339 if (err < 0) {1340 of_node_put(child_node);1341 return err;1342 }1343 else if (!err) {1344 dev_dbg(dev, "Activating DAI: %s\n",1345 cygnus_ssp_dai[active_port_count].name);1346 active_port_count++;1347 }1348 }1349 1350 cygaud->dev = dev;1351 cygaud->active_ports = 0;1352 1353 dev_dbg(dev, "Registering %d DAIs\n", active_port_count);1354 err = devm_snd_soc_register_component(dev, &cygnus_ssp_component,1355 cygnus_ssp_dai, active_port_count);1356 if (err) {1357 dev_err(dev, "snd_soc_register_dai failed\n");1358 return err;1359 }1360 1361 cygaud->irq_num = platform_get_irq(pdev, 0);1362 if (cygaud->irq_num <= 0)1363 return cygaud->irq_num;1364 1365 err = audio_clk_init(pdev, cygaud);1366 if (err) {1367 dev_err(dev, "audio clock initialization failed\n");1368 return err;1369 }1370 1371 err = cygnus_soc_platform_register(dev, cygaud);1372 if (err) {1373 dev_err(dev, "platform reg error %d\n", err);1374 return err;1375 }1376 1377 return 0;1378}1379 1380static void cygnus_ssp_remove(struct platform_device *pdev)1381{1382 cygnus_soc_platform_unregister(&pdev->dev);1383}1384 1385static const struct of_device_id cygnus_ssp_of_match[] = {1386 { .compatible = "brcm,cygnus-audio" },1387 {},1388};1389MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);1390 1391static struct platform_driver cygnus_ssp_driver = {1392 .probe = cygnus_ssp_probe,1393 .remove = cygnus_ssp_remove,1394 .driver = {1395 .name = "cygnus-ssp",1396 .of_match_table = cygnus_ssp_of_match,1397 },1398};1399 1400module_platform_driver(cygnus_ssp_driver);1401 1402MODULE_ALIAS("platform:cygnus-ssp");1403MODULE_LICENSE("GPL v2");1404MODULE_AUTHOR("Broadcom");1405MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");1406