cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mpc5200_psc_i2s.c (6768B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Freescale MPC5200 PSC in I2S mode
      4// ALSA SoC Digital Audio Interface (DAI) driver
      5//
      6// Copyright (C) 2008 Secret Lab Technologies Ltd.
      7// Copyright (C) 2009 Jon Smirl, Digispeaker
      8
      9#include <linux/module.h>
     10#include <linux/of_device.h>
     11#include <linux/of_platform.h>
     12
     13#include <sound/pcm.h>
     14#include <sound/pcm_params.h>
     15#include <sound/soc.h>
     16
     17#include <asm/mpc52xx_psc.h>
     18
     19#include "mpc5200_dma.h"
     20
     21/**
     22 * PSC_I2S_RATES: sample rates supported by the I2S
     23 *
     24 * This driver currently only supports the PSC running in I2S slave mode,
     25 * which means the codec determines the sample rate.  Therefore, we tell
     26 * ALSA that we support all rates and let the codec driver decide what rates
     27 * are really supported.
     28 */
     29#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
     30
     31/**
     32 * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
     33 */
     34#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
     35			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
     36
     37static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
     38				 struct snd_pcm_hw_params *params,
     39				 struct snd_soc_dai *dai)
     40{
     41	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     42	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
     43	u32 mode;
     44
     45	dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
     46		" periods=%i buffer_size=%i  buffer_bytes=%i\n",
     47		__func__, substream, params_period_size(params),
     48		params_period_bytes(params), params_periods(params),
     49		params_buffer_size(params), params_buffer_bytes(params));
     50
     51	switch (params_format(params)) {
     52	case SNDRV_PCM_FORMAT_S8:
     53		mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
     54		break;
     55	case SNDRV_PCM_FORMAT_S16_BE:
     56		mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
     57		break;
     58	case SNDRV_PCM_FORMAT_S24_BE:
     59		mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
     60		break;
     61	case SNDRV_PCM_FORMAT_S32_BE:
     62		mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
     63		break;
     64	default:
     65		dev_dbg(psc_dma->dev, "invalid format\n");
     66		return -EINVAL;
     67	}
     68	out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
     69
     70	return 0;
     71}
     72
     73/**
     74 * psc_i2s_set_sysclk: set the clock frequency and direction
     75 *
     76 * This function is called by the machine driver to tell us what the clock
     77 * frequency and direction are.
     78 *
     79 * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
     80 * and we don't care about the frequency.  Return an error if the direction
     81 * is not SND_SOC_CLOCK_IN.
     82 *
     83 * @clk_id: reserved, should be zero
     84 * @freq: the frequency of the given clock ID, currently ignored
     85 * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
     86 */
     87static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
     88			      int clk_id, unsigned int freq, int dir)
     89{
     90	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
     91	dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
     92				cpu_dai, dir);
     93	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
     94}
     95
     96/**
     97 * psc_i2s_set_fmt: set the serial format.
     98 *
     99 * This function is called by the machine driver to tell us what serial
    100 * format to use.
    101 *
    102 * This driver only supports I2S mode.  Return an error if the format is
    103 * not SND_SOC_DAIFMT_I2S.
    104 *
    105 * @format: one of SND_SOC_DAIFMT_xxx
    106 */
    107static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
    108{
    109	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
    110	dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
    111				cpu_dai, format);
    112	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
    113}
    114
    115/* ---------------------------------------------------------------------
    116 * ALSA SoC Bindings
    117 *
    118 * - Digital Audio Interface (DAI) template
    119 * - create/destroy dai hooks
    120 */
    121
    122/**
    123 * psc_i2s_dai_template: template CPU Digital Audio Interface
    124 */
    125static const struct snd_soc_dai_ops psc_i2s_dai_ops = {
    126	.hw_params	= psc_i2s_hw_params,
    127	.set_sysclk	= psc_i2s_set_sysclk,
    128	.set_fmt	= psc_i2s_set_fmt,
    129};
    130
    131static struct snd_soc_dai_driver psc_i2s_dai[] = {{
    132	.name = "mpc5200-psc-i2s.0",
    133	.playback = {
    134		.stream_name = "I2S Playback",
    135		.channels_min = 2,
    136		.channels_max = 2,
    137		.rates = PSC_I2S_RATES,
    138		.formats = PSC_I2S_FORMATS,
    139	},
    140	.capture = {
    141		.stream_name = "I2S Capture",
    142		.channels_min = 2,
    143		.channels_max = 2,
    144		.rates = PSC_I2S_RATES,
    145		.formats = PSC_I2S_FORMATS,
    146	},
    147	.ops = &psc_i2s_dai_ops,
    148} };
    149
    150static const struct snd_soc_component_driver psc_i2s_component = {
    151	.name		= "mpc5200-i2s",
    152};
    153
    154/* ---------------------------------------------------------------------
    155 * OF platform bus binding code:
    156 * - Probe/remove operations
    157 * - OF device match table
    158 */
    159static int psc_i2s_of_probe(struct platform_device *op)
    160{
    161	int rc;
    162	struct psc_dma *psc_dma;
    163	struct mpc52xx_psc __iomem *regs;
    164
    165	rc = mpc5200_audio_dma_create(op);
    166	if (rc != 0)
    167		return rc;
    168
    169	rc = snd_soc_register_component(&op->dev, &psc_i2s_component,
    170					psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
    171	if (rc != 0) {
    172		pr_err("Failed to register DAI\n");
    173		return rc;
    174	}
    175
    176	psc_dma = dev_get_drvdata(&op->dev);
    177	regs = psc_dma->psc_regs;
    178
    179	/* Configure the serial interface mode; defaulting to CODEC8 mode */
    180	psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
    181			MPC52xx_PSC_SICR_CLKPOL;
    182	out_be32(&psc_dma->psc_regs->sicr,
    183		 psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
    184
    185	/* Check for the codec handle.  If it is not present then we
    186	 * are done */
    187	if (!of_get_property(op->dev.of_node, "codec-handle", NULL))
    188		return 0;
    189
    190	/* Due to errata in the dma mode; need to line up enabling
    191	 * the transmitter with a transition on the frame sync
    192	 * line */
    193
    194	/* first make sure it is low */
    195	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
    196		;
    197	/* then wait for the transition to high */
    198	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
    199		;
    200	/* Finally, enable the PSC.
    201	 * Receiver must always be enabled; even when we only want
    202	 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
    203
    204	/* Go */
    205	out_8(&psc_dma->psc_regs->command,
    206			MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
    207
    208	return 0;
    209
    210}
    211
    212static int psc_i2s_of_remove(struct platform_device *op)
    213{
    214	mpc5200_audio_dma_destroy(op);
    215	snd_soc_unregister_component(&op->dev);
    216	return 0;
    217}
    218
    219/* Match table for of_platform binding */
    220static const struct of_device_id psc_i2s_match[] = {
    221	{ .compatible = "fsl,mpc5200-psc-i2s", },
    222	{ .compatible = "fsl,mpc5200b-psc-i2s", },
    223	{}
    224};
    225MODULE_DEVICE_TABLE(of, psc_i2s_match);
    226
    227static struct platform_driver psc_i2s_driver = {
    228	.probe = psc_i2s_of_probe,
    229	.remove = psc_i2s_of_remove,
    230	.driver = {
    231		.name = "mpc5200-psc-i2s",
    232		.of_match_table = psc_i2s_match,
    233	},
    234};
    235
    236module_platform_driver(psc_i2s_driver);
    237
    238MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
    239MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
    240MODULE_LICENSE("GPL");
    241