From b07597367001c2c4f36a97863530f71b84060d3d Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 7 Nov 2014 12:24:39 +0530 Subject: ASoC: Samsung: Add quirk for internal DMA Internal DMA is available only on some of Samsung platforms. So added a quirk for the same and made it optional. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc/samsung') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9d513473b300..38b9a524cc9f 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -987,7 +987,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, i2s->addr + I2SCON); - if (i2s->quirks & QUIRK_SEC_DAI) + if (i2s->quirks & QUIRK_SUPPORTS_IDMA) idma_reg_addr_init(i2s->addr, i2s->sec_dai->idma_playback.dma_addr); @@ -1199,10 +1199,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) quirks = i2s_dai_data->quirks; if (of_property_read_u32(np, "samsung,idma-addr", &idma_addr)) { - if (quirks & QUIRK_SEC_DAI) { - dev_err(&pdev->dev, "idma address is not"\ + if (quirks & QUIRK_SUPPORTS_IDMA) { + dev_info(&pdev->dev, "idma address is not"\ "specified"); - return -EINVAL; } } } @@ -1309,13 +1308,14 @@ static const struct samsung_i2s_dai_data i2sv3_dai_type = { static const struct samsung_i2s_dai_data i2sv5_dai_type = { .dai_type = TYPE_PRI, - .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_IDMA, }; static const struct samsung_i2s_dai_data i2sv6_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | - QUIRK_SUPPORTS_TDM, + QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, }; static const struct samsung_i2s_dai_data samsung_dai_type_pri = { -- cgit v1.2.3-59-g8ed1b From a5a56871f804edac93a53b5e871c0e9818fb9033 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 7 Nov 2014 12:24:40 +0530 Subject: ASoC: samsung: add support for exynos7 I2S controller Exynos7 I2S controller has no internal dma, supports more no. of root clock sampling frequencies and has more no.of Rx fifos to support 7.1CH recording in TDM mode. Due to more no. of root clock frequency values some of the bit offsets got shifted up by one. Also I2S1 on previous Samsung platforms uses v3 dai type but on Exynos7 it is upgraded to v5 with slightly modified register offsets for supporting more no.of RFS values. Due to the above changes, the driver has to be modified to handle all versions of I2S controller. For this I introduced a new structure to hold modified bit offsets and masks which is passed as dai data. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/samsung-i2s.txt | 15 +- sound/soc/samsung/Kconfig | 2 +- sound/soc/samsung/i2s-regs.h | 10 +- sound/soc/samsung/i2s.c | 218 +++++++++++++++------ 4 files changed, 174 insertions(+), 71 deletions(-) (limited to 'sound/soc/samsung') diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 7386d444ada1..d188296bb6ec 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -6,10 +6,17 @@ Required SoC Specific Properties: - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with secondary fifo, s/w reset control and internal mux for root clk src. - - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with - secondary fifo, s/w reset control, internal mux for root clk src and - TDM support. TDM (Time division multiplexing) is to allow transfer of - multiple channel audio data on single data line. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for + playback, sterio channel capture, secondary fifo using internal + or external dma, s/w reset control, internal mux for root clk src + and 7.1 channel TDM support for playback. TDM (Time division multiplexing) + is to allow transfer of multiple channel audio data on single data line. + - samsung,exynos7-i2s: with all the available features of exynos5 i2s, + exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo + with only external dma and more no.of root clk sampling frequencies. + - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports + stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with + slightly modified bit offsets. - reg: physical base address of the controller and length of memory mapped region. diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 55a38697443d..e0e737faadd9 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,6 +1,6 @@ config SND_SOC_SAMSUNG tristate "ASoC support for Samsung" - depends on PLAT_SAMSUNG + depends on (PLAT_SAMSUNG || ARCH_EXYNOS) depends on S3C64XX_PL080 || !ARCH_S3C64XX depends on S3C24XX_DMAC || !ARCH_S3C24XX select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 821a50231002..9170c311d66e 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -33,8 +33,9 @@ #define I2SLVL3ADDR 0x3c #define I2SSTR1 0x40 #define I2SVER 0x44 -#define I2SFIC2 0x48 +#define I2SFIC1 0x48 #define I2STDM 0x4c +#define I2SFSTA 0x50 #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -93,8 +94,6 @@ #define MOD_BLC_24BIT (2 << 13) #define MOD_BLC_MASK (3 << 13) -#define MOD_IMS_SYSMUX (1 << 10) -#define MOD_SLAVE (1 << 11) #define MOD_TXONLY (0 << 8) #define MOD_RXONLY (1 << 8) #define MOD_TXRX (2 << 8) @@ -132,7 +131,10 @@ #define EXYNOS5420_MOD_BCLK_256FS 8 #define EXYNOS5420_MOD_BCLK_MASK 0xf -#define MOD_CDCLKCON (1 << 12) +#define EXYNOS7_MOD_RCLK_64FS 4 +#define EXYNOS7_MOD_RCLK_128FS 5 +#define EXYNOS7_MOD_RCLK_96FS 6 +#define EXYNOS7_MOD_RCLK_192FS 7 #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 38b9a524cc9f..947352d00ddf 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -36,9 +36,24 @@ enum samsung_dai_type { TYPE_SEC, }; +struct samsung_i2s_variant_regs { + unsigned int bfs_off; + unsigned int rfs_off; + unsigned int sdf_off; + unsigned int txr_off; + unsigned int rclksrc_off; + unsigned int mss_off; + unsigned int cdclkcon_off; + unsigned int lrp_off; + unsigned int bfs_mask; + unsigned int rfs_mask; + unsigned int ftx0cnt_off; +}; + struct samsung_i2s_dai_data { int dai_type; u32 quirks; + const struct samsung_i2s_variant_regs *i2s_variant_regs; }; struct i2s_dai { @@ -81,6 +96,7 @@ struct i2s_dai { u32 suspend_i2scon; u32 suspend_i2spsr; unsigned long gpios[7]; /* i2s gpio line numbers */ + const struct samsung_i2s_variant_regs *variant_regs; }; /* Lock for cross i/f checks */ @@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s) /* If operating in SoC-Slave mode */ static inline bool is_slave(struct i2s_dai *i2s) { - return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false; + u32 mod = readl(i2s->addr + I2SMOD); + return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; } /* If this interface of the controller is transmitting data */ @@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s) static inline unsigned get_rfs(struct i2s_dai *i2s) { u32 rfs; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) - rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; - else - rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); - rfs &= MOD_RCLK_MASK; + rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off; + rfs &= i2s->variant_regs->rfs_mask; switch (rfs) { + case 7: return 192; + case 6: return 96; + case 5: return 128; + case 4: return 64; case 3: return 768; case 2: return 384; case 1: return 512; @@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift; + int rfs_shift = i2s->variant_regs->rfs_off; - if (i2s->quirks & QUIRK_SUPPORTS_TDM) - rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; - else - rfs_shift = MOD_RCLK_SHIFT; - mod &= ~(MOD_RCLK_MASK << rfs_shift); + mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift); switch (rfs) { + case 192: + mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); + break; + case 96: + mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); + break; + case 128: + mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); + break; + case 64: + mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); + break; case 768: mod |= (MOD_RCLK_768FS << rfs_shift); break; @@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) static inline unsigned get_bfs(struct i2s_dai *i2s) { u32 bfs; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; - bfs &= EXYNOS5420_MOD_BCLK_MASK; - } else { - bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; - bfs &= MOD_BCLK_MASK; - } + bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off; + bfs &= i2s->variant_regs->bfs_mask; switch (bfs) { case 8: return 256; @@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); - int bfs_shift; int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; - mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); - } else { - bfs_shift = MOD_BCLK_SHIFT; - mod &= ~(MOD_BCLK_MASK << bfs_shift); - } + int bfs_shift = i2s->variant_regs->bfs_off; /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ if (!tdm && bfs > 48) { @@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) return; } + mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift); + switch (bfs) { case 48: mod |= (MOD_BCLK_48FS << bfs_shift); @@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s) static void i2s_txctrl(struct i2s_dai *i2s, int on) { void __iomem *addr = i2s->addr; + int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); - u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); if (on) { con |= CON_ACTIVE; @@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) } if (any_rx_active(i2s)) - mod |= MOD_TXRX; + mod |= 2 << txr_off; else - mod |= MOD_TXONLY; + mod |= 0 << txr_off; } else { if (is_secondary(i2s)) { con |= CON_TXSDMA_PAUSE; @@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) con |= CON_TXCH_PAUSE; if (any_rx_active(i2s)) - mod |= MOD_RXONLY; + mod |= 1 << txr_off; else con &= ~CON_ACTIVE; } @@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) static void i2s_rxctrl(struct i2s_dai *i2s, int on) { void __iomem *addr = i2s->addr; + int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); - u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); if (on) { con |= CON_RXDMA_ACTIVE | CON_ACTIVE; con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); if (any_tx_active(i2s)) - mod |= MOD_TXRX; + mod |= 2 << txr_off; else - mod |= MOD_RXONLY; + mod |= 1 << txr_off; } else { con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; con &= ~CON_RXDMA_ACTIVE; if (any_tx_active(i2s)) - mod |= MOD_TXONLY; + mod |= 0 << txr_off; else con &= ~CON_ACTIVE; } @@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; u32 mod = readl(i2s->addr + I2SMOD); + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; + unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; + unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; switch (clk_id) { case SAMSUNG_I2S_OPCLK: @@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if ((rfs && other && other->rfs && (other->rfs != rfs)) || (any_active(i2s) && (((dir == SND_SOC_CLOCK_IN) - && !(mod & MOD_CDCLKCON)) || + && !(mod & cdcon_mask)) || ((dir == SND_SOC_CLOCK_OUT) - && (mod & MOD_CDCLKCON))))) { + && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } if (dir == SND_SOC_CLOCK_IN) - mod |= MOD_CDCLKCON; + mod |= 1 << i2s_regs->cdclkcon_off; else - mod &= ~MOD_CDCLKCON; + mod &= 0 << i2s_regs->cdclkcon_off; i2s->rfs = rfs; break; @@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if (!any_active(i2s)) { if (i2s->op_clk && !IS_ERR(i2s->op_clk)) { - if ((clk_id && !(mod & MOD_IMS_SYSMUX)) || - (!clk_id && (mod & MOD_IMS_SYSMUX))) { + if ((clk_id && !(mod & rsrc_mask)) || + (!clk_id && (mod & rsrc_mask))) { clk_disable_unprepare(i2s->op_clk); clk_put(i2s->op_clk); } else { @@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, other->op_clk = i2s->op_clk; other->rclk_srcrate = i2s->rclk_srcrate; } - } else if ((!clk_id && (mod & MOD_IMS_SYSMUX)) - || (clk_id && !(mod & MOD_IMS_SYSMUX))) { + } else if ((!clk_id && (mod & rsrc_mask)) + || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -533,10 +551,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (clk_id == 0) - mod &= ~MOD_IMS_SYSMUX; + mod &= 0 << i2s_regs->rclksrc_off; else - mod |= MOD_IMS_SYSMUX; - break; + mod |= 1 << i2s_regs->rclksrc_off; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); @@ -553,16 +570,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); - int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; u32 tmp = 0; - if (i2s->quirks & QUIRK_SUPPORTS_TDM) { - lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; - sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; - } else { - lrp_shift = MOD_LRP_SHIFT; - sdf_shift = MOD_SDF_SHIFT; - } + lrp_shift = i2s->variant_regs->lrp_off; + sdf_shift = i2s->variant_regs->sdf_off; + mod_slave = 1 << i2s->variant_regs->mss_off; sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -605,7 +618,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - tmp |= MOD_SLAVE; + tmp |= mod_slave; break; case SND_SOC_DAIFMT_CBS_CFS: /* Set default source clock in Master mode */ @@ -623,13 +636,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, * channel. */ if (any_active(i2s) && - ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) { + ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } - mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE); + mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; writel(mod, i2s->addr + I2SMOD); @@ -751,6 +764,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; unsigned long flags; + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; spin_lock_irqsave(&lock, flags); @@ -761,7 +775,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, other->mode |= DAI_MANAGER; } else { u32 mod = readl(i2s->addr + I2SMOD); - i2s->cdclk_out = !(mod & MOD_CDCLKCON); + i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off)); if (other) other->cdclk_out = i2s->cdclk_out; } @@ -914,13 +928,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct i2s_dai *i2s = to_info(dai); u32 reg = readl(i2s->addr + I2SFIC); snd_pcm_sframes_t delay; + const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) delay = FIC_RXCOUNT(reg); else if (is_secondary(i2s)) delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); else - delay = FIC_TXCOUNT(reg); + delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f; return delay; } @@ -1227,6 +1242,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->dma_capture.dma_size = 4; pri_dai->base = regs_base; pri_dai->quirks = quirks; + pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; if (quirks & QUIRK_PRI_6CHAN) pri_dai->i2s_dai_drv.playback.channels_max = 6; @@ -1301,21 +1317,93 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static const struct samsung_i2s_variant_regs i2sv3_regs = { + .bfs_off = 1, + .rfs_off = 3, + .sdf_off = 5, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 7, + .bfs_mask = 0x3, + .rfs_mask = 0x3, + .ftx0cnt_off = 8, +}; + +static const struct samsung_i2s_variant_regs i2sv6_regs = { + .bfs_off = 0, + .rfs_off = 4, + .sdf_off = 6, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 15, + .bfs_mask = 0xf, + .rfs_mask = 0x3, + .ftx0cnt_off = 8, +}; + +static const struct samsung_i2s_variant_regs i2sv7_regs = { + .bfs_off = 0, + .rfs_off = 4, + .sdf_off = 7, + .txr_off = 9, + .rclksrc_off = 11, + .mss_off = 12, + .cdclkcon_off = 22, + .lrp_off = 15, + .bfs_mask = 0xf, + .rfs_mask = 0x7, + .ftx0cnt_off = 0, +}; + +static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { + .bfs_off = 0, + .rfs_off = 3, + .sdf_off = 6, + .txr_off = 8, + .rclksrc_off = 10, + .mss_off = 11, + .cdclkcon_off = 12, + .lrp_off = 15, + .bfs_mask = 0x7, + .rfs_mask = 0x7, + .ftx0cnt_off = 8, +}; + static const struct samsung_i2s_dai_data i2sv3_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_NO_MUXPSR, + .i2s_variant_regs = &i2sv3_regs, }; static const struct samsung_i2s_dai_data i2sv5_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_IDMA, + .i2s_variant_regs = &i2sv3_regs, }; static const struct samsung_i2s_dai_data i2sv6_dai_type = { .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, + .i2s_variant_regs = &i2sv6_regs, +}; + +static const struct samsung_i2s_dai_data i2sv7_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, + .i2s_variant_regs = &i2sv7_regs, +}; + +static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, + .i2s_variant_regs = &i2sv5_i2s1_regs, }; static const struct samsung_i2s_dai_data samsung_dai_type_pri = { @@ -1349,6 +1437,12 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,exynos5420-i2s", .data = &i2sv6_dai_type, + }, { + .compatible = "samsung,exynos7-i2s", + .data = &i2sv7_dai_type, + }, { + .compatible = "samsung,exynos7-i2s1", + .data = &i2sv5_dai_type_i2s1, }, {}, }; -- cgit v1.2.3-59-g8ed1b From 0099c762855eeee8d3eacc11fcc1e0819e77b2ed Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 9 Nov 2014 12:38:56 +0100 Subject: ASoC: simple-card: Remove useless casts There is no need to cast the cpu_of_node or codec_of_node of the dai_links when calling of_put_node. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 7 ++----- sound/soc/samsung/odroidx2_max98090.c | 4 ++-- sound/soc/ux500/mop500.c | 6 ++---- 3 files changed, 6 insertions(+), 11 deletions(-) (limited to 'sound/soc/samsung') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index cd49d5010e14..3e3fec4e5c4e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -457,16 +457,13 @@ static int asoc_simple_card_unref(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_dai_link *dai_link; - struct device_node *np; int num_links; for (num_links = 0, dai_link = card->dai_link; num_links < card->num_links; num_links++, dai_link++) { - np = (struct device_node *) dai_link->cpu_of_node; - of_node_put(np); - np = (struct device_node *) dai_link->codec_of_node; - of_node_put(np); + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); } return 0; } diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c index 3c8f60423e82..d7640e72cb1d 100644 --- a/sound/soc/samsung/odroidx2_max98090.c +++ b/sound/soc/samsung/odroidx2_max98090.c @@ -153,8 +153,8 @@ static int odroidx2_audio_remove(struct platform_device *pdev) snd_soc_unregister_card(card); - of_node_put((struct device_node *)odroidx2_dai[0].cpu_of_node); - of_node_put((struct device_node *)odroidx2_dai[0].codec_of_node); + of_node_put(odroidx2_dai[0].cpu_of_node); + of_node_put(odroidx2_dai[0].codec_of_node); return 0; } diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index b3b66aa98dce..ea9ba2844510 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -64,11 +64,9 @@ static void mop500_of_node_put(void) for (i = 0; i < 2; i++) { if (mop500_dai_links[i].cpu_of_node) - of_node_put((struct device_node *) - mop500_dai_links[i].cpu_of_node); + of_node_put(mop500_dai_links[i].cpu_of_node); if (mop500_dai_links[i].codec_of_node) - of_node_put((struct device_node *) - mop500_dai_links[i].codec_of_node); + of_node_put(mop500_dai_links[i].codec_of_node); } } -- cgit v1.2.3-59-g8ed1b From bc2632140435cc84f9817f1c362479b23dbdfebc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 10 Nov 2014 22:41:52 +0100 Subject: ASoC: Rename snd_soc_dai_driver struct ac97_control field to bus_control Setting the ac97_control field on a CPU DAI tells the ASoC core that this DAI in addition to audio data also transports control data to the CODEC. This causes the core to suspend the DAI after the CODEC and resume it before the CODEC so communication to the CODEC is still possible. This is not necessarily something that is specific to AC'97 and can be used by other buses with the same requirement. This patch renames the flag from ac97_control to bus_control to make this explicit. While we are at it also change the type from int to bool. The following semantich patch was used for automatic conversion of the drivers: // @@ identifier drv; @@ struct snd_soc_dai_driver drv = { - .ac97_control + .bus_control = - 1 + true }; // Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 3 ++- sound/soc/au1x/ac97c.c | 2 +- sound/soc/au1x/psc-ac97.c | 2 +- sound/soc/blackfin/bf5xx-ac97.c | 2 +- sound/soc/cirrus/ep93xx-ac97.c | 2 +- sound/soc/fsl/fsl_ssi.c | 2 +- sound/soc/fsl/imx-ssi.c | 2 +- sound/soc/fsl/mpc5200_psc_ac97.c | 4 ++-- sound/soc/nuc900/nuc900-ac97.c | 2 +- sound/soc/pxa/pxa2xx-ac97.c | 6 +++--- sound/soc/samsung/ac97.c | 4 ++-- sound/soc/sh/hac.c | 2 +- sound/soc/soc-core.c | 26 ++++++++++++++------------ sound/soc/tegra/tegra20_ac97.c | 2 +- sound/soc/txx9/txx9aclc-ac97.c | 2 +- 15 files changed, 33 insertions(+), 30 deletions(-) (limited to 'sound/soc/samsung') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index c0e04688c6ed..a3738be45563 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -206,7 +206,6 @@ struct snd_soc_dai_driver { /* DAI description */ const char *name; unsigned int id; - int ac97_control; unsigned int base; /* DAI driver callbacks */ @@ -216,6 +215,8 @@ struct snd_soc_dai_driver { int (*resume)(struct snd_soc_dai *dai); /* compress dai */ bool compress_dai; + /* DAI is also used for the control bus */ + bool bus_control; /* ops */ const struct snd_soc_dai_ops *ops; diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index c8a2de103c5f..5159a50a45a6 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver au1xac97c_dai_driver = { .name = "alchemy-ac97c", - .ac97_control = 1, + .bus_control = true, .probe = au1xac97c_dai_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 84f31e1f9d24..c6daec98ff89 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { }; static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = { - .ac97_control = 1, + .bus_control = true, .probe = au1xpsc_ac97_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index e82eb373a731..6bf21a6c02e4 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) #endif static struct snd_soc_dai_driver bfin_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .suspend = bf5xx_ac97_suspend, .resume = bf5xx_ac97_resume, .playback = { diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index f30dadf85b99..6b8a366b0211 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = { static struct snd_soc_dai_driver ep93xx_ac97_dai = { .name = "ep93xx-ac97", .id = 0, - .ac97_control = 1, + .bus_control = true, .probe = ep93xx_ac97_dai_probe, .playback = { .stream_name = "AC97 Playback", diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index e6955170dc42..7fd3cbcd74c0 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1099,7 +1099,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = { }; static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index ab2fdd76b693..60b0a5b1f1f1 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = { static struct snd_soc_dai_driver imx_ac97_dai = { .probe = imx_ssi_dai_probe, - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 640801a60c12..c6ed6ba965a9 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = { static struct snd_soc_dai_driver psc_ac97_dai[] = { { .name = "mpc5200-psc-ac97.0", - .ac97_control = 1, + .bus_control = true, .probe = psc_ac97_probe, .playback = { .stream_name = "AC97 Playback", @@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = { }, { .name = "mpc5200-psc-ac97.1", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 SPDIF", .channels_min = 1, diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index f2f67942b229..dff443e4b657 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = { static struct snd_soc_dai_driver nuc900_ac97_dai = { .probe = nuc900_ac97_probe, .remove = nuc900_ac97_remove, - .ac97_control = 1, + .bus_control = true, .playback = { .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index ae956e3f4b9d..73ca2820c08c 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { { .name = "pxa2xx-ac97", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-aux", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Aux Playback", .channels_min = 1, @@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-mic", - .ac97_control = 1, + .bus_control = true, .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index e1615113fd84..7952a625669d 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver s3c_ac97_dai[] = { [S3C_AC97_DAI_PCM] = { .name = "samsung-ac97", - .ac97_control = 1, + .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = { }, [S3C_AC97_DAI_MIC] = { .name = "samsung-ac97-mic", - .ac97_control = 1, + .bus_control = true, .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 0af2e4dfd139..d5f567e085ff 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = { static struct snd_soc_dai_driver sh4_hac_dai[] = { { .name = "hac-dai.0", - .ac97_control = 1, + .bus_control = true, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fba6e28e18d3..f5bebca84b71 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -550,7 +550,7 @@ int snd_soc_suspend(struct device *dev) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control) + if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) cpu_dai->driver->suspend(cpu_dai); if (platform->driver->suspend && !platform->suspended) { platform->driver->suspend(cpu_dai); @@ -629,7 +629,7 @@ int snd_soc_suspend(struct device *dev) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control) + if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) cpu_dai->driver->suspend(cpu_dai); /* deactivate pins to sleep state */ @@ -665,14 +665,14 @@ static void soc_resume_deferred(struct work_struct *work) if (card->resume_pre) card->resume_pre(card); - /* resume AC97 DAIs */ + /* resume control bus DAIs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control) + if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) cpu_dai->driver->resume(cpu_dai); } @@ -733,7 +733,7 @@ static void soc_resume_deferred(struct work_struct *work) if (card->rtd[i].dai_link->ignore_suspend) continue; - if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control) + if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) cpu_dai->driver->resume(cpu_dai); if (platform->driver->resume && platform->suspended) { platform->driver->resume(cpu_dai); @@ -758,7 +758,8 @@ static void soc_resume_deferred(struct work_struct *work) int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - int i, ac97_control = 0; + bool bus_control = false; + int i; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) @@ -781,17 +782,18 @@ int snd_soc_resume(struct device *dev) } } - /* AC97 devices might have other drivers hanging off them so - * need to resume immediately. Other drivers don't have that - * problem and may take a substantial amount of time to resume + /* + * DAIs that also act as the control bus master might have other drivers + * hanging off them so need to resume immediately. Other drivers don't + * have that problem and may take a substantial amount of time to resume * due to I/O costs and anti-pop so handle them out of line. */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - ac97_control |= cpu_dai->driver->ac97_control; + bus_control |= cpu_dai->driver->bus_control; } - if (ac97_control) { - dev_dbg(dev, "ASoC: Resuming AC97 immediately\n"); + if (bus_control) { + dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); soc_resume_deferred(&card->deferred_resume_work); } else { dev_dbg(dev, "ASoC: Scheduling resume work\n"); diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 3b0fa12dbff7..29a9957d335a 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver tegra20_ac97_dai = { .name = "tegra-ac97-pcm", - .ac97_control = 1, + .bus_control = true, .probe = tegra20_ac97_probe, .playback = { .stream_name = "PCM Playback", diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 9edd68db9f48..f7135cdaa2ca 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai) } static struct snd_soc_dai_driver txx9aclc_ac97_dai = { - .ac97_control = 1, + .bus_control = true, .probe = txx9aclc_ac97_probe, .remove = txx9aclc_ac97_remove, .playback = { -- cgit v1.2.3-59-g8ed1b From b2de1d20a05d8691cb1889c859de2ab56938b82a Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 20 Nov 2014 15:33:17 +0530 Subject: ASoC: samsung: ASoC: samsung: Fix IISMOD setting in i2s_set_sysclk() In the i2s_set_sysclk() callback we are currently clearing all bits of the IISMOD register in i2s_set_sysclk. It's due to an incorrect mask used for the AND operation which is introduced in commit a5a56871f804edac93a53b5e871c0e9818fb9033 (ASoC: samsung: add support for exynos7 I2S controller) and also adds the missing break statement. Signed-off-by: Sylwester Nawrocki Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/soc/samsung') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 947352d00ddf..0d76bc15b785 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -494,7 +494,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, if (dir == SND_SOC_CLOCK_IN) mod |= 1 << i2s_regs->cdclkcon_off; else - mod &= 0 << i2s_regs->cdclkcon_off; + mod &= ~(1 << i2s_regs->cdclkcon_off); i2s->rfs = rfs; break; @@ -551,10 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (clk_id == 0) - mod &= 0 << i2s_regs->rclksrc_off; + mod &= ~(1 << i2s_regs->rclksrc_off); else mod |= 1 << i2s_regs->rclksrc_off; + break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From d683d0b690c13437d752ccce47963ac64119b07a Mon Sep 17 00:00:00 2001 From: Krishna Mohan Dani Date: Wed, 26 Nov 2014 14:53:04 +0530 Subject: ASoC: Samsung: Add arndale_rt5631 machine driver and binding Adding machine driver to instantiate I2S based realtek's ALC5631 sound card on Arndale board. There are other variants of Audio Daughter Cards for Arndale Board for which support already exists but there is no support for Realtek's alc5631 codec hence support for ALC5631 based machine driver is being added. This patch also documents the device tree binding for the Arndale board based machine driver. Signed-off-by: Claude Youn Signed-off-by: Krishna Mohan Dani Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/arndale.txt | 24 ++++ sound/soc/samsung/Kconfig | 6 + sound/soc/samsung/Makefile | 2 + sound/soc/samsung/arndale_rt5631.c | 150 +++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/arndale.txt create mode 100644 sound/soc/samsung/arndale_rt5631.c (limited to 'sound/soc/samsung') diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt new file mode 100644 index 000000000000..0e76946385ae --- /dev/null +++ b/Documentation/devicetree/bindings/sound/arndale.txt @@ -0,0 +1,24 @@ +Audio Binding for Arndale boards + +Required properties: +- compatible : Can be the following, + "samsung,arndale-rt5631" + +- samsung,audio-cpu: The phandle of the Samsung I2S controller +- samsung,audio-codec: The phandle of the audio codec + +Optional: +- samsung,model: The name of the sound-card + +Arndale Boards has many audio daughter cards, one of them is +rt5631/alc5631. Below example shows audio bindings for rt5631/ +alc5631 based codec. + +Example: + +sound { + compatible = "samsung,arndale-rt5631"; + + samsung,audio-cpu = <&i2s0> + samsung,audio-codec = <&rt5631>; +}; diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index e0e737faadd9..fc67f97f19f6 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2 select SND_SAMSUNG_I2S help Say Y here to enable audio support for the Odroid-X2/U3. + +config SND_SOC_ARNDALE_RT5631_ALC5631 + tristate "Audio support for RT5631(ALC5631) on Arndale Board" + depends on SND_SOC_SAMSUNG + select SND_SAMSUNG_I2S + select SND_SOC_RT5631 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 91505ddaaf95..31e3dba7e3b5 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o snd-soc-littlemill-objs := littlemill.o snd-soc-bells-objs := bells.o snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o +snd-soc-arndale-rt5631-objs := arndale_rt5631.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o +obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c new file mode 100644 index 000000000000..1e2b61ca8db2 --- /dev/null +++ b/sound/soc/samsung/arndale_rt5631.c @@ -0,0 +1,150 @@ +/* + * arndale_rt5631.c + * + * Copyright (c) 2014, Insignal Co., Ltd. + * + * Author: Claude + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "i2s.h" + +static int arndale_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int rfs, ret; + unsigned long rclk; + + rfs = 256; + + rclk = params_rate(params) * rfs; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, + 0, SND_SOC_CLOCK_OUT); + + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops arndale_ops = { + .hw_params = arndale_hw_params, +}; + +static struct snd_soc_dai_link arndale_rt5631_dai[] = { + { + .name = "RT5631 HiFi", + .stream_name = "Primary", + .codec_dai_name = "rt5631-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .ops = &arndale_ops, + }, +}; + +static struct snd_soc_card arndale_rt5631 = { + .name = "Arndale RT5631", + .dai_link = arndale_rt5631_dai, + .num_links = ARRAY_SIZE(arndale_rt5631_dai), +}; + +static int arndale_audio_probe(struct platform_device *pdev) +{ + int n, ret; + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &arndale_rt5631; + + card->dev = &pdev->dev; + + for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) { + if (!arndale_rt5631_dai[n].cpu_dai_name) { + arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np, + "samsung,audio-cpu", n); + + if (!arndale_rt5631_dai[n].cpu_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-cpu' missing or invalid\n"); + return -EINVAL; + } + } + if (!arndale_rt5631_dai[n].platform_name) + arndale_rt5631_dai[n].platform_of_node = + arndale_rt5631_dai[n].cpu_of_node; + + arndale_rt5631_dai[n].codec_name = NULL; + arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np, + "samsung,audio-codec", n); + if (!arndale_rt5631_dai[0].codec_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-codec' missing or invalid\n"); + return -EINVAL; + } + } + + ret = devm_snd_soc_register_card(card->dev, card); + + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + + return ret; +} + +static int arndale_audio_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = { + { .compatible = "samsung,arndale-rt5631", }, + { .compatible = "samsung,arndale-alc5631", }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match); + +static struct platform_driver arndale_audio_driver = { + .driver = { + .name = "arndale-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), + }, + .probe = arndale_audio_probe, + .remove = arndale_audio_remove, +}; + +module_platform_driver(arndale_audio_driver); + +MODULE_AUTHOR("Claude "); +MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From ba56447c3586465fd6eaf9869410dafb748a4d0d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Dec 2014 19:58:32 +0000 Subject: ASoC: samsung: Fix error handling for clock lookup Return the error code we got from clk_get() and check to make sure that clk_prepare_enable() worked. Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sound/soc/samsung') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 0d76bc15b785..c60ab07ef3d1 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -972,6 +972,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + int ret; if (other && other->clk) { /* If this is probe on secondary */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, @@ -989,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (IS_ERR(i2s->clk)) { dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); iounmap(i2s->addr); - return -ENOENT; + return PTR_ERR(i2s->clk); + } + + ret = clk_prepare_enable(i2s->clk); + if (ret != 0) { + dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; } - clk_prepare_enable(i2s->clk); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); -- cgit v1.2.3-59-g8ed1b From 3f024980fbf39ef6448e53345d51fc59f1da08ae Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Dec 2014 19:56:17 +0000 Subject: ASoC: samsung: Fix non-DT use of I2S controller The changes in commit a5a56871f804e (ASoC: samsung: add support for exynos7 I2S controller) introduce a new variant_regs structure in the driver data which is now mandatory for accessing registers. Unfortunately this is only hooked up for DT platforms so non-DT platforms like my primary development platform for audio are broken by this change and crash on boot. Since the only non-DT user of these device is s3c64xx fix this by making the standard samsung-i2s device be of type I2Sv3 and add a new I2Sv4 name to the platform data section, currently using the I2Sv5 information which should be about right. Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/soc/samsung') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index c60ab07ef3d1..c7aafcd95de3 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1424,10 +1424,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = { static struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", - .driver_data = (kernel_ulong_t)&samsung_dai_type_pri, + .driver_data = (kernel_ulong_t)&i2sv3_dai_type, }, { .name = "samsung-i2s-sec", .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, + }, { + .name = "samsung-i2sv4", + .driver_data = (kernel_ulong_t)&i2sv5_dai_type, }, {}, }; -- cgit v1.2.3-59-g8ed1b From 7e5d8706dd3ee19f8626977935ab16e59b3603be Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 8 Dec 2014 18:45:54 +0100 Subject: ASoC: samsung: i2s: Add missing assignment of variant_regs Add assignment of the variant_regs field which is missing in commit a5a56871f804edac93a53b5e871c0e9818fb9033 ("ASoC: samsung: add support for exynos7 I2S controller"). Without this attempting to probe the secondary DAI fails with an error like: [ 1.763026] Unable to handle kernel NULL pointer dereference at virtual address 0000000c [ 1.780895] pgd = c0004000 [ 1.783606] [0000000c] *pgd=00000000 [ 1.838255] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 1.843514] Modules linked in: [ 1.846558] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc1-00009-g5dcb01e-dirty #1521 [ 1.854887] task: ee00a800 ti: ee088000 task.ti: ee088000 [ 1.860284] PC is at i2s_txctrl+0x40/0x2d4 [ 1.864350] LR is at i2s_txctrl+0x28/0x2d4 [ 1.868428] pc : [] lr : [] psr: 60000153 [ 1.868428] sp : ee089dc0 ip : 00000000 fp : ee21f000 [ 1.879883] r10: 00000000 r9 : ee21fb00 r8 : c06406c4 [ 1.885091] r7 : ee21fb00 r6 : 00000000 r5 : f00f6000 r4 : ed943410 [ 1.891601] r3 : 0000016c r2 : c0464550 r1 : c055cef8 r0 : ed943610 [ 1.898113] Flags: nZCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment kernel [ 1.905490] Control: 10c5387d Table: 4000404a DAC: 00000015 [ 1.911218] Process swapper/0 (pid: 1, stack limit = 0xee088240) [ 1.917208] Stack: (0xee089dc0 to 0xee08a000) ... [ 2.068431] [] (i2s_txctrl) from [] (samsung_i2s_dai_probe+0xb8/0x450) [ 2.076676] [] (samsung_i2s_dai_probe) from [] (snd_soc_register_card+0xd98/0x1348) [ 2.086044] [] (snd_soc_register_card) from [] (odroidx2_audio_probe+0xa8/0x11c) [ 2.095160] [] (odroidx2_audio_probe) from [] (platform_drv_probe+0x48/0xa4) [ 2.103922] [] (platform_drv_probe) from [] (driver_probe_device+0x10c/0x22c) [ 2.112773] [] (driver_probe_device) from [] (__driver_attach+0x8c/0x90) [ 2.121192] [] (__driver_attach) from [] (bus_for_each_dev+0x54/0x88) [ 2.129352] [] (bus_for_each_dev) from [] (bus_add_driver+0xd4/0x1d0) [ 2.137510] [] (bus_add_driver) from [] (driver_register+0x78/0xf4) [ 2.145499] [] (driver_register) from [] (do_one_initcall+0x80/0x1b8) [ 2.153670] [] (do_one_initcall) from [] (kernel_init_freeable+0xfc/0x1c8) [ 2.162260] [] (kernel_init_freeable) from [] (kernel_init+0x8/0xec) [ 2.170330] [] (kernel_init) from [] (ret_from_fork+0x14/0x3c) [ 2.177873] Code: e5940000 e59f128c e59f228c e2800010 (e59c700c) Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc/samsung') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9d513473b300..7f98ee69d15d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1239,6 +1239,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err; } + + sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.ch_name = "tx-sec"; -- cgit v1.2.3-59-g8ed1b