18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale MPC5200 PSC in I2S mode 48c2ecf20Sopenharmony_ci// ALSA SoC Digital Audio Interface (DAI) driver 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (C) 2008 Secret Lab Technologies Ltd. 78c2ecf20Sopenharmony_ci// Copyright (C) 2009 Jon Smirl, Digispeaker 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_device.h> 118c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <sound/pcm.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 158c2ecf20Sopenharmony_ci#include <sound/soc.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/mpc52xx_psc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "mpc5200_dma.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/** 228c2ecf20Sopenharmony_ci * PSC_I2S_RATES: sample rates supported by the I2S 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This driver currently only supports the PSC running in I2S slave mode, 258c2ecf20Sopenharmony_ci * which means the codec determines the sample rate. Therefore, we tell 268c2ecf20Sopenharmony_ci * ALSA that we support all rates and let the codec driver decide what rates 278c2ecf20Sopenharmony_ci * are really supported. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ 358c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int psc_i2s_hw_params(struct snd_pcm_substream *substream, 388c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 398c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 428c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 438c2ecf20Sopenharmony_ci u32 mode; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" 468c2ecf20Sopenharmony_ci " periods=%i buffer_size=%i buffer_bytes=%i\n", 478c2ecf20Sopenharmony_ci __func__, substream, params_period_size(params), 488c2ecf20Sopenharmony_ci params_period_bytes(params), params_periods(params), 498c2ecf20Sopenharmony_ci params_buffer_size(params), params_buffer_bytes(params)); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci switch (params_format(params)) { 528c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 538c2ecf20Sopenharmony_ci mode = MPC52xx_PSC_SICR_SIM_CODEC_8; 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 568c2ecf20Sopenharmony_ci mode = MPC52xx_PSC_SICR_SIM_CODEC_16; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_BE: 598c2ecf20Sopenharmony_ci mode = MPC52xx_PSC_SICR_SIM_CODEC_24; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_BE: 628c2ecf20Sopenharmony_ci mode = MPC52xx_PSC_SICR_SIM_CODEC_32; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci default: 658c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "invalid format\n"); 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/** 748c2ecf20Sopenharmony_ci * psc_i2s_set_sysclk: set the clock frequency and direction 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * This function is called by the machine driver to tell us what the clock 778c2ecf20Sopenharmony_ci * frequency and direction are. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), 808c2ecf20Sopenharmony_ci * and we don't care about the frequency. Return an error if the direction 818c2ecf20Sopenharmony_ci * is not SND_SOC_CLOCK_IN. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * @clk_id: reserved, should be zero 848c2ecf20Sopenharmony_ci * @freq: the frequency of the given clock ID, currently ignored 858c2ecf20Sopenharmony_ci * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistatic int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, 888c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); 918c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", 928c2ecf20Sopenharmony_ci cpu_dai, dir); 938c2ecf20Sopenharmony_ci return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * psc_i2s_set_fmt: set the serial format. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * This function is called by the machine driver to tell us what serial 1008c2ecf20Sopenharmony_ci * format to use. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * This driver only supports I2S mode. Return an error if the format is 1038c2ecf20Sopenharmony_ci * not SND_SOC_DAIFMT_I2S. 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * @format: one of SND_SOC_DAIFMT_xxx 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); 1108c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", 1118c2ecf20Sopenharmony_ci cpu_dai, format); 1128c2ecf20Sopenharmony_ci return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 1168c2ecf20Sopenharmony_ci * ALSA SoC Bindings 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * - Digital Audio Interface (DAI) template 1198c2ecf20Sopenharmony_ci * - create/destroy dai hooks 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/** 1238c2ecf20Sopenharmony_ci * psc_i2s_dai_template: template CPU Digital Audio Interface 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops psc_i2s_dai_ops = { 1268c2ecf20Sopenharmony_ci .hw_params = psc_i2s_hw_params, 1278c2ecf20Sopenharmony_ci .set_sysclk = psc_i2s_set_sysclk, 1288c2ecf20Sopenharmony_ci .set_fmt = psc_i2s_set_fmt, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver psc_i2s_dai[] = {{ 1328c2ecf20Sopenharmony_ci .name = "mpc5200-psc-i2s.0", 1338c2ecf20Sopenharmony_ci .playback = { 1348c2ecf20Sopenharmony_ci .stream_name = "I2S Playback", 1358c2ecf20Sopenharmony_ci .channels_min = 2, 1368c2ecf20Sopenharmony_ci .channels_max = 2, 1378c2ecf20Sopenharmony_ci .rates = PSC_I2S_RATES, 1388c2ecf20Sopenharmony_ci .formats = PSC_I2S_FORMATS, 1398c2ecf20Sopenharmony_ci }, 1408c2ecf20Sopenharmony_ci .capture = { 1418c2ecf20Sopenharmony_ci .stream_name = "I2S Capture", 1428c2ecf20Sopenharmony_ci .channels_min = 2, 1438c2ecf20Sopenharmony_ci .channels_max = 2, 1448c2ecf20Sopenharmony_ci .rates = PSC_I2S_RATES, 1458c2ecf20Sopenharmony_ci .formats = PSC_I2S_FORMATS, 1468c2ecf20Sopenharmony_ci }, 1478c2ecf20Sopenharmony_ci .ops = &psc_i2s_dai_ops, 1488c2ecf20Sopenharmony_ci} }; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver psc_i2s_component = { 1518c2ecf20Sopenharmony_ci .name = "mpc5200-i2s", 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 1558c2ecf20Sopenharmony_ci * OF platform bus binding code: 1568c2ecf20Sopenharmony_ci * - Probe/remove operations 1578c2ecf20Sopenharmony_ci * - OF device match table 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic int psc_i2s_of_probe(struct platform_device *op) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int rc; 1628c2ecf20Sopenharmony_ci struct psc_dma *psc_dma; 1638c2ecf20Sopenharmony_ci struct mpc52xx_psc __iomem *regs; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci rc = mpc5200_audio_dma_create(op); 1668c2ecf20Sopenharmony_ci if (rc != 0) 1678c2ecf20Sopenharmony_ci return rc; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rc = snd_soc_register_component(&op->dev, &psc_i2s_component, 1708c2ecf20Sopenharmony_ci psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); 1718c2ecf20Sopenharmony_ci if (rc != 0) { 1728c2ecf20Sopenharmony_ci pr_err("Failed to register DAI\n"); 1738c2ecf20Sopenharmony_ci return rc; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci psc_dma = dev_get_drvdata(&op->dev); 1778c2ecf20Sopenharmony_ci regs = psc_dma->psc_regs; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Configure the serial interface mode; defaulting to CODEC8 mode */ 1808c2ecf20Sopenharmony_ci psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | 1818c2ecf20Sopenharmony_ci MPC52xx_PSC_SICR_CLKPOL; 1828c2ecf20Sopenharmony_ci out_be32(&psc_dma->psc_regs->sicr, 1838c2ecf20Sopenharmony_ci psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Check for the codec handle. If it is not present then we 1868c2ecf20Sopenharmony_ci * are done */ 1878c2ecf20Sopenharmony_ci if (!of_get_property(op->dev.of_node, "codec-handle", NULL)) 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Due to errata in the dma mode; need to line up enabling 1918c2ecf20Sopenharmony_ci * the transmitter with a transition on the frame sync 1928c2ecf20Sopenharmony_ci * line */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* first make sure it is low */ 1958c2ecf20Sopenharmony_ci while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) 1968c2ecf20Sopenharmony_ci ; 1978c2ecf20Sopenharmony_ci /* then wait for the transition to high */ 1988c2ecf20Sopenharmony_ci while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) 1998c2ecf20Sopenharmony_ci ; 2008c2ecf20Sopenharmony_ci /* Finally, enable the PSC. 2018c2ecf20Sopenharmony_ci * Receiver must always be enabled; even when we only want 2028c2ecf20Sopenharmony_ci * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Go */ 2058c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, 2068c2ecf20Sopenharmony_ci MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int psc_i2s_of_remove(struct platform_device *op) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci mpc5200_audio_dma_destroy(op); 2158c2ecf20Sopenharmony_ci snd_soc_unregister_component(&op->dev); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Match table for of_platform binding */ 2208c2ecf20Sopenharmony_cistatic const struct of_device_id psc_i2s_match[] = { 2218c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200-psc-i2s", }, 2228c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200b-psc-i2s", }, 2238c2ecf20Sopenharmony_ci {} 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, psc_i2s_match); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic struct platform_driver psc_i2s_driver = { 2288c2ecf20Sopenharmony_ci .probe = psc_i2s_of_probe, 2298c2ecf20Sopenharmony_ci .remove = psc_i2s_of_remove, 2308c2ecf20Sopenharmony_ci .driver = { 2318c2ecf20Sopenharmony_ci .name = "mpc5200-psc-i2s", 2328c2ecf20Sopenharmony_ci .of_match_table = psc_i2s_match, 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cimodule_platform_driver(psc_i2s_driver); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); 2398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); 2408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2418c2ecf20Sopenharmony_ci 242