162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Apple SoCs MCA driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) The Asahi Linux Contributors 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// The MCA peripheral is made up of a number of identical units called clusters. 862306a36Sopenharmony_ci// Each cluster has its separate clock parent, SYNC signal generator, carries 962306a36Sopenharmony_ci// four SERDES units and has a dedicated I2S port on the SoC's periphery. 1062306a36Sopenharmony_ci// 1162306a36Sopenharmony_ci// The clusters can operate independently, or can be combined together in a 1262306a36Sopenharmony_ci// configurable manner. We mostly treat them as self-contained independent 1362306a36Sopenharmony_ci// units and don't configure any cross-cluster connections except for the I2S 1462306a36Sopenharmony_ci// ports. The I2S ports can be routed to any of the clusters (irrespective 1562306a36Sopenharmony_ci// of their native cluster). We map this onto ASoC's (DPCM) notion of backend 1662306a36Sopenharmony_ci// and frontend DAIs. The 'cluster guts' are frontends which are dynamically 1762306a36Sopenharmony_ci// routed to backend I2S ports. 1862306a36Sopenharmony_ci// 1962306a36Sopenharmony_ci// DAI references in devicetree are resolved to backends. The routing between 2062306a36Sopenharmony_ci// frontends and backends is determined by the machine driver in the DAPM paths 2162306a36Sopenharmony_ci// it supplies. 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/bitfield.h> 2462306a36Sopenharmony_ci#include <linux/clk.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci#include <linux/init.h> 2762306a36Sopenharmony_ci#include <linux/kernel.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/of_clk.h> 3162306a36Sopenharmony_ci#include <linux/of_dma.h> 3262306a36Sopenharmony_ci#include <linux/platform_device.h> 3362306a36Sopenharmony_ci#include <linux/pm_domain.h> 3462306a36Sopenharmony_ci#include <linux/regmap.h> 3562306a36Sopenharmony_ci#include <linux/reset.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <sound/core.h> 3962306a36Sopenharmony_ci#include <sound/pcm.h> 4062306a36Sopenharmony_ci#include <sound/pcm_params.h> 4162306a36Sopenharmony_ci#include <sound/soc.h> 4262306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define USE_RXB_FOR_CAPTURE 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Relative to cluster base */ 4762306a36Sopenharmony_ci#define REG_STATUS 0x0 4862306a36Sopenharmony_ci#define STATUS_MCLK_EN BIT(0) 4962306a36Sopenharmony_ci#define REG_MCLK_CONF 0x4 5062306a36Sopenharmony_ci#define MCLK_CONF_DIV GENMASK(11, 8) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define REG_SYNCGEN_STATUS 0x100 5362306a36Sopenharmony_ci#define SYNCGEN_STATUS_EN BIT(0) 5462306a36Sopenharmony_ci#define REG_SYNCGEN_MCLK_SEL 0x104 5562306a36Sopenharmony_ci#define SYNCGEN_MCLK_SEL GENMASK(3, 0) 5662306a36Sopenharmony_ci#define REG_SYNCGEN_HI_PERIOD 0x108 5762306a36Sopenharmony_ci#define REG_SYNCGEN_LO_PERIOD 0x10c 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define REG_PORT_ENABLES 0x600 6062306a36Sopenharmony_ci#define PORT_ENABLES_CLOCKS GENMASK(2, 1) 6162306a36Sopenharmony_ci#define PORT_ENABLES_TX_DATA BIT(3) 6262306a36Sopenharmony_ci#define REG_PORT_CLOCK_SEL 0x604 6362306a36Sopenharmony_ci#define PORT_CLOCK_SEL GENMASK(11, 8) 6462306a36Sopenharmony_ci#define REG_PORT_DATA_SEL 0x608 6562306a36Sopenharmony_ci#define PORT_DATA_SEL_TXA(cl) (1 << ((cl)*2)) 6662306a36Sopenharmony_ci#define PORT_DATA_SEL_TXB(cl) (2 << ((cl)*2)) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define REG_INTSTATE 0x700 6962306a36Sopenharmony_ci#define REG_INTMASK 0x704 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Bases of serdes units (relative to cluster) */ 7262306a36Sopenharmony_ci#define CLUSTER_RXA_OFF 0x200 7362306a36Sopenharmony_ci#define CLUSTER_TXA_OFF 0x300 7462306a36Sopenharmony_ci#define CLUSTER_RXB_OFF 0x400 7562306a36Sopenharmony_ci#define CLUSTER_TXB_OFF 0x500 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define CLUSTER_TX_OFF CLUSTER_TXA_OFF 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#ifndef USE_RXB_FOR_CAPTURE 8062306a36Sopenharmony_ci#define CLUSTER_RX_OFF CLUSTER_RXA_OFF 8162306a36Sopenharmony_ci#else 8262306a36Sopenharmony_ci#define CLUSTER_RX_OFF CLUSTER_RXB_OFF 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Relative to serdes unit base */ 8662306a36Sopenharmony_ci#define REG_SERDES_STATUS 0x00 8762306a36Sopenharmony_ci#define SERDES_STATUS_EN BIT(0) 8862306a36Sopenharmony_ci#define SERDES_STATUS_RST BIT(1) 8962306a36Sopenharmony_ci#define REG_TX_SERDES_CONF 0x04 9062306a36Sopenharmony_ci#define REG_RX_SERDES_CONF 0x08 9162306a36Sopenharmony_ci#define SERDES_CONF_NCHANS GENMASK(3, 0) 9262306a36Sopenharmony_ci#define SERDES_CONF_WIDTH_MASK GENMASK(8, 4) 9362306a36Sopenharmony_ci#define SERDES_CONF_WIDTH_16BIT 0x40 9462306a36Sopenharmony_ci#define SERDES_CONF_WIDTH_20BIT 0x80 9562306a36Sopenharmony_ci#define SERDES_CONF_WIDTH_24BIT 0xc0 9662306a36Sopenharmony_ci#define SERDES_CONF_WIDTH_32BIT 0x100 9762306a36Sopenharmony_ci#define SERDES_CONF_BCLK_POL 0x400 9862306a36Sopenharmony_ci#define SERDES_CONF_LSB_FIRST 0x800 9962306a36Sopenharmony_ci#define SERDES_CONF_UNK1 BIT(12) 10062306a36Sopenharmony_ci#define SERDES_CONF_UNK2 BIT(13) 10162306a36Sopenharmony_ci#define SERDES_CONF_UNK3 BIT(14) 10262306a36Sopenharmony_ci#define SERDES_CONF_NO_DATA_FEEDBACK BIT(15) 10362306a36Sopenharmony_ci#define SERDES_CONF_SYNC_SEL GENMASK(18, 16) 10462306a36Sopenharmony_ci#define REG_TX_SERDES_BITSTART 0x08 10562306a36Sopenharmony_ci#define REG_RX_SERDES_BITSTART 0x0c 10662306a36Sopenharmony_ci#define REG_TX_SERDES_SLOTMASK 0x0c 10762306a36Sopenharmony_ci#define REG_RX_SERDES_SLOTMASK 0x10 10862306a36Sopenharmony_ci#define REG_RX_SERDES_PORT 0x04 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* Relative to switch base */ 11162306a36Sopenharmony_ci#define REG_DMA_ADAPTER_A(cl) (0x8000 * (cl)) 11262306a36Sopenharmony_ci#define REG_DMA_ADAPTER_B(cl) (0x8000 * (cl) + 0x4000) 11362306a36Sopenharmony_ci#define DMA_ADAPTER_TX_LSB_PAD GENMASK(4, 0) 11462306a36Sopenharmony_ci#define DMA_ADAPTER_TX_NCHANS GENMASK(6, 5) 11562306a36Sopenharmony_ci#define DMA_ADAPTER_RX_MSB_PAD GENMASK(12, 8) 11662306a36Sopenharmony_ci#define DMA_ADAPTER_RX_NCHANS GENMASK(14, 13) 11762306a36Sopenharmony_ci#define DMA_ADAPTER_NCHANS GENMASK(22, 20) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define SWITCH_STRIDE 0x8000 12062306a36Sopenharmony_ci#define CLUSTER_STRIDE 0x4000 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define MAX_NCLUSTERS 6 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define APPLE_MCA_FMTBITS (SNDRV_PCM_FMTBIT_S16_LE | \ 12562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 12662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistruct mca_cluster { 12962306a36Sopenharmony_ci int no; 13062306a36Sopenharmony_ci __iomem void *base; 13162306a36Sopenharmony_ci struct mca_data *host; 13262306a36Sopenharmony_ci struct device *pd_dev; 13362306a36Sopenharmony_ci struct clk *clk_parent; 13462306a36Sopenharmony_ci struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci bool port_started[SNDRV_PCM_STREAM_LAST + 1]; 13762306a36Sopenharmony_ci int port_driver; /* The cluster driving this cluster's port */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; 14062306a36Sopenharmony_ci struct device_link *pd_link; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci unsigned int bclk_ratio; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Masks etc. picked up via the set_tdm_slot method */ 14562306a36Sopenharmony_ci int tdm_slots; 14662306a36Sopenharmony_ci int tdm_slot_width; 14762306a36Sopenharmony_ci unsigned int tdm_tx_mask; 14862306a36Sopenharmony_ci unsigned int tdm_rx_mask; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistruct mca_data { 15262306a36Sopenharmony_ci struct device *dev; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci __iomem void *switch_base; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci struct device *pd_dev; 15762306a36Sopenharmony_ci struct reset_control *rstc; 15862306a36Sopenharmony_ci struct device_link *pd_link; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Mutex for accessing port_driver of foreign clusters */ 16162306a36Sopenharmony_ci struct mutex port_mutex; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci int nclusters; 16462306a36Sopenharmony_ci struct mca_cluster clusters[]; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci __iomem void *ptr = cl->base + regoffset; 17062306a36Sopenharmony_ci u32 newval; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci newval = (val & mask) | (readl_relaxed(ptr) & ~mask); 17362306a36Sopenharmony_ci writel_relaxed(newval, ptr); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * Get the cluster of FE or BE DAI 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct mca_data *mca = snd_soc_dai_get_drvdata(dai); 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * FE DAIs are 0 ... nclusters - 1 18462306a36Sopenharmony_ci * BE DAIs are nclusters ... 2*nclusters - 1 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci int cluster_no = dai->id % mca->nclusters; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return &mca->clusters[cluster_no]; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* called before PCM trigger */ 19262306a36Sopenharmony_cistatic void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, 19362306a36Sopenharmony_ci struct snd_soc_dai *dai) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 19662306a36Sopenharmony_ci bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 19762306a36Sopenharmony_ci int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF; 19862306a36Sopenharmony_ci int serdes_conf = 19962306a36Sopenharmony_ci serdes_unit + (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci switch (cmd) { 20262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 20362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 20462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 20562306a36Sopenharmony_ci mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, 20662306a36Sopenharmony_ci FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); 20762306a36Sopenharmony_ci mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, 20862306a36Sopenharmony_ci FIELD_PREP(SERDES_CONF_SYNC_SEL, 7)); 20962306a36Sopenharmony_ci mca_modify(cl, serdes_unit + REG_SERDES_STATUS, 21062306a36Sopenharmony_ci SERDES_STATUS_EN | SERDES_STATUS_RST, 21162306a36Sopenharmony_ci SERDES_STATUS_RST); 21262306a36Sopenharmony_ci /* 21362306a36Sopenharmony_ci * Experiments suggest that it takes at most ~1 us 21462306a36Sopenharmony_ci * for the bit to clear, so wait 2 us for good measure. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci udelay(2); 21762306a36Sopenharmony_ci WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & 21862306a36Sopenharmony_ci SERDES_STATUS_RST); 21962306a36Sopenharmony_ci mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, 22062306a36Sopenharmony_ci FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); 22162306a36Sopenharmony_ci mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, 22262306a36Sopenharmony_ci FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci default: 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, 23062306a36Sopenharmony_ci struct snd_soc_dai *dai) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 23362306a36Sopenharmony_ci bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 23462306a36Sopenharmony_ci int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci switch (cmd) { 23762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 23862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 23962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 24062306a36Sopenharmony_ci mca_modify(cl, serdes_unit + REG_SERDES_STATUS, 24162306a36Sopenharmony_ci SERDES_STATUS_EN | SERDES_STATUS_RST, 24262306a36Sopenharmony_ci SERDES_STATUS_EN); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 24662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 24762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 24862306a36Sopenharmony_ci mca_modify(cl, serdes_unit + REG_SERDES_STATUS, 24962306a36Sopenharmony_ci SERDES_STATUS_EN, 0); 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci default: 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int mca_fe_enable_clocks(struct mca_cluster *cl) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct mca_data *mca = cl->host; 26262306a36Sopenharmony_ci int ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = clk_prepare_enable(cl->clk_parent); 26562306a36Sopenharmony_ci if (ret) { 26662306a36Sopenharmony_ci dev_err(mca->dev, 26762306a36Sopenharmony_ci "cluster %d: unable to enable clock parent: %d\n", 26862306a36Sopenharmony_ci cl->no, ret); 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * We can't power up the device earlier than this because 27462306a36Sopenharmony_ci * the power state driver would error out on seeing the device 27562306a36Sopenharmony_ci * as clock-gated. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci cl->pd_link = device_link_add(mca->dev, cl->pd_dev, 27862306a36Sopenharmony_ci DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | 27962306a36Sopenharmony_ci DL_FLAG_RPM_ACTIVE); 28062306a36Sopenharmony_ci if (!cl->pd_link) { 28162306a36Sopenharmony_ci dev_err(mca->dev, 28262306a36Sopenharmony_ci "cluster %d: unable to prop-up power domain\n", cl->no); 28362306a36Sopenharmony_ci clk_disable_unprepare(cl->clk_parent); 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci writel_relaxed(cl->no + 1, cl->base + REG_SYNCGEN_MCLK_SEL); 28862306a36Sopenharmony_ci mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 28962306a36Sopenharmony_ci SYNCGEN_STATUS_EN); 29062306a36Sopenharmony_ci mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, STATUS_MCLK_EN); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void mca_fe_disable_clocks(struct mca_cluster *cl) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); 29862306a36Sopenharmony_ci mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, 0); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci device_link_del(cl->pd_link); 30162306a36Sopenharmony_ci clk_disable_unprepare(cl->clk_parent); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic bool mca_fe_clocks_in_use(struct mca_cluster *cl) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct mca_data *mca = cl->host; 30762306a36Sopenharmony_ci struct mca_cluster *be_cl; 30862306a36Sopenharmony_ci int stream, i; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci mutex_lock(&mca->port_mutex); 31162306a36Sopenharmony_ci for (i = 0; i < mca->nclusters; i++) { 31262306a36Sopenharmony_ci be_cl = &mca->clusters[i]; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (be_cl->port_driver != cl->no) 31562306a36Sopenharmony_ci continue; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for_each_pcm_streams(stream) { 31862306a36Sopenharmony_ci if (be_cl->clocks_in_use[stream]) { 31962306a36Sopenharmony_ci mutex_unlock(&mca->port_mutex); 32062306a36Sopenharmony_ci return true; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci mutex_unlock(&mca->port_mutex); 32562306a36Sopenharmony_ci return false; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int mca_be_prepare(struct snd_pcm_substream *substream, 32962306a36Sopenharmony_ci struct snd_soc_dai *dai) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 33262306a36Sopenharmony_ci struct mca_data *mca = cl->host; 33362306a36Sopenharmony_ci struct mca_cluster *fe_cl; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (cl->port_driver < 0) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci fe_cl = &mca->clusters[cl->port_driver]; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * Typically the CODECs we are paired with will require clocks 34362306a36Sopenharmony_ci * to be present at time of unmute with the 'mute_stream' op 34462306a36Sopenharmony_ci * or at time of DAPM widget power-up. We need to enable clocks 34562306a36Sopenharmony_ci * here at the latest (frontend prepare would be too late). 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci if (!mca_fe_clocks_in_use(fe_cl)) { 34862306a36Sopenharmony_ci ret = mca_fe_enable_clocks(fe_cl); 34962306a36Sopenharmony_ci if (ret < 0) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci cl->clocks_in_use[substream->stream] = true; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int mca_be_hw_free(struct snd_pcm_substream *substream, 35962306a36Sopenharmony_ci struct snd_soc_dai *dai) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 36262306a36Sopenharmony_ci struct mca_data *mca = cl->host; 36362306a36Sopenharmony_ci struct mca_cluster *fe_cl; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (cl->port_driver < 0) 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * We are operating on a foreign cluster here, but since we 37062306a36Sopenharmony_ci * belong to the same PCM, accesses should have been 37162306a36Sopenharmony_ci * synchronized at ASoC level. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci fe_cl = &mca->clusters[cl->port_driver]; 37462306a36Sopenharmony_ci if (!mca_fe_clocks_in_use(fe_cl)) 37562306a36Sopenharmony_ci return 0; /* Nothing to do */ 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci cl->clocks_in_use[substream->stream] = false; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!mca_fe_clocks_in_use(fe_cl)) 38062306a36Sopenharmony_ci mca_fe_disable_clocks(fe_cl); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic unsigned int mca_crop_mask(unsigned int mask, int nchans) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci while (hweight32(mask) > nchans) 38862306a36Sopenharmony_ci mask &= ~(1 << __fls(mask)); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return mask; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, 39462306a36Sopenharmony_ci unsigned int mask, int slots, int nchans, 39562306a36Sopenharmony_ci int slot_width, bool is_tx, int port) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci __iomem void *serdes_base = cl->base + serdes_unit; 39862306a36Sopenharmony_ci u32 serdes_conf, serdes_conf_mask; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci serdes_conf_mask = SERDES_CONF_WIDTH_MASK | SERDES_CONF_NCHANS; 40162306a36Sopenharmony_ci serdes_conf = FIELD_PREP(SERDES_CONF_NCHANS, max(slots, 1) - 1); 40262306a36Sopenharmony_ci switch (slot_width) { 40362306a36Sopenharmony_ci case 16: 40462306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_WIDTH_16BIT; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci case 20: 40762306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_WIDTH_20BIT; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case 24: 41062306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_WIDTH_24BIT; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case 32: 41362306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_WIDTH_32BIT; 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci default: 41662306a36Sopenharmony_ci goto err; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci serdes_conf_mask |= SERDES_CONF_SYNC_SEL; 42062306a36Sopenharmony_ci serdes_conf |= FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (is_tx) { 42362306a36Sopenharmony_ci serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 | 42462306a36Sopenharmony_ci SERDES_CONF_UNK3; 42562306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 | 42662306a36Sopenharmony_ci SERDES_CONF_UNK3; 42762306a36Sopenharmony_ci } else { 42862306a36Sopenharmony_ci serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 | 42962306a36Sopenharmony_ci SERDES_CONF_UNK3 | 43062306a36Sopenharmony_ci SERDES_CONF_NO_DATA_FEEDBACK; 43162306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 | 43262306a36Sopenharmony_ci SERDES_CONF_NO_DATA_FEEDBACK; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mca_modify(cl, 43662306a36Sopenharmony_ci serdes_unit + 43762306a36Sopenharmony_ci (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF), 43862306a36Sopenharmony_ci serdes_conf_mask, serdes_conf); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (is_tx) { 44162306a36Sopenharmony_ci writel_relaxed(0xffffffff, 44262306a36Sopenharmony_ci serdes_base + REG_TX_SERDES_SLOTMASK); 44362306a36Sopenharmony_ci writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), 44462306a36Sopenharmony_ci serdes_base + REG_TX_SERDES_SLOTMASK + 0x4); 44562306a36Sopenharmony_ci writel_relaxed(0xffffffff, 44662306a36Sopenharmony_ci serdes_base + REG_TX_SERDES_SLOTMASK + 0x8); 44762306a36Sopenharmony_ci writel_relaxed(~((u32)mask), 44862306a36Sopenharmony_ci serdes_base + REG_TX_SERDES_SLOTMASK + 0xc); 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci writel_relaxed(0xffffffff, 45162306a36Sopenharmony_ci serdes_base + REG_RX_SERDES_SLOTMASK); 45262306a36Sopenharmony_ci writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), 45362306a36Sopenharmony_ci serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); 45462306a36Sopenharmony_ci writel_relaxed(1 << port, 45562306a36Sopenharmony_ci serdes_base + REG_RX_SERDES_PORT); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cierr: 46162306a36Sopenharmony_ci dev_err(cl->host->dev, 46262306a36Sopenharmony_ci "unsupported SERDES configuration requested (mask=0x%x slots=%d slot_width=%d)\n", 46362306a36Sopenharmony_ci mask, slots, slot_width); 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 46862306a36Sopenharmony_ci unsigned int rx_mask, int slots, int slot_width) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci cl->tdm_slots = slots; 47362306a36Sopenharmony_ci cl->tdm_slot_width = slot_width; 47462306a36Sopenharmony_ci cl->tdm_tx_mask = tx_mask; 47562306a36Sopenharmony_ci cl->tdm_rx_mask = rx_mask; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 48362306a36Sopenharmony_ci struct mca_data *mca = cl->host; 48462306a36Sopenharmony_ci bool fpol_inv = false; 48562306a36Sopenharmony_ci u32 serdes_conf = 0; 48662306a36Sopenharmony_ci u32 bitstart; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != 48962306a36Sopenharmony_ci SND_SOC_DAIFMT_BP_FP) 49062306a36Sopenharmony_ci goto err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 49362306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 49462306a36Sopenharmony_ci fpol_inv = 0; 49562306a36Sopenharmony_ci bitstart = 1; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 49862306a36Sopenharmony_ci fpol_inv = 1; 49962306a36Sopenharmony_ci bitstart = 0; 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci default: 50262306a36Sopenharmony_ci goto err; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 50662306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 50762306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 50862306a36Sopenharmony_ci fpol_inv ^= 1; 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 51362306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 51462306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 51562306a36Sopenharmony_ci serdes_conf |= SERDES_CONF_BCLK_POL; 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (!fpol_inv) 52062306a36Sopenharmony_ci goto err; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci mca_modify(cl, CLUSTER_TX_OFF + REG_TX_SERDES_CONF, 52362306a36Sopenharmony_ci SERDES_CONF_BCLK_POL, serdes_conf); 52462306a36Sopenharmony_ci mca_modify(cl, CLUSTER_RX_OFF + REG_RX_SERDES_CONF, 52562306a36Sopenharmony_ci SERDES_CONF_BCLK_POL, serdes_conf); 52662306a36Sopenharmony_ci writel_relaxed(bitstart, 52762306a36Sopenharmony_ci cl->base + CLUSTER_TX_OFF + REG_TX_SERDES_BITSTART); 52862306a36Sopenharmony_ci writel_relaxed(bitstart, 52962306a36Sopenharmony_ci cl->base + CLUSTER_RX_OFF + REG_RX_SERDES_BITSTART); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cierr: 53462306a36Sopenharmony_ci dev_err(mca->dev, "unsupported DAI format (0x%x) requested\n", fmt); 53562306a36Sopenharmony_ci return -EINVAL; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci cl->bclk_ratio = ratio; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int mca_fe_get_port(struct snd_pcm_substream *substream) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 55062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *be; 55162306a36Sopenharmony_ci struct snd_soc_dpcm *dpcm; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci be = NULL; 55462306a36Sopenharmony_ci for_each_dpcm_be(fe, substream->stream, dpcm) { 55562306a36Sopenharmony_ci be = dpcm->be; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (!be) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return mca_dai_to_cluster(asoc_rtd_to_cpu(be, 0))->no; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int mca_fe_hw_params(struct snd_pcm_substream *substream, 56662306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 56762306a36Sopenharmony_ci struct snd_soc_dai *dai) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 57062306a36Sopenharmony_ci struct mca_data *mca = cl->host; 57162306a36Sopenharmony_ci struct device *dev = mca->dev; 57262306a36Sopenharmony_ci unsigned int samp_rate = params_rate(params); 57362306a36Sopenharmony_ci bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 57462306a36Sopenharmony_ci bool refine_tdm = false; 57562306a36Sopenharmony_ci unsigned long bclk_ratio; 57662306a36Sopenharmony_ci unsigned int tdm_slots, tdm_slot_width, tdm_mask; 57762306a36Sopenharmony_ci u32 regval, pad; 57862306a36Sopenharmony_ci int ret, port, nchans_ceiled; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!cl->tdm_slot_width) { 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * We were not given TDM settings from above, set initial 58362306a36Sopenharmony_ci * guesses which will later be refined. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci tdm_slot_width = params_width(params); 58662306a36Sopenharmony_ci tdm_slots = params_channels(params); 58762306a36Sopenharmony_ci refine_tdm = true; 58862306a36Sopenharmony_ci } else { 58962306a36Sopenharmony_ci tdm_slot_width = cl->tdm_slot_width; 59062306a36Sopenharmony_ci tdm_slots = cl->tdm_slots; 59162306a36Sopenharmony_ci tdm_mask = is_tx ? cl->tdm_tx_mask : cl->tdm_rx_mask; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (cl->bclk_ratio) 59562306a36Sopenharmony_ci bclk_ratio = cl->bclk_ratio; 59662306a36Sopenharmony_ci else 59762306a36Sopenharmony_ci bclk_ratio = tdm_slot_width * tdm_slots; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (refine_tdm) { 60062306a36Sopenharmony_ci int nchannels = params_channels(params); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (nchannels > 2) { 60362306a36Sopenharmony_ci dev_err(dev, "missing TDM for stream with two or more channels\n"); 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if ((bclk_ratio % nchannels) != 0) { 60862306a36Sopenharmony_ci dev_err(dev, "BCLK ratio (%ld) not divisible by no. of channels (%d)\n", 60962306a36Sopenharmony_ci bclk_ratio, nchannels); 61062306a36Sopenharmony_ci return -EINVAL; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci tdm_slot_width = bclk_ratio / nchannels; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (tdm_slot_width > 32 && nchannels == 1) 61662306a36Sopenharmony_ci tdm_slot_width = 32; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (tdm_slot_width < params_width(params)) { 61962306a36Sopenharmony_ci dev_err(dev, "TDM slots too narrow (tdm=%d params=%d)\n", 62062306a36Sopenharmony_ci tdm_slot_width, params_width(params)); 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci tdm_mask = (1 << tdm_slots) - 1; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci port = mca_fe_get_port(substream); 62862306a36Sopenharmony_ci if (port < 0) 62962306a36Sopenharmony_ci return port; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, 63262306a36Sopenharmony_ci tdm_mask, tdm_slots, params_channels(params), 63362306a36Sopenharmony_ci tdm_slot_width, is_tx, port); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci pad = 32 - params_width(params); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * TODO: Here the register semantics aren't clear. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci nchans_ceiled = min_t(int, params_channels(params), 4); 64362306a36Sopenharmony_ci regval = FIELD_PREP(DMA_ADAPTER_NCHANS, nchans_ceiled) | 64462306a36Sopenharmony_ci FIELD_PREP(DMA_ADAPTER_TX_NCHANS, 0x2) | 64562306a36Sopenharmony_ci FIELD_PREP(DMA_ADAPTER_RX_NCHANS, 0x2) | 64662306a36Sopenharmony_ci FIELD_PREP(DMA_ADAPTER_TX_LSB_PAD, pad) | 64762306a36Sopenharmony_ci FIELD_PREP(DMA_ADAPTER_RX_MSB_PAD, pad); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci#ifndef USE_RXB_FOR_CAPTURE 65062306a36Sopenharmony_ci writel_relaxed(regval, mca->switch_base + REG_DMA_ADAPTER_A(cl->no)); 65162306a36Sopenharmony_ci#else 65262306a36Sopenharmony_ci if (is_tx) 65362306a36Sopenharmony_ci writel_relaxed(regval, 65462306a36Sopenharmony_ci mca->switch_base + REG_DMA_ADAPTER_A(cl->no)); 65562306a36Sopenharmony_ci else 65662306a36Sopenharmony_ci writel_relaxed(regval, 65762306a36Sopenharmony_ci mca->switch_base + REG_DMA_ADAPTER_B(cl->no)); 65862306a36Sopenharmony_ci#endif 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!mca_fe_clocks_in_use(cl)) { 66162306a36Sopenharmony_ci /* 66262306a36Sopenharmony_ci * Set up FSYNC duty cycle as even as possible. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci writel_relaxed((bclk_ratio / 2) - 1, 66562306a36Sopenharmony_ci cl->base + REG_SYNCGEN_HI_PERIOD); 66662306a36Sopenharmony_ci writel_relaxed(((bclk_ratio + 1) / 2) - 1, 66762306a36Sopenharmony_ci cl->base + REG_SYNCGEN_LO_PERIOD); 66862306a36Sopenharmony_ci writel_relaxed(FIELD_PREP(MCLK_CONF_DIV, 0x1), 66962306a36Sopenharmony_ci cl->base + REG_MCLK_CONF); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = clk_set_rate(cl->clk_parent, bclk_ratio * samp_rate); 67262306a36Sopenharmony_ci if (ret) { 67362306a36Sopenharmony_ci dev_err(mca->dev, "cluster %d: unable to set clock parent: %d\n", 67462306a36Sopenharmony_ci cl->no, ret); 67562306a36Sopenharmony_ci return ret; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mca_fe_ops = { 68362306a36Sopenharmony_ci .set_fmt = mca_fe_set_fmt, 68462306a36Sopenharmony_ci .set_bclk_ratio = mca_set_bclk_ratio, 68562306a36Sopenharmony_ci .set_tdm_slot = mca_fe_set_tdm_slot, 68662306a36Sopenharmony_ci .hw_params = mca_fe_hw_params, 68762306a36Sopenharmony_ci .trigger = mca_fe_trigger, 68862306a36Sopenharmony_ci}; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic bool mca_be_started(struct mca_cluster *cl) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci int stream; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci for_each_pcm_streams(stream) 69562306a36Sopenharmony_ci if (cl->port_started[stream]) 69662306a36Sopenharmony_ci return true; 69762306a36Sopenharmony_ci return false; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int mca_be_startup(struct snd_pcm_substream *substream, 70162306a36Sopenharmony_ci struct snd_soc_dai *dai) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *be = asoc_substream_to_rtd(substream); 70462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *fe; 70562306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 70662306a36Sopenharmony_ci struct mca_cluster *fe_cl; 70762306a36Sopenharmony_ci struct mca_data *mca = cl->host; 70862306a36Sopenharmony_ci struct snd_soc_dpcm *dpcm; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci fe = NULL; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci for_each_dpcm_fe(be, substream->stream, dpcm) { 71362306a36Sopenharmony_ci if (fe && dpcm->fe != fe) { 71462306a36Sopenharmony_ci dev_err(mca->dev, "many FE per one BE unsupported\n"); 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci fe = dpcm->fe; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (!fe) 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci fe_cl = mca_dai_to_cluster(asoc_rtd_to_cpu(fe, 0)); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (mca_be_started(cl)) { 72762306a36Sopenharmony_ci /* 72862306a36Sopenharmony_ci * Port is already started in the other direction. 72962306a36Sopenharmony_ci * Make sure there isn't a conflict with another cluster 73062306a36Sopenharmony_ci * driving the port. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci if (cl->port_driver != fe_cl->no) 73362306a36Sopenharmony_ci return -EINVAL; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci cl->port_started[substream->stream] = true; 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, 74062306a36Sopenharmony_ci cl->base + REG_PORT_ENABLES); 74162306a36Sopenharmony_ci writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), 74262306a36Sopenharmony_ci cl->base + REG_PORT_CLOCK_SEL); 74362306a36Sopenharmony_ci writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), 74462306a36Sopenharmony_ci cl->base + REG_PORT_DATA_SEL); 74562306a36Sopenharmony_ci mutex_lock(&mca->port_mutex); 74662306a36Sopenharmony_ci cl->port_driver = fe_cl->no; 74762306a36Sopenharmony_ci mutex_unlock(&mca->port_mutex); 74862306a36Sopenharmony_ci cl->port_started[substream->stream] = true; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic void mca_be_shutdown(struct snd_pcm_substream *substream, 75462306a36Sopenharmony_ci struct snd_soc_dai *dai) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(dai); 75762306a36Sopenharmony_ci struct mca_data *mca = cl->host; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci cl->port_started[substream->stream] = false; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (!mca_be_started(cl)) { 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * Were we the last direction to shutdown? 76462306a36Sopenharmony_ci * Turn off the lights. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci writel_relaxed(0, cl->base + REG_PORT_ENABLES); 76762306a36Sopenharmony_ci writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); 76862306a36Sopenharmony_ci mutex_lock(&mca->port_mutex); 76962306a36Sopenharmony_ci cl->port_driver = -1; 77062306a36Sopenharmony_ci mutex_unlock(&mca->port_mutex); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mca_be_ops = { 77562306a36Sopenharmony_ci .prepare = mca_be_prepare, 77662306a36Sopenharmony_ci .hw_free = mca_be_hw_free, 77762306a36Sopenharmony_ci .startup = mca_be_startup, 77862306a36Sopenharmony_ci .shutdown = mca_be_shutdown, 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int mca_set_runtime_hwparams(struct snd_soc_component *component, 78262306a36Sopenharmony_ci struct snd_pcm_substream *substream, 78362306a36Sopenharmony_ci struct dma_chan *chan) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct device *dma_dev = chan->device->dev; 78662306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_data = {}; 78762306a36Sopenharmony_ci int ret; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci struct snd_pcm_hardware hw; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci memset(&hw, 0, sizeof(hw)); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 79462306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED; 79562306a36Sopenharmony_ci hw.periods_min = 2; 79662306a36Sopenharmony_ci hw.periods_max = UINT_MAX; 79762306a36Sopenharmony_ci hw.period_bytes_min = 256; 79862306a36Sopenharmony_ci hw.period_bytes_max = dma_get_max_seg_size(dma_dev); 79962306a36Sopenharmony_ci hw.buffer_bytes_max = SIZE_MAX; 80062306a36Sopenharmony_ci hw.fifo_size = 16; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, &dma_data, 80362306a36Sopenharmony_ci &hw, chan); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (ret) 80662306a36Sopenharmony_ci return ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return snd_soc_set_runtime_hwparams(substream, &hw); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int mca_pcm_open(struct snd_soc_component *component, 81262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 81562306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0)); 81662306a36Sopenharmony_ci struct dma_chan *chan = cl->dma_chans[substream->stream]; 81762306a36Sopenharmony_ci int ret; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci ret = mca_set_runtime_hwparams(component, substream, chan); 82362306a36Sopenharmony_ci if (ret) 82462306a36Sopenharmony_ci return ret; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return snd_dmaengine_pcm_open(substream, chan); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int mca_hw_params(struct snd_soc_component *component, 83062306a36Sopenharmony_ci struct snd_pcm_substream *substream, 83162306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 83462306a36Sopenharmony_ci struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 83562306a36Sopenharmony_ci struct dma_slave_config slave_config; 83662306a36Sopenharmony_ci int ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci memset(&slave_config, 0, sizeof(slave_config)); 84262306a36Sopenharmony_ci ret = snd_hwparams_to_dma_slave_config(substream, params, 84362306a36Sopenharmony_ci &slave_config); 84462306a36Sopenharmony_ci if (ret < 0) 84562306a36Sopenharmony_ci return ret; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 84862306a36Sopenharmony_ci slave_config.dst_port_window_size = 84962306a36Sopenharmony_ci min_t(u32, params_channels(params), 4); 85062306a36Sopenharmony_ci else 85162306a36Sopenharmony_ci slave_config.src_port_window_size = 85262306a36Sopenharmony_ci min_t(u32, params_channels(params), 4); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return dmaengine_slave_config(chan, &slave_config); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int mca_close(struct snd_soc_component *component, 85862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return snd_dmaengine_pcm_close(substream); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int mca_trigger(struct snd_soc_component *component, 86962306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* 87762306a36Sopenharmony_ci * Before we do the PCM trigger proper, insert an opportunity 87862306a36Sopenharmony_ci * to reset the frontend's SERDES. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci mca_fe_early_trigger(substream, cmd, asoc_rtd_to_cpu(rtd, 0)); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return snd_dmaengine_pcm_trigger(substream, cmd); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic snd_pcm_uframes_t mca_pointer(struct snd_soc_component *component, 88662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 89162306a36Sopenharmony_ci return -ENOTSUPP; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return snd_dmaengine_pcm_pointer(substream); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic struct dma_chan *mca_request_dma_channel(struct mca_cluster *cl, unsigned int stream) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci bool is_tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); 89962306a36Sopenharmony_ci#ifndef USE_RXB_FOR_CAPTURE 90062306a36Sopenharmony_ci char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL, 90162306a36Sopenharmony_ci is_tx ? "tx%da" : "rx%da", cl->no); 90262306a36Sopenharmony_ci#else 90362306a36Sopenharmony_ci char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL, 90462306a36Sopenharmony_ci is_tx ? "tx%da" : "rx%db", cl->no); 90562306a36Sopenharmony_ci#endif 90662306a36Sopenharmony_ci return of_dma_request_slave_channel(cl->host->dev->of_node, name); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void mca_pcm_free(struct snd_soc_component *component, 91162306a36Sopenharmony_ci struct snd_pcm *pcm) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = snd_pcm_chip(pcm); 91462306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0)); 91562306a36Sopenharmony_ci unsigned int i; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 91862306a36Sopenharmony_ci return; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci for_each_pcm_streams(i) { 92162306a36Sopenharmony_ci struct snd_pcm_substream *substream = 92262306a36Sopenharmony_ci rtd->pcm->streams[i].substream; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!substream || !cl->dma_chans[i]) 92562306a36Sopenharmony_ci continue; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci dma_release_channel(cl->dma_chans[i]); 92862306a36Sopenharmony_ci cl->dma_chans[i] = NULL; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int mca_pcm_new(struct snd_soc_component *component, 93462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0)); 93762306a36Sopenharmony_ci unsigned int i; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci for_each_pcm_streams(i) { 94362306a36Sopenharmony_ci struct snd_pcm_substream *substream = 94462306a36Sopenharmony_ci rtd->pcm->streams[i].substream; 94562306a36Sopenharmony_ci struct dma_chan *chan; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (!substream) 94862306a36Sopenharmony_ci continue; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci chan = mca_request_dma_channel(cl, i); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(chan)) { 95362306a36Sopenharmony_ci mca_pcm_free(component, rtd->pcm); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (chan && PTR_ERR(chan) == -EPROBE_DEFER) 95662306a36Sopenharmony_ci return PTR_ERR(chan); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci dev_err(component->dev, "unable to obtain DMA channel (stream %d cluster %d): %pe\n", 95962306a36Sopenharmony_ci i, cl->no, chan); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (!chan) 96262306a36Sopenharmony_ci return -EINVAL; 96362306a36Sopenharmony_ci return PTR_ERR(chan); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci cl->dma_chans[i] = chan; 96762306a36Sopenharmony_ci snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_IRAM, 96862306a36Sopenharmony_ci chan->device->dev, 512 * 1024 * 6, 96962306a36Sopenharmony_ci SIZE_MAX); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic const struct snd_soc_component_driver mca_component = { 97662306a36Sopenharmony_ci .name = "apple-mca", 97762306a36Sopenharmony_ci .open = mca_pcm_open, 97862306a36Sopenharmony_ci .close = mca_close, 97962306a36Sopenharmony_ci .hw_params = mca_hw_params, 98062306a36Sopenharmony_ci .trigger = mca_trigger, 98162306a36Sopenharmony_ci .pointer = mca_pointer, 98262306a36Sopenharmony_ci .pcm_construct = mca_pcm_new, 98362306a36Sopenharmony_ci .pcm_destruct = mca_pcm_free, 98462306a36Sopenharmony_ci}; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic void apple_mca_release(struct mca_data *mca) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci int i; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci for (i = 0; i < mca->nclusters; i++) { 99162306a36Sopenharmony_ci struct mca_cluster *cl = &mca->clusters[i]; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(cl->clk_parent)) 99462306a36Sopenharmony_ci clk_put(cl->clk_parent); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(cl->pd_dev)) 99762306a36Sopenharmony_ci dev_pm_domain_detach(cl->pd_dev, true); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (mca->pd_link) 100162306a36Sopenharmony_ci device_link_del(mca->pd_link); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(mca->pd_dev)) 100462306a36Sopenharmony_ci dev_pm_domain_detach(mca->pd_dev, true); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci reset_control_rearm(mca->rstc); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic int apple_mca_probe(struct platform_device *pdev) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct mca_data *mca; 101262306a36Sopenharmony_ci struct mca_cluster *clusters; 101362306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drivers; 101462306a36Sopenharmony_ci struct resource *res; 101562306a36Sopenharmony_ci void __iomem *base; 101662306a36Sopenharmony_ci int nclusters; 101762306a36Sopenharmony_ci int ret, i; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 102062306a36Sopenharmony_ci if (IS_ERR(base)) 102162306a36Sopenharmony_ci return PTR_ERR(base); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (resource_size(res) < CLUSTER_STRIDE) 102462306a36Sopenharmony_ci return -EINVAL; 102562306a36Sopenharmony_ci nclusters = (resource_size(res) - CLUSTER_STRIDE) / CLUSTER_STRIDE + 1; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci mca = devm_kzalloc(&pdev->dev, struct_size(mca, clusters, nclusters), 102862306a36Sopenharmony_ci GFP_KERNEL); 102962306a36Sopenharmony_ci if (!mca) 103062306a36Sopenharmony_ci return -ENOMEM; 103162306a36Sopenharmony_ci mca->dev = &pdev->dev; 103262306a36Sopenharmony_ci mca->nclusters = nclusters; 103362306a36Sopenharmony_ci mutex_init(&mca->port_mutex); 103462306a36Sopenharmony_ci platform_set_drvdata(pdev, mca); 103562306a36Sopenharmony_ci clusters = mca->clusters; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci mca->switch_base = 103862306a36Sopenharmony_ci devm_platform_ioremap_resource(pdev, 1); 103962306a36Sopenharmony_ci if (IS_ERR(mca->switch_base)) 104062306a36Sopenharmony_ci return PTR_ERR(mca->switch_base); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci mca->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL); 104362306a36Sopenharmony_ci if (IS_ERR(mca->rstc)) 104462306a36Sopenharmony_ci return PTR_ERR(mca->rstc); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci dai_drivers = devm_kzalloc( 104762306a36Sopenharmony_ci &pdev->dev, sizeof(*dai_drivers) * 2 * nclusters, GFP_KERNEL); 104862306a36Sopenharmony_ci if (!dai_drivers) 104962306a36Sopenharmony_ci return -ENOMEM; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci mca->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, 0); 105262306a36Sopenharmony_ci if (IS_ERR(mca->pd_dev)) 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci mca->pd_link = device_link_add(&pdev->dev, mca->pd_dev, 105662306a36Sopenharmony_ci DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | 105762306a36Sopenharmony_ci DL_FLAG_RPM_ACTIVE); 105862306a36Sopenharmony_ci if (!mca->pd_link) { 105962306a36Sopenharmony_ci ret = -EINVAL; 106062306a36Sopenharmony_ci /* Prevent an unbalanced reset rearm */ 106162306a36Sopenharmony_ci mca->rstc = NULL; 106262306a36Sopenharmony_ci goto err_release; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci reset_control_reset(mca->rstc); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci for (i = 0; i < nclusters; i++) { 106862306a36Sopenharmony_ci struct mca_cluster *cl = &clusters[i]; 106962306a36Sopenharmony_ci struct snd_soc_dai_driver *fe = 107062306a36Sopenharmony_ci &dai_drivers[mca->nclusters + i]; 107162306a36Sopenharmony_ci struct snd_soc_dai_driver *be = &dai_drivers[i]; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci cl->host = mca; 107462306a36Sopenharmony_ci cl->no = i; 107562306a36Sopenharmony_ci cl->base = base + CLUSTER_STRIDE * i; 107662306a36Sopenharmony_ci cl->port_driver = -1; 107762306a36Sopenharmony_ci cl->clk_parent = of_clk_get(pdev->dev.of_node, i); 107862306a36Sopenharmony_ci if (IS_ERR(cl->clk_parent)) { 107962306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", 108062306a36Sopenharmony_ci i, PTR_ERR(cl->clk_parent)); 108162306a36Sopenharmony_ci ret = PTR_ERR(cl->clk_parent); 108262306a36Sopenharmony_ci goto err_release; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci cl->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i + 1); 108562306a36Sopenharmony_ci if (IS_ERR(cl->pd_dev)) { 108662306a36Sopenharmony_ci dev_err(&pdev->dev, 108762306a36Sopenharmony_ci "unable to obtain cluster %d PD: %ld\n", i, 108862306a36Sopenharmony_ci PTR_ERR(cl->pd_dev)); 108962306a36Sopenharmony_ci ret = PTR_ERR(cl->pd_dev); 109062306a36Sopenharmony_ci goto err_release; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci fe->id = i; 109462306a36Sopenharmony_ci fe->name = 109562306a36Sopenharmony_ci devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-pcm-%d", i); 109662306a36Sopenharmony_ci if (!fe->name) { 109762306a36Sopenharmony_ci ret = -ENOMEM; 109862306a36Sopenharmony_ci goto err_release; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci fe->ops = &mca_fe_ops; 110162306a36Sopenharmony_ci fe->playback.channels_min = 1; 110262306a36Sopenharmony_ci fe->playback.channels_max = 32; 110362306a36Sopenharmony_ci fe->playback.rates = SNDRV_PCM_RATE_8000_192000; 110462306a36Sopenharmony_ci fe->playback.formats = APPLE_MCA_FMTBITS; 110562306a36Sopenharmony_ci fe->capture.channels_min = 1; 110662306a36Sopenharmony_ci fe->capture.channels_max = 32; 110762306a36Sopenharmony_ci fe->capture.rates = SNDRV_PCM_RATE_8000_192000; 110862306a36Sopenharmony_ci fe->capture.formats = APPLE_MCA_FMTBITS; 110962306a36Sopenharmony_ci fe->symmetric_rate = 1; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci fe->playback.stream_name = 111262306a36Sopenharmony_ci devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d TX", i); 111362306a36Sopenharmony_ci fe->capture.stream_name = 111462306a36Sopenharmony_ci devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d RX", i); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (!fe->playback.stream_name || !fe->capture.stream_name) { 111762306a36Sopenharmony_ci ret = -ENOMEM; 111862306a36Sopenharmony_ci goto err_release; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci be->id = i + nclusters; 112262306a36Sopenharmony_ci be->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-i2s-%d", i); 112362306a36Sopenharmony_ci if (!be->name) { 112462306a36Sopenharmony_ci ret = -ENOMEM; 112562306a36Sopenharmony_ci goto err_release; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci be->ops = &mca_be_ops; 112862306a36Sopenharmony_ci be->playback.channels_min = 1; 112962306a36Sopenharmony_ci be->playback.channels_max = 32; 113062306a36Sopenharmony_ci be->playback.rates = SNDRV_PCM_RATE_8000_192000; 113162306a36Sopenharmony_ci be->playback.formats = APPLE_MCA_FMTBITS; 113262306a36Sopenharmony_ci be->capture.channels_min = 1; 113362306a36Sopenharmony_ci be->capture.channels_max = 32; 113462306a36Sopenharmony_ci be->capture.rates = SNDRV_PCM_RATE_8000_192000; 113562306a36Sopenharmony_ci be->capture.formats = APPLE_MCA_FMTBITS; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci be->playback.stream_name = 113862306a36Sopenharmony_ci devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d TX", i); 113962306a36Sopenharmony_ci be->capture.stream_name = 114062306a36Sopenharmony_ci devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d RX", i); 114162306a36Sopenharmony_ci if (!be->playback.stream_name || !be->capture.stream_name) { 114262306a36Sopenharmony_ci ret = -ENOMEM; 114362306a36Sopenharmony_ci goto err_release; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &mca_component, 114862306a36Sopenharmony_ci dai_drivers, nclusters * 2); 114962306a36Sopenharmony_ci if (ret) { 115062306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to register ASoC component: %d\n", 115162306a36Sopenharmony_ci ret); 115262306a36Sopenharmony_ci goto err_release; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return 0; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cierr_release: 115862306a36Sopenharmony_ci apple_mca_release(mca); 115962306a36Sopenharmony_ci return ret; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic void apple_mca_remove(struct platform_device *pdev) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct mca_data *mca = platform_get_drvdata(pdev); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 116762306a36Sopenharmony_ci apple_mca_release(mca); 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic const struct of_device_id apple_mca_of_match[] = { 117162306a36Sopenharmony_ci { .compatible = "apple,mca", }, 117262306a36Sopenharmony_ci {} 117362306a36Sopenharmony_ci}; 117462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, apple_mca_of_match); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic struct platform_driver apple_mca_driver = { 117762306a36Sopenharmony_ci .driver = { 117862306a36Sopenharmony_ci .name = "apple-mca", 117962306a36Sopenharmony_ci .of_match_table = apple_mca_of_match, 118062306a36Sopenharmony_ci }, 118162306a36Sopenharmony_ci .probe = apple_mca_probe, 118262306a36Sopenharmony_ci .remove_new = apple_mca_remove, 118362306a36Sopenharmony_ci}; 118462306a36Sopenharmony_cimodule_platform_driver(apple_mca_driver); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ciMODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>"); 118762306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC Apple MCA driver"); 118862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1189