// SPDX-License-Identifier: GPL-2.0-only
//
// Apple SoCs MCA driver
//
// Copyright (C) The Asahi Linux Contributors
//
// The MCA peripheral is made up of a number of identical units called clusters.
// Each cluster has its separate clock parent, SYNC signal generator, carries
// four SERDES units and has a dedicated I2S port on the SoC's periphery.
//
// The clusters can operate independently, or can be combined together in a
// configurable manner. We mostly treat them as self-contained independent
// units and don't configure any cross-cluster connections except for the I2S
// ports. The I2S ports can be routed to any of the clusters (irrespective
// of their native cluster). We map this onto ASoC's (DPCM) notion of backend
// and frontend DAIs. The 'cluster guts' are frontends which are dynamically
// routed to backend I2S ports.
//
// DAI references in devicetree are resolved to backends. The routing between
// frontends and backends is determined by the machine driver in the DAPM paths
// it supplies.
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#define USE_RXB_FOR_CAPTURE
/* Relative to cluster base */
#define REG_STATUS 0x0
#define STATUS_MCLK_EN BIT(0)
#define REG_MCLK_CONF 0x4
#define MCLK_CONF_DIV GENMASK(11, 8)
#define REG_SYNCGEN_STATUS 0x100
#define SYNCGEN_STATUS_EN BIT(0)
#define REG_SYNCGEN_MCLK_SEL 0x104
#define SYNCGEN_MCLK_SEL GENMASK(3, 0)
#define REG_SYNCGEN_HI_PERIOD 0x108
#define REG_SYNCGEN_LO_PERIOD 0x10c
#define REG_PORT_ENABLES 0x600
#define PORT_ENABLES_CLOCKS GENMASK(2, 1)
#define PORT_ENABLES_TX_DATA BIT(3)
#define REG_PORT_CLOCK_SEL 0x604
#define PORT_CLOCK_SEL GENMASK(11, 8)
#define REG_PORT_DATA_SEL 0x608
#define PORT_DATA_SEL_TXA(cl) (1 << ((cl)*2))
#define PORT_DATA_SEL_TXB(cl) (2 << ((cl)*2))
#define REG_INTSTATE 0x700
#define REG_INTMASK 0x704
/* Bases of serdes units (relative to cluster) */
#define CLUSTER_RXA_OFF 0x200
#define CLUSTER_TXA_OFF 0x300
#define CLUSTER_RXB_OFF 0x400
#define CLUSTER_TXB_OFF 0x500
#define CLUSTER_TX_OFF CLUSTER_TXA_OFF
#ifndef USE_RXB_FOR_CAPTURE
#define CLUSTER_RX_OFF CLUSTER_RXA_OFF
#else
#define CLUSTER_RX_OFF CLUSTER_RXB_OFF
#endif
/* Relative to serdes unit base */
#define REG_SERDES_STATUS 0x00
#define SERDES_STATUS_EN BIT(0)
#define SERDES_STATUS_RST BIT(1)
#define REG_TX_SERDES_CONF 0x04
#define REG_RX_SERDES_CONF 0x08
#define SERDES_CONF_NCHANS GENMASK(3, 0)
#define SERDES_CONF_WIDTH_MASK GENMASK(8, 4)
#define SERDES_CONF_WIDTH_16BIT 0x40
#define SERDES_CONF_WIDTH_20BIT 0x80
#define SERDES_CONF_WIDTH_24BIT 0xc0
#define SERDES_CONF_WIDTH_32BIT 0x100
#define SERDES_CONF_BCLK_POL 0x400
#define SERDES_CONF_LSB_FIRST 0x800
#define SERDES_CONF_UNK1 BIT(12)
#define SERDES_CONF_UNK2 BIT(13)
#define SERDES_CONF_UNK3 BIT(14)
#define SERDES_CONF_NO_DATA_FEEDBACK BIT(15)
#define SERDES_CONF_SYNC_SEL GENMASK(18, 16)
#define REG_TX_SERDES_BITSTART 0x08
#define REG_RX_SERDES_BITSTART 0x0c
#define REG_TX_SERDES_SLOTMASK 0x0c
#define REG_RX_SERDES_SLOTMASK 0x10
#define REG_RX_SERDES_PORT 0x04
/* Relative to switch base */
#define REG_DMA_ADAPTER_A(cl) (0x8000 * (cl))
#define REG_DMA_ADAPTER_B(cl) (0x8000 * (cl) + 0x4000)
#define DMA_ADAPTER_TX_LSB_PAD GENMASK(4, 0)
#define DMA_ADAPTER_TX_NCHANS GENMASK(6, 5)
#define DMA_ADAPTER_RX_MSB_PAD GENMASK(12, 8)
#define DMA_ADAPTER_RX_NCHANS GENMASK(14, 13)
#define DMA_ADAPTER_NCHANS GENMASK(22, 20)
#define SWITCH_STRIDE 0x8000
#define CLUSTER_STRIDE 0x4000
#define MAX_NCLUSTERS 6
#define APPLE_MCA_FMTBITS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
struct mca_cluster {
int no;
__iomem void *base;
struct mca_data *host;
struct device *pd_dev;
struct clk *clk_parent;
struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];
bool port_started[SNDRV_PCM_STREAM_LAST + 1];
int port_driver; /* The cluster driving this cluster's port */
bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
struct device_link *pd_link;
unsigned int bclk_ratio;
/* Masks etc. picked up via the set_tdm_slot method */
int tdm_slots;
int tdm_slot_width;
unsigned int tdm_tx_mask;
unsigned int tdm_rx_mask;
};
struct mca_data {
struct device *dev;
__iomem void *switch_base;
struct device *pd_dev;
struct reset_control *rstc;
struct device_link *pd_link;
/* Mutex for accessing port_driver of foreign clusters */
struct mutex port_mutex;
int nclusters;
struct mca_cluster clusters[] __counted_by(nclusters);
};
static void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val)
{
__iomem void *ptr = cl->base + regoffset;
u32 newval;
newval = (val & mask) | (readl_relaxed(ptr) & ~mask);
writel_relaxed(newval, ptr);
}
/*
* Get the cluster of FE or BE DAI
*/
static struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai)
{
struct mca_data *mca = snd_soc_dai_get_drvdata(dai);
/*
* FE DAIs are 0 ... nclusters - 1
* BE DAIs are nclusters ... 2*nclusters - 1
*/
int cluster_no = dai->id % mca->nclusters;
return &mca->clusters[cluster_no];
}
/* called before PCM trigger */
static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct mca_cluster *cl = mca_dai_to_cluster(dai);
bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
int serdes_conf =
serdes_unit + (is_tx ? REG_TX_SERDES_CONF :