162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/of_device.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/clk-provider.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/time.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/pcm.h> 2062306a36Sopenharmony_ci#include <sound/pcm_params.h> 2162306a36Sopenharmony_ci#include <sound/soc.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "mxs-saif.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MXS_SET_ADDR 0x4 2662306a36Sopenharmony_ci#define MXS_CLR_ADDR 0x8 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct mxs_saif *mxs_saif[2]; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * SAIF is a little different with other normal SOC DAIs on clock using. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * For MXS, two SAIF modules are instantiated on-chip. 3462306a36Sopenharmony_ci * Each SAIF has a set of clock pins and can be operating in master 3562306a36Sopenharmony_ci * mode simultaneously if they are connected to different off-chip codecs. 3662306a36Sopenharmony_ci * Also, one of the two SAIFs can master or drive the clock pins while the 3762306a36Sopenharmony_ci * other SAIF, in slave mode, receives clocking from the master SAIF. 3862306a36Sopenharmony_ci * This also means that both SAIFs must operate at the same sample rate. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * We abstract this as each saif has a master, the master could be 4162306a36Sopenharmony_ci * itself or other saifs. In the generic saif driver, saif does not need 4262306a36Sopenharmony_ci * to know the different clkmux. Saif only needs to know who is its master 4362306a36Sopenharmony_ci * and operating its master to generate the proper clock rate for it. 4462306a36Sopenharmony_ci * The master id is provided in mach-specific layer according to different 4562306a36Sopenharmony_ci * clkmux setting. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 4962306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci switch (clk_id) { 5462306a36Sopenharmony_ci case MXS_SAIF_MCLK: 5562306a36Sopenharmony_ci saif->mclk = freq; 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci default: 5862306a36Sopenharmony_ci return -EINVAL; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK 6562306a36Sopenharmony_ci * is provided by other SAIF, we provide a interface here to get its master 6662306a36Sopenharmony_ci * from its master_id. 6762306a36Sopenharmony_ci * Note that the master could be itself. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return mxs_saif[saif->master_id]; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Set SAIF clock and MCLK 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int mxs_saif_set_clk(struct mxs_saif *saif, 7862306a36Sopenharmony_ci unsigned int mclk, 7962306a36Sopenharmony_ci unsigned int rate) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 scr; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci struct mxs_saif *master_saif; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci dev_dbg(saif->dev, "mclk %d rate %d\n", mclk, rate); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Set master saif to generate proper clock */ 8862306a36Sopenharmony_ci master_saif = mxs_saif_get_master(saif); 8962306a36Sopenharmony_ci if (!master_saif) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci dev_dbg(saif->dev, "master saif%d\n", master_saif->id); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Checking if can playback and capture simutaneously */ 9562306a36Sopenharmony_ci if (master_saif->ongoing && rate != master_saif->cur_rate) { 9662306a36Sopenharmony_ci dev_err(saif->dev, 9762306a36Sopenharmony_ci "can not change clock, master saif%d(rate %d) is ongoing\n", 9862306a36Sopenharmony_ci master_saif->id, master_saif->cur_rate); 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci scr = __raw_readl(master_saif->base + SAIF_CTRL); 10362306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; 10462306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Set SAIF clock 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * The SAIF clock should be either 384*fs or 512*fs. 11062306a36Sopenharmony_ci * If MCLK is used, the SAIF clk ratio needs to match mclk ratio. 11162306a36Sopenharmony_ci * For 256x, 128x, 64x, and 32x sub-rates, set saif clk as 512*fs. 11262306a36Sopenharmony_ci * For 192x, 96x, and 48x sub-rates, set saif clk as 384*fs. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * If MCLK is not used, we just set saif clk to 512*fs. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci ret = clk_prepare_enable(master_saif->clk); 11762306a36Sopenharmony_ci if (ret) 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (master_saif->mclk_in_use) { 12162306a36Sopenharmony_ci switch (mclk / rate) { 12262306a36Sopenharmony_ci case 32: 12362306a36Sopenharmony_ci case 64: 12462306a36Sopenharmony_ci case 128: 12562306a36Sopenharmony_ci case 256: 12662306a36Sopenharmony_ci case 512: 12762306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; 12862306a36Sopenharmony_ci ret = clk_set_rate(master_saif->clk, 512 * rate); 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci case 48: 13162306a36Sopenharmony_ci case 96: 13262306a36Sopenharmony_ci case 192: 13362306a36Sopenharmony_ci case 384: 13462306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; 13562306a36Sopenharmony_ci ret = clk_set_rate(master_saif->clk, 384 * rate); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci /* SAIF MCLK should be a sub-rate of 512x or 384x */ 13962306a36Sopenharmony_ci clk_disable_unprepare(master_saif->clk); 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci ret = clk_set_rate(master_saif->clk, 512 * rate); 14462306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci clk_disable_unprepare(master_saif->clk); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci master_saif->cur_rate = rate; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!master_saif->mclk_in_use) { 15562306a36Sopenharmony_ci __raw_writel(scr, master_saif->base + SAIF_CTRL); 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Program the over-sample rate for MCLK output 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * The available MCLK range is 32x, 48x... 512x. The rate 16362306a36Sopenharmony_ci * could be from 8kHz to 192kH. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci switch (mclk / rate) { 16662306a36Sopenharmony_ci case 32: 16762306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case 64: 17062306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case 128: 17362306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci case 256: 17662306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case 512: 17962306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case 48: 18262306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case 96: 18562306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case 192: 18862306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case 384: 19162306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci default: 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci __raw_writel(scr, master_saif->base + SAIF_CTRL); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Put and disable MCLK. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ciint mxs_saif_put_mclk(unsigned int saif_id) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct mxs_saif *saif = mxs_saif[saif_id]; 20862306a36Sopenharmony_ci u32 stat; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!saif) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci stat = __raw_readl(saif->base + SAIF_STAT); 21462306a36Sopenharmony_ci if (stat & BM_SAIF_STAT_BUSY) { 21562306a36Sopenharmony_ci dev_err(saif->dev, "error: busy\n"); 21662306a36Sopenharmony_ci return -EBUSY; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci clk_disable_unprepare(saif->clk); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* disable MCLK output */ 22262306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_CLKGATE, 22362306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_SET_ADDR); 22462306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 22562306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci saif->mclk_in_use = 0; 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mxs_saif_put_mclk); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Get MCLK and set clock rate, then enable it 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * This interface is used for codecs who are using MCLK provided 23662306a36Sopenharmony_ci * by saif. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ciint mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, 23962306a36Sopenharmony_ci unsigned int rate) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct mxs_saif *saif = mxs_saif[saif_id]; 24262306a36Sopenharmony_ci u32 stat; 24362306a36Sopenharmony_ci int ret; 24462306a36Sopenharmony_ci struct mxs_saif *master_saif; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!saif) 24762306a36Sopenharmony_ci return -EINVAL; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Clear Reset */ 25062306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_SFTRST, 25162306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* FIXME: need clear clk gate for register r/w */ 25462306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_CLKGATE, 25562306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci master_saif = mxs_saif_get_master(saif); 25862306a36Sopenharmony_ci if (saif != master_saif) { 25962306a36Sopenharmony_ci dev_err(saif->dev, "can not get mclk from a non-master saif\n"); 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci stat = __raw_readl(saif->base + SAIF_STAT); 26462306a36Sopenharmony_ci if (stat & BM_SAIF_STAT_BUSY) { 26562306a36Sopenharmony_ci dev_err(saif->dev, "error: busy\n"); 26662306a36Sopenharmony_ci return -EBUSY; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci saif->mclk_in_use = 1; 27062306a36Sopenharmony_ci ret = mxs_saif_set_clk(saif, mclk, rate); 27162306a36Sopenharmony_ci if (ret) 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = clk_prepare_enable(saif->clk); 27562306a36Sopenharmony_ci if (ret) 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* enable MCLK output */ 27962306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 28062306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_SET_ADDR); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mxs_saif_get_mclk); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * SAIF DAI format configuration. 28862306a36Sopenharmony_ci * Should only be called when port is inactive. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_cistatic int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci u32 scr, stat; 29362306a36Sopenharmony_ci u32 scr0; 29462306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci stat = __raw_readl(saif->base + SAIF_STAT); 29762306a36Sopenharmony_ci if (stat & BM_SAIF_STAT_BUSY) { 29862306a36Sopenharmony_ci dev_err(cpu_dai->dev, "error: busy\n"); 29962306a36Sopenharmony_ci return -EBUSY; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* If SAIF1 is configured as slave, the clk gate needs to be cleared 30362306a36Sopenharmony_ci * before the register can be written. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci if (saif->id != saif->master_id) { 30662306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_SFTRST, 30762306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 30862306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_CLKGATE, 30962306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci scr0 = __raw_readl(saif->base + SAIF_CTRL); 31362306a36Sopenharmony_ci scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \ 31462306a36Sopenharmony_ci & ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY; 31562306a36Sopenharmony_ci scr = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* DAI mode */ 31862306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 31962306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 32062306a36Sopenharmony_ci /* data frame low 1clk before data */ 32162306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_DELAY; 32262306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 32562306a36Sopenharmony_ci /* data frame high with data */ 32662306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_DELAY; 32762306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; 32862306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_JUSTIFY; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* DAI clock inversion */ 33562306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 33662306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 33762306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_BITCLK_EDGE; 33862306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_LRCLK_POLARITY; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 34162306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_BITCLK_EDGE; 34262306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 34562306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; 34662306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_LRCLK_POLARITY; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 34962306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; 35062306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Note: We simply just support master mode since SAIF TX can only 35662306a36Sopenharmony_ci * work as master. 35762306a36Sopenharmony_ci * Here the master is relative to codec side. 35862306a36Sopenharmony_ci * Saif internally could be slave when working on EXTMASTER mode. 35962306a36Sopenharmony_ci * We just hide this to machine driver. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 36262306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 36362306a36Sopenharmony_ci if (saif->id == saif->master_id) 36462306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_SLAVE_MODE; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_SLAVE_MODE; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci __raw_writel(scr | scr0, saif->base + SAIF_CTRL); 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci default: 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int mxs_saif_startup(struct snd_pcm_substream *substream, 37862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 38162306a36Sopenharmony_ci int ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* clear error status to 0 for each re-open */ 38462306a36Sopenharmony_ci saif->fifo_underrun = 0; 38562306a36Sopenharmony_ci saif->fifo_overrun = 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Clear Reset for normal operations */ 38862306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_SFTRST, 38962306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* clear clock gate */ 39262306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_CLKGATE, 39362306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = clk_prepare(saif->clk); 39662306a36Sopenharmony_ci if (ret) 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void mxs_saif_shutdown(struct snd_pcm_substream *substream, 40362306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci clk_unprepare(saif->clk); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * Should only be called when port is inactive. 41262306a36Sopenharmony_ci * although can be called multiple times by upper layers. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic int mxs_saif_hw_params(struct snd_pcm_substream *substream, 41562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 41662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 41962306a36Sopenharmony_ci struct mxs_saif *master_saif; 42062306a36Sopenharmony_ci u32 scr, stat; 42162306a36Sopenharmony_ci int ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci master_saif = mxs_saif_get_master(saif); 42462306a36Sopenharmony_ci if (!master_saif) 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* mclk should already be set */ 42862306a36Sopenharmony_ci if (!saif->mclk && saif->mclk_in_use) { 42962306a36Sopenharmony_ci dev_err(cpu_dai->dev, "set mclk first\n"); 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci stat = __raw_readl(saif->base + SAIF_STAT); 43462306a36Sopenharmony_ci if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) { 43562306a36Sopenharmony_ci dev_err(cpu_dai->dev, "error: busy\n"); 43662306a36Sopenharmony_ci return -EBUSY; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * Set saif clk based on sample rate. 44162306a36Sopenharmony_ci * If mclk is used, we also set mclk, if not, saif->mclk is 44262306a36Sopenharmony_ci * default 0, means not used. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params)); 44562306a36Sopenharmony_ci if (ret) { 44662306a36Sopenharmony_ci dev_err(cpu_dai->dev, "unable to get proper clk\n"); 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (saif != master_saif) { 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Set an initial clock rate for the saif internal logic to work 45362306a36Sopenharmony_ci * properly. This is important when working in EXTMASTER mode 45462306a36Sopenharmony_ci * that uses the other saif's BITCLK&LRCLK but it still needs a 45562306a36Sopenharmony_ci * basic clock which should be fast enough for the internal 45662306a36Sopenharmony_ci * logic. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci ret = clk_enable(saif->clk); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = clk_set_rate(saif->clk, 24000000); 46362306a36Sopenharmony_ci clk_disable(saif->clk); 46462306a36Sopenharmony_ci if (ret) 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = clk_prepare(master_saif->clk); 46862306a36Sopenharmony_ci if (ret) 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci scr = __raw_readl(saif->base + SAIF_CTRL); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_WORD_LENGTH; 47562306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; 47662306a36Sopenharmony_ci switch (params_format(params)) { 47762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 47862306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_WORD_LENGTH(0); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 48162306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_WORD_LENGTH(4); 48262306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 48562306a36Sopenharmony_ci scr |= BF_SAIF_CTRL_WORD_LENGTH(8); 48662306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci default: 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Tx/Rx config */ 49362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 49462306a36Sopenharmony_ci /* enable TX mode */ 49562306a36Sopenharmony_ci scr &= ~BM_SAIF_CTRL_READ_MODE; 49662306a36Sopenharmony_ci } else { 49762306a36Sopenharmony_ci /* enable RX mode */ 49862306a36Sopenharmony_ci scr |= BM_SAIF_CTRL_READ_MODE; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci __raw_writel(scr, saif->base + SAIF_CTRL); 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int mxs_saif_prepare(struct snd_pcm_substream *substream, 50662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* enable FIFO error irqs */ 51162306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN, 51262306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_SET_ADDR); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, 51862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 52162306a36Sopenharmony_ci struct mxs_saif *master_saif; 52262306a36Sopenharmony_ci u32 delay; 52362306a36Sopenharmony_ci int ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci master_saif = mxs_saif_get_master(saif); 52662306a36Sopenharmony_ci if (!master_saif) 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci switch (cmd) { 53062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 53162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 53262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 53362306a36Sopenharmony_ci if (saif->state == MXS_SAIF_STATE_RUNNING) 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dev_dbg(cpu_dai->dev, "start\n"); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = clk_enable(master_saif->clk); 53962306a36Sopenharmony_ci if (ret) { 54062306a36Sopenharmony_ci dev_err(saif->dev, "Failed to enable master clock\n"); 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * If the saif's master is not itself, we also need to enable 54662306a36Sopenharmony_ci * itself clk for its internal basic logic to work. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (saif != master_saif) { 54962306a36Sopenharmony_ci ret = clk_enable(saif->clk); 55062306a36Sopenharmony_ci if (ret) { 55162306a36Sopenharmony_ci dev_err(saif->dev, "Failed to enable master clock\n"); 55262306a36Sopenharmony_ci clk_disable(master_saif->clk); 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 55762306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_SET_ADDR); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (!master_saif->mclk_in_use) 56162306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 56262306a36Sopenharmony_ci master_saif->base + SAIF_CTRL + MXS_SET_ADDR); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * write data to saif data register to trigger 56762306a36Sopenharmony_ci * the transfer. 56862306a36Sopenharmony_ci * For 24-bit format the 32-bit FIFO register stores 56962306a36Sopenharmony_ci * only one channel, so we need to write twice. 57062306a36Sopenharmony_ci * This is also safe for the other non 24-bit formats. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci __raw_writel(0, saif->base + SAIF_DATA); 57362306a36Sopenharmony_ci __raw_writel(0, saif->base + SAIF_DATA); 57462306a36Sopenharmony_ci } else { 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * read data from saif data register to trigger 57762306a36Sopenharmony_ci * the receive. 57862306a36Sopenharmony_ci * For 24-bit format the 32-bit FIFO register stores 57962306a36Sopenharmony_ci * only one channel, so we need to read twice. 58062306a36Sopenharmony_ci * This is also safe for the other non 24-bit formats. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_DATA); 58362306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_DATA); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci master_saif->ongoing = 1; 58762306a36Sopenharmony_ci saif->state = MXS_SAIF_STATE_RUNNING; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n", 59062306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_CTRL), 59162306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_STAT)); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci dev_dbg(master_saif->dev, "CTRL 0x%x STAT 0x%x\n", 59462306a36Sopenharmony_ci __raw_readl(master_saif->base + SAIF_CTRL), 59562306a36Sopenharmony_ci __raw_readl(master_saif->base + SAIF_STAT)); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 59862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 59962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 60062306a36Sopenharmony_ci if (saif->state == MXS_SAIF_STATE_STOPPED) 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci dev_dbg(cpu_dai->dev, "stop\n"); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* wait a while for the current sample to complete */ 60662306a36Sopenharmony_ci delay = USEC_PER_SEC / master_saif->cur_rate; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!master_saif->mclk_in_use) { 60962306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 61062306a36Sopenharmony_ci master_saif->base + SAIF_CTRL + MXS_CLR_ADDR); 61162306a36Sopenharmony_ci udelay(delay); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci clk_disable(master_saif->clk); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (saif != master_saif) { 61662306a36Sopenharmony_ci __raw_writel(BM_SAIF_CTRL_RUN, 61762306a36Sopenharmony_ci saif->base + SAIF_CTRL + MXS_CLR_ADDR); 61862306a36Sopenharmony_ci udelay(delay); 61962306a36Sopenharmony_ci clk_disable(saif->clk); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci master_saif->ongoing = 0; 62362306a36Sopenharmony_ci saif->state = MXS_SAIF_STATE_STOPPED; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci default: 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci#define MXS_SAIF_RATES SNDRV_PCM_RATE_8000_192000 63462306a36Sopenharmony_ci#define MXS_SAIF_FORMATS \ 63562306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 63662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mxs_saif_dai_ops = { 63962306a36Sopenharmony_ci .startup = mxs_saif_startup, 64062306a36Sopenharmony_ci .shutdown = mxs_saif_shutdown, 64162306a36Sopenharmony_ci .trigger = mxs_saif_trigger, 64262306a36Sopenharmony_ci .prepare = mxs_saif_prepare, 64362306a36Sopenharmony_ci .hw_params = mxs_saif_hw_params, 64462306a36Sopenharmony_ci .set_sysclk = mxs_saif_set_dai_sysclk, 64562306a36Sopenharmony_ci .set_fmt = mxs_saif_set_dai_fmt, 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic struct snd_soc_dai_driver mxs_saif_dai = { 64962306a36Sopenharmony_ci .name = "mxs-saif", 65062306a36Sopenharmony_ci .playback = { 65162306a36Sopenharmony_ci .channels_min = 2, 65262306a36Sopenharmony_ci .channels_max = 2, 65362306a36Sopenharmony_ci .rates = MXS_SAIF_RATES, 65462306a36Sopenharmony_ci .formats = MXS_SAIF_FORMATS, 65562306a36Sopenharmony_ci }, 65662306a36Sopenharmony_ci .capture = { 65762306a36Sopenharmony_ci .channels_min = 2, 65862306a36Sopenharmony_ci .channels_max = 2, 65962306a36Sopenharmony_ci .rates = MXS_SAIF_RATES, 66062306a36Sopenharmony_ci .formats = MXS_SAIF_FORMATS, 66162306a36Sopenharmony_ci }, 66262306a36Sopenharmony_ci .ops = &mxs_saif_dai_ops, 66362306a36Sopenharmony_ci}; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic const struct snd_soc_component_driver mxs_saif_component = { 66662306a36Sopenharmony_ci .name = "mxs-saif", 66762306a36Sopenharmony_ci .legacy_dai_naming = 1, 66862306a36Sopenharmony_ci}; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic irqreturn_t mxs_saif_irq(int irq, void *dev_id) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct mxs_saif *saif = dev_id; 67362306a36Sopenharmony_ci unsigned int stat; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci stat = __raw_readl(saif->base + SAIF_STAT); 67662306a36Sopenharmony_ci if (!(stat & (BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ | 67762306a36Sopenharmony_ci BM_SAIF_STAT_FIFO_OVERFLOW_IRQ))) 67862306a36Sopenharmony_ci return IRQ_NONE; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) { 68162306a36Sopenharmony_ci dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun); 68262306a36Sopenharmony_ci __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ, 68362306a36Sopenharmony_ci saif->base + SAIF_STAT + MXS_CLR_ADDR); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ) { 68762306a36Sopenharmony_ci dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun); 68862306a36Sopenharmony_ci __raw_writel(BM_SAIF_STAT_FIFO_OVERFLOW_IRQ, 68962306a36Sopenharmony_ci saif->base + SAIF_STAT + MXS_CLR_ADDR); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci dev_dbg(saif->dev, "SAIF_CTRL %x SAIF_STAT %x\n", 69362306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_CTRL), 69462306a36Sopenharmony_ci __raw_readl(saif->base + SAIF_STAT)); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return IRQ_HANDLED; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int mxs_saif_mclk_init(struct platform_device *pdev) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct mxs_saif *saif = platform_get_drvdata(pdev); 70262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 70362306a36Sopenharmony_ci struct clk *clk; 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci clk = clk_register_divider(&pdev->dev, "mxs_saif_mclk", 70762306a36Sopenharmony_ci __clk_get_name(saif->clk), 0, 70862306a36Sopenharmony_ci saif->base + SAIF_CTRL, 70962306a36Sopenharmony_ci BP_SAIF_CTRL_BITCLK_MULT_RATE, 3, 71062306a36Sopenharmony_ci 0, NULL); 71162306a36Sopenharmony_ci if (IS_ERR(clk)) { 71262306a36Sopenharmony_ci ret = PTR_ERR(clk); 71362306a36Sopenharmony_ci if (ret == -EEXIST) 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register mclk: %d\n", ret); 71662306a36Sopenharmony_ci return PTR_ERR(clk); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); 72062306a36Sopenharmony_ci if (ret) 72162306a36Sopenharmony_ci return ret; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int mxs_saif_probe(struct platform_device *pdev) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 72962306a36Sopenharmony_ci struct mxs_saif *saif; 73062306a36Sopenharmony_ci int irq, ret; 73162306a36Sopenharmony_ci struct device_node *master; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); 73462306a36Sopenharmony_ci if (!saif) 73562306a36Sopenharmony_ci return -ENOMEM; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = of_alias_get_id(np, "saif"); 73862306a36Sopenharmony_ci if (ret < 0) 73962306a36Sopenharmony_ci return ret; 74062306a36Sopenharmony_ci else 74162306a36Sopenharmony_ci saif->id = ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (saif->id >= ARRAY_SIZE(mxs_saif)) { 74462306a36Sopenharmony_ci dev_err(&pdev->dev, "get wrong saif id\n"); 74562306a36Sopenharmony_ci return -EINVAL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* 74962306a36Sopenharmony_ci * If there is no "fsl,saif-master" phandle, it's a saif 75062306a36Sopenharmony_ci * master. Otherwise, it's a slave and its phandle points 75162306a36Sopenharmony_ci * to the master. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci master = of_parse_phandle(np, "fsl,saif-master", 0); 75462306a36Sopenharmony_ci if (!master) { 75562306a36Sopenharmony_ci saif->master_id = saif->id; 75662306a36Sopenharmony_ci } else { 75762306a36Sopenharmony_ci ret = of_alias_get_id(master, "saif"); 75862306a36Sopenharmony_ci of_node_put(master); 75962306a36Sopenharmony_ci if (ret < 0) 76062306a36Sopenharmony_ci return ret; 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci saif->master_id = ret; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (saif->master_id >= ARRAY_SIZE(mxs_saif)) { 76562306a36Sopenharmony_ci dev_err(&pdev->dev, "get wrong master id\n"); 76662306a36Sopenharmony_ci return -EINVAL; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci mxs_saif[saif->id] = saif; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci saif->clk = devm_clk_get(&pdev->dev, NULL); 77362306a36Sopenharmony_ci if (IS_ERR(saif->clk)) { 77462306a36Sopenharmony_ci ret = PTR_ERR(saif->clk); 77562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot get the clock: %d\n", 77662306a36Sopenharmony_ci ret); 77762306a36Sopenharmony_ci return ret; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci saif->base = devm_platform_ioremap_resource(pdev, 0); 78162306a36Sopenharmony_ci if (IS_ERR(saif->base)) 78262306a36Sopenharmony_ci return PTR_ERR(saif->base); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 78562306a36Sopenharmony_ci if (irq < 0) 78662306a36Sopenharmony_ci return irq; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci saif->dev = &pdev->dev; 78962306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, 79062306a36Sopenharmony_ci dev_name(&pdev->dev), saif); 79162306a36Sopenharmony_ci if (ret) { 79262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request irq\n"); 79362306a36Sopenharmony_ci return ret; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci platform_set_drvdata(pdev, saif); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* We only support saif0 being tx and clock master */ 79962306a36Sopenharmony_ci if (saif->id == 0) { 80062306a36Sopenharmony_ci ret = mxs_saif_mclk_init(pdev); 80162306a36Sopenharmony_ci if (ret) 80262306a36Sopenharmony_ci dev_warn(&pdev->dev, "failed to init clocks\n"); 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, 80662306a36Sopenharmony_ci &mxs_saif_dai, 1); 80762306a36Sopenharmony_ci if (ret) { 80862306a36Sopenharmony_ci dev_err(&pdev->dev, "register DAI failed\n"); 80962306a36Sopenharmony_ci return ret; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = mxs_pcm_platform_register(&pdev->dev); 81362306a36Sopenharmony_ci if (ret) { 81462306a36Sopenharmony_ci dev_err(&pdev->dev, "register PCM failed: %d\n", ret); 81562306a36Sopenharmony_ci return ret; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic const struct of_device_id mxs_saif_dt_ids[] = { 82262306a36Sopenharmony_ci { .compatible = "fsl,imx28-saif", }, 82362306a36Sopenharmony_ci { /* sentinel */ } 82462306a36Sopenharmony_ci}; 82562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxs_saif_dt_ids); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic struct platform_driver mxs_saif_driver = { 82862306a36Sopenharmony_ci .probe = mxs_saif_probe, 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci .driver = { 83162306a36Sopenharmony_ci .name = "mxs-saif", 83262306a36Sopenharmony_ci .of_match_table = mxs_saif_dt_ids, 83362306a36Sopenharmony_ci }, 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cimodule_platform_driver(mxs_saif_driver); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 83962306a36Sopenharmony_ciMODULE_DESCRIPTION("MXS ASoC SAIF driver"); 84062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 84162306a36Sopenharmony_ciMODULE_ALIAS("platform:mxs-saif"); 842