1c1b2db4dSSylwester Nawrocki // SPDX-License-Identifier: GPL-2.0 2c1b2db4dSSylwester Nawrocki // 3c1b2db4dSSylwester Nawrocki // ALSA SoC Audio Layer - Samsung I2S Controller driver 4c1b2db4dSSylwester Nawrocki // 5c1b2db4dSSylwester Nawrocki // Copyright (c) 2010 Samsung Electronics Co. Ltd. 6c1b2db4dSSylwester Nawrocki // Jaswinder Singh <jassisinghbrar@gmail.com> 75033f43cSJassi Brar 8074b89bbSSylwester Nawrocki #include <dt-bindings/sound/samsung-i2s.h> 95033f43cSJassi Brar #include <linux/delay.h> 105033f43cSJassi Brar #include <linux/slab.h> 115033f43cSJassi Brar #include <linux/clk.h> 12074b89bbSSylwester Nawrocki #include <linux/clk-provider.h> 135033f43cSJassi Brar #include <linux/io.h> 14da155d5bSPaul Gortmaker #include <linux/module.h> 1540476f61SPadmavathi Venna #include <linux/of.h> 162f7b5d14SSylwester Nawrocki #include <linux/of_device.h> 1740476f61SPadmavathi Venna #include <linux/of_gpio.h> 18c5cf4dbcSMark Brown #include <linux/pm_runtime.h> 195033f43cSJassi Brar 205033f43cSJassi Brar #include <sound/soc.h> 210378b6acSSeungwhan Youn #include <sound/pcm_params.h> 225033f43cSJassi Brar 23436d42c6SArnd Bergmann #include <linux/platform_data/asoc-s3c.h> 245033f43cSJassi Brar 255033f43cSJassi Brar #include "dma.h" 2661100f40SSangbeom Kim #include "idma.h" 275033f43cSJassi Brar #include "i2s.h" 28172a453dSSangbeom Kim #include "i2s-regs.h" 295033f43cSJassi Brar 305033f43cSJassi Brar #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 315033f43cSJassi Brar 32a404b72dSSylwester Nawrocki #define SAMSUNG_I2S_ID_PRIMARY 1 33a404b72dSSylwester Nawrocki #define SAMSUNG_I2S_ID_SECONDARY 2 34a404b72dSSylwester Nawrocki 35a5a56871SPadmavathi Venna struct samsung_i2s_variant_regs { 36a5a56871SPadmavathi Venna unsigned int bfs_off; 37a5a56871SPadmavathi Venna unsigned int rfs_off; 38a5a56871SPadmavathi Venna unsigned int sdf_off; 39a5a56871SPadmavathi Venna unsigned int txr_off; 40a5a56871SPadmavathi Venna unsigned int rclksrc_off; 41a5a56871SPadmavathi Venna unsigned int mss_off; 42a5a56871SPadmavathi Venna unsigned int cdclkcon_off; 43a5a56871SPadmavathi Venna unsigned int lrp_off; 44a5a56871SPadmavathi Venna unsigned int bfs_mask; 45a5a56871SPadmavathi Venna unsigned int rfs_mask; 46a5a56871SPadmavathi Venna unsigned int ftx0cnt_off; 47a5a56871SPadmavathi Venna }; 48a5a56871SPadmavathi Venna 4940476f61SPadmavathi Venna struct samsung_i2s_dai_data { 507da493e9SPadmavathi Venna u32 quirks; 514720c2feSJaechul Lee unsigned int pcm_rates; 52a5a56871SPadmavathi Venna const struct samsung_i2s_variant_regs *i2s_variant_regs; 5340476f61SPadmavathi Venna }; 5440476f61SPadmavathi Venna 555033f43cSJassi Brar struct i2s_dai { 565033f43cSJassi Brar /* Platform device for this DAI */ 575033f43cSJassi Brar struct platform_device *pdev; 58e2e16fa6SSylwester Nawrocki 599f9f8a5bSSylwester Nawrocki /* Frame clock */ 605033f43cSJassi Brar unsigned frmclk; 615033f43cSJassi Brar /* 629f9f8a5bSSylwester Nawrocki * Specifically requested RCLK, BCLK by machine driver. 635033f43cSJassi Brar * 0 indicates CPU driver is free to choose any value. 645033f43cSJassi Brar */ 655033f43cSJassi Brar unsigned rfs, bfs; 665033f43cSJassi Brar /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ 675033f43cSJassi Brar struct i2s_dai *pri_dai; 685033f43cSJassi Brar /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ 695033f43cSJassi Brar struct i2s_dai *sec_dai; 709f9f8a5bSSylwester Nawrocki 719f9f8a5bSSylwester Nawrocki #define DAI_OPENED (1 << 0) /* DAI is opened */ 729f9f8a5bSSylwester Nawrocki #define DAI_MANAGER (1 << 1) /* DAI is the manager */ 735033f43cSJassi Brar unsigned mode; 74a404b72dSSylwester Nawrocki 755033f43cSJassi Brar /* Driver for this DAI */ 76a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *drv; 77a404b72dSSylwester Nawrocki 785033f43cSJassi Brar /* DMA parameters */ 7969e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data dma_playback; 8069e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data dma_capture; 8169e7a69aSSylwester Nawrocki struct snd_dmaengine_dai_dma_data idma_playback; 829bdca822SArnd Bergmann dma_filter_fn filter; 83f3670536SSylwester Nawrocki 8489d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv; 855033f43cSJassi Brar }; 865033f43cSJassi Brar 87a404b72dSSylwester Nawrocki struct samsung_i2s_priv { 88a404b72dSSylwester Nawrocki struct platform_device *pdev; 897196c64cSSylwester Nawrocki struct platform_device *pdev_sec; 90a404b72dSSylwester Nawrocki 919f9f8a5bSSylwester Nawrocki /* Lock for cross interface checks */ 92defc67c6SSylwester Nawrocki spinlock_t pcm_lock; 93defc67c6SSylwester Nawrocki 94a404b72dSSylwester Nawrocki /* CPU DAIs and their corresponding drivers */ 95a404b72dSSylwester Nawrocki struct i2s_dai *dai; 96a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *dai_drv; 97a404b72dSSylwester Nawrocki int num_dais; 9889d2e831SSylwester Nawrocki 99b5d015e6SSylwester Nawrocki /* The I2S controller's core clock */ 100b5d015e6SSylwester Nawrocki struct clk *clk; 101b5d015e6SSylwester Nawrocki 1023b0fa51fSSylwester Nawrocki /* Clock for generating I2S signals */ 1033b0fa51fSSylwester Nawrocki struct clk *op_clk; 1043b0fa51fSSylwester Nawrocki 1053b0fa51fSSylwester Nawrocki /* Rate of RCLK source clock */ 1063b0fa51fSSylwester Nawrocki unsigned long rclk_srcrate; 1073b0fa51fSSylwester Nawrocki 10881bcbf2cSSylwester Nawrocki /* Cache of selected I2S registers for system suspend */ 10981bcbf2cSSylwester Nawrocki u32 suspend_i2smod; 11081bcbf2cSSylwester Nawrocki u32 suspend_i2scon; 11181bcbf2cSSylwester Nawrocki u32 suspend_i2spsr; 11281bcbf2cSSylwester Nawrocki 1135bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *variant_regs; 1145944170fSSylwester Nawrocki u32 quirks; 1155bfaeddcSSylwester Nawrocki 11689d2e831SSylwester Nawrocki /* The clock provider's data */ 11789d2e831SSylwester Nawrocki struct clk *clk_table[3]; 11889d2e831SSylwester Nawrocki struct clk_onecell_data clk_data; 119f29eec79SSylwester Nawrocki 120f29eec79SSylwester Nawrocki /* Spinlock protecting member fields below */ 121f29eec79SSylwester Nawrocki spinlock_t lock; 122f29eec79SSylwester Nawrocki 123f29eec79SSylwester Nawrocki /* Memory mapped SFR region */ 124f29eec79SSylwester Nawrocki void __iomem *addr; 125f29eec79SSylwester Nawrocki 126f29eec79SSylwester Nawrocki /* A flag indicating the I2S slave mode operation */ 127f29eec79SSylwester Nawrocki bool slave_mode; 128a404b72dSSylwester Nawrocki }; 129a404b72dSSylwester Nawrocki 130a404b72dSSylwester Nawrocki /* Returns true if this is the 'overlay' stereo DAI */ 1315033f43cSJassi Brar static inline bool is_secondary(struct i2s_dai *i2s) 1325033f43cSJassi Brar { 133a404b72dSSylwester Nawrocki return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY; 1345033f43cSJassi Brar } 1355033f43cSJassi Brar 1365033f43cSJassi Brar /* If this interface of the controller is transmitting data */ 1375033f43cSJassi Brar static inline bool tx_active(struct i2s_dai *i2s) 1385033f43cSJassi Brar { 1395033f43cSJassi Brar u32 active; 1405033f43cSJassi Brar 1415033f43cSJassi Brar if (!i2s) 1425033f43cSJassi Brar return false; 1435033f43cSJassi Brar 144e2e16fa6SSylwester Nawrocki active = readl(i2s->priv->addr + I2SCON); 1455033f43cSJassi Brar 1465033f43cSJassi Brar if (is_secondary(i2s)) 1475033f43cSJassi Brar active &= CON_TXSDMA_ACTIVE; 1485033f43cSJassi Brar else 1495033f43cSJassi Brar active &= CON_TXDMA_ACTIVE; 1505033f43cSJassi Brar 1515033f43cSJassi Brar return active ? true : false; 1525033f43cSJassi Brar } 1535033f43cSJassi Brar 154dcd60fc3SSylwester Nawrocki /* Return pointer to the other DAI */ 155dcd60fc3SSylwester Nawrocki static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s) 156dcd60fc3SSylwester Nawrocki { 157dcd60fc3SSylwester Nawrocki return i2s->pri_dai ? : i2s->sec_dai; 158dcd60fc3SSylwester Nawrocki } 159dcd60fc3SSylwester Nawrocki 1605033f43cSJassi Brar /* If the other interface of the controller is transmitting data */ 1615033f43cSJassi Brar static inline bool other_tx_active(struct i2s_dai *i2s) 1625033f43cSJassi Brar { 163dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 1645033f43cSJassi Brar 1655033f43cSJassi Brar return tx_active(other); 1665033f43cSJassi Brar } 1675033f43cSJassi Brar 1685033f43cSJassi Brar /* If any interface of the controller is transmitting data */ 1695033f43cSJassi Brar static inline bool any_tx_active(struct i2s_dai *i2s) 1705033f43cSJassi Brar { 1715033f43cSJassi Brar return tx_active(i2s) || other_tx_active(i2s); 1725033f43cSJassi Brar } 1735033f43cSJassi Brar 1745033f43cSJassi Brar /* If this interface of the controller is receiving data */ 1755033f43cSJassi Brar static inline bool rx_active(struct i2s_dai *i2s) 1765033f43cSJassi Brar { 1775033f43cSJassi Brar u32 active; 1785033f43cSJassi Brar 1795033f43cSJassi Brar if (!i2s) 1805033f43cSJassi Brar return false; 1815033f43cSJassi Brar 182e2e16fa6SSylwester Nawrocki active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE; 1835033f43cSJassi Brar 1845033f43cSJassi Brar return active ? true : false; 1855033f43cSJassi Brar } 1865033f43cSJassi Brar 1875033f43cSJassi Brar /* If the other interface of the controller is receiving data */ 1885033f43cSJassi Brar static inline bool other_rx_active(struct i2s_dai *i2s) 1895033f43cSJassi Brar { 190dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 1915033f43cSJassi Brar 1925033f43cSJassi Brar return rx_active(other); 1935033f43cSJassi Brar } 1945033f43cSJassi Brar 1955033f43cSJassi Brar /* If any interface of the controller is receiving data */ 1965033f43cSJassi Brar static inline bool any_rx_active(struct i2s_dai *i2s) 1975033f43cSJassi Brar { 1985033f43cSJassi Brar return rx_active(i2s) || other_rx_active(i2s); 1995033f43cSJassi Brar } 2005033f43cSJassi Brar 2015033f43cSJassi Brar /* If the other DAI is transmitting or receiving data */ 2025033f43cSJassi Brar static inline bool other_active(struct i2s_dai *i2s) 2035033f43cSJassi Brar { 2045033f43cSJassi Brar return other_rx_active(i2s) || other_tx_active(i2s); 2055033f43cSJassi Brar } 2065033f43cSJassi Brar 2075033f43cSJassi Brar /* If this DAI is transmitting or receiving data */ 2085033f43cSJassi Brar static inline bool this_active(struct i2s_dai *i2s) 2095033f43cSJassi Brar { 2105033f43cSJassi Brar return tx_active(i2s) || rx_active(i2s); 2115033f43cSJassi Brar } 2125033f43cSJassi Brar 2135033f43cSJassi Brar /* If the controller is active anyway */ 2145033f43cSJassi Brar static inline bool any_active(struct i2s_dai *i2s) 2155033f43cSJassi Brar { 2165033f43cSJassi Brar return this_active(i2s) || other_active(i2s); 2175033f43cSJassi Brar } 2185033f43cSJassi Brar 2195033f43cSJassi Brar static inline struct i2s_dai *to_info(struct snd_soc_dai *dai) 2205033f43cSJassi Brar { 221a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 222a404b72dSSylwester Nawrocki 223a404b72dSSylwester Nawrocki return &priv->dai[dai->id - 1]; 2245033f43cSJassi Brar } 2255033f43cSJassi Brar 2265033f43cSJassi Brar static inline bool is_opened(struct i2s_dai *i2s) 2275033f43cSJassi Brar { 2285033f43cSJassi Brar if (i2s && (i2s->mode & DAI_OPENED)) 2295033f43cSJassi Brar return true; 2305033f43cSJassi Brar else 2315033f43cSJassi Brar return false; 2325033f43cSJassi Brar } 2335033f43cSJassi Brar 2345033f43cSJassi Brar static inline bool is_manager(struct i2s_dai *i2s) 2355033f43cSJassi Brar { 2365033f43cSJassi Brar if (is_opened(i2s) && (i2s->mode & DAI_MANAGER)) 2375033f43cSJassi Brar return true; 2385033f43cSJassi Brar else 2395033f43cSJassi Brar return false; 2405033f43cSJassi Brar } 2415033f43cSJassi Brar 2425033f43cSJassi Brar /* Read RCLK of I2S (in multiples of LRCLK) */ 2435033f43cSJassi Brar static inline unsigned get_rfs(struct i2s_dai *i2s) 2445033f43cSJassi Brar { 245e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 2464ca0c0d4SPadmavathi Venna u32 rfs; 247e2e16fa6SSylwester Nawrocki 2485bfaeddcSSylwester Nawrocki rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off; 2495bfaeddcSSylwester Nawrocki rfs &= priv->variant_regs->rfs_mask; 2505033f43cSJassi Brar 2515033f43cSJassi Brar switch (rfs) { 252a5a56871SPadmavathi Venna case 7: return 192; 253a5a56871SPadmavathi Venna case 6: return 96; 254a5a56871SPadmavathi Venna case 5: return 128; 255a5a56871SPadmavathi Venna case 4: return 64; 2565033f43cSJassi Brar case 3: return 768; 2575033f43cSJassi Brar case 2: return 384; 2585033f43cSJassi Brar case 1: return 512; 2595033f43cSJassi Brar default: return 256; 2605033f43cSJassi Brar } 2615033f43cSJassi Brar } 2625033f43cSJassi Brar 2635033f43cSJassi Brar /* Write RCLK of I2S (in multiples of LRCLK) */ 2645033f43cSJassi Brar static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) 2655033f43cSJassi Brar { 266e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 267e2e16fa6SSylwester Nawrocki u32 mod = readl(priv->addr + I2SMOD); 2685bfaeddcSSylwester Nawrocki int rfs_shift = priv->variant_regs->rfs_off; 2695033f43cSJassi Brar 2705bfaeddcSSylwester Nawrocki mod &= ~(priv->variant_regs->rfs_mask << rfs_shift); 2715033f43cSJassi Brar 2725033f43cSJassi Brar switch (rfs) { 273a5a56871SPadmavathi Venna case 192: 274a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); 275a5a56871SPadmavathi Venna break; 276a5a56871SPadmavathi Venna case 96: 277a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); 278a5a56871SPadmavathi Venna break; 279a5a56871SPadmavathi Venna case 128: 280a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); 281a5a56871SPadmavathi Venna break; 282a5a56871SPadmavathi Venna case 64: 283a5a56871SPadmavathi Venna mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); 284a5a56871SPadmavathi Venna break; 2855033f43cSJassi Brar case 768: 286b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_768FS << rfs_shift); 2875033f43cSJassi Brar break; 2885033f43cSJassi Brar case 512: 289b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_512FS << rfs_shift); 2905033f43cSJassi Brar break; 2915033f43cSJassi Brar case 384: 292b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_384FS << rfs_shift); 2935033f43cSJassi Brar break; 2945033f43cSJassi Brar default: 295b60be4aaSPadmavathi Venna mod |= (MOD_RCLK_256FS << rfs_shift); 2965033f43cSJassi Brar break; 2975033f43cSJassi Brar } 2985033f43cSJassi Brar 299e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 3005033f43cSJassi Brar } 3015033f43cSJassi Brar 3029f9f8a5bSSylwester Nawrocki /* Read bit-clock of I2S (in multiples of LRCLK) */ 3035033f43cSJassi Brar static inline unsigned get_bfs(struct i2s_dai *i2s) 3045033f43cSJassi Brar { 305e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 3064ca0c0d4SPadmavathi Venna u32 bfs; 307e2e16fa6SSylwester Nawrocki 3085bfaeddcSSylwester Nawrocki bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off; 3095bfaeddcSSylwester Nawrocki bfs &= priv->variant_regs->bfs_mask; 3105033f43cSJassi Brar 3115033f43cSJassi Brar switch (bfs) { 3124ca0c0d4SPadmavathi Venna case 8: return 256; 3134ca0c0d4SPadmavathi Venna case 7: return 192; 3144ca0c0d4SPadmavathi Venna case 6: return 128; 3154ca0c0d4SPadmavathi Venna case 5: return 96; 3164ca0c0d4SPadmavathi Venna case 4: return 64; 3175033f43cSJassi Brar case 3: return 24; 3185033f43cSJassi Brar case 2: return 16; 3195033f43cSJassi Brar case 1: return 48; 3205033f43cSJassi Brar default: return 32; 3215033f43cSJassi Brar } 3225033f43cSJassi Brar } 3235033f43cSJassi Brar 3249f9f8a5bSSylwester Nawrocki /* Write bit-clock of I2S (in multiples of LRCLK) */ 3255033f43cSJassi Brar static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) 3265033f43cSJassi Brar { 327e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 328e2e16fa6SSylwester Nawrocki u32 mod = readl(priv->addr + I2SMOD); 3295944170fSSylwester Nawrocki int tdm = priv->quirks & QUIRK_SUPPORTS_TDM; 3305bfaeddcSSylwester Nawrocki int bfs_shift = priv->variant_regs->bfs_off; 3314ca0c0d4SPadmavathi Venna 3324ca0c0d4SPadmavathi Venna /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ 3334ca0c0d4SPadmavathi Venna if (!tdm && bfs > 48) { 3344ca0c0d4SPadmavathi Venna dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); 3354ca0c0d4SPadmavathi Venna return; 3364ca0c0d4SPadmavathi Venna } 3375033f43cSJassi Brar 3385bfaeddcSSylwester Nawrocki mod &= ~(priv->variant_regs->bfs_mask << bfs_shift); 339a5a56871SPadmavathi Venna 3405033f43cSJassi Brar switch (bfs) { 3415033f43cSJassi Brar case 48: 342b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_48FS << bfs_shift); 3435033f43cSJassi Brar break; 3445033f43cSJassi Brar case 32: 345b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_32FS << bfs_shift); 3465033f43cSJassi Brar break; 3475033f43cSJassi Brar case 24: 348b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_24FS << bfs_shift); 3495033f43cSJassi Brar break; 3505033f43cSJassi Brar case 16: 351b60be4aaSPadmavathi Venna mod |= (MOD_BCLK_16FS << bfs_shift); 3525033f43cSJassi Brar break; 3534ca0c0d4SPadmavathi Venna case 64: 3544ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); 3554ca0c0d4SPadmavathi Venna break; 3564ca0c0d4SPadmavathi Venna case 96: 3574ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); 3584ca0c0d4SPadmavathi Venna break; 3594ca0c0d4SPadmavathi Venna case 128: 3604ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); 3614ca0c0d4SPadmavathi Venna break; 3624ca0c0d4SPadmavathi Venna case 192: 3634ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); 3644ca0c0d4SPadmavathi Venna break; 3654ca0c0d4SPadmavathi Venna case 256: 3664ca0c0d4SPadmavathi Venna mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); 3675033f43cSJassi Brar break; 3685033f43cSJassi Brar default: 3695033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); 3705033f43cSJassi Brar return; 3715033f43cSJassi Brar } 3725033f43cSJassi Brar 373e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 3745033f43cSJassi Brar } 3755033f43cSJassi Brar 3769f9f8a5bSSylwester Nawrocki /* Sample size */ 3775033f43cSJassi Brar static inline int get_blc(struct i2s_dai *i2s) 3785033f43cSJassi Brar { 379e2e16fa6SSylwester Nawrocki int blc = readl(i2s->priv->addr + I2SMOD); 3805033f43cSJassi Brar 3815033f43cSJassi Brar blc = (blc >> 13) & 0x3; 3825033f43cSJassi Brar 3835033f43cSJassi Brar switch (blc) { 3845033f43cSJassi Brar case 2: return 24; 3855033f43cSJassi Brar case 1: return 8; 3865033f43cSJassi Brar default: return 16; 3875033f43cSJassi Brar } 3885033f43cSJassi Brar } 3895033f43cSJassi Brar 3909f9f8a5bSSylwester Nawrocki /* TX channel control */ 3915033f43cSJassi Brar static void i2s_txctrl(struct i2s_dai *i2s, int on) 3925033f43cSJassi Brar { 393e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 394e2e16fa6SSylwester Nawrocki void __iomem *addr = priv->addr; 3955bfaeddcSSylwester Nawrocki int txr_off = priv->variant_regs->txr_off; 3965033f43cSJassi Brar u32 con = readl(addr + I2SCON); 397a5a56871SPadmavathi Venna u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); 3985033f43cSJassi Brar 3995033f43cSJassi Brar if (on) { 4005033f43cSJassi Brar con |= CON_ACTIVE; 4015033f43cSJassi Brar con &= ~CON_TXCH_PAUSE; 4025033f43cSJassi Brar 4035033f43cSJassi Brar if (is_secondary(i2s)) { 4045033f43cSJassi Brar con |= CON_TXSDMA_ACTIVE; 4055033f43cSJassi Brar con &= ~CON_TXSDMA_PAUSE; 4065033f43cSJassi Brar } else { 4075033f43cSJassi Brar con |= CON_TXDMA_ACTIVE; 4085033f43cSJassi Brar con &= ~CON_TXDMA_PAUSE; 4095033f43cSJassi Brar } 4105033f43cSJassi Brar 4115033f43cSJassi Brar if (any_rx_active(i2s)) 412a5a56871SPadmavathi Venna mod |= 2 << txr_off; 4135033f43cSJassi Brar else 414a5a56871SPadmavathi Venna mod |= 0 << txr_off; 4155033f43cSJassi Brar } else { 4165033f43cSJassi Brar if (is_secondary(i2s)) { 4175033f43cSJassi Brar con |= CON_TXSDMA_PAUSE; 4185033f43cSJassi Brar con &= ~CON_TXSDMA_ACTIVE; 4195033f43cSJassi Brar } else { 4205033f43cSJassi Brar con |= CON_TXDMA_PAUSE; 4215033f43cSJassi Brar con &= ~CON_TXDMA_ACTIVE; 4225033f43cSJassi Brar } 4235033f43cSJassi Brar 4245033f43cSJassi Brar if (other_tx_active(i2s)) { 4255033f43cSJassi Brar writel(con, addr + I2SCON); 4265033f43cSJassi Brar return; 4275033f43cSJassi Brar } 4285033f43cSJassi Brar 4295033f43cSJassi Brar con |= CON_TXCH_PAUSE; 4305033f43cSJassi Brar 4315033f43cSJassi Brar if (any_rx_active(i2s)) 432a5a56871SPadmavathi Venna mod |= 1 << txr_off; 4335033f43cSJassi Brar else 4345033f43cSJassi Brar con &= ~CON_ACTIVE; 4355033f43cSJassi Brar } 4365033f43cSJassi Brar 4375033f43cSJassi Brar writel(mod, addr + I2SMOD); 4385033f43cSJassi Brar writel(con, addr + I2SCON); 4395033f43cSJassi Brar } 4405033f43cSJassi Brar 4415033f43cSJassi Brar /* RX Channel Control */ 4425033f43cSJassi Brar static void i2s_rxctrl(struct i2s_dai *i2s, int on) 4435033f43cSJassi Brar { 444e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 445e2e16fa6SSylwester Nawrocki void __iomem *addr = priv->addr; 4465bfaeddcSSylwester Nawrocki int txr_off = priv->variant_regs->txr_off; 4475033f43cSJassi Brar u32 con = readl(addr + I2SCON); 448a5a56871SPadmavathi Venna u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); 4495033f43cSJassi Brar 4505033f43cSJassi Brar if (on) { 4515033f43cSJassi Brar con |= CON_RXDMA_ACTIVE | CON_ACTIVE; 4525033f43cSJassi Brar con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); 4535033f43cSJassi Brar 4545033f43cSJassi Brar if (any_tx_active(i2s)) 455a5a56871SPadmavathi Venna mod |= 2 << txr_off; 4565033f43cSJassi Brar else 457a5a56871SPadmavathi Venna mod |= 1 << txr_off; 4585033f43cSJassi Brar } else { 4595033f43cSJassi Brar con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; 4605033f43cSJassi Brar con &= ~CON_RXDMA_ACTIVE; 4615033f43cSJassi Brar 4625033f43cSJassi Brar if (any_tx_active(i2s)) 463a5a56871SPadmavathi Venna mod |= 0 << txr_off; 4645033f43cSJassi Brar else 4655033f43cSJassi Brar con &= ~CON_ACTIVE; 4665033f43cSJassi Brar } 4675033f43cSJassi Brar 4685033f43cSJassi Brar writel(mod, addr + I2SMOD); 4695033f43cSJassi Brar writel(con, addr + I2SCON); 4705033f43cSJassi Brar } 4715033f43cSJassi Brar 4725033f43cSJassi Brar /* Flush FIFO of an interface */ 4735033f43cSJassi Brar static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) 4745033f43cSJassi Brar { 4755033f43cSJassi Brar void __iomem *fic; 4765033f43cSJassi Brar u32 val; 4775033f43cSJassi Brar 4785033f43cSJassi Brar if (!i2s) 4795033f43cSJassi Brar return; 4805033f43cSJassi Brar 4815033f43cSJassi Brar if (is_secondary(i2s)) 482e2e16fa6SSylwester Nawrocki fic = i2s->priv->addr + I2SFICS; 4835033f43cSJassi Brar else 484e2e16fa6SSylwester Nawrocki fic = i2s->priv->addr + I2SFIC; 4855033f43cSJassi Brar 4865033f43cSJassi Brar /* Flush the FIFO */ 4875033f43cSJassi Brar writel(readl(fic) | flush, fic); 4885033f43cSJassi Brar 4895033f43cSJassi Brar /* Be patient */ 4905033f43cSJassi Brar val = msecs_to_loops(1) / 1000; /* 1 usec */ 4915033f43cSJassi Brar while (--val) 4925033f43cSJassi Brar cpu_relax(); 4935033f43cSJassi Brar 4945033f43cSJassi Brar writel(readl(fic) & ~flush, fic); 4955033f43cSJassi Brar } 4965033f43cSJassi Brar 4973b0fa51fSSylwester Nawrocki static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, 4983b0fa51fSSylwester Nawrocki int dir) 4995033f43cSJassi Brar { 5003b0fa51fSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 5015033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 502dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 5035bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs; 504a5a56871SPadmavathi Venna unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; 505a5a56871SPadmavathi Venna unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; 506ce8bcdbbSSylwester Nawrocki u32 mod, mask, val = 0; 507316fa9e0SCharles Keepax unsigned long flags; 508dc938ddbSMarek Szyprowski int ret = 0; 509dc938ddbSMarek Szyprowski 510dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 511ce8bcdbbSSylwester Nawrocki 5129d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 513e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 5149d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 5155033f43cSJassi Brar 5165033f43cSJassi Brar switch (clk_id) { 517c86d50f9SSylwester Nawrocki case SAMSUNG_I2S_OPCLK: 518ce8bcdbbSSylwester Nawrocki mask = MOD_OPCLK_MASK; 51945ae70e8SSylwester Nawrocki val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK; 520c86d50f9SSylwester Nawrocki break; 5215033f43cSJassi Brar case SAMSUNG_I2S_CDCLK: 522ce8bcdbbSSylwester Nawrocki mask = 1 << i2s_regs->cdclkcon_off; 5235033f43cSJassi Brar /* Shouldn't matter in GATING(CLOCK_IN) mode */ 5245033f43cSJassi Brar if (dir == SND_SOC_CLOCK_IN) 5255033f43cSJassi Brar rfs = 0; 5265033f43cSJassi Brar 527133c2681SCharles Keepax if ((rfs && other && other->rfs && (other->rfs != rfs)) || 5285033f43cSJassi Brar (any_active(i2s) && 5295033f43cSJassi Brar (((dir == SND_SOC_CLOCK_IN) 530a5a56871SPadmavathi Venna && !(mod & cdcon_mask)) || 5315033f43cSJassi Brar ((dir == SND_SOC_CLOCK_OUT) 532a5a56871SPadmavathi Venna && (mod & cdcon_mask))))) { 5335033f43cSJassi Brar dev_err(&i2s->pdev->dev, 5345033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 535dc938ddbSMarek Szyprowski ret = -EAGAIN; 536dc938ddbSMarek Szyprowski goto err; 5375033f43cSJassi Brar } 5385033f43cSJassi Brar 5395033f43cSJassi Brar if (dir == SND_SOC_CLOCK_IN) 540ce8bcdbbSSylwester Nawrocki val = 1 << i2s_regs->cdclkcon_off; 5415033f43cSJassi Brar 5425033f43cSJassi Brar i2s->rfs = rfs; 5435033f43cSJassi Brar break; 5445033f43cSJassi Brar 5455033f43cSJassi Brar case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ 5465033f43cSJassi Brar case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ 547ce8bcdbbSSylwester Nawrocki mask = 1 << i2s_regs->rclksrc_off; 548ce8bcdbbSSylwester Nawrocki 5495944170fSSylwester Nawrocki if ((priv->quirks & QUIRK_NO_MUXPSR) 5505033f43cSJassi Brar || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) 5515033f43cSJassi Brar clk_id = 0; 5525033f43cSJassi Brar else 5535033f43cSJassi Brar clk_id = 1; 5545033f43cSJassi Brar 5555033f43cSJassi Brar if (!any_active(i2s)) { 5563b0fa51fSSylwester Nawrocki if (priv->op_clk && !IS_ERR(priv->op_clk)) { 557a5a56871SPadmavathi Venna if ((clk_id && !(mod & rsrc_mask)) || 558a5a56871SPadmavathi Venna (!clk_id && (mod & rsrc_mask))) { 5593b0fa51fSSylwester Nawrocki clk_disable_unprepare(priv->op_clk); 5603b0fa51fSSylwester Nawrocki clk_put(priv->op_clk); 5615033f43cSJassi Brar } else { 5623b0fa51fSSylwester Nawrocki priv->rclk_srcrate = 5633b0fa51fSSylwester Nawrocki clk_get_rate(priv->op_clk); 564dc938ddbSMarek Szyprowski goto done; 5655033f43cSJassi Brar } 5665033f43cSJassi Brar } 5675033f43cSJassi Brar 5681974a042SPadmavathi Venna if (clk_id) 5693b0fa51fSSylwester Nawrocki priv->op_clk = clk_get(&i2s->pdev->dev, 5701974a042SPadmavathi Venna "i2s_opclk1"); 5711974a042SPadmavathi Venna else 5723b0fa51fSSylwester Nawrocki priv->op_clk = clk_get(&i2s->pdev->dev, 5731974a042SPadmavathi Venna "i2s_opclk0"); 574a6aba536SSylwester Nawrocki 5753b0fa51fSSylwester Nawrocki if (WARN_ON(IS_ERR(priv->op_clk))) { 5763b0fa51fSSylwester Nawrocki ret = PTR_ERR(priv->op_clk); 5773b0fa51fSSylwester Nawrocki priv->op_clk = NULL; 578dc938ddbSMarek Szyprowski goto err; 579dc938ddbSMarek Szyprowski } 580a6aba536SSylwester Nawrocki 5813b0fa51fSSylwester Nawrocki ret = clk_prepare_enable(priv->op_clk); 5826431a7e3SChristophe Jaillet if (ret) { 5833b0fa51fSSylwester Nawrocki clk_put(priv->op_clk); 5843b0fa51fSSylwester Nawrocki priv->op_clk = NULL; 585f5c97c7bSArvind Yadav goto err; 5866431a7e3SChristophe Jaillet } 5873b0fa51fSSylwester Nawrocki priv->rclk_srcrate = clk_get_rate(priv->op_clk); 5885033f43cSJassi Brar 589a5a56871SPadmavathi Venna } else if ((!clk_id && (mod & rsrc_mask)) 590a5a56871SPadmavathi Venna || (clk_id && !(mod & rsrc_mask))) { 5915033f43cSJassi Brar dev_err(&i2s->pdev->dev, 5925033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 593dc938ddbSMarek Szyprowski ret = -EAGAIN; 594dc938ddbSMarek Szyprowski goto err; 5955033f43cSJassi Brar } else { 5965033f43cSJassi Brar /* Call can't be on the active DAI */ 597dc938ddbSMarek Szyprowski goto done; 5985033f43cSJassi Brar } 5995033f43cSJassi Brar 600ce8bcdbbSSylwester Nawrocki if (clk_id == 1) 601ce8bcdbbSSylwester Nawrocki val = 1 << i2s_regs->rclksrc_off; 602b2de1d20SPadmavathi Venna break; 6035033f43cSJassi Brar default: 6045033f43cSJassi Brar dev_err(&i2s->pdev->dev, "We don't serve that!\n"); 605dc938ddbSMarek Szyprowski ret = -EINVAL; 606dc938ddbSMarek Szyprowski goto err; 6075033f43cSJassi Brar } 6085033f43cSJassi Brar 6099d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 610e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 611ce8bcdbbSSylwester Nawrocki mod = (mod & ~mask) | val; 612e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 6139d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 614dc938ddbSMarek Szyprowski done: 615dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 6165033f43cSJassi Brar 6175033f43cSJassi Brar return 0; 618dc938ddbSMarek Szyprowski err: 619dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 620dc938ddbSMarek Szyprowski return ret; 6215033f43cSJassi Brar } 6225033f43cSJassi Brar 62389d2e831SSylwester Nawrocki static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 6245033f43cSJassi Brar { 62589d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 6265033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 627a5a56871SPadmavathi Venna int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; 628ce8bcdbbSSylwester Nawrocki u32 mod, tmp = 0; 629316fa9e0SCharles Keepax unsigned long flags; 6305033f43cSJassi Brar 6315bfaeddcSSylwester Nawrocki lrp_shift = priv->variant_regs->lrp_off; 6325bfaeddcSSylwester Nawrocki sdf_shift = priv->variant_regs->sdf_off; 6335bfaeddcSSylwester Nawrocki mod_slave = 1 << priv->variant_regs->mss_off; 6344ca0c0d4SPadmavathi Venna 635b60be4aaSPadmavathi Venna sdf_mask = MOD_SDF_MASK << sdf_shift; 636b60be4aaSPadmavathi Venna lrp_rlow = MOD_LR_RLOW << lrp_shift; 637b60be4aaSPadmavathi Venna 6385033f43cSJassi Brar /* Format is priority */ 6395033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 6405033f43cSJassi Brar case SND_SOC_DAIFMT_RIGHT_J: 641b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 642b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_MSB << sdf_shift); 6435033f43cSJassi Brar break; 6445033f43cSJassi Brar case SND_SOC_DAIFMT_LEFT_J: 645b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 646b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_LSB << sdf_shift); 6475033f43cSJassi Brar break; 6485033f43cSJassi Brar case SND_SOC_DAIFMT_I2S: 649b60be4aaSPadmavathi Venna tmp |= (MOD_SDF_IIS << sdf_shift); 6505033f43cSJassi Brar break; 6515033f43cSJassi Brar default: 6525033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Format not supported\n"); 6535033f43cSJassi Brar return -EINVAL; 6545033f43cSJassi Brar } 6555033f43cSJassi Brar 6565033f43cSJassi Brar /* 6575033f43cSJassi Brar * INV flag is relative to the FORMAT flag - if set it simply 6585033f43cSJassi Brar * flips the polarity specified by the Standard 6595033f43cSJassi Brar */ 6605033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 6615033f43cSJassi Brar case SND_SOC_DAIFMT_NB_NF: 6625033f43cSJassi Brar break; 6635033f43cSJassi Brar case SND_SOC_DAIFMT_NB_IF: 664b60be4aaSPadmavathi Venna if (tmp & lrp_rlow) 665b60be4aaSPadmavathi Venna tmp &= ~lrp_rlow; 6665033f43cSJassi Brar else 667b60be4aaSPadmavathi Venna tmp |= lrp_rlow; 6685033f43cSJassi Brar break; 6695033f43cSJassi Brar default: 6705033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Polarity not supported\n"); 6715033f43cSJassi Brar return -EINVAL; 6725033f43cSJassi Brar } 6735033f43cSJassi Brar 6745033f43cSJassi Brar switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 6755033f43cSJassi Brar case SND_SOC_DAIFMT_CBM_CFM: 676a5a56871SPadmavathi Venna tmp |= mod_slave; 6775033f43cSJassi Brar break; 6785033f43cSJassi Brar case SND_SOC_DAIFMT_CBS_CFS: 679647d04f8SSylwester Nawrocki /* 680647d04f8SSylwester Nawrocki * Set default source clock in Master mode, only when the 681647d04f8SSylwester Nawrocki * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any 682647d04f8SSylwester Nawrocki * clock configuration assigned in DT is not overwritten. 683647d04f8SSylwester Nawrocki */ 6843b0fa51fSSylwester Nawrocki if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL) 6855033f43cSJassi Brar i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, 6865033f43cSJassi Brar 0, SND_SOC_CLOCK_IN); 6875033f43cSJassi Brar break; 6885033f43cSJassi Brar default: 6895033f43cSJassi Brar dev_err(&i2s->pdev->dev, "master/slave format not supported\n"); 6905033f43cSJassi Brar return -EINVAL; 6915033f43cSJassi Brar } 6925033f43cSJassi Brar 693dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 6949d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 695e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 696b60be4aaSPadmavathi Venna /* 697b60be4aaSPadmavathi Venna * Don't change the I2S mode if any controller is active on this 698b60be4aaSPadmavathi Venna * channel. 699b60be4aaSPadmavathi Venna */ 7005033f43cSJassi Brar if (any_active(i2s) && 701a5a56871SPadmavathi Venna ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { 7029d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 703dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 7045033f43cSJassi Brar dev_err(&i2s->pdev->dev, 7055033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 7065033f43cSJassi Brar return -EAGAIN; 7075033f43cSJassi Brar } 7085033f43cSJassi Brar 709a5a56871SPadmavathi Venna mod &= ~(sdf_mask | lrp_rlow | mod_slave); 7105033f43cSJassi Brar mod |= tmp; 711e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 712f29eec79SSylwester Nawrocki priv->slave_mode = (mod & mod_slave); 7139d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 714dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 7155033f43cSJassi Brar 7165033f43cSJassi Brar return 0; 7175033f43cSJassi Brar } 7185033f43cSJassi Brar 7195033f43cSJassi Brar static int i2s_hw_params(struct snd_pcm_substream *substream, 7205033f43cSJassi Brar struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 7215033f43cSJassi Brar { 72289d2e831SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 7235033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 724ce8bcdbbSSylwester Nawrocki u32 mod, mask = 0, val = 0; 725860b454cSSylwester Nawrocki struct clk *rclksrc; 726316fa9e0SCharles Keepax unsigned long flags; 7275033f43cSJassi Brar 728dc938ddbSMarek Szyprowski WARN_ON(!pm_runtime_active(dai->dev)); 729dc938ddbSMarek Szyprowski 7305033f43cSJassi Brar if (!is_secondary(i2s)) 731ce8bcdbbSSylwester Nawrocki mask |= (MOD_DC2_EN | MOD_DC1_EN); 7325033f43cSJassi Brar 7335033f43cSJassi Brar switch (params_channels(params)) { 7345033f43cSJassi Brar case 6: 735ce8bcdbbSSylwester Nawrocki val |= MOD_DC2_EN; 736*df561f66SGustavo A. R. Silva fallthrough; 7375033f43cSJassi Brar case 4: 738ce8bcdbbSSylwester Nawrocki val |= MOD_DC1_EN; 7395033f43cSJassi Brar break; 7405033f43cSJassi Brar case 2: 741588fb705SSangsu Park if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 74269e7a69aSSylwester Nawrocki i2s->dma_playback.addr_width = 4; 743588fb705SSangsu Park else 74469e7a69aSSylwester Nawrocki i2s->dma_capture.addr_width = 4; 745588fb705SSangsu Park break; 746588fb705SSangsu Park case 1: 747588fb705SSangsu Park if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 74869e7a69aSSylwester Nawrocki i2s->dma_playback.addr_width = 2; 749588fb705SSangsu Park else 75069e7a69aSSylwester Nawrocki i2s->dma_capture.addr_width = 2; 751588fb705SSangsu Park 7525033f43cSJassi Brar break; 7535033f43cSJassi Brar default: 7545033f43cSJassi Brar dev_err(&i2s->pdev->dev, "%d channels not supported\n", 7555033f43cSJassi Brar params_channels(params)); 7565033f43cSJassi Brar return -EINVAL; 7575033f43cSJassi Brar } 7585033f43cSJassi Brar 7595033f43cSJassi Brar if (is_secondary(i2s)) 760ce8bcdbbSSylwester Nawrocki mask |= MOD_BLCS_MASK; 7615033f43cSJassi Brar else 762ce8bcdbbSSylwester Nawrocki mask |= MOD_BLCP_MASK; 7635033f43cSJassi Brar 7645033f43cSJassi Brar if (is_manager(i2s)) 765ce8bcdbbSSylwester Nawrocki mask |= MOD_BLC_MASK; 7665033f43cSJassi Brar 76788ce1465STushar Behera switch (params_width(params)) { 76888ce1465STushar Behera case 8: 7695033f43cSJassi Brar if (is_secondary(i2s)) 770ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_8BIT; 7715033f43cSJassi Brar else 772ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_8BIT; 7735033f43cSJassi Brar if (is_manager(i2s)) 774ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_8BIT; 7755033f43cSJassi Brar break; 77688ce1465STushar Behera case 16: 7775033f43cSJassi Brar if (is_secondary(i2s)) 778ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_16BIT; 7795033f43cSJassi Brar else 780ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_16BIT; 7815033f43cSJassi Brar if (is_manager(i2s)) 782ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_16BIT; 7835033f43cSJassi Brar break; 78488ce1465STushar Behera case 24: 7855033f43cSJassi Brar if (is_secondary(i2s)) 786ce8bcdbbSSylwester Nawrocki val |= MOD_BLCS_24BIT; 7875033f43cSJassi Brar else 788ce8bcdbbSSylwester Nawrocki val |= MOD_BLCP_24BIT; 7895033f43cSJassi Brar if (is_manager(i2s)) 790ce8bcdbbSSylwester Nawrocki val |= MOD_BLC_24BIT; 7915033f43cSJassi Brar break; 7925033f43cSJassi Brar default: 7935033f43cSJassi Brar dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", 7945033f43cSJassi Brar params_format(params)); 7955033f43cSJassi Brar return -EINVAL; 7965033f43cSJassi Brar } 797ce8bcdbbSSylwester Nawrocki 7989d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 799e2e16fa6SSylwester Nawrocki mod = readl(priv->addr + I2SMOD); 800ce8bcdbbSSylwester Nawrocki mod = (mod & ~mask) | val; 801e2e16fa6SSylwester Nawrocki writel(mod, priv->addr + I2SMOD); 8029d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 8035033f43cSJassi Brar 80469e7a69aSSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); 805d37bdf73SMark Brown 8065033f43cSJassi Brar i2s->frmclk = params_rate(params); 8075033f43cSJassi Brar 80889d2e831SSylwester Nawrocki rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC]; 809860b454cSSylwester Nawrocki if (rclksrc && !IS_ERR(rclksrc)) 8103b0fa51fSSylwester Nawrocki priv->rclk_srcrate = clk_get_rate(rclksrc); 811860b454cSSylwester Nawrocki 8125033f43cSJassi Brar return 0; 8135033f43cSJassi Brar } 8145033f43cSJassi Brar 8159f9f8a5bSSylwester Nawrocki /* We set constraints on the substream according to the version of I2S */ 8165033f43cSJassi Brar static int i2s_startup(struct snd_pcm_substream *substream, 8175033f43cSJassi Brar struct snd_soc_dai *dai) 8185033f43cSJassi Brar { 8195944170fSSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 8205033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 821dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8225033f43cSJassi Brar unsigned long flags; 8235033f43cSJassi Brar 824dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 825dc938ddbSMarek Szyprowski 826defc67c6SSylwester Nawrocki spin_lock_irqsave(&priv->pcm_lock, flags); 8275033f43cSJassi Brar 8285033f43cSJassi Brar i2s->mode |= DAI_OPENED; 8295033f43cSJassi Brar 8305033f43cSJassi Brar if (is_manager(other)) 8315033f43cSJassi Brar i2s->mode &= ~DAI_MANAGER; 8325033f43cSJassi Brar else 8335033f43cSJassi Brar i2s->mode |= DAI_MANAGER; 8345033f43cSJassi Brar 8355944170fSSylwester Nawrocki if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR)) 836e2e16fa6SSylwester Nawrocki writel(CON_RSTCLR, i2s->priv->addr + I2SCON); 8372d77828dSPadmavathi Venna 838defc67c6SSylwester Nawrocki spin_unlock_irqrestore(&priv->pcm_lock, flags); 8395033f43cSJassi Brar 8405033f43cSJassi Brar return 0; 8415033f43cSJassi Brar } 8425033f43cSJassi Brar 8435033f43cSJassi Brar static void i2s_shutdown(struct snd_pcm_substream *substream, 8445033f43cSJassi Brar struct snd_soc_dai *dai) 8455033f43cSJassi Brar { 846defc67c6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 8475033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 848dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8495033f43cSJassi Brar unsigned long flags; 8505033f43cSJassi Brar 851defc67c6SSylwester Nawrocki spin_lock_irqsave(&priv->pcm_lock, flags); 8525033f43cSJassi Brar 8535033f43cSJassi Brar i2s->mode &= ~DAI_OPENED; 8545033f43cSJassi Brar i2s->mode &= ~DAI_MANAGER; 8555033f43cSJassi Brar 856074b89bbSSylwester Nawrocki if (is_opened(other)) 8575033f43cSJassi Brar other->mode |= DAI_MANAGER; 858074b89bbSSylwester Nawrocki 8595033f43cSJassi Brar /* Reset any constraint on RFS and BFS */ 8605033f43cSJassi Brar i2s->rfs = 0; 8615033f43cSJassi Brar i2s->bfs = 0; 8625033f43cSJassi Brar 863defc67c6SSylwester Nawrocki spin_unlock_irqrestore(&priv->pcm_lock, flags); 864dc938ddbSMarek Szyprowski 865dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 8665033f43cSJassi Brar } 8675033f43cSJassi Brar 8685033f43cSJassi Brar static int config_setup(struct i2s_dai *i2s) 8695033f43cSJassi Brar { 8703b0fa51fSSylwester Nawrocki struct samsung_i2s_priv *priv = i2s->priv; 871dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 8725033f43cSJassi Brar unsigned rfs, bfs, blc; 8735033f43cSJassi Brar u32 psr; 8745033f43cSJassi Brar 8755033f43cSJassi Brar blc = get_blc(i2s); 8765033f43cSJassi Brar 8775033f43cSJassi Brar bfs = i2s->bfs; 8785033f43cSJassi Brar 8795033f43cSJassi Brar if (!bfs && other) 8805033f43cSJassi Brar bfs = other->bfs; 8815033f43cSJassi Brar 8825033f43cSJassi Brar /* Select least possible multiple(2) if no constraint set */ 8835033f43cSJassi Brar if (!bfs) 8845033f43cSJassi Brar bfs = blc * 2; 8855033f43cSJassi Brar 8865033f43cSJassi Brar rfs = i2s->rfs; 8875033f43cSJassi Brar 8885033f43cSJassi Brar if (!rfs && other) 8895033f43cSJassi Brar rfs = other->rfs; 8905033f43cSJassi Brar 8915033f43cSJassi Brar if ((rfs == 256 || rfs == 512) && (blc == 24)) { 8925033f43cSJassi Brar dev_err(&i2s->pdev->dev, 8935033f43cSJassi Brar "%d-RFS not supported for 24-blc\n", rfs); 8945033f43cSJassi Brar return -EINVAL; 8955033f43cSJassi Brar } 8965033f43cSJassi Brar 8975033f43cSJassi Brar if (!rfs) { 8985033f43cSJassi Brar if (bfs == 16 || bfs == 32) 8995033f43cSJassi Brar rfs = 256; 9005033f43cSJassi Brar else 9015033f43cSJassi Brar rfs = 384; 9025033f43cSJassi Brar } 9035033f43cSJassi Brar 9045033f43cSJassi Brar /* If already setup and running */ 9055033f43cSJassi Brar if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) { 9065033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9075033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 9085033f43cSJassi Brar return -EAGAIN; 9095033f43cSJassi Brar } 9105033f43cSJassi Brar 9115033f43cSJassi Brar set_bfs(i2s, bfs); 9125033f43cSJassi Brar set_rfs(i2s, rfs); 9135033f43cSJassi Brar 91477010010SPadmavathi Venna /* Don't bother with PSR in Slave mode */ 915f29eec79SSylwester Nawrocki if (priv->slave_mode) 91677010010SPadmavathi Venna return 0; 91777010010SPadmavathi Venna 9185944170fSSylwester Nawrocki if (!(priv->quirks & QUIRK_NO_MUXPSR)) { 9193b0fa51fSSylwester Nawrocki psr = priv->rclk_srcrate / i2s->frmclk / rfs; 920e2e16fa6SSylwester Nawrocki writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR); 9215033f43cSJassi Brar dev_dbg(&i2s->pdev->dev, 9225033f43cSJassi Brar "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n", 9233b0fa51fSSylwester Nawrocki priv->rclk_srcrate, psr, rfs, bfs); 9245033f43cSJassi Brar } 9255033f43cSJassi Brar 9265033f43cSJassi Brar return 0; 9275033f43cSJassi Brar } 9285033f43cSJassi Brar 9295033f43cSJassi Brar static int i2s_trigger(struct snd_pcm_substream *substream, 9305033f43cSJassi Brar int cmd, struct snd_soc_dai *dai) 9315033f43cSJassi Brar { 9329d7939c9SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 9335033f43cSJassi Brar int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 934c101ce88SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 9357de6b6bcSKuninori Morimoto struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); 9365033f43cSJassi Brar unsigned long flags; 9375033f43cSJassi Brar 9385033f43cSJassi Brar switch (cmd) { 9395033f43cSJassi Brar case SNDRV_PCM_TRIGGER_START: 9405033f43cSJassi Brar case SNDRV_PCM_TRIGGER_RESUME: 9415033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 942dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 9439d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 9445033f43cSJassi Brar 9455033f43cSJassi Brar if (config_setup(i2s)) { 9469d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 9475033f43cSJassi Brar return -EINVAL; 9485033f43cSJassi Brar } 9495033f43cSJassi Brar 9505033f43cSJassi Brar if (capture) 9515033f43cSJassi Brar i2s_rxctrl(i2s, 1); 9525033f43cSJassi Brar else 9535033f43cSJassi Brar i2s_txctrl(i2s, 1); 9545033f43cSJassi Brar 9559d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 9565033f43cSJassi Brar break; 9575033f43cSJassi Brar case SNDRV_PCM_TRIGGER_STOP: 9585033f43cSJassi Brar case SNDRV_PCM_TRIGGER_SUSPEND: 9595033f43cSJassi Brar case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 9609d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 9615033f43cSJassi Brar 962c90887feSJassi Brar if (capture) { 9635033f43cSJassi Brar i2s_rxctrl(i2s, 0); 964775bc971SJassi Brar i2s_fifo(i2s, FIC_RXFLUSH); 965c90887feSJassi Brar } else { 966c90887feSJassi Brar i2s_txctrl(i2s, 0); 967775bc971SJassi Brar i2s_fifo(i2s, FIC_TXFLUSH); 968c90887feSJassi Brar } 969775bc971SJassi Brar 9709d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 971dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 9725033f43cSJassi Brar break; 9735033f43cSJassi Brar } 9745033f43cSJassi Brar 9755033f43cSJassi Brar return 0; 9765033f43cSJassi Brar } 9775033f43cSJassi Brar 9785033f43cSJassi Brar static int i2s_set_clkdiv(struct snd_soc_dai *dai, 9795033f43cSJassi Brar int div_id, int div) 9805033f43cSJassi Brar { 9815033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 982dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 9835033f43cSJassi Brar 9845033f43cSJassi Brar switch (div_id) { 9855033f43cSJassi Brar case SAMSUNG_I2S_DIV_BCLK: 986dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 9875033f43cSJassi Brar if ((any_active(i2s) && div && (get_bfs(i2s) != div)) 9885033f43cSJassi Brar || (other && other->bfs && (other->bfs != div))) { 989dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 9905033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9915033f43cSJassi Brar "%s:%d Other DAI busy\n", __func__, __LINE__); 9925033f43cSJassi Brar return -EAGAIN; 9935033f43cSJassi Brar } 9945033f43cSJassi Brar i2s->bfs = div; 995dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 9965033f43cSJassi Brar break; 9975033f43cSJassi Brar default: 9985033f43cSJassi Brar dev_err(&i2s->pdev->dev, 9995033f43cSJassi Brar "Invalid clock divider(%d)\n", div_id); 10005033f43cSJassi Brar return -EINVAL; 10015033f43cSJassi Brar } 10025033f43cSJassi Brar 10035033f43cSJassi Brar return 0; 10045033f43cSJassi Brar } 10055033f43cSJassi Brar 10065033f43cSJassi Brar static snd_pcm_sframes_t 10075033f43cSJassi Brar i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 10085033f43cSJassi Brar { 1009e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 10105033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 1011e2e16fa6SSylwester Nawrocki u32 reg = readl(priv->addr + I2SFIC); 10125033f43cSJassi Brar snd_pcm_sframes_t delay; 10135033f43cSJassi Brar 1014dc938ddbSMarek Szyprowski WARN_ON(!pm_runtime_active(dai->dev)); 1015dc938ddbSMarek Szyprowski 10165033f43cSJassi Brar if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 10175033f43cSJassi Brar delay = FIC_RXCOUNT(reg); 10185033f43cSJassi Brar else if (is_secondary(i2s)) 1019e2e16fa6SSylwester Nawrocki delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS)); 10205033f43cSJassi Brar else 10215bfaeddcSSylwester Nawrocki delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f; 10225033f43cSJassi Brar 10235033f43cSJassi Brar return delay; 10245033f43cSJassi Brar } 10255033f43cSJassi Brar 10265033f43cSJassi Brar #ifdef CONFIG_PM 102721385a4fSKuninori Morimoto static int i2s_suspend(struct snd_soc_component *component) 10285033f43cSJassi Brar { 102921385a4fSKuninori Morimoto return pm_runtime_force_suspend(component->dev); 10305033f43cSJassi Brar } 10315033f43cSJassi Brar 103221385a4fSKuninori Morimoto static int i2s_resume(struct snd_soc_component *component) 10335033f43cSJassi Brar { 103421385a4fSKuninori Morimoto return pm_runtime_force_resume(component->dev); 10355033f43cSJassi Brar } 10365033f43cSJassi Brar #else 10375033f43cSJassi Brar #define i2s_suspend NULL 10385033f43cSJassi Brar #define i2s_resume NULL 10395033f43cSJassi Brar #endif 10405033f43cSJassi Brar 10415033f43cSJassi Brar static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) 10425033f43cSJassi Brar { 1043e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 10445033f43cSJassi Brar struct i2s_dai *i2s = to_info(dai); 1045dcd60fc3SSylwester Nawrocki struct i2s_dai *other = get_other_dai(i2s); 1046ce8bcdbbSSylwester Nawrocki unsigned long flags; 10475033f43cSJassi Brar 1048dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 1049dc938ddbSMarek Szyprowski 10509f9f8a5bSSylwester Nawrocki if (is_secondary(i2s)) { 10519f9f8a5bSSylwester Nawrocki /* If this is probe on the secondary DAI */ 1052eb540d39SSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL); 1053872c26bdSSylwester Nawrocki } else { 105469e7a69aSSylwester Nawrocki snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, 1055872c26bdSSylwester Nawrocki &i2s->dma_capture); 1056511e3033SMark Brown 10575944170fSSylwester Nawrocki if (priv->quirks & QUIRK_NEED_RSTCLR) 1058e2e16fa6SSylwester Nawrocki writel(CON_RSTCLR, priv->addr + I2SCON); 10595033f43cSJassi Brar 10605944170fSSylwester Nawrocki if (priv->quirks & QUIRK_SUPPORTS_IDMA) 1061e2e16fa6SSylwester Nawrocki idma_reg_addr_init(priv->addr, 1062eb540d39SSylwester Nawrocki other->idma_playback.addr); 1063872c26bdSSylwester Nawrocki } 106461100f40SSangbeom Kim 10655033f43cSJassi Brar /* Reset any constraint on RFS and BFS */ 10665033f43cSJassi Brar i2s->rfs = 0; 10675033f43cSJassi Brar i2s->bfs = 0; 1068ce8bcdbbSSylwester Nawrocki 10699d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 10705033f43cSJassi Brar i2s_txctrl(i2s, 0); 10715033f43cSJassi Brar i2s_rxctrl(i2s, 0); 10725033f43cSJassi Brar i2s_fifo(i2s, FIC_TXFLUSH); 10735033f43cSJassi Brar i2s_fifo(other, FIC_TXFLUSH); 10745033f43cSJassi Brar i2s_fifo(i2s, FIC_RXFLUSH); 10759d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 10765033f43cSJassi Brar 10775033f43cSJassi Brar /* Gate CDCLK by default */ 10785033f43cSJassi Brar if (!is_opened(other)) 10795033f43cSJassi Brar i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 10805033f43cSJassi Brar 0, SND_SOC_CLOCK_IN); 1081dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 10825033f43cSJassi Brar 10835033f43cSJassi Brar return 0; 10845033f43cSJassi Brar } 10855033f43cSJassi Brar 10865033f43cSJassi Brar static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) 10875033f43cSJassi Brar { 1088e2e16fa6SSylwester Nawrocki struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); 1089a404b72dSSylwester Nawrocki struct i2s_dai *i2s = to_info(dai); 10905faf071dSCharles Keepax unsigned long flags; 10915033f43cSJassi Brar 1092dc938ddbSMarek Szyprowski pm_runtime_get_sync(dai->dev); 1093dc938ddbSMarek Szyprowski 1094c92f1d0eSSylwester Nawrocki if (!is_secondary(i2s)) { 10955944170fSSylwester Nawrocki if (priv->quirks & QUIRK_NEED_RSTCLR) { 10969d7939c9SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 1097e2e16fa6SSylwester Nawrocki writel(0, priv->addr + I2SCON); 10989d7939c9SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 1099ce8bcdbbSSylwester Nawrocki } 11005033f43cSJassi Brar } 11015033f43cSJassi Brar 1102dc938ddbSMarek Szyprowski pm_runtime_put(dai->dev); 1103dc938ddbSMarek Szyprowski 11045033f43cSJassi Brar return 0; 11055033f43cSJassi Brar } 11065033f43cSJassi Brar 110785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { 11085033f43cSJassi Brar .trigger = i2s_trigger, 11095033f43cSJassi Brar .hw_params = i2s_hw_params, 11105033f43cSJassi Brar .set_fmt = i2s_set_fmt, 11115033f43cSJassi Brar .set_clkdiv = i2s_set_clkdiv, 11125033f43cSJassi Brar .set_sysclk = i2s_set_sysclk, 11135033f43cSJassi Brar .startup = i2s_startup, 11145033f43cSJassi Brar .shutdown = i2s_shutdown, 11155033f43cSJassi Brar .delay = i2s_delay, 11165033f43cSJassi Brar }; 11175033f43cSJassi Brar 111864aba9bcSSylwester Nawrocki static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { 111964aba9bcSSylwester Nawrocki /* Backend DAI */ 112064aba9bcSSylwester Nawrocki SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0), 112164aba9bcSSylwester Nawrocki SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0), 112264aba9bcSSylwester Nawrocki 112364aba9bcSSylwester Nawrocki /* Playback Mixer */ 112464aba9bcSSylwester Nawrocki SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 112564aba9bcSSylwester Nawrocki }; 112664aba9bcSSylwester Nawrocki 112764aba9bcSSylwester Nawrocki static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { 112842e4ceddSSylwester Nawrocki { "Playback Mixer", NULL, "Primary Playback" }, 112942e4ceddSSylwester Nawrocki { "Playback Mixer", NULL, "Secondary Playback" }, 113064aba9bcSSylwester Nawrocki 113164aba9bcSSylwester Nawrocki { "Mixer DAI TX", NULL, "Playback Mixer" }, 113242e4ceddSSylwester Nawrocki { "Primary Capture", NULL, "Mixer DAI RX" }, 113364aba9bcSSylwester Nawrocki }; 113464aba9bcSSylwester Nawrocki 11354b828535SKuninori Morimoto static const struct snd_soc_component_driver samsung_i2s_component = { 11364b828535SKuninori Morimoto .name = "samsung-i2s", 113764aba9bcSSylwester Nawrocki 113864aba9bcSSylwester Nawrocki .dapm_widgets = samsung_i2s_widgets, 113964aba9bcSSylwester Nawrocki .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), 114064aba9bcSSylwester Nawrocki 114164aba9bcSSylwester Nawrocki .dapm_routes = samsung_i2s_dapm_routes, 114264aba9bcSSylwester Nawrocki .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes), 114321385a4fSKuninori Morimoto 114421385a4fSKuninori Morimoto .suspend = i2s_suspend, 114521385a4fSKuninori Morimoto .resume = i2s_resume, 11464b828535SKuninori Morimoto }; 11474b828535SKuninori Morimoto 1148c5ba6192SSylwester Nawrocki #define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ 11495033f43cSJassi Brar SNDRV_PCM_FMTBIT_S24_LE) 11505033f43cSJassi Brar 1151a404b72dSSylwester Nawrocki static int i2s_alloc_dais(struct samsung_i2s_priv *priv, 11524720c2feSJaechul Lee const struct samsung_i2s_dai_data *i2s_dai_data, 1153a404b72dSSylwester Nawrocki int num_dais) 11545033f43cSJassi Brar { 1155a404b72dSSylwester Nawrocki static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" }; 115642e4ceddSSylwester Nawrocki static const char *stream_names[] = { "Primary Playback", 115742e4ceddSSylwester Nawrocki "Secondary Playback" }; 1158a404b72dSSylwester Nawrocki struct snd_soc_dai_driver *dai_drv; 1159a404b72dSSylwester Nawrocki struct i2s_dai *dai; 1160a404b72dSSylwester Nawrocki int i; 11615033f43cSJassi Brar 1162a404b72dSSylwester Nawrocki priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais, 1163a404b72dSSylwester Nawrocki sizeof(*dai), GFP_KERNEL); 1164a404b72dSSylwester Nawrocki if (!priv->dai) 1165a404b72dSSylwester Nawrocki return -ENOMEM; 11665033f43cSJassi Brar 1167a404b72dSSylwester Nawrocki priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais, 1168a404b72dSSylwester Nawrocki sizeof(*dai_drv), GFP_KERNEL); 1169a404b72dSSylwester Nawrocki if (!priv->dai_drv) 1170a404b72dSSylwester Nawrocki return -ENOMEM; 11715033f43cSJassi Brar 1172a404b72dSSylwester Nawrocki for (i = 0; i < num_dais; i++) { 1173a404b72dSSylwester Nawrocki dai_drv = &priv->dai_drv[i]; 1174a404b72dSSylwester Nawrocki 1175a404b72dSSylwester Nawrocki dai_drv->probe = samsung_i2s_dai_probe; 1176a404b72dSSylwester Nawrocki dai_drv->remove = samsung_i2s_dai_remove; 1177a404b72dSSylwester Nawrocki 1178a404b72dSSylwester Nawrocki dai_drv->symmetric_rates = 1; 1179a404b72dSSylwester Nawrocki dai_drv->ops = &samsung_i2s_dai_ops; 1180a404b72dSSylwester Nawrocki 1181a404b72dSSylwester Nawrocki dai_drv->playback.channels_min = 1; 1182a404b72dSSylwester Nawrocki dai_drv->playback.channels_max = 2; 1183a404b72dSSylwester Nawrocki dai_drv->playback.rates = i2s_dai_data->pcm_rates; 1184a404b72dSSylwester Nawrocki dai_drv->playback.formats = SAMSUNG_I2S_FMTS; 118564aba9bcSSylwester Nawrocki dai_drv->playback.stream_name = stream_names[i]; 1186a404b72dSSylwester Nawrocki 1187a404b72dSSylwester Nawrocki dai_drv->id = i + 1; 1188a404b72dSSylwester Nawrocki dai_drv->name = dai_names[i]; 1189a404b72dSSylwester Nawrocki 1190a404b72dSSylwester Nawrocki priv->dai[i].drv = &priv->dai_drv[i]; 1191a404b72dSSylwester Nawrocki priv->dai[i].pdev = priv->pdev; 1192c6f9b1ebSPrathyush K } 1193a404b72dSSylwester Nawrocki 1194a404b72dSSylwester Nawrocki /* Initialize capture only for the primary DAI */ 1195a404b72dSSylwester Nawrocki dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1]; 1196a404b72dSSylwester Nawrocki 1197a404b72dSSylwester Nawrocki dai_drv->capture.channels_min = 1; 1198a404b72dSSylwester Nawrocki dai_drv->capture.channels_max = 2; 1199a404b72dSSylwester Nawrocki dai_drv->capture.rates = i2s_dai_data->pcm_rates; 1200a404b72dSSylwester Nawrocki dai_drv->capture.formats = SAMSUNG_I2S_FMTS; 120142e4ceddSSylwester Nawrocki dai_drv->capture.stream_name = "Primary Capture"; 1202a404b72dSSylwester Nawrocki 1203a404b72dSSylwester Nawrocki return 0; 12045033f43cSJassi Brar } 12055033f43cSJassi Brar 1206641d334bSRafael J. Wysocki #ifdef CONFIG_PM 12075b1d3c34SR. Chandrasekar static int i2s_runtime_suspend(struct device *dev) 12085b1d3c34SR. Chandrasekar { 1209b5d015e6SSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(dev); 12105b1d3c34SR. Chandrasekar 1211e2e16fa6SSylwester Nawrocki priv->suspend_i2smod = readl(priv->addr + I2SMOD); 1212e2e16fa6SSylwester Nawrocki priv->suspend_i2scon = readl(priv->addr + I2SCON); 1213e2e16fa6SSylwester Nawrocki priv->suspend_i2spsr = readl(priv->addr + I2SPSR); 1214e7e52dfcSMarek Szyprowski 12153b0fa51fSSylwester Nawrocki if (priv->op_clk) 12163b0fa51fSSylwester Nawrocki clk_disable_unprepare(priv->op_clk); 1217b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 12185b1d3c34SR. Chandrasekar 12195b1d3c34SR. Chandrasekar return 0; 12205b1d3c34SR. Chandrasekar } 12215b1d3c34SR. Chandrasekar 12225b1d3c34SR. Chandrasekar static int i2s_runtime_resume(struct device *dev) 12235b1d3c34SR. Chandrasekar { 1224b5d015e6SSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(dev); 1225f5c97c7bSArvind Yadav int ret; 12265b1d3c34SR. Chandrasekar 1227b5d015e6SSylwester Nawrocki ret = clk_prepare_enable(priv->clk); 1228f5c97c7bSArvind Yadav if (ret) 1229f5c97c7bSArvind Yadav return ret; 1230f5c97c7bSArvind Yadav 12313b0fa51fSSylwester Nawrocki if (priv->op_clk) { 12323b0fa51fSSylwester Nawrocki ret = clk_prepare_enable(priv->op_clk); 1233f5c97c7bSArvind Yadav if (ret) { 1234b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 1235f5c97c7bSArvind Yadav return ret; 1236f5c97c7bSArvind Yadav } 1237f5c97c7bSArvind Yadav } 12385b1d3c34SR. Chandrasekar 1239e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2scon, priv->addr + I2SCON); 1240e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2smod, priv->addr + I2SMOD); 1241e2e16fa6SSylwester Nawrocki writel(priv->suspend_i2spsr, priv->addr + I2SPSR); 12425b1d3c34SR. Chandrasekar 12435b1d3c34SR. Chandrasekar return 0; 12445b1d3c34SR. Chandrasekar } 1245641d334bSRafael J. Wysocki #endif /* CONFIG_PM */ 12465b1d3c34SR. Chandrasekar 124789d2e831SSylwester Nawrocki static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) 1248074b89bbSSylwester Nawrocki { 1249074b89bbSSylwester Nawrocki int i; 1250074b89bbSSylwester Nawrocki 125189d2e831SSylwester Nawrocki for (i = 0; i < priv->clk_data.clk_num; i++) { 125289d2e831SSylwester Nawrocki if (!IS_ERR(priv->clk_table[i])) 125389d2e831SSylwester Nawrocki clk_unregister(priv->clk_table[i]); 1254074b89bbSSylwester Nawrocki } 1255074b89bbSSylwester Nawrocki } 1256074b89bbSSylwester Nawrocki 125789d2e831SSylwester Nawrocki static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv) 1258074b89bbSSylwester Nawrocki { 125989d2e831SSylwester Nawrocki of_clk_del_provider(priv->pdev->dev.of_node); 126089d2e831SSylwester Nawrocki i2s_unregister_clocks(priv); 1261074b89bbSSylwester Nawrocki } 1262074b89bbSSylwester Nawrocki 126389d2e831SSylwester Nawrocki 126489d2e831SSylwester Nawrocki static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) 1265074b89bbSSylwester Nawrocki { 1266a404b72dSSylwester Nawrocki 1267aa274c5cSSylwester Nawrocki const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" }; 1268074b89bbSSylwester Nawrocki const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; 1269074b89bbSSylwester Nawrocki const char *p_names[2] = { NULL }; 127089d2e831SSylwester Nawrocki struct device *dev = &priv->pdev->dev; 12715bfaeddcSSylwester Nawrocki const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs; 1272aa274c5cSSylwester Nawrocki const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)]; 1273074b89bbSSylwester Nawrocki struct clk *rclksrc; 1274074b89bbSSylwester Nawrocki int ret, i; 1275074b89bbSSylwester Nawrocki 1276074b89bbSSylwester Nawrocki /* Register the clock provider only if it's expected in the DTB */ 1277074b89bbSSylwester Nawrocki if (!of_find_property(dev->of_node, "#clock-cells", NULL)) 1278074b89bbSSylwester Nawrocki return 0; 1279074b89bbSSylwester Nawrocki 1280074b89bbSSylwester Nawrocki /* Get the RCLKSRC mux clock parent clock names */ 1281074b89bbSSylwester Nawrocki for (i = 0; i < ARRAY_SIZE(p_names); i++) { 1282074b89bbSSylwester Nawrocki rclksrc = clk_get(dev, clk_name[i]); 1283074b89bbSSylwester Nawrocki if (IS_ERR(rclksrc)) 1284074b89bbSSylwester Nawrocki continue; 1285074b89bbSSylwester Nawrocki p_names[i] = __clk_get_name(rclksrc); 1286074b89bbSSylwester Nawrocki clk_put(rclksrc); 1287074b89bbSSylwester Nawrocki } 1288074b89bbSSylwester Nawrocki 1289aa274c5cSSylwester Nawrocki for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) { 1290aa274c5cSSylwester Nawrocki i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s", 1291aa274c5cSSylwester Nawrocki dev_name(dev), i2s_clk_desc[i]); 1292aa274c5cSSylwester Nawrocki if (!i2s_clk_name[i]) 1293aa274c5cSSylwester Nawrocki return -ENOMEM; 1294aa274c5cSSylwester Nawrocki } 1295aa274c5cSSylwester Nawrocki 12965944170fSSylwester Nawrocki if (!(priv->quirks & QUIRK_NO_MUXPSR)) { 1297074b89bbSSylwester Nawrocki /* Activate the prescaler */ 1298e2e16fa6SSylwester Nawrocki u32 val = readl(priv->addr + I2SPSR); 1299e2e16fa6SSylwester Nawrocki writel(val | PSR_PSREN, priv->addr + I2SPSR); 1300074b89bbSSylwester Nawrocki 130189d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, 1302aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_SRC], p_names, 1303aa274c5cSSylwester Nawrocki ARRAY_SIZE(p_names), 1304074b89bbSSylwester Nawrocki CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 1305e2e16fa6SSylwester Nawrocki priv->addr + I2SMOD, reg_info->rclksrc_off, 13069d7939c9SSylwester Nawrocki 1, 0, &priv->lock); 1307074b89bbSSylwester Nawrocki 130889d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, 1309aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_PSR], 1310aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_RCLK_SRC], 1311074b89bbSSylwester Nawrocki CLK_SET_RATE_PARENT, 13129d7939c9SSylwester Nawrocki priv->addr + I2SPSR, 8, 6, 0, &priv->lock); 1313074b89bbSSylwester Nawrocki 1314aa274c5cSSylwester Nawrocki p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; 131589d2e831SSylwester Nawrocki priv->clk_data.clk_num = 2; 1316074b89bbSSylwester Nawrocki } 1317074b89bbSSylwester Nawrocki 131889d2e831SSylwester Nawrocki priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, 1319aa274c5cSSylwester Nawrocki i2s_clk_name[CLK_I2S_CDCLK], p_names[0], 1320aa274c5cSSylwester Nawrocki CLK_SET_RATE_PARENT, 1321e2e16fa6SSylwester Nawrocki priv->addr + I2SMOD, reg_info->cdclkcon_off, 13229d7939c9SSylwester Nawrocki CLK_GATE_SET_TO_DISABLE, &priv->lock); 1323074b89bbSSylwester Nawrocki 132489d2e831SSylwester Nawrocki priv->clk_data.clk_num += 1; 132589d2e831SSylwester Nawrocki priv->clk_data.clks = priv->clk_table; 1326074b89bbSSylwester Nawrocki 1327074b89bbSSylwester Nawrocki ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, 132889d2e831SSylwester Nawrocki &priv->clk_data); 1329074b89bbSSylwester Nawrocki if (ret < 0) { 1330074b89bbSSylwester Nawrocki dev_err(dev, "failed to add clock provider: %d\n", ret); 133189d2e831SSylwester Nawrocki i2s_unregister_clocks(priv); 1332074b89bbSSylwester Nawrocki } 1333074b89bbSSylwester Nawrocki 1334074b89bbSSylwester Nawrocki return ret; 1335074b89bbSSylwester Nawrocki } 1336074b89bbSSylwester Nawrocki 13377196c64cSSylwester Nawrocki /* Create platform device for the secondary PCM */ 13387196c64cSSylwester Nawrocki static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) 13397196c64cSSylwester Nawrocki { 1340c6bebefaSSylwester Nawrocki struct platform_device *pdev_sec; 1341c6bebefaSSylwester Nawrocki const char *devname; 13427196c64cSSylwester Nawrocki int ret; 13437196c64cSSylwester Nawrocki 1344c6bebefaSSylwester Nawrocki devname = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s-sec", 1345c6bebefaSSylwester Nawrocki dev_name(&priv->pdev->dev)); 1346c6bebefaSSylwester Nawrocki if (!devname) 13477196c64cSSylwester Nawrocki return -ENOMEM; 13487196c64cSSylwester Nawrocki 1349c6bebefaSSylwester Nawrocki pdev_sec = platform_device_alloc(devname, -1); 1350c6bebefaSSylwester Nawrocki if (!pdev_sec) 1351c6bebefaSSylwester Nawrocki return -ENOMEM; 1352c6bebefaSSylwester Nawrocki 1353c6bebefaSSylwester Nawrocki pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL); 1354c6bebefaSSylwester Nawrocki 1355c6bebefaSSylwester Nawrocki ret = platform_device_add(pdev_sec); 13567196c64cSSylwester Nawrocki if (ret < 0) { 1357c6bebefaSSylwester Nawrocki platform_device_put(pdev_sec); 13587196c64cSSylwester Nawrocki return ret; 13597196c64cSSylwester Nawrocki } 13607196c64cSSylwester Nawrocki 1361c6bebefaSSylwester Nawrocki ret = device_attach(&pdev_sec->dev); 1362c6bebefaSSylwester Nawrocki if (ret <= 0) { 1363c6bebefaSSylwester Nawrocki platform_device_unregister(priv->pdev_sec); 1364c6bebefaSSylwester Nawrocki dev_info(&pdev_sec->dev, "device_attach() failed\n"); 1365c6bebefaSSylwester Nawrocki return ret; 1366c6bebefaSSylwester Nawrocki } 1367c6bebefaSSylwester Nawrocki 1368c6bebefaSSylwester Nawrocki priv->pdev_sec = pdev_sec; 13697196c64cSSylwester Nawrocki 13707196c64cSSylwester Nawrocki return 0; 13717196c64cSSylwester Nawrocki } 13727196c64cSSylwester Nawrocki 13737196c64cSSylwester Nawrocki static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv) 13747196c64cSSylwester Nawrocki { 1375022c4156SSylwester Nawrocki platform_device_unregister(priv->pdev_sec); 13767196c64cSSylwester Nawrocki priv->pdev_sec = NULL; 13777196c64cSSylwester Nawrocki } 1378022c4156SSylwester Nawrocki 1379fdca21adSBill Pemberton static int samsung_i2s_probe(struct platform_device *pdev) 13805033f43cSJassi Brar { 13815033f43cSJassi Brar struct i2s_dai *pri_dai, *sec_dai = NULL; 138240476f61SPadmavathi Venna struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; 13836e434122SSylwester Nawrocki u32 regs_base, idma_addr = 0; 138440476f61SPadmavathi Venna struct device_node *np = pdev->dev.of_node; 13857da493e9SPadmavathi Venna const struct samsung_i2s_dai_data *i2s_dai_data; 1386c6bebefaSSylwester Nawrocki const struct platform_device_id *id; 1387a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv; 1388c6bebefaSSylwester Nawrocki struct resource *res; 1389c6bebefaSSylwester Nawrocki int num_dais, ret; 13905033f43cSJassi Brar 1391c6bebefaSSylwester Nawrocki if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { 13922f7b5d14SSylwester Nawrocki i2s_dai_data = of_device_get_match_data(&pdev->dev); 1393c6bebefaSSylwester Nawrocki } else { 1394c6bebefaSSylwester Nawrocki id = platform_get_device_id(pdev); 13957c62eebbSPadmavathi Venna 13967196c64cSSylwester Nawrocki /* Nothing to do if it is the secondary device probe */ 1397c6bebefaSSylwester Nawrocki if (!id) 13987196c64cSSylwester Nawrocki return 0; 13997196c64cSSylwester Nawrocki 1400c6bebefaSSylwester Nawrocki i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data; 1401c6bebefaSSylwester Nawrocki } 1402c6bebefaSSylwester Nawrocki 1403a404b72dSSylwester Nawrocki priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1404a404b72dSSylwester Nawrocki if (!priv) 140540476f61SPadmavathi Venna return -ENOMEM; 140640476f61SPadmavathi Venna 14076e434122SSylwester Nawrocki if (np) { 14086e434122SSylwester Nawrocki priv->quirks = i2s_dai_data->quirks; 14096e434122SSylwester Nawrocki } else { 14106e434122SSylwester Nawrocki if (!i2s_pdata) { 14116e434122SSylwester Nawrocki dev_err(&pdev->dev, "Missing platform data\n"); 14126e434122SSylwester Nawrocki return -EINVAL; 14136e434122SSylwester Nawrocki } 14146e434122SSylwester Nawrocki priv->quirks = i2s_pdata->type.quirks; 14156e434122SSylwester Nawrocki } 14166e434122SSylwester Nawrocki 14176e434122SSylwester Nawrocki num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1; 1418a404b72dSSylwester Nawrocki priv->pdev = pdev; 14195944170fSSylwester Nawrocki priv->variant_regs = i2s_dai_data->i2s_variant_regs; 1420a404b72dSSylwester Nawrocki 1421a404b72dSSylwester Nawrocki ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); 1422a404b72dSSylwester Nawrocki if (ret < 0) 1423a404b72dSSylwester Nawrocki return ret; 1424a404b72dSSylwester Nawrocki 1425a404b72dSSylwester Nawrocki pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; 1426a404b72dSSylwester Nawrocki 14279d7939c9SSylwester Nawrocki spin_lock_init(&priv->lock); 1428defc67c6SSylwester Nawrocki spin_lock_init(&priv->pcm_lock); 1429f3670536SSylwester Nawrocki 143040476f61SPadmavathi Venna if (!np) { 143169e7a69aSSylwester Nawrocki pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback; 143269e7a69aSSylwester Nawrocki pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture; 14339bdca822SArnd Bergmann pri_dai->filter = i2s_pdata->dma_filter; 1434b9a1a743SArnd Bergmann 1435409c69beSKrzysztof Kozlowski idma_addr = i2s_pdata->type.idma_addr; 143640476f61SPadmavathi Venna } else { 143740476f61SPadmavathi Venna if (of_property_read_u32(np, "samsung,idma-addr", 143840476f61SPadmavathi Venna &idma_addr)) { 14396e434122SSylwester Nawrocki if (priv->quirks & QUIRK_SUPPORTS_IDMA) { 1440b0759736SPadmavathi Venna dev_info(&pdev->dev, "idma address is not"\ 144140476f61SPadmavathi Venna "specified"); 144240476f61SPadmavathi Venna } 144340476f61SPadmavathi Venna } 144440476f61SPadmavathi Venna } 14455033f43cSJassi Brar 14465033f43cSJassi Brar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1447e2e16fa6SSylwester Nawrocki priv->addr = devm_ioremap_resource(&pdev->dev, res); 1448e2e16fa6SSylwester Nawrocki if (IS_ERR(priv->addr)) 1449e2e16fa6SSylwester Nawrocki return PTR_ERR(priv->addr); 14505033f43cSJassi Brar 14515033f43cSJassi Brar regs_base = res->start; 14525033f43cSJassi Brar 1453b5d015e6SSylwester Nawrocki priv->clk = devm_clk_get(&pdev->dev, "iis"); 1454b5d015e6SSylwester Nawrocki if (IS_ERR(priv->clk)) { 14550ec2ba80SSylwester Nawrocki dev_err(&pdev->dev, "Failed to get iis clock\n"); 1456b5d015e6SSylwester Nawrocki return PTR_ERR(priv->clk); 14570ec2ba80SSylwester Nawrocki } 1458c92f1d0eSSylwester Nawrocki 1459b5d015e6SSylwester Nawrocki ret = clk_prepare_enable(priv->clk); 1460c92f1d0eSSylwester Nawrocki if (ret != 0) { 1461c92f1d0eSSylwester Nawrocki dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); 1462c92f1d0eSSylwester Nawrocki return ret; 1463c92f1d0eSSylwester Nawrocki } 146469e7a69aSSylwester Nawrocki pri_dai->dma_playback.addr = regs_base + I2STXD; 146569e7a69aSSylwester Nawrocki pri_dai->dma_capture.addr = regs_base + I2SRXD; 1466b8ab0cccSSylwester Nawrocki pri_dai->dma_playback.chan_name = "tx"; 1467b8ab0cccSSylwester Nawrocki pri_dai->dma_capture.chan_name = "rx"; 146869e7a69aSSylwester Nawrocki pri_dai->dma_playback.addr_width = 4; 146969e7a69aSSylwester Nawrocki pri_dai->dma_capture.addr_width = 4; 147089d2e831SSylwester Nawrocki pri_dai->priv = priv; 14715033f43cSJassi Brar 14726e434122SSylwester Nawrocki if (priv->quirks & QUIRK_PRI_6CHAN) 1473a404b72dSSylwester Nawrocki pri_dai->drv->playback.channels_max = 6; 14745033f43cSJassi Brar 147573f5dfc6SMarek Szyprowski ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, 14760f928c19SSylwester Nawrocki "tx", "rx", NULL); 147773f5dfc6SMarek Szyprowski if (ret < 0) 147873f5dfc6SMarek Szyprowski goto err_disable_clk; 147973f5dfc6SMarek Szyprowski 14806e434122SSylwester Nawrocki if (priv->quirks & QUIRK_SEC_DAI) { 1481a404b72dSSylwester Nawrocki sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; 14827e5d8706SSylwester Nawrocki 148369e7a69aSSylwester Nawrocki sec_dai->dma_playback.addr = regs_base + I2STXDS; 1484b8ab0cccSSylwester Nawrocki sec_dai->dma_playback.chan_name = "tx-sec"; 148540476f61SPadmavathi Venna 14869bdca822SArnd Bergmann if (!np) { 148769e7a69aSSylwester Nawrocki sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec; 14889bdca822SArnd Bergmann sec_dai->filter = i2s_pdata->dma_filter; 14899bdca822SArnd Bergmann } 149040476f61SPadmavathi Venna 149169e7a69aSSylwester Nawrocki sec_dai->dma_playback.addr_width = 4; 149269e7a69aSSylwester Nawrocki sec_dai->idma_playback.addr = idma_addr; 14935033f43cSJassi Brar sec_dai->pri_dai = pri_dai; 149489d2e831SSylwester Nawrocki sec_dai->priv = priv; 14955033f43cSJassi Brar pri_dai->sec_dai = sec_dai; 1496be2c92ebSMarek Szyprowski 14977196c64cSSylwester Nawrocki ret = i2s_create_secondary_device(priv); 14987196c64cSSylwester Nawrocki if (ret < 0) 14997196c64cSSylwester Nawrocki goto err_disable_clk; 15007196c64cSSylwester Nawrocki 15017196c64cSSylwester Nawrocki ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev, 15027196c64cSSylwester Nawrocki sec_dai->filter, "tx-sec", NULL, 15037196c64cSSylwester Nawrocki &pdev->dev); 1504be2c92ebSMarek Szyprowski if (ret < 0) 1505022c4156SSylwester Nawrocki goto err_del_sec; 1506be2c92ebSMarek Szyprowski 15075033f43cSJassi Brar } 15085033f43cSJassi Brar 15090429ffefSMark Brown if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { 151040476f61SPadmavathi Venna dev_err(&pdev->dev, "Unable to configure gpio\n"); 1511fd61576fSWei Yongjun ret = -EINVAL; 1512022c4156SSylwester Nawrocki goto err_del_sec; 151340476f61SPadmavathi Venna } 15145033f43cSJassi Brar 1515a404b72dSSylwester Nawrocki dev_set_drvdata(&pdev->dev, priv); 1516a404b72dSSylwester Nawrocki 1517a404b72dSSylwester Nawrocki ret = devm_snd_soc_register_component(&pdev->dev, 1518a404b72dSSylwester Nawrocki &samsung_i2s_component, 1519a404b72dSSylwester Nawrocki priv->dai_drv, num_dais); 1520a404b72dSSylwester Nawrocki if (ret < 0) 1521022c4156SSylwester Nawrocki goto err_del_sec; 15222b960386SSylwester Nawrocki 1523dc938ddbSMarek Szyprowski pm_runtime_set_active(&pdev->dev); 1524c5cf4dbcSMark Brown pm_runtime_enable(&pdev->dev); 1525c5cf4dbcSMark Brown 152689d2e831SSylwester Nawrocki ret = i2s_register_clock_provider(priv); 152748279c53SSylwester Nawrocki if (ret < 0) 152848279c53SSylwester Nawrocki goto err_disable_pm; 152948279c53SSylwester Nawrocki 15303b0fa51fSSylwester Nawrocki priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]); 153148279c53SSylwester Nawrocki 15322b960386SSylwester Nawrocki return 0; 1533a08485d8SPadmavathi Venna 153448279c53SSylwester Nawrocki err_disable_pm: 15352b960386SSylwester Nawrocki pm_runtime_disable(&pdev->dev); 1536022c4156SSylwester Nawrocki err_del_sec: 1537022c4156SSylwester Nawrocki i2s_delete_secondary_device(priv); 1538fd61576fSWei Yongjun err_disable_clk: 1539b5d015e6SSylwester Nawrocki clk_disable_unprepare(priv->clk); 15402b960386SSylwester Nawrocki return ret; 15415033f43cSJassi Brar } 15425033f43cSJassi Brar 1543fdca21adSBill Pemberton static int samsung_i2s_remove(struct platform_device *pdev) 15445033f43cSJassi Brar { 1545a404b72dSSylwester Nawrocki struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev); 1546be2c92ebSMarek Szyprowski 15477196c64cSSylwester Nawrocki /* The secondary device has no driver data assigned */ 15487196c64cSSylwester Nawrocki if (!priv) 15497196c64cSSylwester Nawrocki return 0; 15507196c64cSSylwester Nawrocki 1551dc938ddbSMarek Szyprowski pm_runtime_get_sync(&pdev->dev); 1552c5cf4dbcSMark Brown pm_runtime_disable(&pdev->dev); 15535033f43cSJassi Brar 155489d2e831SSylwester Nawrocki i2s_unregister_clock_provider(priv); 15557196c64cSSylwester Nawrocki i2s_delete_secondary_device(priv); 1556022c4156SSylwester Nawrocki clk_disable_unprepare(priv->clk); 1557022c4156SSylwester Nawrocki 1558022c4156SSylwester Nawrocki pm_runtime_put_noidle(&pdev->dev); 15595033f43cSJassi Brar 15605033f43cSJassi Brar return 0; 15615033f43cSJassi Brar } 15625033f43cSJassi Brar 1563a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv3_regs = { 1564a5a56871SPadmavathi Venna .bfs_off = 1, 1565a5a56871SPadmavathi Venna .rfs_off = 3, 1566a5a56871SPadmavathi Venna .sdf_off = 5, 1567a5a56871SPadmavathi Venna .txr_off = 8, 1568a5a56871SPadmavathi Venna .rclksrc_off = 10, 1569a5a56871SPadmavathi Venna .mss_off = 11, 1570a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1571a5a56871SPadmavathi Venna .lrp_off = 7, 1572a5a56871SPadmavathi Venna .bfs_mask = 0x3, 1573a5a56871SPadmavathi Venna .rfs_mask = 0x3, 1574a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1575a5a56871SPadmavathi Venna }; 1576a5a56871SPadmavathi Venna 1577a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv6_regs = { 1578a5a56871SPadmavathi Venna .bfs_off = 0, 1579a5a56871SPadmavathi Venna .rfs_off = 4, 1580a5a56871SPadmavathi Venna .sdf_off = 6, 1581a5a56871SPadmavathi Venna .txr_off = 8, 1582a5a56871SPadmavathi Venna .rclksrc_off = 10, 1583a5a56871SPadmavathi Venna .mss_off = 11, 1584a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1585a5a56871SPadmavathi Venna .lrp_off = 15, 1586a5a56871SPadmavathi Venna .bfs_mask = 0xf, 1587a5a56871SPadmavathi Venna .rfs_mask = 0x3, 1588a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1589a5a56871SPadmavathi Venna }; 1590a5a56871SPadmavathi Venna 1591a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv7_regs = { 1592a5a56871SPadmavathi Venna .bfs_off = 0, 1593a5a56871SPadmavathi Venna .rfs_off = 4, 1594a5a56871SPadmavathi Venna .sdf_off = 7, 1595a5a56871SPadmavathi Venna .txr_off = 9, 1596a5a56871SPadmavathi Venna .rclksrc_off = 11, 1597a5a56871SPadmavathi Venna .mss_off = 12, 1598a5a56871SPadmavathi Venna .cdclkcon_off = 22, 1599a5a56871SPadmavathi Venna .lrp_off = 15, 1600a5a56871SPadmavathi Venna .bfs_mask = 0xf, 1601a5a56871SPadmavathi Venna .rfs_mask = 0x7, 1602a5a56871SPadmavathi Venna .ftx0cnt_off = 0, 1603a5a56871SPadmavathi Venna }; 1604a5a56871SPadmavathi Venna 1605a5a56871SPadmavathi Venna static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { 1606a5a56871SPadmavathi Venna .bfs_off = 0, 1607a5a56871SPadmavathi Venna .rfs_off = 3, 1608a5a56871SPadmavathi Venna .sdf_off = 6, 1609a5a56871SPadmavathi Venna .txr_off = 8, 1610a5a56871SPadmavathi Venna .rclksrc_off = 10, 1611a5a56871SPadmavathi Venna .mss_off = 11, 1612a5a56871SPadmavathi Venna .cdclkcon_off = 12, 1613a5a56871SPadmavathi Venna .lrp_off = 15, 1614a5a56871SPadmavathi Venna .bfs_mask = 0x7, 1615a5a56871SPadmavathi Venna .rfs_mask = 0x7, 1616a5a56871SPadmavathi Venna .ftx0cnt_off = 8, 1617a5a56871SPadmavathi Venna }; 1618a5a56871SPadmavathi Venna 16197da493e9SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv3_dai_type = { 16207da493e9SPadmavathi Venna .quirks = QUIRK_NO_MUXPSR, 16214720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1622a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv3_regs, 16237da493e9SPadmavathi Venna }; 16247da493e9SPadmavathi Venna 16257da493e9SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv5_dai_type = { 1626b0759736SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1627b0759736SPadmavathi Venna QUIRK_SUPPORTS_IDMA, 16284720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1629a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv3_regs, 16307da493e9SPadmavathi Venna }; 16317da493e9SPadmavathi Venna 16324ca0c0d4SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv6_dai_type = { 16334ca0c0d4SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1634b0759736SPadmavathi Venna QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, 16354720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1636a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv6_regs, 1637a5a56871SPadmavathi Venna }; 1638a5a56871SPadmavathi Venna 1639a5a56871SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv7_dai_type = { 1640a5a56871SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | 1641a5a56871SPadmavathi Venna QUIRK_SUPPORTS_TDM, 16424720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_192000, 1643a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv7_regs, 1644a5a56871SPadmavathi Venna }; 1645a5a56871SPadmavathi Venna 1646a5a56871SPadmavathi Venna static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { 1647a5a56871SPadmavathi Venna .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, 16484720c2feSJaechul Lee .pcm_rates = SNDRV_PCM_RATE_8000_96000, 1649a5a56871SPadmavathi Venna .i2s_variant_regs = &i2sv5_i2s1_regs, 16504ca0c0d4SPadmavathi Venna }; 16514ca0c0d4SPadmavathi Venna 1652eb8ca0faSKrzysztof Kozlowski static const struct platform_device_id samsung_i2s_driver_ids[] = { 16537c62eebbSPadmavathi Venna { 16547c62eebbSPadmavathi Venna .name = "samsung-i2s", 16553f024980SMark Brown .driver_data = (kernel_ulong_t)&i2sv3_dai_type, 16567c62eebbSPadmavathi Venna }, 16577c62eebbSPadmavathi Venna {}, 16587c62eebbSPadmavathi Venna }; 16592af19558SArnd Bergmann MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); 16607c62eebbSPadmavathi Venna 166140476f61SPadmavathi Venna #ifdef CONFIG_OF 166240476f61SPadmavathi Venna static const struct of_device_id exynos_i2s_match[] = { 16637da493e9SPadmavathi Venna { 16647da493e9SPadmavathi Venna .compatible = "samsung,s3c6410-i2s", 16657da493e9SPadmavathi Venna .data = &i2sv3_dai_type, 16667da493e9SPadmavathi Venna }, { 16677da493e9SPadmavathi Venna .compatible = "samsung,s5pv210-i2s", 16687da493e9SPadmavathi Venna .data = &i2sv5_dai_type, 16694ca0c0d4SPadmavathi Venna }, { 16704ca0c0d4SPadmavathi Venna .compatible = "samsung,exynos5420-i2s", 16714ca0c0d4SPadmavathi Venna .data = &i2sv6_dai_type, 1672a5a56871SPadmavathi Venna }, { 1673a5a56871SPadmavathi Venna .compatible = "samsung,exynos7-i2s", 1674a5a56871SPadmavathi Venna .data = &i2sv7_dai_type, 1675a5a56871SPadmavathi Venna }, { 1676a5a56871SPadmavathi Venna .compatible = "samsung,exynos7-i2s1", 1677a5a56871SPadmavathi Venna .data = &i2sv5_dai_type_i2s1, 167840476f61SPadmavathi Venna }, 167940476f61SPadmavathi Venna {}, 168040476f61SPadmavathi Venna }; 168140476f61SPadmavathi Venna MODULE_DEVICE_TABLE(of, exynos_i2s_match); 168240476f61SPadmavathi Venna #endif 168340476f61SPadmavathi Venna 16845b1d3c34SR. Chandrasekar static const struct dev_pm_ops samsung_i2s_pm = { 16855b1d3c34SR. Chandrasekar SET_RUNTIME_PM_OPS(i2s_runtime_suspend, 16865b1d3c34SR. Chandrasekar i2s_runtime_resume, NULL) 1687e7e52dfcSMarek Szyprowski SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1688e7e52dfcSMarek Szyprowski pm_runtime_force_resume) 16895b1d3c34SR. Chandrasekar }; 16905b1d3c34SR. Chandrasekar 16915033f43cSJassi Brar static struct platform_driver samsung_i2s_driver = { 16925033f43cSJassi Brar .probe = samsung_i2s_probe, 1693fdca21adSBill Pemberton .remove = samsung_i2s_remove, 16947c62eebbSPadmavathi Venna .id_table = samsung_i2s_driver_ids, 16955033f43cSJassi Brar .driver = { 16965033f43cSJassi Brar .name = "samsung-i2s", 169740476f61SPadmavathi Venna .of_match_table = of_match_ptr(exynos_i2s_match), 16985b1d3c34SR. Chandrasekar .pm = &samsung_i2s_pm, 16995033f43cSJassi Brar }, 17005033f43cSJassi Brar }; 17015033f43cSJassi Brar 1702e00c3f55SMark Brown module_platform_driver(samsung_i2s_driver); 17035033f43cSJassi Brar 17045033f43cSJassi Brar /* Module information */ 1705df8ad335SJaswinder Singh MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 17065033f43cSJassi Brar MODULE_DESCRIPTION("Samsung I2S Interface"); 17075033f43cSJassi Brar MODULE_ALIAS("platform:samsung-i2s"); 17085033f43cSJassi Brar MODULE_LICENSE("GPL"); 1709