15033f43cSJassi Brar /* sound/soc/samsung/i2s.c 25033f43cSJassi Brar * 35033f43cSJassi Brar * ALSA SoC Audio Layer - Samsung I2S Controller driver 45033f43cSJassi Brar * 55033f43cSJassi Brar * Copyright (c) 2010 Samsung Electronics Co. Ltd. 6df8ad335SJaswinder Singh * Jaswinder Singh <jassisinghbrar@gmail.com> 75033f43cSJassi Brar * 85033f43cSJassi Brar * This program is free software; you can redistribute it and/or modify 95033f43cSJassi Brar * it under the terms of the GNU General Public License version 2 as 105033f43cSJassi Brar * published by the Free Software Foundation. 115033f43cSJassi Brar */ 125033f43cSJassi Brar 13074b89bbSSylwester Nawrocki #include <dt-bindings/sound/samsung-i2s.h> 145033f43cSJassi Brar #include <linux/delay.h> 155033f43cSJassi Brar #include <linux/slab.h> 165033f43cSJassi Brar #include <linux/clk.h> 17074b89bbSSylwester Nawrocki #include <linux/clk-provider.h> 185033f43cSJassi Brar #include <linux/io.h> 19da155d5bSPaul Gortmaker #include <linux/module.h> 2040476f61SPadmavathi Venna #include <linux/of.h> 212f7b5d14SSylwester Nawrocki #include <linux/of_device.h> 2240476f61SPadmavathi Venna #include <linux/of_gpio.h> 23c5cf4dbcSMark Brown #include <linux/pm_runtime.h> 245033f43cSJassi Brar 255033f43cSJassi Brar #include <sound/soc.h> 260378b6acSSeungwhan Youn #include <sound/pcm_params.h> 275033f43cSJassi Brar 28436d42c6SArnd Bergmann #include <linux/platform_data/asoc-s3c.h> 295033f43cSJassi Brar 305033f43cSJassi Brar #include "dma.h" 3161100f40SSangbeom Kim #include "idma.h" 325033f43cSJassi Brar #include "i2s.h" 33172a453dSSangbeom Kim #include "i2s-regs.h" 345033f43cSJassi Brar 355033f43cSJassi Brar #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 365033f43cSJassi Brar 37a404b72dSSylwester Nawrocki #define SAMSUNG_I2S_ID_PRIMARY 1 38a404b72dSSylwester Nawrocki #define SAMSUNG_I2S_ID_SECONDARY 2 39a404b72dSSylwester Nawrocki 40a5a56871SPadmavathi Venna struct samsung_i2s_variant_regs { 41a5a56871SPadmavathi Venna unsigned int bfs_off; 42a5a56871SPadmavathi Venna unsigned int rfs_off; 43a5a56871SPadmavathi Venna unsigned int sdf_off; 44a5a56871SPadmavathi Venna unsigned int txr_off; 45a5a56871SPadmavathi Venna unsigned int rclksrc_off; 46a5a56871SPadmavathi Venna unsigned int mss_off; 47a5a56871SPadmavathi Venna unsigned int cdclkcon_off; 48a5a56871SPadmavathi Venna unsigned int lrp_off; 49a5a56871SPadmavathi Venna unsigned int bfs_mask; 50a5a56871SPadmavathi Venna unsigned int rfs_mask; 51a5a56871SPadmavathi Venna unsigned int ftx0cnt_off; 52a5a56871SPadmavathi Venna }; 53a5a56871SPadmavathi Venna 5440476f61SPadmavathi Venna struct samsung_i2s_dai_data { 557da493e9SPadmavathi Venna u32 quirks; 564720c2feSJaechul Lee unsigned int pcm_rates; 57a5a56871SPadmavathi Venna const struct samsung_i2s_variant_regs *i2s_variant_regs; 5840476f61SPadmavathi Venna }; 5940476f61SPadmavathi Venna 605033f43cSJassi Brar struct i2s_dai { 615033f43cSJassi Brar /* Platform device for this DAI */ 625033f43cSJassi Brar struct platform_device *pdev; 63e2e16fa6SSylwester Nawrocki 645033f43cSJassi Brar /* Frame Clock */ 655033f43cSJassi Brar unsigned frmclk; 665033f43cSJassi Brar /* 675033f43cSJassi Brar * Specifically requested RCLK,BCLK by MACHINE Driver. 685033f43cSJassi Brar * 0 indicates CPU driver is free to choose any value. 695033f43cSJassi Brar */ 705033f43cSJassi Brar unsigned rfs, bfs; 715033f43cSJassi Brar /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ 725033f43cSJassi Brar struct i2s_dai *pri_dai; 735033f43cSJassi Brar /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ 745033f43cSJassi Brar struct i2s_dai *sec_dai; 755033f43cSJassi Brar #define DAI_OPENED (1 << 0) /* Dai is opened */ 765033f43cSJassi Brar #define DAI_MANAGER (1 << 1) /* Dai is the manager */ 775033f43cSJassi Brar unsigned mode; 78a404b72dSSylwester Nawrocki 795033f43cSJassi Brar /* Driver for this DAI */ 80a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *drv; 81a404b72dSSylwester Nawrocki 825033f43cSJassi Brar /* DMA parameters */ 8369e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data dma_playback; 8469e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data dma_capture; 8569e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data idma_playback; 869bdca822SArnd Bergmann dma_filter_fn filter; 87f3670536SSylwester Nawrocki 8889d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv; 895033f43cSJassi Brar }; 905033f43cSJassi Brar 915033f43cSJassi Brar /* Lock for cross i/f checks */ 925033f43cSJassi Brar static DEFINE_SPINLOCK(lock); 935033f43cSJassi Brar 94a404b72dSSylwester Nawrocki struct samsung_i2s_priv { 95a404b72dSSylwester Nawrocki struct platform_device *pdev; 967196c64cSSylwester Nawrocki struct platform_device *pdev_sec; 97a404b72dSSylwester Nawrocki 98e2e16fa6SSylwester Nawrocki /* Memory mapped SFR region */ 99e2e16fa6SSylwester Nawrocki void __iomem *addr; 100e2e16fa6SSylwester Nawrocki 101a404b72dSSylwester Nawrocki /* Spinlock protecting access to the device's registers */ 1029d7939c9SSylwester Nawrocki spinlock_t lock; 103a404b72dSSylwester Nawrocki 104a404b72dSSylwester Nawrocki /* CPU DAIs and their corresponding drivers */ 105a404b72dSSylwester Nawrocki struct i2s_dai *dai; 106a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *dai_drv; 107a404b72dSSylwester Nawrocki int num_dais; 10889d2e831SSylwester Nawrocki 109b5d015e6SSylwester Nawrocki /* The I2S controller's core clock */ 110b5d015e6SSylwester Nawrocki struct clk *clk; 111b5d015e6SSylwester Nawrocki 1123b0fa51fSSylwester Nawrocki /* Clock for generating I2S signals */ 1133b0fa51fSSylwester Nawrocki struct clk *op_clk; 1143b0fa51fSSylwester Nawrocki 1153b0fa51fSSylwester Nawrocki /* Rate of RCLK source clock */ 1163b0fa51fSSylwester Nawrocki unsigned long rclk_srcrate; 1173b0fa51fSSylwester Nawrocki 11881bcbf2cSSylwester Nawrocki /* Cache of selected I2S registers for system suspend */ 11981bcbf2cSSylwester Nawrocki u32 suspend_i2smod; 12081bcbf2cSSylwester Nawrocki u32 suspend_i2scon; 12181bcbf2cSSylwester Nawrocki u32 suspend_i2spsr; 12281bcbf2cSSylwester Nawrocki 1235bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *variant_regs; 124*5944170fSSylwester Nawrocki u32 quirks; 1255bfaeddcSSylwester Nawrocki 12689d2e831SSylwester Nawrocki /* The clock provider's data */ 12789d2e831SSylwester Nawrocki struct clk *clk_table[3]; 12889d2e831SSylwester Nawrocki struct clk_onecell_data clk_data; 129a404b72dSSylwester Nawrocki }; 130a404b72dSSylwester Nawrocki 131a404b72dSSylwester Nawrocki /* Returns true if this is the 'overlay' stereo DAI */ 1325033f43cSJassi Brar static inline bool is_secondary(struct i2s_dai *i2s) 1335033f43cSJassi Brar { 134a404b72dSSylwester Nawrocki return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY; 1355033f43cSJassi Brar } 1365033f43cSJassi Brar 1375033f43cSJassi Brar /* If operating in SoC-Slave mode */ 1385033f43cSJassi Brar static inline bool is_slave(struct i2s_dai *i2s) 1395033f43cSJassi Brar { 140e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 141e2e16fa6SSylwester Nawrocki 142e2e16fa6SSylwester Nawrocki u32 mod = readl(priv->addr + I2SMOD); 1435bfaeddcSSylwester Nawrocki return (mod & (1 << priv->variant_regs->mss_off)) ? true : false; 1445033f43cSJassi Brar } 1455033f43cSJassi Brar 1465033f43cSJassi Brar /* If this interface of the controller is transmitting data */ 1475033f43cSJassi Brar static inline bool tx_active(struct i2s_dai *i2s) 1485033f43cSJassi Brar { 1495033f43cSJassi Brar u32 active; 1505033f43cSJassi Brar 1515033f43cSJassi Brar if (!i2s) 1525033f43cSJassi Brar return false; 1535033f43cSJassi Brar 154e2e16fa6SSylwester Nawrocki active = readl(i2s->priv->addr + I2SCON); 1555033f43cSJassi Brar 1565033f43cSJassi Brar if (is_secondary(i2s)) 1575033f43cSJassi Brar active &= CON_TXSDMA_ACTIVE; 1585033f43cSJassi Brar else 1595033f43cSJassi Brar active &= CON_TXDMA_ACTIVE; 1605033f43cSJassi Brar 1615033f43cSJassi Brar return active ? true : false; 1625033f43cSJassi Brar } 1635033f43cSJassi Brar 164dcd60fc3SSylwester Nawrocki /* Return pointer to the other DAI */ 165dcd60fc3SSylwester Nawrocki static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s) 166dcd60fc3SSylwester Nawrocki { 167dcd60fc3SSylwester Nawrocki return i2s->pri_dai ? : i2s->sec_dai; 168dcd60fc3SSylwester Nawrocki } 169dcd60fc3SSylwester Nawrocki 1705033f43cSJassi Brar /* If the other interface of the controller is transmitting data */ 1715033f43cSJassi Brar static inline bool other_tx_active(struct i2s_dai *i2s) 1725033f43cSJassi Brar { 173dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 1745033f43cSJassi Brar 1755033f43cSJassi Brar return tx_active(other); 1765033f43cSJassi Brar } 1775033f43cSJassi Brar 1785033f43cSJassi Brar /* If any interface of the controller is transmitting data */ 1795033f43cSJassi Brar static inline bool any_tx_active(struct i2s_dai *i2s) 1805033f43cSJassi Brar { 1815033f43cSJassi Brar return tx_active(i2s) || other_tx_active(i2s); 1825033f43cSJassi Brar } 1835033f43cSJassi Brar 1845033f43cSJassi Brar /* If this interface of the controller is receiving data */ 1855033f43cSJassi Brar static inline bool rx_active(struct i2s_dai *i2s) 1865033f43cSJassi Brar { 1875033f43cSJassi Brar u32 active; 1885033f43cSJassi Brar 1895033f43cSJassi Brar if (!i2s) 1905033f43cSJassi Brar return false; 1915033f43cSJassi Brar 192e2e16fa6SSylwester Nawrocki active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE; 1935033f43cSJassi Brar 1945033f43cSJassi Brar return active ? true : false; 1955033f43cSJassi Brar } 1965033f43cSJassi Brar 1975033f43cSJassi Brar /* If the other interface of the controller is receiving data */ 1985033f43cSJassi Brar static inline bool other_rx_active(struct i2s_dai *i2s) 1995033f43cSJassi Brar { 200dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 2015033f43cSJassi Brar 2025033f43cSJassi Brar return rx_active(other); 2035033f43cSJassi Brar } 2045033f43cSJassi Brar 2055033f43cSJassi Brar /* If any interface of the controller is receiving data */ 2065033f43cSJassi Brar static inline bool any_rx_active(struct i2s_dai *i2s) 2075033f43cSJassi Brar { 2085033f43cSJassi Brar return rx_active(i2s) || other_rx_active(i2s); 2095033f43cSJassi Brar } 2105033f43cSJassi Brar 2115033f43cSJassi Brar /* If the other DAI is transmitting or receiving data */ 2125033f43cSJassi Brar static inline bool other_active(struct i2s_dai *i2s) 2135033f43cSJassi Brar { 2145033f43cSJassi Brar return other_rx_active(i2s) || other_tx_active(i2s); 2155033f43cSJassi Brar } 2165033f43cSJassi Brar 2175033f43cSJassi Brar /* If this DAI is transmitting or receiving data */ 2185033f43cSJassi Brar static inline bool this_active(struct i2s_dai *i2s) 2195033f43cSJassi Brar { 2205033f43cSJassi Brar return tx_active(i2s) || rx_active(i2s); 2215033f43cSJassi Brar } 2225033f43cSJassi Brar 2235033f43cSJassi Brar /* If the controller is active anyway */ 2245033f43cSJassi Brar static inline bool any_active(struct i2s_dai *i2s) 2255033f43cSJassi Brar { 2265033f43cSJassi Brar return this_active(i2s) || other_active(i2s); 2275033f43cSJassi Brar } 2285033f43cSJassi Brar 2295033f43cSJassi Brar static inline struct i2s_dai *to_info(struct snd_soc_dai *dai) 2305033f43cSJassi Brar { 231a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 232a404b72dSSylwester Nawrocki 233a404b72dSSylwester Nawrocki return &priv->dai[dai->id - 1]; 2345033f43cSJassi Brar } 2355033f43cSJassi Brar 2365033f43cSJassi Brar static inline bool is_opened(struct i2s_dai *i2s) 2375033f43cSJassi Brar { 2385033f43cSJassi Brar if (i2s && (i2s->mode & DAI_OPENED)) 2395033f43cSJassi Brar return true; 2405033f43cSJassi Brar else 2415033f43cSJassi Brar return false; 2425033f43cSJassi Brar } 2435033f43cSJassi Brar 2445033f43cSJassi Brar static inline bool is_manager(struct i2s_dai *i2s) 2455033f43cSJassi Brar { 2465033f43cSJassi Brar if (is_opened(i2s) && (i2s->mode & DAI_MANAGER)) 2475033f43cSJassi Brar return true; 2485033f43cSJassi Brar else 2495033f43cSJassi Brar return false; 2505033f43cSJassi Brar } 2515033f43cSJassi Brar 2525033f43cSJassi Brar /* Read RCLK of I2S (in multiples of LRCLK) */ 2535033f43cSJassi Brar static inline unsigned get_rfs(struct i2s_dai *i2s) 2545033f43cSJassi Brar { 255e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 2564ca0c0d4SPadmavathi Venna u32 rfs; 257e2e16fa6SSylwester Nawrocki 2585bfaeddcSSylwester Nawrocki rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off; 2595bfaeddcSSylwester Nawrocki rfs &= priv->variant_regs->rfs_mask; 2605033f43cSJassi Brar 2615033f43cSJassi Brar switch (rfs) { 262a5a56871SPadmavathi Venna case 7: return 192; 263a5a56871SPadmavathi Venna case 6: return 96; 264a5a56871SPadmavathi Venna case 5: return 128; 265a5a56871SPadmavathi Venna case 4: return 64; 2665033f43cSJassi Brar case 3: return 768; 2675033f43cSJassi Brar case 2: return 384; 2685033f43cSJassi Brar case 1: return 512; 2695033f43cSJassi Brar default: return 256; 2705033f43cSJassi Brar } 2715033f43cSJassi Brar } 2725033f43cSJassi Brar 2735033f43cSJassi Brar /* Write RCLK of I2S (in multiples of LRCLK) */ 2745033f43cSJassi Brar static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) 2755033f43cSJassi Brar { 276e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 277e2e16fa6SSylwester Nawrocki u32 mod = readl(priv->addr + I2SMOD); 2785bfaeddcSSylwester Nawrocki int rfs_shift = priv->variant_regs->rfs_off; 2795033f43cSJassi Brar 2805bfaeddcSSylwester Nawrocki mod &= ~(priv->variant_regs->rfs_mask << rfs_shift); 2815033f43cSJassi Brar 2825033f43cSJassi Brar switch (rfs) { 283a5a56871SPadmavathi Venna case 192: 284a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); 285a5a56871SPadmavathi Venna break; 286a5a56871SPadmavathi Venna case 96: 287a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); 288a5a56871SPadmavathi Venna break; 289a5a56871SPadmavathi Venna case 128: 290a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); 291a5a56871SPadmavathi Venna break; 292a5a56871SPadmavathi Venna case 64: 293a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); 294a5a56871SPadmavathi Venna break; 2955033f43cSJassi Brar case 768: 296b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_768FS << rfs_shift); 2975033f43cSJassi Brar break; 2985033f43cSJassi Brar case 512: 299b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_512FS << rfs_shift); 3005033f43cSJassi Brar break; 3015033f43cSJassi Brar case 384: 302b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_384FS << rfs_shift); 3035033f43cSJassi Brar break; 3045033f43cSJassi Brar default: 305b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_256FS << rfs_shift); 3065033f43cSJassi Brar break; 3075033f43cSJassi Brar } 3085033f43cSJassi Brar 309e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 3105033f43cSJassi Brar } 3115033f43cSJassi Brar 3125033f43cSJassi Brar /* Read Bit-Clock of I2S (in multiples of LRCLK) */ 3135033f43cSJassi Brar static inline unsigned get_bfs(struct i2s_dai *i2s) 3145033f43cSJassi Brar { 315e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 3164ca0c0d4SPadmavathi Venna u32 bfs; 317e2e16fa6SSylwester Nawrocki 3185bfaeddcSSylwester Nawrocki bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off; 3195bfaeddcSSylwester Nawrocki bfs &= priv->variant_regs->bfs_mask; 3205033f43cSJassi Brar 3215033f43cSJassi Brar switch (bfs) { 3224ca0c0d4SPadmavathi Venna case 8: return 256; 3234ca0c0d4SPadmavathi Venna case 7: return 192; 3244ca0c0d4SPadmavathi Venna case 6: return 128; 3254ca0c0d4SPadmavathi Venna case 5: return 96; 3264ca0c0d4SPadmavathi Venna case 4: return 64; 3275033f43cSJassi Brar case 3: return 24; 3285033f43cSJassi Brar case 2: return 16; 3295033f43cSJassi Brar case 1: return 48; 3305033f43cSJassi Brar default: return 32; 3315033f43cSJassi Brar } 3325033f43cSJassi Brar } 3335033f43cSJassi Brar 3345033f43cSJassi Brar /* Write Bit-Clock of I2S (in multiples of LRCLK) */ 3355033f43cSJassi Brar static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) 3365033f43cSJassi Brar { 337e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 338e2e16fa6SSylwester Nawrocki u32 mod = readl(priv->addr + I2SMOD); 339*5944170fSSylwester Nawrocki int tdm = priv->quirks & QUIRK_SUPPORTS_TDM; 3405bfaeddcSSylwester Nawrocki int bfs_shift = priv->variant_regs->bfs_off; 3414ca0c0d4SPadmavathi Venna 3424ca0c0d4SPadmavathi Venna /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ 3434ca0c0d4SPadmavathi Venna if (!tdm && bfs > 48) { 3444ca0c0d4SPadmavathi Venna dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); 3454ca0c0d4SPadmavathi Venna return; 3464ca0c0d4SPadmavathi Venna } 3475033f43cSJassi Brar 3485bfaeddcSSylwester Nawrocki mod &= ~(priv->variant_regs->bfs_mask << bfs_shift); 349a5a56871SPadmavathi Venna 3505033f43cSJassi Brar switch (bfs) { 3515033f43cSJassi Brar case 48: 352b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_48FS << bfs_shift); 3535033f43cSJassi Brar break; 3545033f43cSJassi Brar case 32: 355b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_32FS << bfs_shift); 3565033f43cSJassi Brar break; 3575033f43cSJassi Brar case 24: 358b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_24FS << bfs_shift); 3595033f43cSJassi Brar break; 3605033f43cSJassi Brar case 16: 361b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_16FS << bfs_shift); 3625033f43cSJassi Brar break; 3634ca0c0d4SPadmavathi Venna case 64: 3644ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); 3654ca0c0d4SPadmavathi Venna break; 3664ca0c0d4SPadmavathi Venna case 96: 3674ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); 3684ca0c0d4SPadmavathi Venna break; 3694ca0c0d4SPadmavathi Venna case 128: 3704ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); 3714ca0c0d4SPadmavathi Venna break; 3724ca0c0d4SPadmavathi Venna case 192: 3734ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); 3744ca0c0d4SPadmavathi Venna break; 3754ca0c0d4SPadmavathi Venna case 256: 3764ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); 3775033f43cSJassi Brar break; 3785033f43cSJassi Brar default: 3795033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); 3805033f43cSJassi Brar return; 3815033f43cSJassi Brar } 3825033f43cSJassi Brar 383e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 3845033f43cSJassi Brar } 3855033f43cSJassi Brar 3865033f43cSJassi Brar /* Sample-Size */ 3875033f43cSJassi Brar static inline int get_blc(struct i2s_dai *i2s) 3885033f43cSJassi Brar { 389e2e16fa6SSylwester Nawrocki int blc = readl(i2s->priv->addr + I2SMOD); 3905033f43cSJassi Brar 3915033f43cSJassi Brar blc = (blc >> 13) & 0x3; 3925033f43cSJassi Brar 3935033f43cSJassi Brar switch (blc) { 3945033f43cSJassi Brar case 2: return 24; 3955033f43cSJassi Brar case 1: return 8; 3965033f43cSJassi Brar default: return 16; 3975033f43cSJassi Brar } 3985033f43cSJassi Brar } 3995033f43cSJassi Brar 4005033f43cSJassi Brar /* TX Channel Control */ 4015033f43cSJassi Brar static void i2s_txctrl(struct i2s_dai *i2s, int on) 4025033f43cSJassi Brar { 403e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 404e2e16fa6SSylwester Nawrocki void __iomem *addr = priv->addr; 4055bfaeddcSSylwester Nawrocki int txr_off = priv->variant_regs->txr_off; 4065033f43cSJassi Brar u32 con = readl(addr + I2SCON); 407a5a56871SPadmavathi Venna u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); 4085033f43cSJassi Brar 4095033f43cSJassi Brar if (on) { 4105033f43cSJassi Brar con |= CON_ACTIVE; 4115033f43cSJassi Brar con &= ~CON_TXCH_PAUSE; 4125033f43cSJassi Brar 4135033f43cSJassi Brar if (is_secondary(i2s)) { 4145033f43cSJassi Brar con |= CON_TXSDMA_ACTIVE; 4155033f43cSJassi Brar con &= ~CON_TXSDMA_PAUSE; 4165033f43cSJassi Brar } else { 4175033f43cSJassi Brar con |= CON_TXDMA_ACTIVE; 4185033f43cSJassi Brar con &= ~CON_TXDMA_PAUSE; 4195033f43cSJassi Brar } 4205033f43cSJassi Brar 4215033f43cSJassi Brar if (any_rx_active(i2s)) 422a5a56871SPadmavathi Venna mod |= 2 << txr_off; 4235033f43cSJassi Brar else 424a5a56871SPadmavathi Venna mod |= 0 << txr_off; 4255033f43cSJassi Brar } else { 4265033f43cSJassi Brar if (is_secondary(i2s)) { 4275033f43cSJassi Brar con |= CON_TXSDMA_PAUSE; 4285033f43cSJassi Brar con &= ~CON_TXSDMA_ACTIVE; 4295033f43cSJassi Brar } else { 4305033f43cSJassi Brar con |= CON_TXDMA_PAUSE; 4315033f43cSJassi Brar con &= ~CON_TXDMA_ACTIVE; 4325033f43cSJassi Brar } 4335033f43cSJassi Brar 4345033f43cSJassi Brar if (other_tx_active(i2s)) { 4355033f43cSJassi Brar writel(con, addr + I2SCON); 4365033f43cSJassi Brar return; 4375033f43cSJassi Brar } 4385033f43cSJassi Brar 4395033f43cSJassi Brar con |= CON_TXCH_PAUSE; 4405033f43cSJassi Brar 4415033f43cSJassi Brar if (any_rx_active(i2s)) 442a5a56871SPadmavathi Venna mod |= 1 << txr_off; 4435033f43cSJassi Brar else 4445033f43cSJassi Brar con &= ~CON_ACTIVE; 4455033f43cSJassi Brar } 4465033f43cSJassi Brar 4475033f43cSJassi Brar writel(mod, addr + I2SMOD); 4485033f43cSJassi Brar writel(con, addr + I2SCON); 4495033f43cSJassi Brar } 4505033f43cSJassi Brar 4515033f43cSJassi Brar /* RX Channel Control */ 4525033f43cSJassi Brar static void i2s_rxctrl(struct i2s_dai *i2s, int on) 4535033f43cSJassi Brar { 454e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 455e2e16fa6SSylwester Nawrocki void __iomem *addr = priv->addr; 4565bfaeddcSSylwester Nawrocki int txr_off = priv->variant_regs->txr_off; 4575033f43cSJassi Brar u32 con = readl(addr + I2SCON); 458a5a56871SPadmavathi Venna u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); 4595033f43cSJassi Brar 4605033f43cSJassi Brar if (on) { 4615033f43cSJassi Brar con |= CON_RXDMA_ACTIVE | CON_ACTIVE; 4625033f43cSJassi Brar con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); 4635033f43cSJassi Brar 4645033f43cSJassi Brar if (any_tx_active(i2s)) 465a5a56871SPadmavathi Venna mod |= 2 << txr_off; 4665033f43cSJassi Brar else 467a5a56871SPadmavathi Venna mod |= 1 << txr_off; 4685033f43cSJassi Brar } else { 4695033f43cSJassi Brar con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; 4705033f43cSJassi Brar con &= ~CON_RXDMA_ACTIVE; 4715033f43cSJassi Brar 4725033f43cSJassi Brar if (any_tx_active(i2s)) 473a5a56871SPadmavathi Venna mod |= 0 << txr_off; 4745033f43cSJassi Brar else 4755033f43cSJassi Brar con &= ~CON_ACTIVE; 4765033f43cSJassi Brar } 4775033f43cSJassi Brar 4785033f43cSJassi Brar writel(mod, addr + I2SMOD); 4795033f43cSJassi Brar writel(con, addr + I2SCON); 4805033f43cSJassi Brar } 4815033f43cSJassi Brar 4825033f43cSJassi Brar /* Flush FIFO of an interface */ 4835033f43cSJassi Brar static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) 4845033f43cSJassi Brar { 4855033f43cSJassi Brar void __iomem *fic; 4865033f43cSJassi Brar u32 val; 4875033f43cSJassi Brar 4885033f43cSJassi Brar if (!i2s) 4895033f43cSJassi Brar return; 4905033f43cSJassi Brar 4915033f43cSJassi Brar if (is_secondary(i2s)) 492e2e16fa6SSylwester Nawrocki fic = i2s->priv->addr + I2SFICS; 4935033f43cSJassi Brar else 494e2e16fa6SSylwester Nawrocki fic = i2s->priv->addr + I2SFIC; 4955033f43cSJassi Brar 4965033f43cSJassi Brar /* Flush the FIFO */ 4975033f43cSJassi Brar writel(readl(fic) | flush, fic); 4985033f43cSJassi Brar 4995033f43cSJassi Brar /* Be patient */ 5005033f43cSJassi Brar val = msecs_to_loops(1) / 1000; /* 1 usec */ 5015033f43cSJassi Brar while (--val) 5025033f43cSJassi Brar cpu_relax(); 5035033f43cSJassi Brar 5045033f43cSJassi Brar writel(readl(fic) & ~flush, fic); 5055033f43cSJassi Brar } 5065033f43cSJassi Brar 5073b0fa51fSSylwester Nawrocki static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, 5083b0fa51fSSylwester Nawrocki int dir) 5095033f43cSJassi Brar { 5103b0fa51fSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 5115033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 512dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 5135bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs; 514a5a56871SPadmavathi Venna unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; 515a5a56871SPadmavathi Venna unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; 516ce8bcdbbSSylwester Nawrocki u32 mod, mask, val = 0; 517316fa9e0SCharles Keepax unsigned long flags; 518dc938ddbSMarek Szyprowski int ret = 0; 519dc938ddbSMarek Szyprowski 520dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 521ce8bcdbbSSylwester Nawrocki 5229d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 523e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 5249d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 5255033f43cSJassi Brar 5265033f43cSJassi Brar switch (clk_id) { 527c86d50f9SSylwester Nawrocki case SAMSUNG_I2S_OPCLK: 528ce8bcdbbSSylwester Nawrocki mask = MOD_OPCLK_MASK; 52945ae70e8SSylwester Nawrocki val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK; 530c86d50f9SSylwester Nawrocki break; 5315033f43cSJassi Brar case SAMSUNG_I2S_CDCLK: 532ce8bcdbbSSylwester Nawrocki mask = 1 << i2s_regs->cdclkcon_off; 5335033f43cSJassi Brar /* Shouldn't matter in GATING(CLOCK_IN) mode */ 5345033f43cSJassi Brar if (dir == SND_SOC_CLOCK_IN) 5355033f43cSJassi Brar rfs = 0; 5365033f43cSJassi Brar 537133c2681SCharles Keepax if ((rfs && other && other->rfs && (other->rfs != rfs)) || 5385033f43cSJassi Brar (any_active(i2s) && 5395033f43cSJassi Brar (((dir == SND_SOC_CLOCK_IN) 540a5a56871SPadmavathi Venna && !(mod & cdcon_mask)) || 5415033f43cSJassi Brar ((dir == SND_SOC_CLOCK_OUT) 542a5a56871SPadmavathi Venna && (mod & cdcon_mask))))) { 5435033f43cSJassi Brar dev_err(&i2s->pdev->dev, 5445033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 545dc938ddbSMarek Szyprowski ret = -EAGAIN; 546dc938ddbSMarek Szyprowski goto err; 5475033f43cSJassi Brar } 5485033f43cSJassi Brar 5495033f43cSJassi Brar if (dir == SND_SOC_CLOCK_IN) 550ce8bcdbbSSylwester Nawrocki val = 1 << i2s_regs->cdclkcon_off; 5515033f43cSJassi Brar 5525033f43cSJassi Brar i2s->rfs = rfs; 5535033f43cSJassi Brar break; 5545033f43cSJassi Brar 5555033f43cSJassi Brar case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ 5565033f43cSJassi Brar case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ 557ce8bcdbbSSylwester Nawrocki mask = 1 << i2s_regs->rclksrc_off; 558ce8bcdbbSSylwester Nawrocki 559*5944170fSSylwester Nawrocki if ((priv->quirks & QUIRK_NO_MUXPSR) 5605033f43cSJassi Brar || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) 5615033f43cSJassi Brar clk_id = 0; 5625033f43cSJassi Brar else 5635033f43cSJassi Brar clk_id = 1; 5645033f43cSJassi Brar 5655033f43cSJassi Brar if (!any_active(i2s)) { 5663b0fa51fSSylwester Nawrocki if (priv->op_clk && !IS_ERR(priv->op_clk)) { 567a5a56871SPadmavathi Venna if ((clk_id && !(mod & rsrc_mask)) || 568a5a56871SPadmavathi Venna (!clk_id && (mod & rsrc_mask))) { 5693b0fa51fSSylwester Nawrocki clk_disable_unprepare(priv->op_clk); 5703b0fa51fSSylwester Nawrocki clk_put(priv->op_clk); 5715033f43cSJassi Brar } else { 5723b0fa51fSSylwester Nawrocki priv->rclk_srcrate = 5733b0fa51fSSylwester Nawrocki clk_get_rate(priv->op_clk); 574dc938ddbSMarek Szyprowski goto done; 5755033f43cSJassi Brar } 5765033f43cSJassi Brar } 5775033f43cSJassi Brar 5781974a042SPadmavathi Venna if (clk_id) 5793b0fa51fSSylwester Nawrocki priv->op_clk = clk_get(&i2s->pdev->dev, 5801974a042SPadmavathi Venna "i2s_opclk1"); 5811974a042SPadmavathi Venna else 5823b0fa51fSSylwester Nawrocki priv->op_clk = clk_get(&i2s->pdev->dev, 5831974a042SPadmavathi Venna "i2s_opclk0"); 584a6aba536SSylwester Nawrocki 5853b0fa51fSSylwester Nawrocki if (WARN_ON(IS_ERR(priv->op_clk))) { 5863b0fa51fSSylwester Nawrocki ret = PTR_ERR(priv->op_clk); 5873b0fa51fSSylwester Nawrocki priv->op_clk = NULL; 588dc938ddbSMarek Szyprowski goto err; 589dc938ddbSMarek Szyprowski } 590a6aba536SSylwester Nawrocki 5913b0fa51fSSylwester Nawrocki ret = clk_prepare_enable(priv->op_clk); 5926431a7e3SChristophe Jaillet if (ret) { 5933b0fa51fSSylwester Nawrocki clk_put(priv->op_clk); 5943b0fa51fSSylwester Nawrocki priv->op_clk = NULL; 595f5c97c7bSArvind Yadav goto err; 5966431a7e3SChristophe Jaillet } 5973b0fa51fSSylwester Nawrocki priv->rclk_srcrate = clk_get_rate(priv->op_clk); 5985033f43cSJassi Brar 599a5a56871SPadmavathi Venna } else if ((!clk_id && (mod & rsrc_mask)) 600a5a56871SPadmavathi Venna || (clk_id && !(mod & rsrc_mask))) { 6015033f43cSJassi Brar dev_err(&i2s->pdev->dev, 6025033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 603dc938ddbSMarek Szyprowski ret = -EAGAIN; 604dc938ddbSMarek Szyprowski goto err; 6055033f43cSJassi Brar } else { 6065033f43cSJassi Brar /* Call can't be on the active DAI */ 607dc938ddbSMarek Szyprowski goto done; 6085033f43cSJassi Brar } 6095033f43cSJassi Brar 610ce8bcdbbSSylwester Nawrocki if (clk_id == 1) 611ce8bcdbbSSylwester Nawrocki val = 1 << i2s_regs->rclksrc_off; 612b2de1d20SPadmavathi Venna break; 6135033f43cSJassi Brar default: 6145033f43cSJassi Brar dev_err(&i2s->pdev->dev, "We don't serve that!\n"); 615dc938ddbSMarek Szyprowski ret = -EINVAL; 616dc938ddbSMarek Szyprowski goto err; 6175033f43cSJassi Brar } 6185033f43cSJassi Brar 6199d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 620e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 621ce8bcdbbSSylwester Nawrocki mod = (mod & ~mask) | val; 622e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 6239d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 624dc938ddbSMarek Szyprowski done: 625dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 6265033f43cSJassi Brar 6275033f43cSJassi Brar return 0; 628dc938ddbSMarek Szyprowski err: 629dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 630dc938ddbSMarek Szyprowski return ret; 6315033f43cSJassi Brar } 6325033f43cSJassi Brar 63389d2e831SSylwester Nawrocki static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 6345033f43cSJassi Brar { 63589d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 6365033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 637a5a56871SPadmavathi Venna int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; 638ce8bcdbbSSylwester Nawrocki u32 mod, tmp = 0; 639316fa9e0SCharles Keepax unsigned long flags; 6405033f43cSJassi Brar 6415bfaeddcSSylwester Nawrocki lrp_shift = priv->variant_regs->lrp_off; 6425bfaeddcSSylwester Nawrocki sdf_shift = priv->variant_regs->sdf_off; 6435bfaeddcSSylwester Nawrocki mod_slave = 1 << priv->variant_regs->mss_off; 6444ca0c0d4SPadmavathi Venna 645b60be4aaSPadmavathi Venna sdf_mask = MOD_SDF_MASK << sdf_shift; 646b60be4aaSPadmavathi Venna lrp_rlow = MOD_LR_RLOW << lrp_shift; 647b60be4aaSPadmavathi Venna 6485033f43cSJassi Brar /* Format is priority */ 6495033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 6505033f43cSJassi Brar case SND_SOC_DAIFMT_RIGHT_J: 651b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 652b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_MSB << sdf_shift); 6535033f43cSJassi Brar break; 6545033f43cSJassi Brar case SND_SOC_DAIFMT_LEFT_J: 655b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 656b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_LSB << sdf_shift); 6575033f43cSJassi Brar break; 6585033f43cSJassi Brar case SND_SOC_DAIFMT_I2S: 659b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_IIS << sdf_shift); 6605033f43cSJassi Brar break; 6615033f43cSJassi Brar default: 6625033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Format not supported\n"); 6635033f43cSJassi Brar return -EINVAL; 6645033f43cSJassi Brar } 6655033f43cSJassi Brar 6665033f43cSJassi Brar /* 6675033f43cSJassi Brar * INV flag is relative to the FORMAT flag - if set it simply 6685033f43cSJassi Brar * flips the polarity specified by the Standard 6695033f43cSJassi Brar */ 6705033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 6715033f43cSJassi Brar case SND_SOC_DAIFMT_NB_NF: 6725033f43cSJassi Brar break; 6735033f43cSJassi Brar case SND_SOC_DAIFMT_NB_IF: 674b60be4aaSPadmavathi Venna if (tmp & lrp_rlow) 675b60be4aaSPadmavathi Venna tmp &= ~lrp_rlow; 6765033f43cSJassi Brar else 677b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 6785033f43cSJassi Brar break; 6795033f43cSJassi Brar default: 6805033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Polarity not supported\n"); 6815033f43cSJassi Brar return -EINVAL; 6825033f43cSJassi Brar } 6835033f43cSJassi Brar 6845033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 6855033f43cSJassi Brar case SND_SOC_DAIFMT_CBM_CFM: 686a5a56871SPadmavathi Venna tmp |= mod_slave; 6875033f43cSJassi Brar break; 6885033f43cSJassi Brar case SND_SOC_DAIFMT_CBS_CFS: 689647d04f8SSylwester Nawrocki /* 690647d04f8SSylwester Nawrocki * Set default source clock in Master mode, only when the 691647d04f8SSylwester Nawrocki * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any 692647d04f8SSylwester Nawrocki * clock configuration assigned in DT is not overwritten. 693647d04f8SSylwester Nawrocki */ 6943b0fa51fSSylwester Nawrocki if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL) 6955033f43cSJassi Brar i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, 6965033f43cSJassi Brar 0, SND_SOC_CLOCK_IN); 6975033f43cSJassi Brar break; 6985033f43cSJassi Brar default: 6995033f43cSJassi Brar dev_err(&i2s->pdev->dev, "master/slave format not supported\n"); 7005033f43cSJassi Brar return -EINVAL; 7015033f43cSJassi Brar } 7025033f43cSJassi Brar 703dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 7049d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 705e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 706b60be4aaSPadmavathi Venna /* 707b60be4aaSPadmavathi Venna * Don't change the I2S mode if any controller is active on this 708b60be4aaSPadmavathi Venna * channel. 709b60be4aaSPadmavathi Venna */ 7105033f43cSJassi Brar if (any_active(i2s) && 711a5a56871SPadmavathi Venna ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { 7129d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 713dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 7145033f43cSJassi Brar dev_err(&i2s->pdev->dev, 7155033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 7165033f43cSJassi Brar return -EAGAIN; 7175033f43cSJassi Brar } 7185033f43cSJassi Brar 719a5a56871SPadmavathi Venna mod &= ~(sdf_mask | lrp_rlow | mod_slave); 7205033f43cSJassi Brar mod |= tmp; 721e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 7229d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 723dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 7245033f43cSJassi Brar 7255033f43cSJassi Brar return 0; 7265033f43cSJassi Brar } 7275033f43cSJassi Brar 7285033f43cSJassi Brar static int i2s_hw_params(struct snd_pcm_substream *substream, 7295033f43cSJassi Brar struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 7305033f43cSJassi Brar { 73189d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 7325033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 733ce8bcdbbSSylwester Nawrocki u32 mod, mask = 0, val = 0; 734860b454cSSylwester Nawrocki struct clk *rclksrc; 735316fa9e0SCharles Keepax unsigned long flags; 7365033f43cSJassi Brar 737dc938ddbSMarek Szyprowski WARN_ON(!pm_runtime_active(dai->dev)); 738dc938ddbSMarek Szyprowski 7395033f43cSJassi Brar if (!is_secondary(i2s)) 740ce8bcdbbSSylwester Nawrocki mask |= (MOD_DC2_EN | MOD_DC1_EN); 7415033f43cSJassi Brar 7425033f43cSJassi Brar switch (params_channels(params)) { 7435033f43cSJassi Brar case 6: 744ce8bcdbbSSylwester Nawrocki val |= MOD_DC2_EN; 7455019027aSGustavo A. R. Silva /* fall through */ 7465033f43cSJassi Brar case 4: 747ce8bcdbbSSylwester Nawrocki val |= MOD_DC1_EN; 7485033f43cSJassi Brar break; 7495033f43cSJassi Brar case 2: 750588fb705SSangsu Park if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 75169e7a69aSSylwester Nawrocki i2s->dma_playback.addr_width = 4; 752588fb705SSangsu Park else 75369e7a69aSSylwester Nawrocki i2s->dma_capture.addr_width = 4; 754588fb705SSangsu Park break; 755588fb705SSangsu Park case 1: 756588fb705SSangsu Park if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 75769e7a69aSSylwester Nawrocki i2s->dma_playback.addr_width = 2; 758588fb705SSangsu Park else 75969e7a69aSSylwester Nawrocki i2s->dma_capture.addr_width = 2; 760588fb705SSangsu Park 7615033f43cSJassi Brar break; 7625033f43cSJassi Brar default: 7635033f43cSJassi Brar dev_err(&i2s->pdev->dev, "%d channels not supported\n", 7645033f43cSJassi Brar params_channels(params)); 7655033f43cSJassi Brar return -EINVAL; 7665033f43cSJassi Brar } 7675033f43cSJassi Brar 7685033f43cSJassi Brar if (is_secondary(i2s)) 769ce8bcdbbSSylwester Nawrocki mask |= MOD_BLCS_MASK; 7705033f43cSJassi Brar else 771ce8bcdbbSSylwester Nawrocki mask |= MOD_BLCP_MASK; 7725033f43cSJassi Brar 7735033f43cSJassi Brar if (is_manager(i2s)) 774ce8bcdbbSSylwester Nawrocki mask |= MOD_BLC_MASK; 7755033f43cSJassi Brar 77688ce1465STushar Behera switch (params_width(params)) { 77788ce1465STushar Behera case 8: 7785033f43cSJassi Brar if (is_secondary(i2s)) 779ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_8BIT; 7805033f43cSJassi Brar else 781ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_8BIT; 7825033f43cSJassi Brar if (is_manager(i2s)) 783ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_8BIT; 7845033f43cSJassi Brar break; 78588ce1465STushar Behera case 16: 7865033f43cSJassi Brar if (is_secondary(i2s)) 787ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_16BIT; 7885033f43cSJassi Brar else 789ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_16BIT; 7905033f43cSJassi Brar if (is_manager(i2s)) 791ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_16BIT; 7925033f43cSJassi Brar break; 79388ce1465STushar Behera case 24: 7945033f43cSJassi Brar if (is_secondary(i2s)) 795ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_24BIT; 7965033f43cSJassi Brar else 797ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_24BIT; 7985033f43cSJassi Brar if (is_manager(i2s)) 799ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_24BIT; 8005033f43cSJassi Brar break; 8015033f43cSJassi Brar default: 8025033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", 8035033f43cSJassi Brar params_format(params)); 8045033f43cSJassi Brar return -EINVAL; 8055033f43cSJassi Brar } 806ce8bcdbbSSylwester Nawrocki 8079d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 808e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 809ce8bcdbbSSylwester Nawrocki mod = (mod & ~mask) | val; 810e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 8119d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 8125033f43cSJassi Brar 81369e7a69aSSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); 814d37bdf73SMark Brown 8155033f43cSJassi Brar i2s->frmclk = params_rate(params); 8165033f43cSJassi Brar 81789d2e831SSylwester Nawrocki rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC]; 818860b454cSSylwester Nawrocki if (rclksrc && !IS_ERR(rclksrc)) 8193b0fa51fSSylwester Nawrocki priv->rclk_srcrate = clk_get_rate(rclksrc); 820860b454cSSylwester Nawrocki 8215033f43cSJassi Brar return 0; 8225033f43cSJassi Brar } 8235033f43cSJassi Brar 8245033f43cSJassi Brar /* We set constraints on the substream acc to the version of I2S */ 8255033f43cSJassi Brar static int i2s_startup(struct snd_pcm_substream *substream, 8265033f43cSJassi Brar struct snd_soc_dai *dai) 8275033f43cSJassi Brar { 828*5944170fSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 8295033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 830dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8315033f43cSJassi Brar unsigned long flags; 8325033f43cSJassi Brar 833dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 834dc938ddbSMarek Szyprowski 8355033f43cSJassi Brar spin_lock_irqsave(&lock, flags); 8365033f43cSJassi Brar 8375033f43cSJassi Brar i2s->mode |= DAI_OPENED; 8385033f43cSJassi Brar 8395033f43cSJassi Brar if (is_manager(other)) 8405033f43cSJassi Brar i2s->mode &= ~DAI_MANAGER; 8415033f43cSJassi Brar else 8425033f43cSJassi Brar i2s->mode |= DAI_MANAGER; 8435033f43cSJassi Brar 844*5944170fSSylwester Nawrocki if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR)) 845e2e16fa6SSylwester Nawrocki writel(CON_RSTCLR, i2s->priv->addr + I2SCON); 8462d77828dSPadmavathi Venna 8475033f43cSJassi Brar spin_unlock_irqrestore(&lock, flags); 8485033f43cSJassi Brar 8495033f43cSJassi Brar return 0; 8505033f43cSJassi Brar } 8515033f43cSJassi Brar 8525033f43cSJassi Brar static void i2s_shutdown(struct snd_pcm_substream *substream, 8535033f43cSJassi Brar struct snd_soc_dai *dai) 8545033f43cSJassi Brar { 8555033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 856dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8575033f43cSJassi Brar unsigned long flags; 8585033f43cSJassi Brar 8595033f43cSJassi Brar spin_lock_irqsave(&lock, flags); 8605033f43cSJassi Brar 8615033f43cSJassi Brar i2s->mode &= ~DAI_OPENED; 8625033f43cSJassi Brar i2s->mode &= ~DAI_MANAGER; 8635033f43cSJassi Brar 864074b89bbSSylwester Nawrocki if (is_opened(other)) 8655033f43cSJassi Brar other->mode |= DAI_MANAGER; 866074b89bbSSylwester Nawrocki 8675033f43cSJassi Brar /* Reset any constraint on RFS and BFS */ 8685033f43cSJassi Brar i2s->rfs = 0; 8695033f43cSJassi Brar i2s->bfs = 0; 8705033f43cSJassi Brar 8715033f43cSJassi Brar spin_unlock_irqrestore(&lock, flags); 872dc938ddbSMarek Szyprowski 873dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 8745033f43cSJassi Brar } 8755033f43cSJassi Brar 8765033f43cSJassi Brar static int config_setup(struct i2s_dai *i2s) 8775033f43cSJassi Brar { 8783b0fa51fSSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 879dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8805033f43cSJassi Brar unsigned rfs, bfs, blc; 8815033f43cSJassi Brar u32 psr; 8825033f43cSJassi Brar 8835033f43cSJassi Brar blc = get_blc(i2s); 8845033f43cSJassi Brar 8855033f43cSJassi Brar bfs = i2s->bfs; 8865033f43cSJassi Brar 8875033f43cSJassi Brar if (!bfs && other) 8885033f43cSJassi Brar bfs = other->bfs; 8895033f43cSJassi Brar 8905033f43cSJassi Brar /* Select least possible multiple(2) if no constraint set */ 8915033f43cSJassi Brar if (!bfs) 8925033f43cSJassi Brar bfs = blc * 2; 8935033f43cSJassi Brar 8945033f43cSJassi Brar rfs = i2s->rfs; 8955033f43cSJassi Brar 8965033f43cSJassi Brar if (!rfs && other) 8975033f43cSJassi Brar rfs = other->rfs; 8985033f43cSJassi Brar 8995033f43cSJassi Brar if ((rfs == 256 || rfs == 512) && (blc == 24)) { 9005033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9015033f43cSJassi Brar "%d-RFS not supported for 24-blc\n", rfs); 9025033f43cSJassi Brar return -EINVAL; 9035033f43cSJassi Brar } 9045033f43cSJassi Brar 9055033f43cSJassi Brar if (!rfs) { 9065033f43cSJassi Brar if (bfs == 16 || bfs == 32) 9075033f43cSJassi Brar rfs = 256; 9085033f43cSJassi Brar else 9095033f43cSJassi Brar rfs = 384; 9105033f43cSJassi Brar } 9115033f43cSJassi Brar 9125033f43cSJassi Brar /* If already setup and running */ 9135033f43cSJassi Brar if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) { 9145033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9155033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 9165033f43cSJassi Brar return -EAGAIN; 9175033f43cSJassi Brar } 9185033f43cSJassi Brar 9195033f43cSJassi Brar set_bfs(i2s, bfs); 9205033f43cSJassi Brar set_rfs(i2s, rfs); 9215033f43cSJassi Brar 92277010010SPadmavathi Venna /* Don't bother with PSR in Slave mode */ 92377010010SPadmavathi Venna if (is_slave(i2s)) 92477010010SPadmavathi Venna return 0; 92577010010SPadmavathi Venna 926*5944170fSSylwester Nawrocki if (!(priv->quirks & QUIRK_NO_MUXPSR)) { 9273b0fa51fSSylwester Nawrocki psr = priv->rclk_srcrate / i2s->frmclk / rfs; 928e2e16fa6SSylwester Nawrocki writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR); 9295033f43cSJassi Brar dev_dbg(&i2s->pdev->dev, 9305033f43cSJassi Brar "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n", 9313b0fa51fSSylwester Nawrocki priv->rclk_srcrate, psr, rfs, bfs); 9325033f43cSJassi Brar } 9335033f43cSJassi Brar 9345033f43cSJassi Brar return 0; 9355033f43cSJassi Brar } 9365033f43cSJassi Brar 9375033f43cSJassi Brar static int i2s_trigger(struct snd_pcm_substream *substream, 9385033f43cSJassi Brar int cmd, struct snd_soc_dai *dai) 9395033f43cSJassi Brar { 9409d7939c9SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 9415033f43cSJassi Brar int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 9425033f43cSJassi Brar struct snd_soc_pcm_runtime *rtd = substream->private_data; 9435033f43cSJassi Brar struct i2s_dai *i2s = to_info(rtd->cpu_dai); 9445033f43cSJassi Brar unsigned long flags; 9455033f43cSJassi Brar 9465033f43cSJassi Brar switch (cmd) { 9475033f43cSJassi Brar case SNDRV_PCM_TRIGGER_START: 9485033f43cSJassi Brar case SNDRV_PCM_TRIGGER_RESUME: 9495033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 950dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 9519d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 9525033f43cSJassi Brar 9535033f43cSJassi Brar if (config_setup(i2s)) { 9549d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 9555033f43cSJassi Brar return -EINVAL; 9565033f43cSJassi Brar } 9575033f43cSJassi Brar 9585033f43cSJassi Brar if (capture) 9595033f43cSJassi Brar i2s_rxctrl(i2s, 1); 9605033f43cSJassi Brar else 9615033f43cSJassi Brar i2s_txctrl(i2s, 1); 9625033f43cSJassi Brar 9639d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 9645033f43cSJassi Brar break; 9655033f43cSJassi Brar case SNDRV_PCM_TRIGGER_STOP: 9665033f43cSJassi Brar case SNDRV_PCM_TRIGGER_SUSPEND: 9675033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 9689d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 9695033f43cSJassi Brar 970c90887feSJassi Brar if (capture) { 9715033f43cSJassi Brar i2s_rxctrl(i2s, 0); 972775bc971SJassi Brar i2s_fifo(i2s, FIC_RXFLUSH); 973c90887feSJassi Brar } else { 974c90887feSJassi Brar i2s_txctrl(i2s, 0); 975775bc971SJassi Brar i2s_fifo(i2s, FIC_TXFLUSH); 976c90887feSJassi Brar } 977775bc971SJassi Brar 9789d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 979dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 9805033f43cSJassi Brar break; 9815033f43cSJassi Brar } 9825033f43cSJassi Brar 9835033f43cSJassi Brar return 0; 9845033f43cSJassi Brar } 9855033f43cSJassi Brar 9865033f43cSJassi Brar static int i2s_set_clkdiv(struct snd_soc_dai *dai, 9875033f43cSJassi Brar int div_id, int div) 9885033f43cSJassi Brar { 9895033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 990dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 9915033f43cSJassi Brar 9925033f43cSJassi Brar switch (div_id) { 9935033f43cSJassi Brar case SAMSUNG_I2S_DIV_BCLK: 994dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 9955033f43cSJassi Brar if ((any_active(i2s) && div && (get_bfs(i2s) != div)) 9965033f43cSJassi Brar || (other && other->bfs && (other->bfs != div))) { 997dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 9985033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9995033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 10005033f43cSJassi Brar return -EAGAIN; 10015033f43cSJassi Brar } 10025033f43cSJassi Brar i2s->bfs = div; 1003dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 10045033f43cSJassi Brar break; 10055033f43cSJassi Brar default: 10065033f43cSJassi Brar dev_err(&i2s->pdev->dev, 10075033f43cSJassi Brar "Invalid clock divider(%d)\n", div_id); 10085033f43cSJassi Brar return -EINVAL; 10095033f43cSJassi Brar } 10105033f43cSJassi Brar 10115033f43cSJassi Brar return 0; 10125033f43cSJassi Brar } 10135033f43cSJassi Brar 10145033f43cSJassi Brar static snd_pcm_sframes_t 10155033f43cSJassi Brar i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 10165033f43cSJassi Brar { 1017e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 10185033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 1019e2e16fa6SSylwester Nawrocki u32 reg = readl(priv->addr + I2SFIC); 10205033f43cSJassi Brar snd_pcm_sframes_t delay; 10215033f43cSJassi Brar 1022dc938ddbSMarek Szyprowski WARN_ON(!pm_runtime_active(dai->dev)); 1023dc938ddbSMarek Szyprowski 10245033f43cSJassi Brar if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 10255033f43cSJassi Brar delay = FIC_RXCOUNT(reg); 10265033f43cSJassi Brar else if (is_secondary(i2s)) 1027e2e16fa6SSylwester Nawrocki delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS)); 10285033f43cSJassi Brar else 10295bfaeddcSSylwester Nawrocki delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f; 10305033f43cSJassi Brar 10315033f43cSJassi Brar return delay; 10325033f43cSJassi Brar } 10335033f43cSJassi Brar 10345033f43cSJassi Brar #ifdef CONFIG_PM 10355033f43cSJassi Brar static int i2s_suspend(struct snd_soc_dai *dai) 10365033f43cSJassi Brar { 1037e7e52dfcSMarek Szyprowski return pm_runtime_force_suspend(dai->dev); 10385033f43cSJassi Brar } 10395033f43cSJassi Brar 10405033f43cSJassi Brar static int i2s_resume(struct snd_soc_dai *dai) 10415033f43cSJassi Brar { 1042e7e52dfcSMarek Szyprowski return pm_runtime_force_resume(dai->dev); 10435033f43cSJassi Brar } 10445033f43cSJassi Brar #else 10455033f43cSJassi Brar #define i2s_suspend NULL 10465033f43cSJassi Brar #define i2s_resume NULL 10475033f43cSJassi Brar #endif 10485033f43cSJassi Brar 10495033f43cSJassi Brar static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) 10505033f43cSJassi Brar { 1051e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 10525033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 1053dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 1054ce8bcdbbSSylwester Nawrocki unsigned long flags; 10555033f43cSJassi Brar 1056dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 1057dc938ddbSMarek Szyprowski 10580ec2ba80SSylwester Nawrocki if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ 105969e7a69aSSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback, 10603688569eSMark Brown NULL); 1061872c26bdSSylwester Nawrocki } else { 106269e7a69aSSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, 1063872c26bdSSylwester Nawrocki &i2s->dma_capture); 1064511e3033SMark Brown 1065*5944170fSSylwester Nawrocki if (priv->quirks & QUIRK_NEED_RSTCLR) 1066e2e16fa6SSylwester Nawrocki writel(CON_RSTCLR, priv->addr + I2SCON); 10675033f43cSJassi Brar 1068*5944170fSSylwester Nawrocki if (priv->quirks & QUIRK_SUPPORTS_IDMA) 1069e2e16fa6SSylwester Nawrocki idma_reg_addr_init(priv->addr, 107069e7a69aSSylwester Nawrocki i2s->sec_dai->idma_playback.addr); 1071872c26bdSSylwester Nawrocki } 107261100f40SSangbeom Kim 10735033f43cSJassi Brar /* Reset any constraint on RFS and BFS */ 10745033f43cSJassi Brar i2s->rfs = 0; 10755033f43cSJassi Brar i2s->bfs = 0; 1076ce8bcdbbSSylwester Nawrocki 10779d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 10785033f43cSJassi Brar i2s_txctrl(i2s, 0); 10795033f43cSJassi Brar i2s_rxctrl(i2s, 0); 10805033f43cSJassi Brar i2s_fifo(i2s, FIC_TXFLUSH); 10815033f43cSJassi Brar i2s_fifo(other, FIC_TXFLUSH); 10825033f43cSJassi Brar i2s_fifo(i2s, FIC_RXFLUSH); 10839d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 10845033f43cSJassi Brar 10855033f43cSJassi Brar /* Gate CDCLK by default */ 10865033f43cSJassi Brar if (!is_opened(other)) 10875033f43cSJassi Brar i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 10885033f43cSJassi Brar 0, SND_SOC_CLOCK_IN); 1089dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 10905033f43cSJassi Brar 10915033f43cSJassi Brar return 0; 10925033f43cSJassi Brar } 10935033f43cSJassi Brar 10945033f43cSJassi Brar static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) 10955033f43cSJassi Brar { 1096e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 1097a404b72dSSylwester Nawrocki struct i2s_dai *i2s = to_info(dai); 10985faf071dSCharles Keepax unsigned long flags; 10995033f43cSJassi Brar 1100dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 1101dc938ddbSMarek Szyprowski 1102c92f1d0eSSylwester Nawrocki if (!is_secondary(i2s)) { 1103*5944170fSSylwester Nawrocki if (priv->quirks & QUIRK_NEED_RSTCLR) { 11049d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 1105e2e16fa6SSylwester Nawrocki writel(0, priv->addr + I2SCON); 11069d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 1107ce8bcdbbSSylwester Nawrocki } 11085033f43cSJassi Brar } 11095033f43cSJassi Brar 1110dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 1111dc938ddbSMarek Szyprowski 11125033f43cSJassi Brar return 0; 11135033f43cSJassi Brar } 11145033f43cSJassi Brar 111585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { 11165033f43cSJassi Brar .trigger = i2s_trigger, 11175033f43cSJassi Brar .hw_params = i2s_hw_params, 11185033f43cSJassi Brar .set_fmt = i2s_set_fmt, 11195033f43cSJassi Brar .set_clkdiv = i2s_set_clkdiv, 11205033f43cSJassi Brar .set_sysclk = i2s_set_sysclk, 11215033f43cSJassi Brar .startup = i2s_startup, 11225033f43cSJassi Brar .shutdown = i2s_shutdown, 11235033f43cSJassi Brar .delay = i2s_delay, 11245033f43cSJassi Brar }; 11255033f43cSJassi Brar 112664aba9bcSSylwester Nawrocki static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { 112764aba9bcSSylwester Nawrocki /* Backend DAI */ 112864aba9bcSSylwester Nawrocki SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0), 112964aba9bcSSylwester Nawrocki SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0), 113064aba9bcSSylwester Nawrocki 113164aba9bcSSylwester Nawrocki /* Playback Mixer */ 113264aba9bcSSylwester Nawrocki SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 113364aba9bcSSylwester Nawrocki }; 113464aba9bcSSylwester Nawrocki 113564aba9bcSSylwester Nawrocki static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { 113664aba9bcSSylwester Nawrocki { "Playback Mixer", NULL, "Primary" }, 113764aba9bcSSylwester Nawrocki { "Playback Mixer", NULL, "Secondary" }, 113864aba9bcSSylwester Nawrocki 113964aba9bcSSylwester Nawrocki { "Mixer DAI TX", NULL, "Playback Mixer" }, 114064aba9bcSSylwester Nawrocki { "Playback Mixer", NULL, "Mixer DAI RX" }, 114164aba9bcSSylwester Nawrocki }; 114264aba9bcSSylwester Nawrocki 11434b828535SKuninori Morimoto static const struct snd_soc_component_driver samsung_i2s_component = { 11444b828535SKuninori Morimoto .name = "samsung-i2s", 114564aba9bcSSylwester Nawrocki 114664aba9bcSSylwester Nawrocki .dapm_widgets = samsung_i2s_widgets, 114764aba9bcSSylwester Nawrocki .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), 114864aba9bcSSylwester Nawrocki 114964aba9bcSSylwester Nawrocki .dapm_routes = samsung_i2s_dapm_routes, 115064aba9bcSSylwester Nawrocki .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes), 11514b828535SKuninori Morimoto }; 11524b828535SKuninori Morimoto 11535033f43cSJassi Brar #define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ 11545033f43cSJassi Brar SNDRV_PCM_FMTBIT_S16_LE | \ 11555033f43cSJassi Brar SNDRV_PCM_FMTBIT_S24_LE) 11565033f43cSJassi Brar 1157a404b72dSSylwester Nawrocki static int i2s_alloc_dais(struct samsung_i2s_priv *priv, 11584720c2feSJaechul Lee const struct samsung_i2s_dai_data *i2s_dai_data, 1159a404b72dSSylwester Nawrocki int num_dais) 11605033f43cSJassi Brar { 1161a404b72dSSylwester Nawrocki static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" }; 116264aba9bcSSylwester Nawrocki static const char *stream_names[] = { "Primary", "Secondary" }; 1163a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *dai_drv; 1164a404b72dSSylwester Nawrocki struct i2s_dai *dai; 1165a404b72dSSylwester Nawrocki int i; 11665033f43cSJassi Brar 1167a404b72dSSylwester Nawrocki priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais, 1168a404b72dSSylwester Nawrocki sizeof(*dai), GFP_KERNEL); 1169a404b72dSSylwester Nawrocki if (!priv->dai) 1170a404b72dSSylwester Nawrocki return -ENOMEM; 11715033f43cSJassi Brar 1172a404b72dSSylwester Nawrocki priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais, 1173a404b72dSSylwester Nawrocki sizeof(*dai_drv), GFP_KERNEL); 1174a404b72dSSylwester Nawrocki if (!priv->dai_drv) 1175a404b72dSSylwester Nawrocki return -ENOMEM; 11765033f43cSJassi Brar 1177a404b72dSSylwester Nawrocki for (i = 0; i < num_dais; i++) { 1178a404b72dSSylwester Nawrocki dai_drv = &priv->dai_drv[i]; 1179a404b72dSSylwester Nawrocki 1180a404b72dSSylwester Nawrocki dai_drv->probe = samsung_i2s_dai_probe; 1181a404b72dSSylwester Nawrocki dai_drv->remove = samsung_i2s_dai_remove; 1182a404b72dSSylwester Nawrocki dai_drv->suspend = i2s_suspend; 1183a404b72dSSylwester Nawrocki dai_drv->resume = i2s_resume; 1184a404b72dSSylwester Nawrocki 1185a404b72dSSylwester Nawrocki dai_drv->symmetric_rates = 1; 1186a404b72dSSylwester Nawrocki dai_drv->ops = &samsung_i2s_dai_ops; 1187a404b72dSSylwester Nawrocki 1188a404b72dSSylwester Nawrocki dai_drv->playback.channels_min = 1; 1189a404b72dSSylwester Nawrocki dai_drv->playback.channels_max = 2; 1190a404b72dSSylwester Nawrocki dai_drv->playback.rates = i2s_dai_data->pcm_rates; 1191a404b72dSSylwester Nawrocki dai_drv->playback.formats = SAMSUNG_I2S_FMTS; 119264aba9bcSSylwester Nawrocki dai_drv->playback.stream_name = stream_names[i]; 1193a404b72dSSylwester Nawrocki 1194a404b72dSSylwester Nawrocki dai_drv->id = i + 1; 1195a404b72dSSylwester Nawrocki dai_drv->name = dai_names[i]; 1196a404b72dSSylwester Nawrocki 1197a404b72dSSylwester Nawrocki priv->dai[i].drv = &priv->dai_drv[i]; 1198a404b72dSSylwester Nawrocki priv->dai[i].pdev = priv->pdev; 1199c6f9b1ebSPrathyush K } 1200a404b72dSSylwester Nawrocki 1201a404b72dSSylwester Nawrocki /* Initialize capture only for the primary DAI */ 1202a404b72dSSylwester Nawrocki dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1]; 1203a404b72dSSylwester Nawrocki 1204a404b72dSSylwester Nawrocki dai_drv->capture.channels_min = 1; 1205a404b72dSSylwester Nawrocki dai_drv->capture.channels_max = 2; 1206a404b72dSSylwester Nawrocki dai_drv->capture.rates = i2s_dai_data->pcm_rates; 1207a404b72dSSylwester Nawrocki dai_drv->capture.formats = SAMSUNG_I2S_FMTS; 1208a404b72dSSylwester Nawrocki 1209a404b72dSSylwester Nawrocki return 0; 12105033f43cSJassi Brar } 12115033f43cSJassi Brar 1212641d334bSRafael J. Wysocki #ifdef CONFIG_PM 12135b1d3c34SR. Chandrasekar static int i2s_runtime_suspend(struct device *dev) 12145b1d3c34SR. Chandrasekar { 1215b5d015e6SSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(dev); 12165b1d3c34SR. Chandrasekar 1217e2e16fa6SSylwester Nawrocki priv->suspend_i2smod = readl(priv->addr + I2SMOD); 1218e2e16fa6SSylwester Nawrocki priv->suspend_i2scon = readl(priv->addr + I2SCON); 1219e2e16fa6SSylwester Nawrocki priv->suspend_i2spsr = readl(priv->addr + I2SPSR); 1220e7e52dfcSMarek Szyprowski 12213b0fa51fSSylwester Nawrocki if (priv->op_clk) 12223b0fa51fSSylwester Nawrocki clk_disable_unprepare(priv->op_clk); 1223b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 12245b1d3c34SR. Chandrasekar 12255b1d3c34SR. Chandrasekar return 0; 12265b1d3c34SR. Chandrasekar } 12275b1d3c34SR. Chandrasekar 12285b1d3c34SR. Chandrasekar static int i2s_runtime_resume(struct device *dev) 12295b1d3c34SR. Chandrasekar { 1230b5d015e6SSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(dev); 1231f5c97c7bSArvind Yadav int ret; 12325b1d3c34SR. Chandrasekar 1233b5d015e6SSylwester Nawrocki ret = clk_prepare_enable(priv->clk); 1234f5c97c7bSArvind Yadav if (ret) 1235f5c97c7bSArvind Yadav return ret; 1236f5c97c7bSArvind Yadav 12373b0fa51fSSylwester Nawrocki if (priv->op_clk) { 12383b0fa51fSSylwester Nawrocki ret = clk_prepare_enable(priv->op_clk); 1239f5c97c7bSArvind Yadav if (ret) { 1240b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 1241f5c97c7bSArvind Yadav return ret; 1242f5c97c7bSArvind Yadav } 1243f5c97c7bSArvind Yadav } 12445b1d3c34SR. Chandrasekar 1245e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2scon, priv->addr + I2SCON); 1246e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2smod, priv->addr + I2SMOD); 1247e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2spsr, priv->addr + I2SPSR); 12485b1d3c34SR. Chandrasekar 12495b1d3c34SR. Chandrasekar return 0; 12505b1d3c34SR. Chandrasekar } 1251641d334bSRafael J. Wysocki #endif /* CONFIG_PM */ 12525b1d3c34SR. Chandrasekar 125389d2e831SSylwester Nawrocki static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) 1254074b89bbSSylwester Nawrocki { 1255074b89bbSSylwester Nawrocki int i; 1256074b89bbSSylwester Nawrocki 125789d2e831SSylwester Nawrocki for (i = 0; i < priv->clk_data.clk_num; i++) { 125889d2e831SSylwester Nawrocki if (!IS_ERR(priv->clk_table[i])) 125989d2e831SSylwester Nawrocki clk_unregister(priv->clk_table[i]); 1260074b89bbSSylwester Nawrocki } 1261074b89bbSSylwester Nawrocki } 1262074b89bbSSylwester Nawrocki 126389d2e831SSylwester Nawrocki static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv) 1264074b89bbSSylwester Nawrocki { 126589d2e831SSylwester Nawrocki of_clk_del_provider(priv->pdev->dev.of_node); 126689d2e831SSylwester Nawrocki i2s_unregister_clocks(priv); 1267074b89bbSSylwester Nawrocki } 1268074b89bbSSylwester Nawrocki 126989d2e831SSylwester Nawrocki 127089d2e831SSylwester Nawrocki static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) 1271074b89bbSSylwester Nawrocki { 1272a404b72dSSylwester Nawrocki 1273aa274c5cSSylwester Nawrocki const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" }; 1274074b89bbSSylwester Nawrocki const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; 1275074b89bbSSylwester Nawrocki const char *p_names[2] = { NULL }; 127689d2e831SSylwester Nawrocki struct device *dev = &priv->pdev->dev; 12775bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs; 1278aa274c5cSSylwester Nawrocki const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)]; 1279074b89bbSSylwester Nawrocki struct clk *rclksrc; 1280074b89bbSSylwester Nawrocki int ret, i; 1281074b89bbSSylwester Nawrocki 1282074b89bbSSylwester Nawrocki /* Register the clock provider only if it's expected in the DTB */ 1283074b89bbSSylwester Nawrocki if (!of_find_property(dev->of_node, "#clock-cells", NULL)) 1284074b89bbSSylwester Nawrocki return 0; 1285074b89bbSSylwester Nawrocki 1286074b89bbSSylwester Nawrocki /* Get the RCLKSRC mux clock parent clock names */ 1287074b89bbSSylwester Nawrocki for (i = 0; i < ARRAY_SIZE(p_names); i++) { 1288074b89bbSSylwester Nawrocki rclksrc = clk_get(dev, clk_name[i]); 1289074b89bbSSylwester Nawrocki if (IS_ERR(rclksrc)) 1290074b89bbSSylwester Nawrocki continue; 1291074b89bbSSylwester Nawrocki p_names[i] = __clk_get_name(rclksrc); 1292074b89bbSSylwester Nawrocki clk_put(rclksrc); 1293074b89bbSSylwester Nawrocki } 1294074b89bbSSylwester Nawrocki 1295aa274c5cSSylwester Nawrocki for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) { 1296aa274c5cSSylwester Nawrocki i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s", 1297aa274c5cSSylwester Nawrocki dev_name(dev), i2s_clk_desc[i]); 1298aa274c5cSSylwester Nawrocki if (!i2s_clk_name[i]) 1299aa274c5cSSylwester Nawrocki return -ENOMEM; 1300aa274c5cSSylwester Nawrocki } 1301aa274c5cSSylwester Nawrocki 1302*5944170fSSylwester Nawrocki if (!(priv->quirks & QUIRK_NO_MUXPSR)) { 1303074b89bbSSylwester Nawrocki /* Activate the prescaler */ 1304e2e16fa6SSylwester Nawrocki u32 val = readl(priv->addr + I2SPSR); 1305e2e16fa6SSylwester Nawrocki writel(val | PSR_PSREN, priv->addr + I2SPSR); 1306074b89bbSSylwester Nawrocki 130789d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, 1308aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_SRC], p_names, 1309aa274c5cSSylwester Nawrocki ARRAY_SIZE(p_names), 1310074b89bbSSylwester Nawrocki CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 1311e2e16fa6SSylwester Nawrocki priv->addr + I2SMOD, reg_info->rclksrc_off, 13129d7939c9SSylwester Nawrocki 1, 0, &priv->lock); 1313074b89bbSSylwester Nawrocki 131489d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, 1315aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_PSR], 1316aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_SRC], 1317074b89bbSSylwester Nawrocki CLK_SET_RATE_PARENT, 13189d7939c9SSylwester Nawrocki priv->addr + I2SPSR, 8, 6, 0, &priv->lock); 1319074b89bbSSylwester Nawrocki 1320aa274c5cSSylwester Nawrocki p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; 132189d2e831SSylwester Nawrocki priv->clk_data.clk_num = 2; 1322074b89bbSSylwester Nawrocki } 1323074b89bbSSylwester Nawrocki 132489d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, 1325aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_CDCLK], p_names[0], 1326aa274c5cSSylwester Nawrocki CLK_SET_RATE_PARENT, 1327e2e16fa6SSylwester Nawrocki priv->addr + I2SMOD, reg_info->cdclkcon_off, 13289d7939c9SSylwester Nawrocki CLK_GATE_SET_TO_DISABLE, &priv->lock); 1329074b89bbSSylwester Nawrocki 133089d2e831SSylwester Nawrocki priv->clk_data.clk_num += 1; 133189d2e831SSylwester Nawrocki priv->clk_data.clks = priv->clk_table; 1332074b89bbSSylwester Nawrocki 1333074b89bbSSylwester Nawrocki ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, 133489d2e831SSylwester Nawrocki &priv->clk_data); 1335074b89bbSSylwester Nawrocki if (ret < 0) { 1336074b89bbSSylwester Nawrocki dev_err(dev, "failed to add clock provider: %d\n", ret); 133789d2e831SSylwester Nawrocki i2s_unregister_clocks(priv); 1338074b89bbSSylwester Nawrocki } 1339074b89bbSSylwester Nawrocki 1340074b89bbSSylwester Nawrocki return ret; 1341074b89bbSSylwester Nawrocki } 1342074b89bbSSylwester Nawrocki 13437196c64cSSylwester Nawrocki /* Create platform device for the secondary PCM */ 13447196c64cSSylwester Nawrocki static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) 13457196c64cSSylwester Nawrocki { 13467196c64cSSylwester Nawrocki struct platform_device *pdev; 13477196c64cSSylwester Nawrocki int ret; 13487196c64cSSylwester Nawrocki 13497196c64cSSylwester Nawrocki pdev = platform_device_register_simple("samsung-i2s-sec", -1, NULL, 0); 13507196c64cSSylwester Nawrocki if (!pdev) 13517196c64cSSylwester Nawrocki return -ENOMEM; 13527196c64cSSylwester Nawrocki 13537196c64cSSylwester Nawrocki ret = device_attach(&pdev->dev); 13547196c64cSSylwester Nawrocki if (ret < 0) { 13557196c64cSSylwester Nawrocki dev_info(&pdev->dev, "device_attach() failed\n"); 13567196c64cSSylwester Nawrocki return ret; 13577196c64cSSylwester Nawrocki } 13587196c64cSSylwester Nawrocki 13597196c64cSSylwester Nawrocki priv->pdev_sec = pdev; 13607196c64cSSylwester Nawrocki 13617196c64cSSylwester Nawrocki return 0; 13627196c64cSSylwester Nawrocki } 13637196c64cSSylwester Nawrocki 13647196c64cSSylwester Nawrocki static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv) 13657196c64cSSylwester Nawrocki { 13667196c64cSSylwester Nawrocki if (priv->pdev_sec) { 13677196c64cSSylwester Nawrocki platform_device_del(priv->pdev_sec); 13687196c64cSSylwester Nawrocki priv->pdev_sec = NULL; 13697196c64cSSylwester Nawrocki } 13707196c64cSSylwester Nawrocki } 1371fdca21adSBill Pemberton static int samsung_i2s_probe(struct platform_device *pdev) 13725033f43cSJassi Brar { 13735033f43cSJassi Brar struct i2s_dai *pri_dai, *sec_dai = NULL; 137440476f61SPadmavathi Venna struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; 13755033f43cSJassi Brar struct resource *res; 137640476f61SPadmavathi Venna u32 regs_base, quirks = 0, idma_addr = 0; 137740476f61SPadmavathi Venna struct device_node *np = pdev->dev.of_node; 13787da493e9SPadmavathi Venna const struct samsung_i2s_dai_data *i2s_dai_data; 1379a404b72dSSylwester Nawrocki int num_dais, ret; 1380a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv; 13815033f43cSJassi Brar 13822f7b5d14SSylwester Nawrocki if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) 13832f7b5d14SSylwester Nawrocki i2s_dai_data = of_device_get_match_data(&pdev->dev); 13842f7b5d14SSylwester Nawrocki else 13852f7b5d14SSylwester Nawrocki i2s_dai_data = (struct samsung_i2s_dai_data *) 13862f7b5d14SSylwester Nawrocki platform_get_device_id(pdev)->driver_data; 13877c62eebbSPadmavathi Venna 13887196c64cSSylwester Nawrocki /* Nothing to do if it is the secondary device probe */ 13897196c64cSSylwester Nawrocki if (!i2s_dai_data) 13907196c64cSSylwester Nawrocki return 0; 13917196c64cSSylwester Nawrocki 1392a404b72dSSylwester Nawrocki priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1393a404b72dSSylwester Nawrocki if (!priv) 139440476f61SPadmavathi Venna return -ENOMEM; 139540476f61SPadmavathi Venna 1396a404b72dSSylwester Nawrocki quirks = np ? i2s_dai_data->quirks : i2s_pdata->type.quirks; 1397a404b72dSSylwester Nawrocki num_dais = (quirks & QUIRK_SEC_DAI) ? 2 : 1; 1398a404b72dSSylwester Nawrocki priv->pdev = pdev; 1399*5944170fSSylwester Nawrocki priv->variant_regs = i2s_dai_data->i2s_variant_regs; 1400*5944170fSSylwester Nawrocki priv->quirks = quirks; 1401a404b72dSSylwester Nawrocki 1402a404b72dSSylwester Nawrocki ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); 1403a404b72dSSylwester Nawrocki if (ret < 0) 1404a404b72dSSylwester Nawrocki return ret; 1405a404b72dSSylwester Nawrocki 1406a404b72dSSylwester Nawrocki pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; 1407a404b72dSSylwester Nawrocki 14089d7939c9SSylwester Nawrocki spin_lock_init(&priv->lock); 1409f3670536SSylwester Nawrocki 141040476f61SPadmavathi Venna if (!np) { 14115033f43cSJassi Brar if (i2s_pdata == NULL) { 14125033f43cSJassi Brar dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); 14135033f43cSJassi Brar return -EINVAL; 14145033f43cSJassi Brar } 14155033f43cSJassi Brar 141669e7a69aSSylwester Nawrocki pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback; 141769e7a69aSSylwester Nawrocki pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture; 14189bdca822SArnd Bergmann pri_dai->filter = i2s_pdata->dma_filter; 1419b9a1a743SArnd Bergmann 1420409c69beSKrzysztof Kozlowski idma_addr = i2s_pdata->type.idma_addr; 142140476f61SPadmavathi Venna } else { 142240476f61SPadmavathi Venna if (of_property_read_u32(np, "samsung,idma-addr", 142340476f61SPadmavathi Venna &idma_addr)) { 1424b0759736SPadmavathi Venna if (quirks & QUIRK_SUPPORTS_IDMA) { 1425b0759736SPadmavathi Venna dev_info(&pdev->dev, "idma address is not"\ 142640476f61SPadmavathi Venna "specified"); 142740476f61SPadmavathi Venna } 142840476f61SPadmavathi Venna } 142940476f61SPadmavathi Venna } 14305033f43cSJassi Brar 14315033f43cSJassi Brar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1432e2e16fa6SSylwester Nawrocki priv->addr = devm_ioremap_resource(&pdev->dev, res); 1433e2e16fa6SSylwester Nawrocki if (IS_ERR(priv->addr)) 1434e2e16fa6SSylwester Nawrocki return PTR_ERR(priv->addr); 14355033f43cSJassi Brar 14365033f43cSJassi Brar regs_base = res->start; 14375033f43cSJassi Brar 1438b5d015e6SSylwester Nawrocki priv->clk = devm_clk_get(&pdev->dev, "iis"); 1439b5d015e6SSylwester Nawrocki if (IS_ERR(priv->clk)) { 14400ec2ba80SSylwester Nawrocki dev_err(&pdev->dev, "Failed to get iis clock\n"); 1441b5d015e6SSylwester Nawrocki return PTR_ERR(priv->clk); 14420ec2ba80SSylwester Nawrocki } 1443c92f1d0eSSylwester Nawrocki 1444b5d015e6SSylwester Nawrocki ret = clk_prepare_enable(priv->clk); 1445c92f1d0eSSylwester Nawrocki if (ret != 0) { 1446c92f1d0eSSylwester Nawrocki dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); 1447c92f1d0eSSylwester Nawrocki return ret; 1448c92f1d0eSSylwester Nawrocki } 144969e7a69aSSylwester Nawrocki pri_dai->dma_playback.addr = regs_base + I2STXD; 145069e7a69aSSylwester Nawrocki pri_dai->dma_capture.addr = regs_base + I2SRXD; 1451b8ab0cccSSylwester Nawrocki pri_dai->dma_playback.chan_name = "tx"; 1452b8ab0cccSSylwester Nawrocki pri_dai->dma_capture.chan_name = "rx"; 145369e7a69aSSylwester Nawrocki pri_dai->dma_playback.addr_width = 4; 145469e7a69aSSylwester Nawrocki pri_dai->dma_capture.addr_width = 4; 145589d2e831SSylwester Nawrocki pri_dai->priv = priv; 14565033f43cSJassi Brar 14575033f43cSJassi Brar if (quirks & QUIRK_PRI_6CHAN) 1458a404b72dSSylwester Nawrocki pri_dai->drv->playback.channels_max = 6; 14595033f43cSJassi Brar 146073f5dfc6SMarek Szyprowski ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, 146196f06cdeSSylwester Nawrocki NULL, NULL, NULL); 146273f5dfc6SMarek Szyprowski if (ret < 0) 146373f5dfc6SMarek Szyprowski goto err_disable_clk; 146473f5dfc6SMarek Szyprowski 14655033f43cSJassi Brar if (quirks & QUIRK_SEC_DAI) { 1466a404b72dSSylwester Nawrocki sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; 14677e5d8706SSylwester Nawrocki 146869e7a69aSSylwester Nawrocki sec_dai->dma_playback.addr = regs_base + I2STXDS; 1469b8ab0cccSSylwester Nawrocki sec_dai->dma_playback.chan_name = "tx-sec"; 147040476f61SPadmavathi Venna 14719bdca822SArnd Bergmann if (!np) { 147269e7a69aSSylwester Nawrocki sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec; 14739bdca822SArnd Bergmann sec_dai->filter = i2s_pdata->dma_filter; 14749bdca822SArnd Bergmann } 147540476f61SPadmavathi Venna 147669e7a69aSSylwester Nawrocki sec_dai->dma_playback.addr_width = 4; 147769e7a69aSSylwester Nawrocki sec_dai->idma_playback.addr = idma_addr; 14785033f43cSJassi Brar sec_dai->pri_dai = pri_dai; 147989d2e831SSylwester Nawrocki sec_dai->priv = priv; 14805033f43cSJassi Brar pri_dai->sec_dai = sec_dai; 1481be2c92ebSMarek Szyprowski 14827196c64cSSylwester Nawrocki ret = i2s_create_secondary_device(priv); 14837196c64cSSylwester Nawrocki if (ret < 0) 14847196c64cSSylwester Nawrocki goto err_disable_clk; 14857196c64cSSylwester Nawrocki 14867196c64cSSylwester Nawrocki ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev, 14877196c64cSSylwester Nawrocki sec_dai->filter, "tx-sec", NULL, 14887196c64cSSylwester Nawrocki &pdev->dev); 1489be2c92ebSMarek Szyprowski if (ret < 0) 1490be2c92ebSMarek Szyprowski goto err_disable_clk; 1491be2c92ebSMarek Szyprowski 14925033f43cSJassi Brar } 14935033f43cSJassi Brar 14940429ffefSMark Brown if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { 149540476f61SPadmavathi Venna dev_err(&pdev->dev, "Unable to configure gpio\n"); 1496fd61576fSWei Yongjun ret = -EINVAL; 1497fd61576fSWei Yongjun goto err_disable_clk; 149840476f61SPadmavathi Venna } 14995033f43cSJassi Brar 1500a404b72dSSylwester Nawrocki dev_set_drvdata(&pdev->dev, priv); 1501a404b72dSSylwester Nawrocki 1502a404b72dSSylwester Nawrocki ret = devm_snd_soc_register_component(&pdev->dev, 1503a404b72dSSylwester Nawrocki &samsung_i2s_component, 1504a404b72dSSylwester Nawrocki priv->dai_drv, num_dais); 1505a404b72dSSylwester Nawrocki if (ret < 0) 1506a404b72dSSylwester Nawrocki goto err_disable_clk; 15072b960386SSylwester Nawrocki 1508dc938ddbSMarek Szyprowski pm_runtime_set_active(&pdev->dev); 1509c5cf4dbcSMark Brown pm_runtime_enable(&pdev->dev); 1510c5cf4dbcSMark Brown 151189d2e831SSylwester Nawrocki ret = i2s_register_clock_provider(priv); 151248279c53SSylwester Nawrocki if (ret < 0) 151348279c53SSylwester Nawrocki goto err_disable_pm; 151448279c53SSylwester Nawrocki 15153b0fa51fSSylwester Nawrocki priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]); 151648279c53SSylwester Nawrocki 15172b960386SSylwester Nawrocki return 0; 1518a08485d8SPadmavathi Venna 151948279c53SSylwester Nawrocki err_disable_pm: 15202b960386SSylwester Nawrocki pm_runtime_disable(&pdev->dev); 1521fd61576fSWei Yongjun err_disable_clk: 1522b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 15237196c64cSSylwester Nawrocki i2s_delete_secondary_device(priv); 15242b960386SSylwester Nawrocki return ret; 15255033f43cSJassi Brar } 15265033f43cSJassi Brar 1527fdca21adSBill Pemberton static int samsung_i2s_remove(struct platform_device *pdev) 15285033f43cSJassi Brar { 1529a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev); 1530be2c92ebSMarek Szyprowski 15317196c64cSSylwester Nawrocki /* The secondary device has no driver data assigned */ 15327196c64cSSylwester Nawrocki if (!priv) 15337196c64cSSylwester Nawrocki return 0; 15347196c64cSSylwester Nawrocki 1535dc938ddbSMarek Szyprowski pm_runtime_get_sync(&pdev->dev); 1536c5cf4dbcSMark Brown pm_runtime_disable(&pdev->dev); 15375033f43cSJassi Brar 153889d2e831SSylwester Nawrocki i2s_unregister_clock_provider(priv); 1539b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 1540dc938ddbSMarek Szyprowski pm_runtime_put_noidle(&pdev->dev); 15417196c64cSSylwester Nawrocki i2s_delete_secondary_device(priv); 15425033f43cSJassi Brar 15435033f43cSJassi Brar return 0; 15445033f43cSJassi Brar } 15455033f43cSJassi Brar 1546a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv3_regs = { 1547a5a56871SPadmavathi Venna .bfs_off = 1, 1548a5a56871SPadmavathi Venna .rfs_off = 3, 1549a5a56871SPadmavathi Venna .sdf_off = 5, 1550a5a56871SPadmavathi Venna .txr_off = 8, 1551a5a56871SPadmavathi Venna .rclksrc_off = 10, 1552a5a56871SPadmavathi Venna .mss_off = 11, 1553a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1554a5a56871SPadmavathi Venna .lrp_off = 7, 1555a5a56871SPadmavathi Venna .bfs_mask = 0x3, 1556a5a56871SPadmavathi Venna .rfs_mask = 0x3, 1557a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1558a5a56871SPadmavathi Venna }; 1559a5a56871SPadmavathi Venna 1560a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv6_regs = { 1561a5a56871SPadmavathi Venna .bfs_off = 0, 1562a5a56871SPadmavathi Venna .rfs_off = 4, 1563a5a56871SPadmavathi Venna .sdf_off = 6, 1564a5a56871SPadmavathi Venna .txr_off = 8, 1565a5a56871SPadmavathi Venna .rclksrc_off = 10, 1566a5a56871SPadmavathi Venna .mss_off = 11, 1567a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1568a5a56871SPadmavathi Venna .lrp_off = 15, 1569a5a56871SPadmavathi Venna .bfs_mask = 0xf, 1570a5a56871SPadmavathi Venna .rfs_mask = 0x3, 1571a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1572a5a56871SPadmavathi Venna }; 1573a5a56871SPadmavathi Venna 1574a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv7_regs = { 1575a5a56871SPadmavathi Venna .bfs_off = 0, 1576a5a56871SPadmavathi Venna .rfs_off = 4, 1577a5a56871SPadmavathi Venna .sdf_off = 7, 1578a5a56871SPadmavathi Venna .txr_off = 9, 1579a5a56871SPadmavathi Venna .rclksrc_off = 11, 1580a5a56871SPadmavathi Venna .mss_off = 12, 1581a5a56871SPadmavathi Venna .cdclkcon_off = 22, 1582a5a56871SPadmavathi Venna .lrp_off = 15, 1583a5a56871SPadmavathi Venna .bfs_mask = 0xf, 1584a5a56871SPadmavathi Venna .rfs_mask = 0x7, 1585a5a56871SPadmavathi Venna .ftx0cnt_off = 0, 1586a5a56871SPadmavathi Venna }; 1587a5a56871SPadmavathi Venna 1588a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { 1589a5a56871SPadmavathi Venna .bfs_off = 0, 1590a5a56871SPadmavathi Venna .rfs_off = 3, 1591a5a56871SPadmavathi Venna .sdf_off = 6, 1592a5a56871SPadmavathi Venna .txr_off = 8, 1593a5a56871SPadmavathi Venna .rclksrc_off = 10, 1594a5a56871SPadmavathi Venna .mss_off = 11, 1595a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1596a5a56871SPadmavathi Venna .lrp_off = 15, 1597a5a56871SPadmavathi Venna .bfs_mask = 0x7, 1598a5a56871SPadmavathi Venna .rfs_mask = 0x7, 1599a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1600a5a56871SPadmavathi Venna }; 1601a5a56871SPadmavathi Venna 16027da493e9SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv3_dai_type = { 16037da493e9SPadmavathi Venna .quirks = QUIRK_NO_MUXPSR, 16044720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1605a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv3_regs, 16067da493e9SPadmavathi Venna }; 16077da493e9SPadmavathi Venna 16087da493e9SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv5_dai_type = { 1609b0759736SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1610b0759736SPadmavathi Venna QUIRK_SUPPORTS_IDMA, 16114720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1612a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv3_regs, 16137da493e9SPadmavathi Venna }; 16147da493e9SPadmavathi Venna 16154ca0c0d4SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv6_dai_type = { 16164ca0c0d4SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1617b0759736SPadmavathi Venna QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, 16184720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1619a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv6_regs, 1620a5a56871SPadmavathi Venna }; 1621a5a56871SPadmavathi Venna 1622a5a56871SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv7_dai_type = { 1623a5a56871SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1624a5a56871SPadmavathi Venna QUIRK_SUPPORTS_TDM, 16254720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_192000, 1626a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv7_regs, 1627a5a56871SPadmavathi Venna }; 1628a5a56871SPadmavathi Venna 1629a5a56871SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { 1630a5a56871SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, 16314720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1632a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv5_i2s1_regs, 16334ca0c0d4SPadmavathi Venna }; 16344ca0c0d4SPadmavathi Venna 1635eb8ca0faSKrzysztof Kozlowski static const struct platform_device_id samsung_i2s_driver_ids[] = { 16367c62eebbSPadmavathi Venna { 16377c62eebbSPadmavathi Venna .name = "samsung-i2s", 16383f024980SMark Brown .driver_data = (kernel_ulong_t)&i2sv3_dai_type, 16397196c64cSSylwester Nawrocki }, { 16407196c64cSSylwester Nawrocki .name = "samsung-i2s-sec", 16417c62eebbSPadmavathi Venna }, 16427c62eebbSPadmavathi Venna {}, 16437c62eebbSPadmavathi Venna }; 16442af19558SArnd Bergmann MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); 16457c62eebbSPadmavathi Venna 164640476f61SPadmavathi Venna #ifdef CONFIG_OF 164740476f61SPadmavathi Venna static const struct of_device_id exynos_i2s_match[] = { 16487da493e9SPadmavathi Venna { 16497da493e9SPadmavathi Venna .compatible = "samsung,s3c6410-i2s", 16507da493e9SPadmavathi Venna .data = &i2sv3_dai_type, 16517da493e9SPadmavathi Venna }, { 16527da493e9SPadmavathi Venna .compatible = "samsung,s5pv210-i2s", 16537da493e9SPadmavathi Venna .data = &i2sv5_dai_type, 16544ca0c0d4SPadmavathi Venna }, { 16554ca0c0d4SPadmavathi Venna .compatible = "samsung,exynos5420-i2s", 16564ca0c0d4SPadmavathi Venna .data = &i2sv6_dai_type, 1657a5a56871SPadmavathi Venna }, { 1658a5a56871SPadmavathi Venna .compatible = "samsung,exynos7-i2s", 1659a5a56871SPadmavathi Venna .data = &i2sv7_dai_type, 1660a5a56871SPadmavathi Venna }, { 1661a5a56871SPadmavathi Venna .compatible = "samsung,exynos7-i2s1", 1662a5a56871SPadmavathi Venna .data = &i2sv5_dai_type_i2s1, 166340476f61SPadmavathi Venna }, 166440476f61SPadmavathi Venna {}, 166540476f61SPadmavathi Venna }; 166640476f61SPadmavathi Venna MODULE_DEVICE_TABLE(of, exynos_i2s_match); 166740476f61SPadmavathi Venna #endif 166840476f61SPadmavathi Venna 16695b1d3c34SR. Chandrasekar static const struct dev_pm_ops samsung_i2s_pm = { 16705b1d3c34SR. Chandrasekar SET_RUNTIME_PM_OPS(i2s_runtime_suspend, 16715b1d3c34SR. Chandrasekar i2s_runtime_resume, NULL) 1672e7e52dfcSMarek Szyprowski SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1673e7e52dfcSMarek Szyprowski pm_runtime_force_resume) 16745b1d3c34SR. Chandrasekar }; 16755b1d3c34SR. Chandrasekar 16765033f43cSJassi Brar static struct platform_driver samsung_i2s_driver = { 16775033f43cSJassi Brar .probe = samsung_i2s_probe, 1678fdca21adSBill Pemberton .remove = samsung_i2s_remove, 16797c62eebbSPadmavathi Venna .id_table = samsung_i2s_driver_ids, 16805033f43cSJassi Brar .driver = { 16815033f43cSJassi Brar .name = "samsung-i2s", 168240476f61SPadmavathi Venna .of_match_table = of_match_ptr(exynos_i2s_match), 16835b1d3c34SR. Chandrasekar .pm = &samsung_i2s_pm, 16845033f43cSJassi Brar }, 16855033f43cSJassi Brar }; 16865033f43cSJassi Brar 1687e00c3f55SMark Brown module_platform_driver(samsung_i2s_driver); 16885033f43cSJassi Brar 16895033f43cSJassi Brar /* Module information */ 1690df8ad335SJaswinder Singh MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 16915033f43cSJassi Brar MODULE_DESCRIPTION("Samsung I2S Interface"); 16925033f43cSJassi Brar MODULE_ALIAS("platform:samsung-i2s"); 16935033f43cSJassi Brar MODULE_LICENSE("GPL"); 1694