1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771 |
- // SPDX-License-Identifier: GPL-2.0-only
- // ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
- // Copyright (c) 2018 Rockchip Electronics Co. Ltd.
- // Author: Sugar Zhang <[email protected]>
- // Author: Nicolas Frattaroli <[email protected]>
- #include <linux/clk.h>
- #include <linux/clk-provider.h>
- #include <linux/delay.h>
- #include <linux/mfd/syscon.h>
- #include <linux/module.h>
- #include <linux/of_address.h>
- #include <linux/of_device.h>
- #include <linux/of_gpio.h>
- #include <linux/pm_runtime.h>
- #include <linux/regmap.h>
- #include <linux/reset.h>
- #include <linux/spinlock.h>
- #include <sound/dmaengine_pcm.h>
- #include <sound/pcm_params.h>
- #include "rockchip_i2s_tdm.h"
- #define DRV_NAME "rockchip-i2s-tdm"
- #define DEFAULT_MCLK_FS 256
- #define CH_GRP_MAX 4 /* The max channel 8 / 2 */
- #define MULTIPLEX_CH_MAX 10
- #define CLK_PPM_MIN -1000
- #define CLK_PPM_MAX 1000
- #define TRCM_TXRX 0
- #define TRCM_TX 1
- #define TRCM_RX 2
- struct txrx_config {
- u32 addr;
- u32 reg;
- u32 txonly;
- u32 rxonly;
- };
- struct rk_i2s_soc_data {
- u32 softrst_offset;
- u32 grf_reg_offset;
- u32 grf_shift;
- int config_count;
- const struct txrx_config *configs;
- int (*init)(struct device *dev, u32 addr);
- };
- struct rk_i2s_tdm_dev {
- struct device *dev;
- struct clk *hclk;
- struct clk *mclk_tx;
- struct clk *mclk_rx;
- /* The mclk_tx_src is parent of mclk_tx */
- struct clk *mclk_tx_src;
- /* The mclk_rx_src is parent of mclk_rx */
- struct clk *mclk_rx_src;
- /*
- * The mclk_root0 and mclk_root1 are root parent and supplies for
- * the different FS.
- *
- * e.g:
- * mclk_root0 is VPLL0, used for FS=48000Hz
- * mclk_root1 is VPLL1, used for FS=44100Hz
- */
- struct clk *mclk_root0;
- struct clk *mclk_root1;
- struct regmap *regmap;
- struct regmap *grf;
- struct snd_dmaengine_dai_dma_data capture_dma_data;
- struct snd_dmaengine_dai_dma_data playback_dma_data;
- struct reset_control *tx_reset;
- struct reset_control *rx_reset;
- struct rk_i2s_soc_data *soc_data;
- bool is_master_mode;
- bool io_multiplex;
- bool mclk_calibrate;
- bool tdm_mode;
- unsigned int mclk_rx_freq;
- unsigned int mclk_tx_freq;
- unsigned int mclk_root0_freq;
- unsigned int mclk_root1_freq;
- unsigned int mclk_root0_initial_freq;
- unsigned int mclk_root1_initial_freq;
- unsigned int frame_width;
- unsigned int clk_trcm;
- unsigned int i2s_sdis[CH_GRP_MAX];
- unsigned int i2s_sdos[CH_GRP_MAX];
- int clk_ppm;
- int refcount;
- spinlock_t lock; /* xfer lock */
- bool has_playback;
- bool has_capture;
- struct snd_soc_dai_driver *dai;
- };
- static int to_ch_num(unsigned int val)
- {
- switch (val) {
- case I2S_CHN_4:
- return 4;
- case I2S_CHN_6:
- return 6;
- case I2S_CHN_8:
- return 8;
- default:
- return 2;
- }
- }
- static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
- {
- clk_disable_unprepare(i2s_tdm->mclk_tx);
- clk_disable_unprepare(i2s_tdm->mclk_rx);
- if (i2s_tdm->mclk_calibrate) {
- clk_disable_unprepare(i2s_tdm->mclk_tx_src);
- clk_disable_unprepare(i2s_tdm->mclk_rx_src);
- clk_disable_unprepare(i2s_tdm->mclk_root0);
- clk_disable_unprepare(i2s_tdm->mclk_root1);
- }
- }
- /**
- * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on
- * failure.
- * @i2s_tdm: rk_i2s_tdm_dev struct
- *
- * This function attempts to enable all mclk clocks, but cleans up after
- * itself on failure. Guarantees to balance its calls.
- *
- * Returns success (0) or negative errno.
- */
- static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
- {
- int ret = 0;
- ret = clk_prepare_enable(i2s_tdm->mclk_tx);
- if (ret)
- goto err_mclk_tx;
- ret = clk_prepare_enable(i2s_tdm->mclk_rx);
- if (ret)
- goto err_mclk_rx;
- if (i2s_tdm->mclk_calibrate) {
- ret = clk_prepare_enable(i2s_tdm->mclk_tx_src);
- if (ret)
- goto err_mclk_rx;
- ret = clk_prepare_enable(i2s_tdm->mclk_rx_src);
- if (ret)
- goto err_mclk_rx_src;
- ret = clk_prepare_enable(i2s_tdm->mclk_root0);
- if (ret)
- goto err_mclk_root0;
- ret = clk_prepare_enable(i2s_tdm->mclk_root1);
- if (ret)
- goto err_mclk_root1;
- }
- return 0;
- err_mclk_root1:
- clk_disable_unprepare(i2s_tdm->mclk_root0);
- err_mclk_root0:
- clk_disable_unprepare(i2s_tdm->mclk_rx_src);
- err_mclk_rx_src:
- clk_disable_unprepare(i2s_tdm->mclk_tx_src);
- err_mclk_rx:
- clk_disable_unprepare(i2s_tdm->mclk_tx);
- err_mclk_tx:
- return ret;
- }
- static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
- regcache_cache_only(i2s_tdm->regmap, true);
- i2s_tdm_disable_unprepare_mclk(i2s_tdm);
- clk_disable_unprepare(i2s_tdm->hclk);
- return 0;
- }
- static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
- int ret;
- ret = clk_prepare_enable(i2s_tdm->hclk);
- if (ret)
- goto err_hclk;
- ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
- if (ret)
- goto err_mclk;
- regcache_cache_only(i2s_tdm->regmap, false);
- regcache_mark_dirty(i2s_tdm->regmap);
- ret = regcache_sync(i2s_tdm->regmap);
- if (ret)
- goto err_regcache;
- return 0;
- err_regcache:
- i2s_tdm_disable_unprepare_mclk(i2s_tdm);
- err_mclk:
- clk_disable_unprepare(i2s_tdm->hclk);
- err_hclk:
- return ret;
- }
- static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai)
- {
- return snd_soc_dai_get_drvdata(dai);
- }
- /*
- * Makes sure that both tx and rx are reset at the same time to sync lrck
- * when clk_trcm > 0.
- */
- static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
- {
- /* This is technically race-y.
- *
- * In an ideal world, we could atomically assert both resets at the
- * same time, through an atomic bulk reset API. This API however does
- * not exist, so what the downstream vendor code used to do was
- * implement half a reset controller here and require the CRU to be
- * passed to the driver as a device tree node. Violating abstractions
- * like that is bad, especially when it influences something like the
- * bindings which are supposed to describe the hardware, not whatever
- * workarounds the driver needs, so it was dropped.
- *
- * In practice, asserting the resets one by one appears to work just
- * fine for playback. During duplex (playback + capture) operation,
- * this might become an issue, but that should be solved by the
- * implementation of the aforementioned API, not by shoving a reset
- * controller into an audio driver.
- */
- reset_control_assert(i2s_tdm->tx_reset);
- reset_control_assert(i2s_tdm->rx_reset);
- udelay(10);
- reset_control_deassert(i2s_tdm->tx_reset);
- reset_control_deassert(i2s_tdm->rx_reset);
- udelay(10);
- }
- static void rockchip_snd_reset(struct reset_control *rc)
- {
- reset_control_assert(rc);
- udelay(10);
- reset_control_deassert(rc);
- udelay(10);
- }
- static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm,
- unsigned int clr)
- {
- unsigned int xfer_mask = 0;
- unsigned int xfer_val = 0;
- unsigned int val;
- int retry = 10;
- bool tx = clr & I2S_CLR_TXC;
- bool rx = clr & I2S_CLR_RXC;
- if (!(rx || tx))
- return;
- if (tx) {
- xfer_mask = I2S_XFER_TXS_START;
- xfer_val = I2S_XFER_TXS_STOP;
- }
- if (rx) {
- xfer_mask |= I2S_XFER_RXS_START;
- xfer_val |= I2S_XFER_RXS_STOP;
- }
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER, xfer_mask, xfer_val);
- udelay(150);
- regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr);
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- /* Wait on the clear operation to finish */
- while (val) {
- udelay(15);
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n",
- tx ? "tx" : "", rx ? "rx" : "");
- if (rx && tx)
- rockchip_snd_xfer_sync_reset(i2s_tdm);
- else if (tx)
- rockchip_snd_reset(i2s_tdm->tx_reset);
- else if (rx)
- rockchip_snd_reset(i2s_tdm->rx_reset);
- break;
- }
- }
- }
- static inline void rockchip_enable_tde(struct regmap *regmap)
- {
- regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_ENABLE);
- }
- static inline void rockchip_disable_tde(struct regmap *regmap)
- {
- regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_DISABLE);
- }
- static inline void rockchip_enable_rde(struct regmap *regmap)
- {
- regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_ENABLE);
- }
- static inline void rockchip_disable_rde(struct regmap *regmap)
- {
- regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_DISABLE);
- }
- /* only used when clk_trcm > 0 */
- static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai, int on)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- unsigned long flags;
- spin_lock_irqsave(&i2s_tdm->lock, flags);
- if (on) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- rockchip_enable_tde(i2s_tdm->regmap);
- else
- rockchip_enable_rde(i2s_tdm->regmap);
- if (++i2s_tdm->refcount == 1) {
- rockchip_snd_xfer_sync_reset(i2s_tdm);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START);
- }
- } else {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- rockchip_disable_tde(i2s_tdm->regmap);
- else
- rockchip_disable_rde(i2s_tdm->regmap);
- if (--i2s_tdm->refcount == 0) {
- rockchip_snd_xfer_clear(i2s_tdm,
- I2S_CLR_TXC | I2S_CLR_RXC);
- }
- }
- spin_unlock_irqrestore(&i2s_tdm->lock, flags);
- }
- static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
- {
- if (on) {
- rockchip_enable_tde(i2s_tdm->regmap);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START,
- I2S_XFER_TXS_START);
- } else {
- rockchip_disable_tde(i2s_tdm->regmap);
- rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC);
- }
- }
- static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
- {
- if (on) {
- rockchip_enable_rde(i2s_tdm->regmap);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_RXS_START,
- I2S_XFER_RXS_START);
- } else {
- rockchip_disable_rde(i2s_tdm->regmap);
- rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC);
- }
- }
- static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
- unsigned int mask, val, tdm_val, txcr_val, rxcr_val;
- int ret;
- bool is_tdm = i2s_tdm->tdm_mode;
- ret = pm_runtime_resume_and_get(cpu_dai->dev);
- if (ret < 0 && ret != -EACCES)
- return ret;
- mask = I2S_CKR_MSS_MASK;
- switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
- case SND_SOC_DAIFMT_BP_FP:
- val = I2S_CKR_MSS_MASTER;
- i2s_tdm->is_master_mode = true;
- break;
- case SND_SOC_DAIFMT_BC_FC:
- val = I2S_CKR_MSS_SLAVE;
- i2s_tdm->is_master_mode = false;
- break;
- default:
- ret = -EINVAL;
- goto err_pm_put;
- }
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
- mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK;
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- val = I2S_CKR_CKP_NORMAL |
- I2S_CKR_TLP_NORMAL |
- I2S_CKR_RLP_NORMAL;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- val = I2S_CKR_CKP_NORMAL |
- I2S_CKR_TLP_INVERTED |
- I2S_CKR_RLP_INVERTED;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- val = I2S_CKR_CKP_INVERTED |
- I2S_CKR_TLP_NORMAL |
- I2S_CKR_RLP_NORMAL;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- val = I2S_CKR_CKP_INVERTED |
- I2S_CKR_TLP_INVERTED |
- I2S_CKR_RLP_INVERTED;
- break;
- default:
- ret = -EINVAL;
- goto err_pm_put;
- }
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- txcr_val = I2S_TXCR_IBM_RSJM;
- rxcr_val = I2S_RXCR_IBM_RSJM;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- txcr_val = I2S_TXCR_IBM_LSJM;
- rxcr_val = I2S_RXCR_IBM_LSJM;
- break;
- case SND_SOC_DAIFMT_I2S:
- txcr_val = I2S_TXCR_IBM_NORMAL;
- rxcr_val = I2S_RXCR_IBM_NORMAL;
- break;
- case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 mode */
- txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
- rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
- break;
- case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
- txcr_val = I2S_TXCR_TFS_PCM;
- rxcr_val = I2S_RXCR_TFS_PCM;
- break;
- default:
- ret = -EINVAL;
- goto err_pm_put;
- }
- mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, txcr_val);
- mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, rxcr_val);
- if (is_tdm) {
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- val = I2S_TXCR_TFS_TDM_I2S;
- tdm_val = TDM_SHIFT_CTRL(2);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- val = I2S_TXCR_TFS_TDM_I2S;
- tdm_val = TDM_SHIFT_CTRL(1);
- break;
- case SND_SOC_DAIFMT_I2S:
- val = I2S_TXCR_TFS_TDM_I2S;
- tdm_val = TDM_SHIFT_CTRL(0);
- break;
- case SND_SOC_DAIFMT_DSP_A:
- val = I2S_TXCR_TFS_TDM_PCM;
- tdm_val = TDM_SHIFT_CTRL(0);
- break;
- case SND_SOC_DAIFMT_DSP_B:
- val = I2S_TXCR_TFS_TDM_PCM;
- tdm_val = TDM_SHIFT_CTRL(2);
- break;
- default:
- ret = -EINVAL;
- goto err_pm_put;
- }
- tdm_val |= TDM_FSYNC_WIDTH_SEL1(1);
- tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME;
- mask = I2S_TXCR_TFS_MASK;
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val);
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val);
- mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK |
- TDM_SHIFT_CTRL_MSK;
- regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
- mask, tdm_val);
- regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
- mask, tdm_val);
- }
- err_pm_put:
- pm_runtime_put(cpu_dai->dev);
- return ret;
- }
- static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream,
- struct rk_i2s_tdm_dev *i2s_tdm)
- {
- int stream;
- stream = SNDRV_PCM_STREAM_LAST - substream->stream;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- rockchip_disable_tde(i2s_tdm->regmap);
- else
- rockchip_disable_rde(i2s_tdm->regmap);
- rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC);
- }
- static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
- struct rk_i2s_tdm_dev *i2s_tdm)
- {
- int stream;
- stream = SNDRV_PCM_STREAM_LAST - substream->stream;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- rockchip_enable_tde(i2s_tdm->regmap);
- else
- rockchip_enable_rde(i2s_tdm->regmap);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START);
- }
- static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm,
- struct clk *clk, unsigned long rate,
- int ppm)
- {
- unsigned long rate_target;
- int delta, ret;
- if (ppm == i2s_tdm->clk_ppm)
- return 0;
- if (ppm < 0)
- delta = -1;
- else
- delta = 1;
- delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000,
- 1000000);
- rate_target = rate + delta;
- if (!rate_target)
- return -EINVAL;
- ret = clk_set_rate(clk, rate_target);
- if (ret)
- return ret;
- i2s_tdm->clk_ppm = ppm;
- return 0;
- }
- static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm,
- struct snd_pcm_substream *substream,
- unsigned int lrck_freq)
- {
- struct clk *mclk_root;
- struct clk *mclk_parent;
- unsigned int mclk_root_freq;
- unsigned int mclk_root_initial_freq;
- unsigned int mclk_parent_freq;
- unsigned int div, delta;
- u64 ppm;
- int ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- mclk_parent = i2s_tdm->mclk_tx_src;
- else
- mclk_parent = i2s_tdm->mclk_rx_src;
- switch (lrck_freq) {
- case 8000:
- case 16000:
- case 24000:
- case 32000:
- case 48000:
- case 64000:
- case 96000:
- case 192000:
- mclk_root = i2s_tdm->mclk_root0;
- mclk_root_freq = i2s_tdm->mclk_root0_freq;
- mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq;
- mclk_parent_freq = DEFAULT_MCLK_FS * 192000;
- break;
- case 11025:
- case 22050:
- case 44100:
- case 88200:
- case 176400:
- mclk_root = i2s_tdm->mclk_root1;
- mclk_root_freq = i2s_tdm->mclk_root1_freq;
- mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq;
- mclk_parent_freq = DEFAULT_MCLK_FS * 176400;
- break;
- default:
- dev_err(i2s_tdm->dev, "Invalid LRCK frequency: %u Hz\n",
- lrck_freq);
- return -EINVAL;
- }
- ret = clk_set_parent(mclk_parent, mclk_root);
- if (ret)
- return ret;
- ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, mclk_root,
- mclk_root_freq, 0);
- if (ret)
- return ret;
- delta = abs(mclk_root_freq % mclk_parent_freq - mclk_parent_freq);
- ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)mclk_root_freq);
- if (ppm) {
- div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq);
- if (!div)
- return -EINVAL;
- mclk_root_freq = mclk_parent_freq * round_up(div, 2);
- ret = clk_set_rate(mclk_root, mclk_root_freq);
- if (ret)
- return ret;
- i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0);
- i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1);
- }
- return clk_set_rate(mclk_parent, mclk_parent_freq);
- }
- static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm,
- struct snd_pcm_substream *substream,
- struct clk **mclk)
- {
- unsigned int mclk_freq;
- int ret;
- if (i2s_tdm->clk_trcm) {
- if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) {
- dev_err(i2s_tdm->dev,
- "clk_trcm, tx: %d and rx: %d should be the same\n",
- i2s_tdm->mclk_tx_freq,
- i2s_tdm->mclk_rx_freq);
- return -EINVAL;
- }
- ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq);
- if (ret)
- return ret;
- ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq);
- if (ret)
- return ret;
- /* mclk_rx is also ok. */
- *mclk = i2s_tdm->mclk_tx;
- } else {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- *mclk = i2s_tdm->mclk_tx;
- mclk_freq = i2s_tdm->mclk_tx_freq;
- } else {
- *mclk = i2s_tdm->mclk_rx;
- mclk_freq = i2s_tdm->mclk_rx_freq;
- }
- ret = clk_set_rate(*mclk, mclk_freq);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture)
- {
- if (substream_capture) {
- switch (ch) {
- case I2S_CHN_4:
- return I2S_IO_6CH_OUT_4CH_IN;
- case I2S_CHN_6:
- return I2S_IO_4CH_OUT_6CH_IN;
- case I2S_CHN_8:
- return I2S_IO_2CH_OUT_8CH_IN;
- default:
- return I2S_IO_8CH_OUT_2CH_IN;
- }
- } else {
- switch (ch) {
- case I2S_CHN_4:
- return I2S_IO_4CH_OUT_6CH_IN;
- case I2S_CHN_6:
- return I2S_IO_6CH_OUT_4CH_IN;
- case I2S_CHN_8:
- return I2S_IO_8CH_OUT_2CH_IN;
- default:
- return I2S_IO_2CH_OUT_8CH_IN;
- }
- }
- }
- static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- int usable_chs = MULTIPLEX_CH_MAX;
- unsigned int val = 0;
- if (!i2s_tdm->io_multiplex)
- return 0;
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- struct snd_pcm_str *playback_str =
- &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
- if (playback_str->substream_opened) {
- regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
- val &= I2S_TXCR_CSR_MASK;
- usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
- }
- regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
- val &= I2S_RXCR_CSR_MASK;
- if (to_ch_num(val) > usable_chs) {
- dev_err(i2s_tdm->dev,
- "Capture channels (%d) > usable channels (%d)\n",
- to_ch_num(val), usable_chs);
- return -EINVAL;
- }
- rockchip_i2s_ch_to_io(val, true);
- } else {
- struct snd_pcm_str *capture_str =
- &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
- if (capture_str->substream_opened) {
- regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
- val &= I2S_RXCR_CSR_MASK;
- usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
- }
- regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
- val &= I2S_TXCR_CSR_MASK;
- if (to_ch_num(val) > usable_chs) {
- dev_err(i2s_tdm->dev,
- "Playback channels (%d) > usable channels (%d)\n",
- to_ch_num(val), usable_chs);
- return -EINVAL;
- }
- }
- val <<= i2s_tdm->soc_data->grf_shift;
- val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16;
- regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val);
- return 0;
- }
- static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai,
- unsigned int div_bclk,
- unsigned int div_lrck,
- unsigned int fmt)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- unsigned long flags;
- if (!i2s_tdm->clk_trcm)
- return 0;
- spin_lock_irqsave(&i2s_tdm->lock, flags);
- if (i2s_tdm->refcount)
- rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm);
- regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
- I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK,
- I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
- I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
- I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck));
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
- I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
- fmt);
- else
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
- I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
- fmt);
- if (i2s_tdm->refcount)
- rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm);
- spin_unlock_irqrestore(&i2s_tdm->lock, flags);
- return 0;
- }
- static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- struct clk *mclk;
- int ret = 0;
- unsigned int val = 0;
- unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64;
- if (i2s_tdm->is_master_mode) {
- if (i2s_tdm->mclk_calibrate)
- rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream,
- params_rate(params));
- ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk);
- if (ret)
- return ret;
- mclk_rate = clk_get_rate(mclk);
- bclk_rate = i2s_tdm->frame_width * params_rate(params);
- if (!bclk_rate)
- return -EINVAL;
- div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
- div_lrck = bclk_rate / params_rate(params);
- }
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- val |= I2S_TXCR_VDW(8);
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- val |= I2S_TXCR_VDW(16);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- val |= I2S_TXCR_VDW(20);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- val |= I2S_TXCR_VDW(24);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- val |= I2S_TXCR_VDW(32);
- break;
- default:
- return -EINVAL;
- }
- switch (params_channels(params)) {
- case 8:
- val |= I2S_CHN_8;
- break;
- case 6:
- val |= I2S_CHN_6;
- break;
- case 4:
- val |= I2S_CHN_4;
- break;
- case 2:
- val |= I2S_CHN_2;
- break;
- default:
- return -EINVAL;
- }
- if (i2s_tdm->clk_trcm) {
- rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val);
- } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
- I2S_CLKDIV_TXM_MASK,
- I2S_CLKDIV_TXM(div_bclk));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
- I2S_CKR_TSD_MASK,
- I2S_CKR_TSD(div_lrck));
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
- I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
- val);
- } else {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
- I2S_CLKDIV_RXM_MASK,
- I2S_CLKDIV_RXM(div_bclk));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
- I2S_CKR_RSD_MASK,
- I2S_CKR_RSD(div_lrck));
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
- I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
- val);
- }
- return rockchip_i2s_io_multiplex(substream, dai);
- }
- static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (i2s_tdm->clk_trcm)
- rockchip_snd_txrxctrl(substream, dai, 1);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s_tdm, 1);
- else
- rockchip_snd_txctrl(i2s_tdm, 1);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (i2s_tdm->clk_trcm)
- rockchip_snd_txrxctrl(substream, dai, 0);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s_tdm, 0);
- else
- rockchip_snd_txctrl(i2s_tdm, 0);
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream,
- unsigned int freq, int dir)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
- /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */
- if (i2s_tdm->clk_trcm) {
- i2s_tdm->mclk_tx_freq = freq;
- i2s_tdm->mclk_rx_freq = freq;
- } else {
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s_tdm->mclk_tx_freq = freq;
- else
- i2s_tdm->mclk_rx_freq = freq;
- }
- dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n",
- stream ? "rx" : "tx", freq);
- return 0;
- }
- static int rockchip_i2s_tdm_clk_compensation_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = CLK_PPM_MIN;
- uinfo->value.integer.max = CLK_PPM_MAX;
- uinfo->value.integer.step = 1;
- return 0;
- }
- static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
- ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm;
- return 0;
- }
- static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
- int ret = 0, ppm = 0;
- int changed = 0;
- unsigned long old_rate;
- if (ucontrol->value.integer.value[0] < CLK_PPM_MIN ||
- ucontrol->value.integer.value[0] > CLK_PPM_MAX)
- return -EINVAL;
- ppm = ucontrol->value.integer.value[0];
- old_rate = clk_get_rate(i2s_tdm->mclk_root0);
- ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0,
- i2s_tdm->mclk_root0_freq, ppm);
- if (ret)
- return ret;
- if (old_rate != clk_get_rate(i2s_tdm->mclk_root0))
- changed = 1;
- if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1))
- return changed;
- old_rate = clk_get_rate(i2s_tdm->mclk_root1);
- ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1,
- i2s_tdm->mclk_root1_freq, ppm);
- if (ret)
- return ret;
- if (old_rate != clk_get_rate(i2s_tdm->mclk_root1))
- changed = 1;
- return changed;
- }
- static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "PCM Clock Compensation in PPM",
- .info = rockchip_i2s_tdm_clk_compensation_info,
- .get = rockchip_i2s_tdm_clk_compensation_get,
- .put = rockchip_i2s_tdm_clk_compensation_put,
- };
- static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
- if (i2s_tdm->has_capture)
- dai->capture_dma_data = &i2s_tdm->capture_dma_data;
- if (i2s_tdm->has_playback)
- dai->playback_dma_data = &i2s_tdm->playback_dma_data;
- if (i2s_tdm->mclk_calibrate)
- snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1);
- return 0;
- }
- static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
- unsigned int mask, val;
- i2s_tdm->tdm_mode = true;
- i2s_tdm->frame_width = slots * slot_width;
- mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK;
- val = TDM_SLOT_BIT_WIDTH(slot_width) |
- TDM_FRAME_WIDTH(slots * slot_width);
- regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
- mask, val);
- regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
- mask, val);
- return 0;
- }
- static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai,
- unsigned int ratio)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
- if (ratio < 32 || ratio > 512 || ratio % 2 == 1)
- return -EINVAL;
- i2s_tdm->frame_width = ratio;
- return 0;
- }
- static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
- .hw_params = rockchip_i2s_tdm_hw_params,
- .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio,
- .set_sysclk = rockchip_i2s_tdm_set_sysclk,
- .set_fmt = rockchip_i2s_tdm_set_fmt,
- .set_tdm_slot = rockchip_dai_tdm_slot,
- .trigger = rockchip_i2s_tdm_trigger,
- };
- static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
- .name = DRV_NAME,
- .legacy_dai_naming = 1,
- };
- static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case I2S_TXCR:
- case I2S_RXCR:
- case I2S_CKR:
- case I2S_DMACR:
- case I2S_INTCR:
- case I2S_XFER:
- case I2S_CLR:
- case I2S_TXDR:
- case I2S_TDM_TXCR:
- case I2S_TDM_RXCR:
- case I2S_CLKDIV:
- return true;
- default:
- return false;
- }
- }
- static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case I2S_TXCR:
- case I2S_RXCR:
- case I2S_CKR:
- case I2S_DMACR:
- case I2S_INTCR:
- case I2S_XFER:
- case I2S_CLR:
- case I2S_TXDR:
- case I2S_RXDR:
- case I2S_TXFIFOLR:
- case I2S_INTSR:
- case I2S_RXFIFOLR:
- case I2S_TDM_TXCR:
- case I2S_TDM_RXCR:
- case I2S_CLKDIV:
- return true;
- default:
- return false;
- }
- }
- static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case I2S_TXFIFOLR:
- case I2S_INTSR:
- case I2S_CLR:
- case I2S_TXDR:
- case I2S_RXDR:
- case I2S_RXFIFOLR:
- return true;
- default:
- return false;
- }
- }
- static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg)
- {
- if (reg == I2S_RXDR)
- return true;
- return false;
- }
- static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = {
- {0x00, 0x7200000f},
- {0x04, 0x01c8000f},
- {0x08, 0x00001f1f},
- {0x10, 0x001f0000},
- {0x14, 0x01f00000},
- {0x30, 0x00003eff},
- {0x34, 0x00003eff},
- {0x38, 0x00000707},
- };
- static const struct regmap_config rockchip_i2s_tdm_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = I2S_CLKDIV,
- .reg_defaults = rockchip_i2s_tdm_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults),
- .writeable_reg = rockchip_i2s_tdm_wr_reg,
- .readable_reg = rockchip_i2s_tdm_rd_reg,
- .volatile_reg = rockchip_i2s_tdm_volatile_reg,
- .precious_reg = rockchip_i2s_tdm_precious_reg,
- .cache_type = REGCACHE_FLAT,
- };
- static int common_soc_init(struct device *dev, u32 addr)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
- const struct txrx_config *configs = i2s_tdm->soc_data->configs;
- u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm;
- int i;
- if (trcm == TRCM_TXRX)
- return 0;
- for (i = 0; i < i2s_tdm->soc_data->config_count; i++) {
- if (addr != configs[i].addr)
- continue;
- reg = configs[i].reg;
- if (trcm == TRCM_TX)
- val = configs[i].txonly;
- else
- val = configs[i].rxonly;
- if (reg)
- regmap_write(i2s_tdm->grf, reg, val);
- }
- return 0;
- }
- static const struct txrx_config px30_txrx_config[] = {
- { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY },
- };
- static const struct txrx_config rk1808_txrx_config[] = {
- { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY },
- };
- static const struct txrx_config rk3308_txrx_config[] = {
- { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY },
- { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY },
- };
- static const struct txrx_config rk3568_txrx_config[] = {
- { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY },
- { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE },
- { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE },
- { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY },
- { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY },
- { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE },
- };
- static const struct txrx_config rv1126_txrx_config[] = {
- { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY },
- };
- static struct rk_i2s_soc_data px30_i2s_soc_data = {
- .softrst_offset = 0x0300,
- .configs = px30_txrx_config,
- .config_count = ARRAY_SIZE(px30_txrx_config),
- .init = common_soc_init,
- };
- static struct rk_i2s_soc_data rk1808_i2s_soc_data = {
- .softrst_offset = 0x0300,
- .configs = rk1808_txrx_config,
- .config_count = ARRAY_SIZE(rk1808_txrx_config),
- .init = common_soc_init,
- };
- static struct rk_i2s_soc_data rk3308_i2s_soc_data = {
- .softrst_offset = 0x0400,
- .grf_reg_offset = 0x0308,
- .grf_shift = 5,
- .configs = rk3308_txrx_config,
- .config_count = ARRAY_SIZE(rk3308_txrx_config),
- .init = common_soc_init,
- };
- static struct rk_i2s_soc_data rk3568_i2s_soc_data = {
- .softrst_offset = 0x0400,
- .configs = rk3568_txrx_config,
- .config_count = ARRAY_SIZE(rk3568_txrx_config),
- .init = common_soc_init,
- };
- static struct rk_i2s_soc_data rv1126_i2s_soc_data = {
- .softrst_offset = 0x0300,
- .configs = rv1126_txrx_config,
- .config_count = ARRAY_SIZE(rv1126_txrx_config),
- .init = common_soc_init,
- };
- static const struct of_device_id rockchip_i2s_tdm_match[] = {
- { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data },
- { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data },
- { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data },
- { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data },
- { .compatible = "rockchip,rv1126-i2s-tdm", .data = &rv1126_i2s_soc_data },
- {},
- };
- static const struct snd_soc_dai_driver i2s_tdm_dai = {
- .probe = rockchip_i2s_tdm_dai_probe,
- .ops = &rockchip_i2s_tdm_dai_ops,
- };
- static int rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm)
- {
- struct snd_soc_dai_driver *dai;
- struct property *dma_names;
- const char *dma_name;
- u64 formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE);
- struct device_node *node = i2s_tdm->dev->of_node;
- of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
- if (!strcmp(dma_name, "tx"))
- i2s_tdm->has_playback = true;
- if (!strcmp(dma_name, "rx"))
- i2s_tdm->has_capture = true;
- }
- dai = devm_kmemdup(i2s_tdm->dev, &i2s_tdm_dai,
- sizeof(*dai), GFP_KERNEL);
- if (!dai)
- return -ENOMEM;
- if (i2s_tdm->has_playback) {
- dai->playback.stream_name = "Playback";
- dai->playback.channels_min = 2;
- dai->playback.channels_max = 8;
- dai->playback.rates = SNDRV_PCM_RATE_8000_192000;
- dai->playback.formats = formats;
- }
- if (i2s_tdm->has_capture) {
- dai->capture.stream_name = "Capture";
- dai->capture.channels_min = 2;
- dai->capture.channels_max = 8;
- dai->capture.rates = SNDRV_PCM_RATE_8000_192000;
- dai->capture.formats = formats;
- }
- if (i2s_tdm->clk_trcm != TRCM_TXRX)
- dai->symmetric_rate = 1;
- i2s_tdm->dai = dai;
- return 0;
- }
- static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm,
- int num,
- bool is_rx_path)
- {
- unsigned int *i2s_data;
- int i, j;
- if (is_rx_path)
- i2s_data = i2s_tdm->i2s_sdis;
- else
- i2s_data = i2s_tdm->i2s_sdos;
- for (i = 0; i < num; i++) {
- if (i2s_data[i] > CH_GRP_MAX - 1) {
- dev_err(i2s_tdm->dev,
- "%s path i2s_data[%d]: %d is too high, max is: %d\n",
- is_rx_path ? "RX" : "TX",
- i, i2s_data[i], CH_GRP_MAX);
- return -EINVAL;
- }
- for (j = 0; j < num; j++) {
- if (i == j)
- continue;
- if (i2s_data[i] == i2s_data[j]) {
- dev_err(i2s_tdm->dev,
- "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n",
- is_rx_path ? "RX" : "TX",
- i, i2s_data[i],
- j, i2s_data[j]);
- return -EINVAL;
- }
- }
- }
- return 0;
- }
- static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
- int num)
- {
- int idx;
- for (idx = 0; idx < num; idx++) {
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
- I2S_TXCR_PATH_MASK(idx),
- I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx]));
- }
- }
- static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
- int num)
- {
- int idx;
- for (idx = 0; idx < num; idx++) {
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
- I2S_RXCR_PATH_MASK(idx),
- I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx]));
- }
- }
- static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
- int num, bool is_rx_path)
- {
- if (is_rx_path)
- rockchip_i2s_tdm_rx_path_config(i2s_tdm, num);
- else
- rockchip_i2s_tdm_tx_path_config(i2s_tdm, num);
- }
- static int rockchip_i2s_tdm_get_calibrate_mclks(struct rk_i2s_tdm_dev *i2s_tdm)
- {
- int num_mclks = 0;
- i2s_tdm->mclk_tx_src = devm_clk_get(i2s_tdm->dev, "mclk_tx_src");
- if (!IS_ERR(i2s_tdm->mclk_tx_src))
- num_mclks++;
- i2s_tdm->mclk_rx_src = devm_clk_get(i2s_tdm->dev, "mclk_rx_src");
- if (!IS_ERR(i2s_tdm->mclk_rx_src))
- num_mclks++;
- i2s_tdm->mclk_root0 = devm_clk_get(i2s_tdm->dev, "mclk_root0");
- if (!IS_ERR(i2s_tdm->mclk_root0))
- num_mclks++;
- i2s_tdm->mclk_root1 = devm_clk_get(i2s_tdm->dev, "mclk_root1");
- if (!IS_ERR(i2s_tdm->mclk_root1))
- num_mclks++;
- if (num_mclks < 4 && num_mclks != 0)
- return -ENOENT;
- if (num_mclks == 4)
- i2s_tdm->mclk_calibrate = 1;
- return 0;
- }
- static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
- struct device_node *np,
- bool is_rx_path)
- {
- char *i2s_tx_path_prop = "rockchip,i2s-tx-route";
- char *i2s_rx_path_prop = "rockchip,i2s-rx-route";
- char *i2s_path_prop;
- unsigned int *i2s_data;
- int num, ret = 0;
- if (is_rx_path) {
- i2s_path_prop = i2s_rx_path_prop;
- i2s_data = i2s_tdm->i2s_sdis;
- } else {
- i2s_path_prop = i2s_tx_path_prop;
- i2s_data = i2s_tdm->i2s_sdos;
- }
- num = of_count_phandle_with_args(np, i2s_path_prop, NULL);
- if (num < 0) {
- if (num != -ENOENT) {
- dev_err(i2s_tdm->dev,
- "Failed to read '%s' num: %d\n",
- i2s_path_prop, num);
- ret = num;
- }
- return ret;
- } else if (num != CH_GRP_MAX) {
- dev_err(i2s_tdm->dev,
- "The num: %d should be: %d\n", num, CH_GRP_MAX);
- return -EINVAL;
- }
- ret = of_property_read_u32_array(np, i2s_path_prop,
- i2s_data, num);
- if (ret < 0) {
- dev_err(i2s_tdm->dev,
- "Failed to read '%s': %d\n",
- i2s_path_prop, ret);
- return ret;
- }
- ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path);
- if (ret < 0) {
- dev_err(i2s_tdm->dev,
- "Failed to check i2s data bus: %d\n", ret);
- return ret;
- }
- rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path);
- return 0;
- }
- static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
- struct device_node *np)
- {
- return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0);
- }
- static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
- struct device_node *np)
- {
- return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1);
- }
- static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
- {
- struct device_node *node = pdev->dev.of_node;
- const struct of_device_id *of_id;
- struct rk_i2s_tdm_dev *i2s_tdm;
- struct resource *res;
- void __iomem *regs;
- int ret;
- i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL);
- if (!i2s_tdm)
- return -ENOMEM;
- i2s_tdm->dev = &pdev->dev;
- of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
- if (!of_id || !of_id->data)
- return -EINVAL;
- spin_lock_init(&i2s_tdm->lock);
- i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data;
- i2s_tdm->frame_width = 64;
- i2s_tdm->clk_trcm = TRCM_TXRX;
- if (of_property_read_bool(node, "rockchip,trcm-sync-tx-only"))
- i2s_tdm->clk_trcm = TRCM_TX;
- if (of_property_read_bool(node, "rockchip,trcm-sync-rx-only")) {
- if (i2s_tdm->clk_trcm) {
- dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n");
- return -EINVAL;
- }
- i2s_tdm->clk_trcm = TRCM_RX;
- }
- ret = rockchip_i2s_tdm_init_dai(i2s_tdm);
- if (ret)
- return ret;
- i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
- if (IS_ERR(i2s_tdm->grf))
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->grf),
- "Error in rockchip,grf\n");
- i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
- "tx-m");
- if (IS_ERR(i2s_tdm->tx_reset)) {
- ret = PTR_ERR(i2s_tdm->tx_reset);
- return dev_err_probe(i2s_tdm->dev, ret,
- "Error in tx-m reset control\n");
- }
- i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
- "rx-m");
- if (IS_ERR(i2s_tdm->rx_reset)) {
- ret = PTR_ERR(i2s_tdm->rx_reset);
- return dev_err_probe(i2s_tdm->dev, ret,
- "Error in rx-m reset control\n");
- }
- i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk");
- if (IS_ERR(i2s_tdm->hclk)) {
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->hclk),
- "Failed to get clock hclk\n");
- }
- i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx");
- if (IS_ERR(i2s_tdm->mclk_tx)) {
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_tx),
- "Failed to get clock mclk_tx\n");
- }
- i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx");
- if (IS_ERR(i2s_tdm->mclk_rx)) {
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_rx),
- "Failed to get clock mclk_rx\n");
- }
- i2s_tdm->io_multiplex =
- of_property_read_bool(node, "rockchip,io-multiplex");
- ret = rockchip_i2s_tdm_get_calibrate_mclks(i2s_tdm);
- if (ret)
- return dev_err_probe(i2s_tdm->dev, ret,
- "mclk-calibrate clocks missing");
- regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(regs)) {
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs),
- "Failed to get resource IORESOURCE_MEM\n");
- }
- i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
- &rockchip_i2s_tdm_regmap_config);
- if (IS_ERR(i2s_tdm->regmap)) {
- return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->regmap),
- "Failed to initialise regmap\n");
- }
- if (i2s_tdm->has_playback) {
- i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
- i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s_tdm->playback_dma_data.maxburst = 8;
- }
- if (i2s_tdm->has_capture) {
- i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;
- i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s_tdm->capture_dma_data.maxburst = 8;
- }
- ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node);
- if (ret < 0) {
- dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret);
- return ret;
- }
- ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node);
- if (ret < 0) {
- dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret);
- return ret;
- }
- dev_set_drvdata(&pdev->dev, i2s_tdm);
- ret = clk_prepare_enable(i2s_tdm->hclk);
- if (ret) {
- return dev_err_probe(i2s_tdm->dev, ret,
- "Failed to enable clock hclk\n");
- }
- ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
- if (ret) {
- ret = dev_err_probe(i2s_tdm->dev, ret,
- "Failed to enable one or more mclks\n");
- goto err_disable_hclk;
- }
- if (i2s_tdm->mclk_calibrate) {
- i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0);
- i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1);
- i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq;
- i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq;
- }
- pm_runtime_enable(&pdev->dev);
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
- I2S_DMACR_TDL(16));
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
- I2S_DMACR_RDL(16));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK,
- i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT);
- if (i2s_tdm->soc_data && i2s_tdm->soc_data->init)
- i2s_tdm->soc_data->init(&pdev->dev, res->start);
- ret = devm_snd_soc_register_component(&pdev->dev,
- &rockchip_i2s_tdm_component,
- i2s_tdm->dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI\n");
- goto err_suspend;
- }
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret) {
- dev_err(&pdev->dev, "Could not register PCM\n");
- goto err_suspend;
- }
- return 0;
- err_suspend:
- if (!pm_runtime_status_suspended(&pdev->dev))
- i2s_tdm_runtime_suspend(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- err_disable_hclk:
- clk_disable_unprepare(i2s_tdm->hclk);
- return ret;
- }
- static int rockchip_i2s_tdm_remove(struct platform_device *pdev)
- {
- if (!pm_runtime_status_suspended(&pdev->dev))
- i2s_tdm_runtime_suspend(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- return 0;
- }
- static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
- regcache_mark_dirty(i2s_tdm->regmap);
- return 0;
- }
- static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev)
- {
- struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
- int ret;
- ret = pm_runtime_resume_and_get(dev);
- if (ret < 0)
- return ret;
- ret = regcache_sync(i2s_tdm->regmap);
- pm_runtime_put(dev);
- return ret;
- }
- static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = {
- SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend,
- rockchip_i2s_tdm_resume)
- };
- static struct platform_driver rockchip_i2s_tdm_driver = {
- .probe = rockchip_i2s_tdm_probe,
- .remove = rockchip_i2s_tdm_remove,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(rockchip_i2s_tdm_match),
- .pm = &rockchip_i2s_tdm_pm_ops,
- },
- };
- module_platform_driver(rockchip_i2s_tdm_driver);
- MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface");
- MODULE_AUTHOR("Sugar Zhang <[email protected]>");
- MODULE_LICENSE("GPL v2");
- MODULE_ALIAS("platform:" DRV_NAME);
- MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match);
|