From c62db3d5abf89ca5502c7fe1f869c2862e48336d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jan 2016 15:53:46 +0800 Subject: ASoC: rt5659: Staticise rt5659_i2c_shutdown Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5659.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 820d8fa62b5e..47c717f4964a 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4184,7 +4184,7 @@ static int rt5659_i2c_remove(struct i2c_client *i2c) return 0; } -void rt5659_i2c_shutdown(struct i2c_client *client) +static void rt5659_i2c_shutdown(struct i2c_client *client) { struct rt5659_priv *rt5659 = i2c_get_clientdata(client); -- cgit v1.2.3 From fcde5a7eca1a28d3f7158399809cb01f83bd0114 Mon Sep 17 00:00:00 2001 From: PC Liao Date: Tue, 12 Jan 2016 14:33:42 +0800 Subject: ASoC: mediatek: Enable 33bit memory address to support 4GB DRAM If platform is embedded with memory more than 3GB, the address will go out of the scope that 32-bit can handle with. This patch sets the dma_mask and MSB properly to describe its address to 33-bit. Signed-off-by: Hidalgo Huang Signed-off-by: PC Liao Signed-off-by: Mark Brown --- sound/soc/mediatek/mtk-afe-common.h | 1 + sound/soc/mediatek/mtk-afe-pcm.c | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h index 9b1af1a70874..f341f623e887 100644 --- a/sound/soc/mediatek/mtk-afe-common.h +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -87,6 +87,7 @@ struct mtk_afe_memif_data { int irq_en_shift; int irq_fs_shift; int irq_clr_shift; + int msb_shift; }; struct mtk_afe_memif { diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index 08af9f5dc4ab..689c51f23d9f 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "mtk-afe-common.h" @@ -35,6 +36,7 @@ #define AFE_I2S_CON1 0x0034 #define AFE_I2S_CON2 0x0038 #define AFE_CONN_24BIT 0x006c +#define AFE_MEMIF_MSB 0x00cc #define AFE_CONN1 0x0024 #define AFE_CONN2 0x0028 @@ -592,6 +594,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int msb_at_bit33 = 0; int ret; dev_dbg(afe->dev, @@ -603,7 +606,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - memif->phys_buf_addr = substream->runtime->dma_addr; + msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; + memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); memif->buffer_size = substream->runtime->dma_bytes; /* start */ @@ -614,6 +618,11 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, memif->phys_buf_addr + memif->buffer_size - 1); + /* set MSB to 33-bit */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + 1 << memif->data->msb_shift, + msb_at_bit33 << memif->data->msb_shift); + /* set channel */ if (memif->data->mono_shift >= 0) { unsigned int mono = (params_channels(params) == 1) ? 1 : 0; @@ -978,6 +987,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 0, .irq_fs_shift = 4, .irq_clr_shift = 0, + .msb_shift = 0, }, { .name = "DL2", .id = MTK_AFE_MEMIF_DL2, @@ -991,6 +1001,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 2, .irq_fs_shift = 16, .irq_clr_shift = 2, + .msb_shift = 1, }, { .name = "VUL", .id = MTK_AFE_MEMIF_VUL, @@ -1004,6 +1015,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 1, .irq_fs_shift = 8, .irq_clr_shift = 1, + .msb_shift = 6, }, { .name = "DAI", .id = MTK_AFE_MEMIF_DAI, @@ -1017,6 +1029,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 3, .irq_fs_shift = 20, .irq_clr_shift = 3, + .msb_shift = 5, }, { .name = "AWB", .id = MTK_AFE_MEMIF_AWB, @@ -1030,6 +1043,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 14, .irq_fs_shift = 24, .irq_clr_shift = 6, + .msb_shift = 3, }, { .name = "MOD_DAI", .id = MTK_AFE_MEMIF_MOD_DAI, @@ -1043,6 +1057,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 3, .irq_fs_shift = 20, .irq_clr_shift = 3, + .msb_shift = 4, }, { .name = "HDMI", .id = MTK_AFE_MEMIF_HDMI, @@ -1056,6 +1071,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 12, .irq_fs_shift = -1, .irq_clr_shift = 4, + .msb_shift = 8, }, }; @@ -1189,6 +1205,10 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_afe *afe; struct resource *res; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; -- cgit v1.2.3 From 517e7a1537ae4663268be5d0c0ec62c563b9fc99 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 12 Jan 2016 12:35:46 +0000 Subject: ASoC: bcm2835: move to use the clock framework Since the move to the new clock framework with commit 94cb7f76caa0 ("ARM: bcm2835: Switch to using the new clock driver support.") this driver was no longer functional as it was manipulating the clock registers locally without going true the framework. This patch moves to use the new clock framework and also moves away from the hardcoded address offsets for DMA getting the dma-address directly from the device tree. Note that the optimal bclk_ratio selection to avoid jitter due to the use of fractional dividers, which is in the current version has been removed, because not all devices support these non power of 2 sized transfers, which resulted in lots of (downstream) modules that use: snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- sound/soc/bcm/bcm2835-i2s.c | 284 ++++++++++---------------------------------- 1 file changed, 64 insertions(+), 220 deletions(-) (limited to 'sound') diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 3303d5f58082..1c1f2210387b 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -46,55 +47,6 @@ #include #include -/* Clock registers */ -#define BCM2835_CLK_PCMCTL_REG 0x00 -#define BCM2835_CLK_PCMDIV_REG 0x04 - -/* Clock register settings */ -#define BCM2835_CLK_PASSWD (0x5a000000) -#define BCM2835_CLK_PASSWD_MASK (0xff000000) -#define BCM2835_CLK_MASH(v) ((v) << 9) -#define BCM2835_CLK_FLIP BIT(8) -#define BCM2835_CLK_BUSY BIT(7) -#define BCM2835_CLK_KILL BIT(5) -#define BCM2835_CLK_ENAB BIT(4) -#define BCM2835_CLK_SRC(v) (v) - -#define BCM2835_CLK_SHIFT (12) -#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT) -#define BCM2835_CLK_DIVF(v) (v) -#define BCM2835_CLK_DIVF_MASK (0xFFF) - -enum { - BCM2835_CLK_MASH_0 = 0, - BCM2835_CLK_MASH_1, - BCM2835_CLK_MASH_2, - BCM2835_CLK_MASH_3, -}; - -enum { - BCM2835_CLK_SRC_GND = 0, - BCM2835_CLK_SRC_OSC, - BCM2835_CLK_SRC_DBG0, - BCM2835_CLK_SRC_DBG1, - BCM2835_CLK_SRC_PLLA, - BCM2835_CLK_SRC_PLLC, - BCM2835_CLK_SRC_PLLD, - BCM2835_CLK_SRC_HDMI, -}; - -/* Most clocks are not useable (freq = 0) */ -static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = { - [BCM2835_CLK_SRC_GND] = 0, - [BCM2835_CLK_SRC_OSC] = 19200000, - [BCM2835_CLK_SRC_DBG0] = 0, - [BCM2835_CLK_SRC_DBG1] = 0, - [BCM2835_CLK_SRC_PLLA] = 0, - [BCM2835_CLK_SRC_PLLC] = 0, - [BCM2835_CLK_SRC_PLLD] = 500000000, - [BCM2835_CLK_SRC_HDMI] = 0, -}; - /* I2S registers */ #define BCM2835_I2S_CS_A_REG 0x00 #define BCM2835_I2S_FIFO_A_REG 0x04 @@ -158,10 +110,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = { #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) -/* I2S DMA interface */ -/* FIXME: Needs IOMMU support */ -#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000) - /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; @@ -169,21 +117,23 @@ struct bcm2835_i2s_dev { unsigned int fmt; unsigned int bclk_ratio; - struct regmap *i2s_regmap; - struct regmap *clk_regmap; + struct regmap *i2s_regmap; + struct clk *clk; + bool clk_prepared; }; static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) { - /* Start the clock if in master mode */ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; + if (dev->clk_prepared) + return; + switch (master) { case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFM: - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB); + clk_prepare_enable(dev->clk); + dev->clk_prepared = true; break; default: break; @@ -192,28 +142,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev) { - uint32_t clkreg; - int timeout = 1000; - - /* Stop clock */ - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD); - - /* Wait for the BUSY flag going down */ - while (--timeout) { - regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg); - if (!(clkreg & BCM2835_CLK_BUSY)) - break; - } - - if (!timeout) { - /* KILL the clock */ - dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK, - BCM2835_CLK_KILL | BCM2835_CLK_PASSWD); - } + if (dev->clk_prepared) + clk_disable_unprepare(dev->clk); + dev->clk_prepared = false; } static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, @@ -223,8 +154,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, uint32_t syncval; uint32_t csreg; uint32_t i2s_active_state; - uint32_t clkreg; - uint32_t clk_active_state; + bool clk_was_prepared; uint32_t off; uint32_t clr; @@ -238,15 +168,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON); - regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg); - clk_active_state = clkreg & BCM2835_CLK_ENAB; - /* Start clock if not running */ - if (!clk_active_state) { - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB); - } + clk_was_prepared = dev->clk_prepared; + if (!clk_was_prepared) + bcm2835_i2s_start_clock(dev); /* Stop I2S module */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0); @@ -280,7 +205,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, dev_err(dev->dev, "I2S SYNC error!\n"); /* Stop clock if it was not running before */ - if (!clk_active_state) + if (!clk_was_prepared) bcm2835_i2s_stop_clock(dev); /* Restore I2S state */ @@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - unsigned int sampling_rate = params_rate(params); unsigned int data_length, data_delay, bclk_ratio; unsigned int ch1pos, ch2pos, mode, format; - unsigned int mash = BCM2835_CLK_MASH_1; - unsigned int divi, divf, target_frequency; - int clk_src = -1; - unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; - bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS - || master == SND_SOC_DAIFMT_CBS_CFM); - - bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS - || master == SND_SOC_DAIFMT_CBM_CFS); uint32_t csreg; /* @@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: data_length = 16; - bclk_ratio = 40; break; case SNDRV_PCM_FORMAT_S32_LE: data_length = 32; - bclk_ratio = 80; break; default: return -EINVAL; @@ -356,69 +269,12 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* If bclk_ratio already set, use that one. */ if (dev->bclk_ratio) bclk_ratio = dev->bclk_ratio; + else + /* otherwise calculate a fitting block ratio */ + bclk_ratio = 2 * data_length; - /* - * Clock Settings - * - * The target frequency of the bit clock is - * sampling rate * frame length - * - * Integer mode: - * Sampling rates that are multiples of 8000 kHz - * can be driven by the oscillator of 19.2 MHz - * with an integer divider as long as the frame length - * is an integer divider of 19200000/8000=2400 as set up above. - * This is no longer possible if the sampling rate - * is too high (e.g. 192 kHz), because the oscillator is too slow. - * - * MASH mode: - * For all other sampling rates, it is not possible to - * have an integer divider. Approximate the clock - * with the MASH module that induces a slight frequency - * variance. To minimize that it is best to have the fastest - * clock here. That is PLLD with 500 MHz. - */ - target_frequency = sampling_rate * bclk_ratio; - clk_src = BCM2835_CLK_SRC_OSC; - mash = BCM2835_CLK_MASH_0; - - if (bcm2835_clk_freq[clk_src] % target_frequency == 0 - && bit_master && frame_master) { - divi = bcm2835_clk_freq[clk_src] / target_frequency; - divf = 0; - } else { - uint64_t dividend; - - if (!dev->bclk_ratio) { - /* - * Overwrite bclk_ratio, because the - * above trick is not needed or can - * not be used. - */ - bclk_ratio = 2 * data_length; - } - - target_frequency = sampling_rate * bclk_ratio; - - clk_src = BCM2835_CLK_SRC_PLLD; - mash = BCM2835_CLK_MASH_1; - - dividend = bcm2835_clk_freq[clk_src]; - dividend <<= BCM2835_CLK_SHIFT; - do_div(dividend, target_frequency); - divi = dividend >> BCM2835_CLK_SHIFT; - divf = dividend & BCM2835_CLK_DIVF_MASK; - } - - /* Set clock divider */ - regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD - | BCM2835_CLK_DIVI(divi) - | BCM2835_CLK_DIVF(divf)); - - /* Setup clock, but don't start it yet */ - regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD - | BCM2835_CLK_MASH(mash) - | BCM2835_CLK_SRC(clk_src)); + /* set target clock rate*/ + clk_set_rate(dev->clk, sampling_rate * bclk_ratio); /* Setup the frame format */ format = BCM2835_I2S_CHEN; @@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .trigger = bcm2835_i2s_trigger, .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, - .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio + .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) @@ -750,34 +606,14 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg) }; } -static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case BCM2835_CLK_PCMCTL_REG: - return true; - default: - return false; - }; -} - -static const struct regmap_config bcm2835_regmap_config[] = { - { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = BCM2835_I2S_GRAY_REG, - .precious_reg = bcm2835_i2s_precious_reg, - .volatile_reg = bcm2835_i2s_volatile_reg, - .cache_type = REGCACHE_RBTREE, - }, - { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = BCM2835_CLK_PCMDIV_REG, - .volatile_reg = bcm2835_clk_volatile_reg, - .cache_type = REGCACHE_RBTREE, - }, +static const struct regmap_config bcm2835_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = BCM2835_I2S_GRAY_REG, + .precious_reg = bcm2835_i2s_precious_reg, + .volatile_reg = bcm2835_i2s_volatile_reg, + .cache_type = REGCACHE_RBTREE, }; static const struct snd_soc_component_driver bcm2835_i2s_component = { @@ -787,42 +623,50 @@ static const struct snd_soc_component_driver bcm2835_i2s_component = { static int bcm2835_i2s_probe(struct platform_device *pdev) { struct bcm2835_i2s_dev *dev; - int i; int ret; - struct regmap *regmap[2]; - struct resource *mem[2]; - - /* Request both ioareas */ - for (i = 0; i <= 1; i++) { - void __iomem *base; - - mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); - base = devm_ioremap_resource(&pdev->dev, mem[i]); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, - &bcm2835_regmap_config[i]); - if (IS_ERR(regmap[i])) - return PTR_ERR(regmap[i]); - } + struct resource *mem; + void __iomem *base; + const __be32 *addr; + dma_addr_t dma_base; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - dev->i2s_regmap = regmap[0]; - dev->clk_regmap = regmap[1]; + /* get the clock */ + dev->clk_prepared = false; + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + dev_err(&pdev->dev, "could not get clk: %ld\n", + PTR_ERR(dev->clk)); + return PTR_ERR(dev->clk); + } + + /* Request ioarea */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base, + &bcm2835_regmap_config); + if (IS_ERR(dev->i2s_regmap)) + return PTR_ERR(dev->i2s_regmap); + + /* Set the DMA address - we have to parse DT ourselves */ + addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); + if (!addr) { + dev_err(&pdev->dev, "could not get DMA-register address\n"); + return -EINVAL; + } + dma_base = be32_to_cpup(addr); - /* Set the DMA address */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = - (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG - + BCM2835_VCMMU_SHIFT; + dma_base + BCM2835_I2S_FIFO_A_REG; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = - (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG - + BCM2835_VCMMU_SHIFT; + dma_base + BCM2835_I2S_FIFO_A_REG; /* Set the bus width */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = -- cgit v1.2.3 From 22363e758770fa24edcc67ba33326f007bcd66a8 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Jan 2016 11:13:12 +0800 Subject: ASoC: ab8500: remove the useless 'break' after 'return' 'break' here is not useful after 'return' or 'goto'. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index affb192238a4..b6820a1f16c7 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2134,7 +2134,6 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) "%s: ERROR: Unsupporter master mask 0x%x\n", __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; - break; } snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val); -- cgit v1.2.3 From 512feb4e19a262ea0dfd7209506a998897feaadc Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 15 Jan 2016 19:35:24 +0800 Subject: ASoC: fsl_sai: Use usleep_range() instead of msleep() Since msleep() will sleep longer than intended time for values less than 20ms, this patch allows the use of usleep_range for just 1ms. usleep_range is a finer precision implementation of msleep and is designed to be a drop-in replacement for udelay where a precise sleep/busy-wait is unnecessary. More details see Documentation/timers/timers-howto.txt. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 43ba5dc26775..4919de9684eb 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -884,7 +885,7 @@ static int fsl_sai_resume(struct device *dev) regcache_cache_only(sai->regmap, false); regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); - msleep(1); + usleep_range(1000, 2000); regmap_write(sai->regmap, FSL_SAI_TCSR, 0); regmap_write(sai->regmap, FSL_SAI_RCSR, 0); return regcache_sync(sai->regmap); -- cgit v1.2.3 From 0d55ad456337487e8cdb72acdd520050e575a0bd Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 15 Jan 2016 19:35:25 +0800 Subject: ASoC: fsl-mpc5200: Use usleep_range() instead of msleep() Since msleep() will sleep longer than intended time for values less than 20ms, this patch allows the use of usleep_range for just 1ms. usleep_range is a finer precision implementation of msleep and is designed to be a drop-in replacement for udelay where a precise sleep/busy-wait is unnecessary. More details see Documentation/timers/timers-howto.txt. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_ac97.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 0bab76051fd8..243700cc29e6 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -127,7 +128,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97) mutex_unlock(&psc_dma->mutex); - msleep(1); + usleep_range(1000, 2000); psc_ac97_warm_reset(ac97); } -- cgit v1.2.3 From 678ea0037263bb8afb139c4035be5ad4bc610a2f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:16 +0000 Subject: ASoC: rsnd: remove unsed *parent Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7ee89da4dd5f..df3ab74adf3e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -64,7 +64,6 @@ #define SSI_NAME "ssi" struct rsnd_ssi { - struct rsnd_ssi *parent; struct rsnd_mod mod; struct rsnd_mod *dma; -- cgit v1.2.3 From 7c89746eede3acbbb67f848a741f8bca412986af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:39 +0000 Subject: ASoC: rsnd: use rsnd_mod_init() for ADG Current ADG doesn't use rsnd_mod_init(), but this limitation is no longer necessary. Let's use common rsnd_mod_init() Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6d3ef366d536..19f5509f908d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -518,13 +518,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; } - /* - * ADG is special module. - * Use ADG mod without rsnd_mod_init() to make debug easy - * for rsnd_write/rsnd_read - */ - adg->mod.ops = &adg_ops; - adg->mod.priv = priv; + rsnd_mod_init(priv, &adg->mod, &adg_ops, + NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); -- cgit v1.2.3 From 4e880b2674a6d36d6c7b9741d88973e00a76922b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:57 +0000 Subject: ASoC: rsnd: add debug message for rsnd_mod_call() rsnd_mod_call() tries to call each IP's relevant function. But it is difficult to understand which function returned error. This patch adds debug message for this purpose Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ca05a0a95a4d..691e22381280 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -337,6 +337,9 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) *status, call ? #func : ""); \ if (call) \ ret = (mod)->ops->func(mod, io, param); \ + if (ret) \ + dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \ ret; \ }) -- cgit v1.2.3 From 6c001f3ab4ce20f01a1b3064b2bd1a9352c5d6b1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:18 +0000 Subject: ASoC: rsnd: don't update status if rsnd_mod_call() doesn't match If system uses CTU/MUX, and if probe error happened, it will try to call rsnd_dai_call(remove, ...). Then, MUX/DVC/SSIU/SSI might be called without calling rsnd_dai_call(probe, ...). Then, each mod status might be un-matched. It doesn't call un-matched remove function by this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 691e22381280..3eb7e9a7b5dc 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -330,8 +330,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - *status = (*status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ + if (add == 0xF) \ + call = 0; \ + else \ + *status = (*status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), \ *status, call ? #func : ""); \ -- cgit v1.2.3 From bfa3119c38afb8177f8e403b9e9b2ecd0306bb26 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:36 +0000 Subject: ASoC: rsnd: try to connect connected mod is not error If system uses CTU/MUX, CTU/MUX/DVC will try to connect same CMD to system, but it is not error in this case. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 3eb7e9a7b5dc..ad97ce3ae844 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -369,6 +369,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod, if (!mod) return -EIO; + if (io->mod[type] == mod) + return 0; + if (io->mod[type]) return -EINVAL; -- cgit v1.2.3 From ea96380baa2794cb6e0d5bfbcca0c78b237a5f8a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:59 +0000 Subject: ASoC: rsnd: always call probe/remove for MUX CTU/MUX/DVC/SSIU/SSI/Audio-DMAC-periperi might be used under multipath. So, probe()/remove() need to be called multiple times. This patch allows it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 1 + sound/soc/sh/rcar/rsnd.h | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ad97ce3ae844..ed0918967def 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -163,6 +163,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod) { if (mod->clk) clk_unprepare(mod->clk); + mod->clk = NULL; } void rsnd_mod_interrupt(struct rsnd_mod *mod, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 317dd793149a..c7b2ba0daf2a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -237,29 +237,29 @@ struct rsnd_mod { /* * status * - * 0xH0000CBA + * 0xH0000CB0 * - * A 0: probe 1: remove * B 0: init 1: quit * C 0: start 1: stop * * H is always called (see __rsnd_mod_call) + * H 0: probe 1: remove * H 0: pcm_new * H 0: fallback * H 0: hw_params */ -#define __rsnd_mod_shift_probe 0 -#define __rsnd_mod_shift_remove 0 #define __rsnd_mod_shift_init 4 #define __rsnd_mod_shift_quit 4 #define __rsnd_mod_shift_start 8 #define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ -#define __rsnd_mod_add_probe 1 -#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 #define __rsnd_mod_add_init 1 #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 @@ -269,7 +269,7 @@ struct rsnd_mod { #define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_init 0 #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 -- cgit v1.2.3 From 654a12b86305b1fd78279616f876b8d21d146a5e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:57:17 +0000 Subject: ASoC: rsnd: don't overwrite io on rsnd_cmd_init() Current rsnd_cmd_init() overwrites "io" which will be used end of this function. This patch solved this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/cmd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cd1f064e63c4..e77e4935b4ea 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -38,6 +38,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, if (mix) { struct rsnd_dai *rdai; + struct rsnd_dai_stream *tio; int i; u32 path[] = { [0] = 0, @@ -55,12 +56,12 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, */ data = 0; for_each_rsnd_dai(rdai, priv, i) { - io = &rdai->playback; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->playback; + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; - io = &rdai->capture; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->capture; + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; } -- cgit v1.2.3 From 72154e50728e8a08940332db20884b63dfa6590d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:57:37 +0000 Subject: ASoC: rsnd: select each SRC correctly for CMD data path To select CMD data patch, it should use correct SRC from each stream in MUX case. But current code is selecting SRC from fixed stream. This patch solves this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/cmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index e77e4935b4ea..4b2d50d9a686 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, { struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); - struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct device *dev = rsnd_priv_to_dev(priv); u32 data; @@ -38,6 +37,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, if (mix) { struct rsnd_dai *rdai; + struct rsnd_mod *src; struct rsnd_dai_stream *tio; int i; u32 path[] = { @@ -57,15 +57,19 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, data = 0; for_each_rsnd_dai(rdai, priv, i) { tio = &rdai->playback; + src = rsnd_io_to_mod_src(tio); if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; tio = &rdai->capture; + src = rsnd_io_to_mod_src(tio); if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; } } else { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + u32 path[] = { [0] = 0x30000, [1] = 0x30001, -- cgit v1.2.3 From 5ba17b42e1755c3c5cfe96370cfd47f34d01f62c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:07 +0000 Subject: ASoC: rsnd: each mod has status again for CTU/MUX support SSI will be used as normal SSI or as clock parent SSI. Therefor, rsnd driver wants to control SSI and parent SSI separately. Otherwise it can't use Playback/Capture in the same time. And it has been done by c2dc47d5cf("ASoC: rsnd: rsnd_dai_stream has each mod's status insted of rsnd_mod") before. OTOH, rsnd driver doesn't want to control CTU/MUX/DVC/SSIU/SSI in separately. Otherwise, these will be re-initialized during playing if MUX merges 2 sounds. Because of these picky reasons, this patch re-defines status on each mod, and add new parent_ssi_status on rsnd_dai_stream. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 2 +- sound/soc/sh/rcar/cmd.c | 3 ++- sound/soc/sh/rcar/core.c | 21 ++++++++++++++++----- sound/soc/sh/rcar/ctu.c | 2 +- sound/soc/sh/rcar/dma.c | 2 +- sound/soc/sh/rcar/dvc.c | 2 +- sound/soc/sh/rcar/mix.c | 2 +- sound/soc/sh/rcar/rsnd.h | 21 ++++++++++++++++----- sound/soc/sh/rcar/src.c | 3 ++- sound/soc/sh/rcar/ssi.c | 37 ++++++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ssiu.c | 3 ++- 11 files changed, 79 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 19f5509f908d..d74e1ccc0f8f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -519,7 +519,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) } rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, 0, 0); + NULL, NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 4b2d50d9a686..abb5eaac854a 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -157,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), - &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i); + &rsnd_cmd_ops, NULL, + rsnd_mod_get_status, RSND_MOD_CMD, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ed0918967def..b460d714d088 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + return &mod->status; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id) + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id) { int ret = clk_prepare(clk); @@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; + mod->get_status = get_status; return ret; } @@ -325,7 +336,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct rsnd_mod *mod = (io)->mod[idx]; \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 *status = (io)->mod_status + idx; \ + u32 *status = mod->get_status(io, mod, idx); \ u32 mask = 0xF << __rsnd_mod_shift_##func; \ u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index d53a225d19e9..109930a20401 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -129,7 +129,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, RSND_MOD_CTU, i); + clk, rsnd_mod_get_status, RSND_MOD_CTU, i); if (ret) goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 418e6fdd06a3..d1cb3c177572 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -681,7 +681,7 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, dma_mod = rsnd_mod_get(dma); ret = rsnd_mod_init(priv, dma_mod, - ops, NULL, type, dma_id); + ops, NULL, rsnd_mod_get_status, type, dma_id); if (ret < 0) return ERR_PTR(ret); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d45ffe496397..302c193f674d 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -373,7 +373,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, RSND_MOD_DVC, i); + clk, rsnd_mod_get_status, RSND_MOD_DVC, i); if (ret) goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 65542b6a89e9..e0e337ad4206 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, RSND_MOD_MIX, i); + clk, rsnd_mod_get_status, RSND_MOD_MIX, i); if (ret) goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c7b2ba0daf2a..28602607cf5e 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -233,6 +233,10 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; + u32 *(*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + u32 status; }; /* * status @@ -286,10 +290,13 @@ struct rsnd_mod { int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id); + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, @@ -297,6 +304,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -319,7 +330,7 @@ struct rsnd_dai_stream { struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; - u32 mod_status[RSND_MOD_MAX]; + u32 parent_ssi_status; int byte_pos; int period_pos; int byte_per_period; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index b438538a0a69..516b0c05451c 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -615,7 +615,8 @@ int rsnd_src_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, RSND_MOD_SRC, i); + &rsnd_src_ops, clk, rsnd_mod_get_status, + RSND_MOD_SRC, i); if (ret) goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index df3ab74adf3e..e68f3a1c9cb4 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -857,6 +857,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } +static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(io, mod, type); +} + int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -919,7 +954,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); + rsnd_ssi_get_status, RSND_MOD_SSI, i); if (ret) goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 06d72828e5bc..11e55889b401 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -206,7 +206,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) for_each_rsnd_ssiu(ssiu, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, RSND_MOD_SSIU, i); + ops, NULL, rsnd_mod_get_status, + RSND_MOD_SSIU, i); if (ret) return ret; } -- cgit v1.2.3 From 355cb84fbe1f098f80c17dad9027ad2c6aec3fa0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:33 +0000 Subject: ASoC: rsnd: attach Audio-DMAC-periperi correctly SSI/SRC will try to attach DMAC as Audio-DMAC or Audio-DMAC-periperi. It is fixed IP, but will be attached to each streams as different module in case of MUX (= multi sound path will be merged). This patch solves this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/dma.c | 56 +++++++++++++++++++++++++----------------------- sound/soc/sh/rcar/rsnd.h | 4 ++-- sound/soc/sh/rcar/src.c | 4 +--- sound/soc/sh/rcar/ssi.c | 5 ++--- 4 files changed, 34 insertions(+), 35 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index d1cb3c177572..7658e8fd7bdc 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, } } -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id) +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod, int id) { - struct rsnd_mod *dma_mod; struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct rsnd_dma *dma; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; enum rsnd_mod_type type; @@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, * rsnd_rdai_continuance_probe() */ if (!dmac) - return ERR_PTR(-EAGAIN); - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return ERR_PTR(-ENOMEM); + return -EAGAIN; rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - /* for Gen2 */ if (mod_from && mod_to) { ops = &rsnd_dmapp_ops; @@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, type = RSND_MOD_AUDMA; } - dma_mod = rsnd_mod_get(dma); + if (!(*dma_mod)) { + struct rsnd_dma *dma; - ret = rsnd_mod_init(priv, dma_mod, - ops, NULL, rsnd_mod_get_status, type, dma_id); - if (ret < 0) - return ERR_PTR(ret); + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + *dma_mod = rsnd_mod_get(dma); - ret = attach(io, dma, id, mod_from, mod_to); - if (ret < 0) - return ERR_PTR(ret); + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; - ret = rsnd_dai_connect(dma_mod, io, type); + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + + ret = attach(io, dma, id, mod_from, mod_to); + if (ret < 0) + return ret; + } + + ret = rsnd_dai_connect(*dma_mod, io, type); if (ret < 0) - return ERR_PTR(ret); + return ret; - return rsnd_mod_get(dma); + return 0; } int rsnd_dma_probe(struct rsnd_priv *priv) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 28602607cf5e..90c732e265a2 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -166,8 +166,8 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id); +int rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id); int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 516b0c05451c..8dc9b483b5fa 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -495,9 +495,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, return ret; } - src->dma = rsnd_dma_attach(io, mod, 0); - if (IS_ERR(src->dma)) - return PTR_ERR(src->dma); + ret = rsnd_dma_attach(io, mod, &src->dma, 0); return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e68f3a1c9cb4..90674137aa90 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -704,9 +704,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, if (ret) return ret; - ssi->dma = rsnd_dma_attach(io, mod, dma_id); - if (IS_ERR(ssi->dma)) - return PTR_ERR(ssi->dma); + /* SSI probe might be called many times in MUX multi path */ + ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); return ret; } -- cgit v1.2.3 From bd9a603fe78fa838a9c884b1e67749120a45508c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:53 +0000 Subject: ASoC: rsnd: ctu: add rsnd_mix_activation() Based on datasheet Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 9 +++++++++ sound/soc/sh/rcar/gen.c | 1 + sound/soc/sh/rcar/rsnd.h | 1 + 3 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 109930a20401..0dc451803eb6 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -24,6 +24,13 @@ struct rsnd_ctu { i++) #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) + +static void rsnd_ctu_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_SWRSR, 0); + rsnd_mod_write(mod, CTU_SWRSR, 1); +} + #define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) #define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) @@ -44,6 +51,8 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, { rsnd_mod_power_on(mod); + rsnd_ctu_activation(mod); + rsnd_ctu_initialize_lock(mod); rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index c7aee9e59e86..2f01e5eb462c 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -260,6 +260,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 90c732e265a2..12227f6b6221 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -86,6 +86,7 @@ enum rsnd_reg { RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ + RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, RSND_REG_MIX_SWRSR, -- cgit v1.2.3 From d0658b31c712327e04ddc2621742144de5562962 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:12 +0000 Subject: ASoC: rsnd: ctu: add rsnd_ctu_halt() Based on datasheet process Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 0dc451803eb6..7b482f36cb63 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -31,6 +31,12 @@ static void rsnd_ctu_activation(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 1); } +static void rsnd_ctu_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + rsnd_mod_write(mod, CTU_SWRSR, 0); +} + #define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) #define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) @@ -66,6 +72,8 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_ctu_halt(mod); + rsnd_mod_power_off(mod); return 0; -- cgit v1.2.3 From dc037afdbc4f285dc2f8107a0b8eaf89892e28ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:33 +0000 Subject: ASoC: rsnd: ctu: settings matches to datasheet Current CTU settings was rough. This patch makes it match to datasheet. But do nothing at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 61 ++++++++++++++++++++++++++++++++++++++---------- sound/soc/sh/rcar/gen.c | 34 +++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 34 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 7b482f36cb63..a10d0f7b73fa 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -37,13 +37,6 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } -#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) -#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) -static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) -{ - rsnd_mod_write(mod, CTU_CTUIR, enable); -} - static int rsnd_ctu_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -51,6 +44,54 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4); } +static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_mod_write(mod, CTU_CPMDR, 0); + rsnd_mod_write(mod, CTU_SCMDR, 0); + rsnd_mod_write(mod, CTU_SV00R, 0); + rsnd_mod_write(mod, CTU_SV01R, 0); + rsnd_mod_write(mod, CTU_SV02R, 0); + rsnd_mod_write(mod, CTU_SV03R, 0); + rsnd_mod_write(mod, CTU_SV04R, 0); + rsnd_mod_write(mod, CTU_SV05R, 0); + rsnd_mod_write(mod, CTU_SV06R, 0); + rsnd_mod_write(mod, CTU_SV07R, 0); + + rsnd_mod_write(mod, CTU_SV10R, 0); + rsnd_mod_write(mod, CTU_SV11R, 0); + rsnd_mod_write(mod, CTU_SV12R, 0); + rsnd_mod_write(mod, CTU_SV13R, 0); + rsnd_mod_write(mod, CTU_SV14R, 0); + rsnd_mod_write(mod, CTU_SV15R, 0); + rsnd_mod_write(mod, CTU_SV16R, 0); + rsnd_mod_write(mod, CTU_SV17R, 0); + + rsnd_mod_write(mod, CTU_SV20R, 0); + rsnd_mod_write(mod, CTU_SV21R, 0); + rsnd_mod_write(mod, CTU_SV22R, 0); + rsnd_mod_write(mod, CTU_SV23R, 0); + rsnd_mod_write(mod, CTU_SV24R, 0); + rsnd_mod_write(mod, CTU_SV25R, 0); + rsnd_mod_write(mod, CTU_SV26R, 0); + rsnd_mod_write(mod, CTU_SV27R, 0); + + rsnd_mod_write(mod, CTU_SV30R, 0); + rsnd_mod_write(mod, CTU_SV31R, 0); + rsnd_mod_write(mod, CTU_SV32R, 0); + rsnd_mod_write(mod, CTU_SV33R, 0); + rsnd_mod_write(mod, CTU_SV34R, 0); + rsnd_mod_write(mod, CTU_SV35R, 0); + rsnd_mod_write(mod, CTU_SV36R, 0); + rsnd_mod_write(mod, CTU_SV37R, 0); + + rsnd_mod_write(mod, CTU_CTUIR, 0); +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -59,11 +100,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, rsnd_ctu_activation(mod); - rsnd_ctu_initialize_lock(mod); - - rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); - - rsnd_ctu_initialize_unlock(mod); + rsnd_ctu_value_init(io, mod); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 2f01e5eb462c..b6e487e06019 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -263,6 +263,40 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), + RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), + RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), + RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), + RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), + RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), + RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), + RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), + RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), + RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), + RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), + RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), + RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), + RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), + RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), + RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), + RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), + RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), + RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), + RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), + RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), + RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), + RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), + RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), + RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), + RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), + RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), + RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), + RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), + RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), + RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), + RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), + RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), + RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100), RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 12227f6b6221..4974db6679c3 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -89,6 +89,40 @@ enum rsnd_reg { RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, + RSND_REG_CTU_CPMDR, + RSND_REG_CTU_SCMDR, + RSND_REG_CTU_SV00R, + RSND_REG_CTU_SV01R, + RSND_REG_CTU_SV02R, + RSND_REG_CTU_SV03R, + RSND_REG_CTU_SV04R, + RSND_REG_CTU_SV05R, + RSND_REG_CTU_SV06R, + RSND_REG_CTU_SV07R, + RSND_REG_CTU_SV10R, + RSND_REG_CTU_SV11R, + RSND_REG_CTU_SV12R, + RSND_REG_CTU_SV13R, + RSND_REG_CTU_SV14R, + RSND_REG_CTU_SV15R, + RSND_REG_CTU_SV16R, + RSND_REG_CTU_SV17R, + RSND_REG_CTU_SV20R, + RSND_REG_CTU_SV21R, + RSND_REG_CTU_SV22R, + RSND_REG_CTU_SV23R, + RSND_REG_CTU_SV24R, + RSND_REG_CTU_SV25R, + RSND_REG_CTU_SV26R, + RSND_REG_CTU_SV27R, + RSND_REG_CTU_SV30R, + RSND_REG_CTU_SV31R, + RSND_REG_CTU_SV32R, + RSND_REG_CTU_SV33R, + RSND_REG_CTU_SV34R, + RSND_REG_CTU_SV35R, + RSND_REG_CTU_SV36R, + RSND_REG_CTU_SV37R, RSND_REG_MIX_SWRSR, RSND_REG_MIX_MIXIR, RSND_REG_MIX_ADINR, -- cgit v1.2.3 From 38587f4cb7937344e12a9eef1ccdc5b8a0e992ba Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:55 +0000 Subject: ASoC: rsnd: tidyup Playback/Capture sequence Based on datasheet sequence Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index b460d714d088..1fcefab03ad6 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -358,15 +358,51 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ret; \ }) +static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { + { + /* CAPTURE */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + }, { + /* PLAYBACK */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + }, +}; + #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ + int type, is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ for (i = 0; i < RSND_MOD_MAX; i++) { \ - mod = (io)->mod[i]; \ + type = rsnd_mod_sequence[is_play][i]; \ + mod = (io)->mod[type]; \ if (!mod) \ continue; \ - ret |= rsnd_mod_call(i, io, fn, param); \ + ret |= rsnd_mod_call(type, io, fn, param); \ } \ ret; \ }) -- cgit v1.2.3 From 31739a689f828345a7ef52fd8dbbb200df1b6555 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 02:00:13 +0000 Subject: ASoC: rsnd: disable SRC.out/in in same time commit b761bf272bce ("ASoC: rsnd: disable SRC.out only when stop timing") disabled SRC.out/in in different timing, but was based on picky HW information. Now, we have confirmed that we can disable both in the same time. This patch do it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 8dc9b483b5fa..7749615bd404 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -367,11 +367,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - /* - * stop SRC output only - * see rsnd_src_quit - */ - rsnd_mod_write(mod, SRC_CTRL, 0x01); + rsnd_mod_write(mod, SRC_CTRL, 0); return 0; } @@ -409,9 +405,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_src_irq_disable(mod); - /* stop both out/in */ - rsnd_mod_write(mod, SRC_CTRL, 0); - rsnd_src_halt(mod); rsnd_mod_power_off(mod); -- cgit v1.2.3 From 6f08cbdaac5a3050ea1898aef8ce326030fd1117 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 14 Jan 2016 21:57:20 +0100 Subject: ASoC: Intel: Use acpi_dev_present() Use shiny new acpi_dev_present() and remove all the boilerplate to search for a particular ACPI device. No functional change. Cf. 2d12b6b381ba ("ACPI / utils: Add acpi_dev_present()"). Signed-off-by: Lukas Wunner Signed-off-by: Mark Brown --- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 17 ++--------------- sound/soc/intel/boards/cht_bsw_rt5645.c | 13 +------------ sound/soc/intel/common/sst-match-acpi.c | 16 +--------------- 3 files changed, 4 insertions(+), 42 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 90588d6e64fc..e609f089593a 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -287,33 +287,20 @@ static struct snd_soc_card snd_soc_card_cht = { .num_controls = ARRAY_SIZE(cht_mc_controls), }; -static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; - bool found = false; struct cht_mc_private *drv; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) return -ENOMEM; - if (ACPI_SUCCESS(acpi_get_devices( - "104C227E", - snd_acpi_codec_match, - &found, NULL)) && found) { - drv->ts3a227e_present = true; - } else { + drv->ts3a227e_present = acpi_dev_present("104C227E"); + if (!drv->ts3a227e_present) { /* no need probe TI jack detection chip */ snd_soc_card_cht.aux_dev = NULL; snd_soc_card_cht.num_aux_devs = 0; - drv->ts3a227e_present = false; } /* register the soc card */ diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 2d3afddb0a2e..e6cf800ab231 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -333,20 +333,12 @@ static struct cht_acpi_card snd_soc_cards[] = { {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, }; -static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; int i; struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; - bool found = false; char codec_name[16]; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); @@ -354,10 +346,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { - if (ACPI_SUCCESS(acpi_get_devices( - snd_soc_cards[i].codec_id, - snd_acpi_codec_match, - &found, NULL)) && found) { + if (acpi_dev_present(snd_soc_cards[i].codec_id)) { dev_dbg(&pdev->dev, "found codec %s\n", snd_soc_cards[i].codec_id); card = snd_soc_cards[i].soc_card; diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index dd077e116d25..c429e2226d40 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -13,29 +13,15 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ -#include -#include -#include -#include #include "sst-acpi.h" -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) { struct sst_acpi_mach *mach; - bool found = false; for (mach = machines; mach->id[0]; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->id, - sst_acpi_mach_match, - &found, NULL)) && found) + if (acpi_dev_present(mach->id)) return mach; return NULL; -- cgit v1.2.3 From 51b2bb3f2568e6d9d81a001d38b8d70c2ba4af99 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Mon, 25 Jan 2016 12:36:43 +0000 Subject: ASoC: wm8974: configure pll and mclk divider automatically This adds a set_sysclk() DAI op so the card driver can set the input clock frequency. If this is done, the pll and mclk divider are configured to produce the required 256x fs clock when the sample rate is set by hw_params(). These additions make the codec work with the simple-card driver. Card drivers calling set_pll() and set_clkdiv() directly are unaffected. Signed-off-by: Mans Rullgard Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8974.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index c284c7b6db8b..dc8c3b1ebb6f 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -28,6 +28,11 @@ #include "wm8974.h" +struct wm8974_priv { + unsigned int mclk; + unsigned int fs; +}; + static const struct reg_default wm8974_reg_defaults[] = { { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, @@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } +static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out, + int *mclkdiv) +{ + unsigned int ratio = 2 * f_in / f_out; + + if (ratio <= 2) { + *mclkdiv = WM8974_MCLKDIV_1; + ratio = 2; + } else if (ratio == 3) { + *mclkdiv = WM8974_MCLKDIV_1_5; + } else if (ratio == 4) { + *mclkdiv = WM8974_MCLKDIV_2; + } else if (ratio <= 6) { + *mclkdiv = WM8974_MCLKDIV_3; + ratio = 6; + } else if (ratio <= 8) { + *mclkdiv = WM8974_MCLKDIV_4; + ratio = 8; + } else if (ratio <= 12) { + *mclkdiv = WM8974_MCLKDIV_6; + ratio = 12; + } else if (ratio <= 16) { + *mclkdiv = WM8974_MCLKDIV_8; + ratio = 16; + } else { + *mclkdiv = WM8974_MCLKDIV_12; + ratio = 24; + } + + return f_out * ratio / 2; +} + +static int wm8974_update_clocks(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int fs256; + unsigned int fpll = 0; + unsigned int f; + int mclkdiv; + + if (!priv->mclk || !priv->fs) + return 0; + + fs256 = 256 * priv->fs; + + f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv); + + if (f != priv->mclk) { + /* The PLL performs best around 90MHz */ + fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv); + } + + wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll); + wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv); + + return 0; +} + +static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + priv->mclk = freq; + + return wm8974_update_clocks(dai); +} + static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; + struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; + int err; + + priv->fs = params_rate(params); + err = wm8974_update_clocks(dai); + if (err) + return err; /* bit size */ switch (params_width(params)) { @@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = { .set_fmt = wm8974_set_dai_fmt, .set_clkdiv = wm8974_set_dai_clkdiv, .set_pll = wm8974_set_dai_pll, + .set_sysclk = wm8974_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8974_dai = { @@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { static int wm8974_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct wm8974_priv *priv; struct regmap *regmap; int ret; + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); -- cgit v1.2.3 From 6a25c8da00284f5612b404368bd07b69efd84aa2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Jan 2016 04:56:14 +0000 Subject: ASoC: rsnd: don't auto-recover when under/over run error Renesas R-Car sound needs recovery (= restart) when under/over run error occurred, and current driver tries it on under/over run error handler automatically. But this recovery should be handled by userland, not kernel. This patch stops XRUN when under/over run error occur, and will leave the recovery of HW in userland. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 39 ++++---------------- sound/soc/sh/rcar/ssi.c | 97 +++++++++++-------------------------------------- 2 files changed, 29 insertions(+), 107 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 7749615bd404..cccca154e4c3 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ u32 convert_rate; /* sampling rate convert */ - int err; int irq; }; @@ -316,7 +315,7 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); } -static bool rsnd_src_record_error(struct rsnd_mod *mod) +static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; @@ -333,12 +332,8 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod) val0 = val0 & 0xffff; if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - - src->err++; + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) ret = true; - } return ret; } @@ -388,8 +383,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_irq_enable(mod); - src->err = 0; - /* reset sync convert_rate */ src->sync.val = 0; @@ -401,7 +394,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); rsnd_src_irq_disable(mod); @@ -409,10 +401,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_mod_power_off(mod); - if (src->err) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); - src->convert_rate = 0; /* reset sync convert_rate */ @@ -425,8 +413,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); + bool stop = false; spin_lock(&priv->lock); @@ -434,26 +421,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_out; - if (rsnd_src_record_error(mod)) { - - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - rsnd_src_stop(mod, io, priv); - rsnd_src_start(mod, io, priv); - } - - if (src->err > 1024) { - rsnd_src_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (rsnd_src_error_occurred(mod)) + stop = true; rsnd_src_status_clear(mod); rsnd_src_interrupt_out: spin_unlock(&priv->lock); + + if (stop) + snd_pcm_stop_xrun(io->substream); } static irqreturn_t rsnd_src_interrupt(int irq, void *data) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 90674137aa90..5870434bbc58 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -74,7 +74,6 @@ struct rsnd_ssi { u32 wsr; int chan; int rate; - int err; int irq; unsigned int usrcnt; }; @@ -385,8 +384,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (ret < 0) return ret; - ssi->err = -1; /* ignore 1st error */ - /* clear error status */ rsnd_ssi_status_clear(mod); @@ -409,13 +406,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, } if (!rsnd_ssi_is_parent(mod, io)) { - if (ssi->err > 0) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - ssi->err); - ssi->cr_own = 0; - ssi->err = 0; rsnd_ssi_irq_disable(mod); } @@ -455,21 +446,9 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, return 0; } -static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - u32 status = rsnd_ssi_status_get(mod); - - /* under/over flow error */ - if (status & (UIRQ | OIRQ)) - ssi->err++; - - return status; -} - -static int __rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; @@ -491,25 +470,21 @@ static int __rsnd_ssi_start(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; + /* - * no limit to start + * don't stop if not last user * see also - * rsnd_ssi_stop + * rsnd_ssi_start * rsnd_ssi_interrupt */ - return __rsnd_ssi_start(mod, io, priv); -} - -static int __rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; + if (ssi->usrcnt > 1) + return 0; /* * disable all IRQ, @@ -531,33 +506,14 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - /* - * don't stop if not last user - * see also - * rsnd_ssi_start - * rsnd_ssi_interrupt - */ - if (ssi->usrcnt > 1) - return 0; - - return __rsnd_ssi_stop(mod, io, priv); -} - static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; + bool stop = false; spin_lock(&priv->lock); @@ -565,7 +521,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_ssi_record_error(ssi); + status = rsnd_ssi_status_get(mod); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -587,23 +543,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, } /* DMA only */ - if (is_dma && (status & (UIRQ | OIRQ))) { - /* - * restart SSI - */ - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - __rsnd_ssi_stop(mod, io, priv); - __rsnd_ssi_start(mod, io, priv); - } - - if (ssi->err > 1024) { - rsnd_ssi_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (is_dma && (status & (UIRQ | OIRQ))) + stop = true; rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: @@ -611,6 +552,10 @@ rsnd_ssi_interrupt_out: if (elapsed) rsnd_dai_period_elapsed(io); + + if (stop) + snd_pcm_stop_xrun(io->substream); + } static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) -- cgit v1.2.3 From b5b442abd9d5cfe4f04a1e83be9900c87444bd66 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Jan 2016 04:56:57 +0000 Subject: ASoC: rsnd: add .irq callback Current rsnd driver has .init/.start/.stop/.quit callbacks, and it needs many IPs (SRC/CTU/MUX/DVC/CMD/SSIU/SSI). Because of these relationship, it might get unnecessary error IRQ when start/stop. This patch adds new .irq callback and control IRQ enable/disable timing to avoid it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 9 ++++++++- sound/soc/sh/rcar/rsnd.h | 6 ++++++ sound/soc/sh/rcar/src.c | 16 ++++++++-------- sound/soc/sh/rcar/ssi.c | 36 +++++++++++++----------------------- 4 files changed, 35 insertions(+), 32 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 1fcefab03ad6..704ba7ae9eaf 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -568,9 +568,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rsnd_dai_call(start, io, priv); if (ret < 0) goto dai_trigger_end; + + ret = rsnd_dai_call(irq, io, priv, 1); + if (ret < 0) + goto dai_trigger_end; + break; case SNDRV_PCM_TRIGGER_STOP: - ret = rsnd_dai_call(stop, io, priv); + ret = rsnd_dai_call(irq, io, priv, 0); + + ret |= rsnd_dai_call(stop, io, priv); ret |= rsnd_dai_call(quit, io, priv); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4974db6679c3..bbaf89b6de8a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -249,6 +249,9 @@ struct rsnd_mod_ops { int (*stop)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + int (*irq)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, int enable); int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); @@ -293,6 +296,7 @@ struct rsnd_mod { #define __rsnd_mod_shift_stop 8 #define __rsnd_mod_shift_probe 28 /* always called */ #define __rsnd_mod_shift_remove 28 /* always called */ +#define __rsnd_mod_shift_irq 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ @@ -303,6 +307,7 @@ struct rsnd_mod { #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 #define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_irq 0 #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 @@ -313,6 +318,7 @@ struct rsnd_mod { #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 #define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_irq 0 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index cccca154e4c3..ab5f13155055 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -271,9 +271,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_adg_set_convert_timing_gen2(mod, io); } -#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1) -#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0) -static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) +static int rsnd_src_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; @@ -305,6 +306,8 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); + + return 0; } static void rsnd_src_status_clear(struct rsnd_mod *mod) @@ -381,8 +384,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_status_clear(mod); - rsnd_src_irq_enable(mod); - /* reset sync convert_rate */ src->sync.val = 0; @@ -395,8 +396,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_src_irq_disable(mod); - rsnd_src_halt(mod); rsnd_mod_power_off(mod); @@ -455,7 +454,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, /* * IRQ is not supported on non-DT * see - * rsnd_src_irq_enable() + * rsnd_src_irq() */ ret = devm_request_irq(dev, irq, rsnd_src_interrupt, @@ -518,6 +517,7 @@ static struct rsnd_mod_ops rsnd_src_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start, .stop = rsnd_src_stop, + .irq = rsnd_src_irq, .hw_params = rsnd_src_hw_params, .pcm_new = rsnd_src_pcm_new, }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5870434bbc58..803e9ae65915 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -142,30 +142,24 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, dev_warn(dev, "status check failed\n"); } -static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod) +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 val = 0; if (rsnd_is_gen1(priv)) return 0; - /* enable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, - rsnd_ssi_is_dma_mode(ssi_mod) ? - 0x0e000000 : 0x0f000000); - - return 0; -} - -static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) + if (ssi->usrcnt != 1) return 0; - /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); return 0; } @@ -387,8 +381,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, /* clear error status */ rsnd_ssi_status_clear(mod); - rsnd_ssi_irq_enable(mod); - return 0; } @@ -405,12 +397,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return -EIO; } - if (!rsnd_ssi_is_parent(mod, io)) { + if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; - rsnd_ssi_irq_disable(mod); - } - rsnd_ssi_master_clk_stop(ssi, io); rsnd_mod_power_off(mod); @@ -627,6 +616,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, .hw_params = rsnd_ssi_hw_params, }; -- cgit v1.2.3 From c670254f63fe09681b1c4c82d3d059c6cc04ce09 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 27 Jan 2016 15:02:49 +0200 Subject: ASoC: davinci-mcasp: Discourage use of fck_parent for clock reparenting The in-driver clock reparenting had been added when we did not had other means to cleanly set the parent for the fck. Now we can use assigned-clocks/assigned-clock-parents in DT binding. Print warning when the fck_parent is present for McASP and recommend the switch to the proper way to handle the clock selection. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 2ccb8bccc9d4..3cf46afb353f 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1517,6 +1517,8 @@ static int mcasp_reparent_fck(struct platform_device *pdev) if (!parent_name) return 0; + dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n"); + gfclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(gfclk)) { dev_err(&pdev->dev, "failed to get fck\n"); -- cgit v1.2.3 From 712a8038cc24dba668afe82f0413714ca87184e0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 27 Jan 2016 14:26:18 +0100 Subject: ASoC: ssm4567: Reset device before regcache_sync() When the ssm4567 is powered up the driver calles regcache_sync() to restore the register map content. regcache_sync() assumes that the device is in its power-on reset state. Make sure that this is the case by explicitly resetting the ssm4567 register map before calling regcache_sync() otherwise we might end up with a incorrect register map which leads to undefined behaviour. One such undefined behaviour was observed when returning from system suspend while a playback stream is active, in that case the ssm4567 was kept muted after resume. Fixes: 1ee44ce03011 ("ASoC: ssm4567: Add driver for Analog Devices SSM4567 amplifier") Reported-by: Harsha Priya Tested-by: Fang, Yang A Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ssm4567.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index e619d5651b09..080c78e88e10 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -352,6 +352,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable) regcache_cache_only(ssm4567->regmap, !enable); if (enable) { + ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, + 0x00); + if (ret) + return ret; + ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_POWER_CTRL, SSM4567_POWER_SPWDN, 0x00); -- cgit v1.2.3 From 7315917f7ccdf5eb5758f663a781daa7599d63d7 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 25 Jan 2016 18:23:30 +0100 Subject: ASoC: rk3036: fix missing dependency on REGMAP_MMIO When SND_SOC_INNO_RK3036 is enabled but REGMAP_MMIO is not, the MODPOST step fails with this error: ... Kernel: arch/x86/boot/bzImage is ready (#3) Building modules, stage 2. MODPOST 3203 modules ERROR: "__devm_regmap_init_mmio_clk" [sound/soc/codecs/snd-soc-inno-rk3036.ko] undefined! Signed-off-by: Antonio Ospite Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..1f7eaa919088 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -497,6 +497,7 @@ config SND_SOC_ICS43432 config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" + select REGMAP_MMIO config SND_SOC_ISABELLE tristate -- cgit v1.2.3 From ba4bc32eaa39ba7687f0958ae90eec94da613b46 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 18:07:33 +0100 Subject: ASoC: s3c24xx: use const snd_soc_component_driver pointer An older patch to convert the API in the s3c i2s driver ended up passing a const pointer into a function that takes a non-const pointer, so we now get a warning: sound/soc/samsung/s3c2412-i2s.c: In function 's3c2412_iis_dev_probe': sound/soc/samsung/s3c2412-i2s.c:172:9: error: passing argument 3 of 's3c_i2sv2_register_component' discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers] However, the s3c_i2sv2_register_component() function again passes the pointer into another function taking a const, so we just need to change its prototype. Fixes: eca3b01d0885 ("ASoC: switch over to use snd_soc_register_component() on s3c i2s") Signed-off-by: Arnd Bergmann Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/s3c-i2s-v2.c | 2 +- sound/soc/samsung/s3c-i2s-v2.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index df65c5b494b1..b6ab3fc5789e 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -709,7 +709,7 @@ static int s3c2412_i2s_resume(struct snd_soc_dai *dai) #endif int s3c_i2sv2_register_component(struct device *dev, int id, - struct snd_soc_component_driver *cmp_drv, + const struct snd_soc_component_driver *cmp_drv, struct snd_soc_dai_driver *dai_drv) { struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops; diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h index 90abab364b49..d0684145ed1f 100644 --- a/sound/soc/samsung/s3c-i2s-v2.h +++ b/sound/soc/samsung/s3c-i2s-v2.h @@ -101,7 +101,7 @@ extern int s3c_i2sv2_probe(struct snd_soc_dai *dai, * soc core. */ extern int s3c_i2sv2_register_component(struct device *dev, int id, - struct snd_soc_component_driver *cmp_drv, + const struct snd_soc_component_driver *cmp_drv, struct snd_soc_dai_driver *dai_drv); #endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */ -- cgit v1.2.3 From a2e2876aa0bab5939daf1cb20ef07ebf38744c1f Mon Sep 17 00:00:00 2001 From: anish kumar Date: Thu, 14 Jan 2016 14:03:33 -0800 Subject: ASoC: Add max98926 codec driver Signed-off-by: anish kumar Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/max98926.txt | 32 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/max98926.c | 604 +++++++++++++++ sound/soc/codecs/max98926.h | 848 +++++++++++++++++++++ 5 files changed, 1490 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/max98926.txt create mode 100644 sound/soc/codecs/max98926.c create mode 100644 sound/soc/codecs/max98926.h (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/max98926.txt b/Documentation/devicetree/bindings/sound/max98926.txt new file mode 100644 index 000000000000..0b7f4e4d5f9a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max98926.txt @@ -0,0 +1,32 @@ +max98926 audio CODEC + +This device supports I2C. + +Required properties: + + - compatible : "maxim,max98926" + + - vmon-slot-no : slot number used to send voltage information + or in inteleave mode this will be used as + interleave slot. + + - imon-slot-no : slot number used to send current information + + - interleave-mode : When using two MAX98926 in a system it is + possible to create ADC data that that will + overflow the frame size. Digital Audio Interleave + mode provides a means to output VMON and IMON data + from two devices on a single DOUT line when running + smaller frames sizes such as 32 BCLKS per LRCLK or + 48 BCLKS per LRCLK. + + - reg : the I2C address of the device for I2C + +Example: + +codec: max98926@1a { + compatible = "maxim,max98926"; + vmon-slot-no = <0>; + imon-slot-no = <2>; + reg = <0x1a>; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..ebaaa51823ef 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -80,6 +80,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB select SND_SOC_MAX98925 if I2C + select SND_SOC_MAX98926 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C @@ -519,6 +520,9 @@ config SND_SOC_MAX98357A config SND_SOC_MAX98925 tristate +config SND_SOC_MAX98926 + tristate + config SND_SOC_MAX9850 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d44f7d347183..16c014a46ae8 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -75,6 +75,7 @@ snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o snd-soc-max98925-objs := max98925.o +snd-soc-max98926-objs := max98926.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o @@ -279,6 +280,7 @@ obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o +obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c new file mode 100644 index 000000000000..66884ebdf2bb --- /dev/null +++ b/sound/soc/codecs/max98926.c @@ -0,0 +1,604 @@ +/* + * max98926.c -- ALSA SoC MAX98926 driver + * Copyright 2013-15 Maxim Integrated Products + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98926.h" + +static const char * const max98926_boost_voltage_txt[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static const char * const max98926_boost_current_txt[] = { + "0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0", + "2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4" +}; + +static const char *const max98926_dai_txt[] = { + "Left", "Right", "LeftRight", "LeftRightDiv2", +}; + +static const char *const max98926_pdm_ch_text[] = { + "Current", "Voltage", +}; + +static const char *const max98926_hpf_cutoff_txt[] = { + "Disable", "DC Block", "100Hz", + "200Hz", "400Hz", "800Hz", +}; + +static struct reg_default max98926_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x04 }, /* DAI Clock Mode 1 */ + { 0x1B, 0x00 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98926_voltage_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_voltage_control = + SOC_DAPM_ENUM("Route", max98926_voltage_enum); + +static const struct soc_enum max98926_current_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_SOURCE_1_SHIFT, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_current_control = + SOC_DAPM_ENUM("Route", max98926_current_enum); + +static const struct snd_kcontrol_new max98926_mixer_controls[] = { + SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, 0), + SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new max98926_dai_controls[] = { + SOC_DAPM_SINGLE("Left", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 0, 0), + SOC_DAPM_SINGLE("Right", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 2, 0), + SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 3, 0), +}; + +static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE, + MAX98926_SPK_EN_SHIFT, 0), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE, + MAX98926_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE, + MAX98926_ADC_IMON_EN_WIDTH | + MAX98926_ADC_VMON_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE, + MAX98926_BST_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, + &max98926_mixer_controls[0], + ARRAY_SIZE(max98926_mixer_controls)), + SND_SOC_DAPM_MIXER("DAI Sel", + MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0, + &max98926_dai_controls[0], + ARRAY_SIZE(max98926_dai_controls)), + SND_SOC_DAPM_MUX("PDM CH1 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_CURRENT_SHIFT, + 0, &max98926_current_control), + SND_SOC_DAPM_MUX("PDM CH0 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_VOLTAGE_SHIFT, + 0, &max98926_voltage_control), +}; + +static const struct snd_soc_dapm_route max98926_audio_map[] = { + {"VI Enable", NULL, "DAI_OUT"}, + {"DAI Sel", "Left", "VI Enable"}, + {"DAI Sel", "Right", "VI Enable"}, + {"DAI Sel", "LeftRight", "VI Enable"}, + {"DAI Sel", "LeftRightDiv2", "VI Enable"}, + {"PCM Sel", "PCM", "DAI Sel"}, + + {"PDM CH1 Source", "Current", "DAI_OUT"}, + {"PDM CH1 Source", "Voltage", "DAI_OUT"}, + {"PDM CH0 Source", "Current", "DAI_OUT"}, + {"PDM CH0 Source", "Voltage", "DAI_OUT"}, + {"PCM Sel", "Analog", "PDM CH1 Source"}, + {"PCM Sel", "Analog", "PDM CH0 Source"}, + {"Amp Enable", NULL, "PCM Sel"}, + + {"BST Enable", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "BST Enable"}, +}; + +static bool max98926_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_VBAT_DATA: + case MAX98926_VBST_DATA: + case MAX98926_LIVE_STATUS0: + case MAX98926_LIVE_STATUS1: + case MAX98926_LIVE_STATUS2: + case MAX98926_STATE0: + case MAX98926_STATE1: + case MAX98926_STATE2: + case MAX98926_FLAG0: + case MAX98926_FLAG1: + case MAX98926_FLAG2: + case MAX98926_VERSION: + return true; + default: + return false; + } +} + +static bool max98926_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_IRQ_CLEAR0: + case MAX98926_IRQ_CLEAR1: + case MAX98926_IRQ_CLEAR2: + case MAX98926_ALC_HOLD_RLS: + return false; + default: + return true; + } +}; + +DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0); +DECLARE_TLV_DB_RANGE(max98926_current_tlv, + 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0), + 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0), +); + +static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff, + MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT, + max98926_hpf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage, + MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT, + max98926_boost_voltage_txt); + +static const struct snd_kcontrol_new max98926_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN, + MAX98926_SPK_GAIN_SHIFT, + (1<regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_EN_MASK, + MAX98926_DAI_VMON_EN_MASK); + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_EN_MASK, + MAX98926_DAI_IMON_EN_MASK); + + if (!max98926->interleave_mode) { + /* set VMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_SLOT_MASK, + max98926->v_slot); + /* set IMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_SLOT_MASK, + max98926->i_slot); + } else { + /* enable interleave mode */ + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_INTERLEAVE_MASK, + MAX98926_DAI_INTERLEAVE_MASK); + /* set interleave slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VBAT, + MAX98926_DAI_INTERLEAVE_SLOT_MASK, + max98926->v_slot); + } +} + +static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + max98926_set_sense_data(max98926); + break; + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = MAX98926_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98926_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_write(max98926->regmap, + MAX98926_FORMAT, MAX98926_DAI_DLY_MASK); + regmap_update_bits(max98926->regmap, MAX98926_FORMAT, + MAX98926_DAI_BCI_MASK, invert); + return 0; +} + +static int max98926_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int dai_sr = -EINVAL; + int rate = params_rate(params), i; + struct snd_soc_codec *codec = dai->codec; + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98926->ch_size; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_16); + max98926->ch_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_24); + max98926->ch_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_32); + max98926->ch_size = 32; + break; + default: + dev_dbg(codec->dev, "format unsupported %d", + params_format(params)); + return -EINVAL; + } + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + /* find the closest rate */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + dai_sr = rate_table[i].sr; + break; + } + } + if (dai_sr < 0) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT); + return 0; +} + +#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops max98926_dai_ops = { + .set_fmt = max98926_dai_set_fmt, + .hw_params = max98926_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98926_dai[] = { +{ + .name = "max98926-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .ops = &max98926_dai_ops, +} +}; + +static int max98926_probe(struct snd_soc_codec *codec) +{ + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + + max98926->codec = codec; + codec->control_data = max98926->regmap; + /* Hi-Z all the slots */ + regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max98926 = { + .probe = max98926_probe, + .controls = max98926_snd_controls, + .num_controls = ARRAY_SIZE(max98926_snd_controls), + .dapm_routes = max98926_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), + .dapm_widgets = max98926_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), +}; + +static struct regmap_config max98926_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98926_VERSION, + .reg_defaults = max98926_reg, + .num_reg_defaults = ARRAY_SIZE(max98926_reg), + .volatile_reg = max98926_volatile_register, + .readable_reg = max98926_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98926_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret, reg; + u32 value; + struct max98926_priv *max98926; + + max98926 = devm_kzalloc(&i2c->dev, + sizeof(*max98926), GFP_KERNEL); + if (!max98926) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98926); + max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap); + if (IS_ERR(max98926->regmap)) { + ret = PTR_ERR(max98926->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err_out; + } + if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) + max98926->interleave_mode = true; + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > MAX98926_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98926->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > MAX98926_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98926->i_slot = value; + } + ret = regmap_read(max98926->regmap, + MAX98926_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %x\n", reg); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926, + max98926_dai, ARRAY_SIZE(max98926_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register codec: %d\n", ret); + dev_info(&i2c->dev, "device version: %x\n", reg); +err_out: + return ret; +} + +static int max98926_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98926_i2c_id[] = { + { "max98926", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); + +static const struct of_device_id max98926_of_match[] = { + { .compatible = "maxim,max98926", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98926_of_match); + +static struct i2c_driver max98926_i2c_driver = { + .driver = { + .name = "max98926", + .of_match_table = of_match_ptr(max98926_of_match), + .pm = NULL, + }, + .probe = max98926_i2c_probe, + .remove = max98926_i2c_remove, + .id_table = max98926_i2c_id, +}; + +module_i2c_driver(max98926_i2c_driver) +MODULE_DESCRIPTION("ALSA SoC MAX98926 driver"); +MODULE_AUTHOR("Anish kumar "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h new file mode 100644 index 000000000000..9d7ab6df79ca --- /dev/null +++ b/sound/soc/codecs/max98926.h @@ -0,0 +1,848 @@ +/* + * max98926.h -- MAX98926 ALSA SoC Audio driver + * Copyright 2013-2015 Maxim Integrated Products + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98926_H +#define _MAX98926_H + +#define MAX98926_CHIP_VERSION 0x40 +#define MAX98926_CHIP_VERSION1 0x50 + +#define MAX98926_VBAT_DATA 0x00 +#define MAX98926_VBST_DATA 0x01 +#define MAX98926_LIVE_STATUS0 0x02 +#define MAX98926_LIVE_STATUS1 0x03 +#define MAX98926_LIVE_STATUS2 0x04 +#define MAX98926_STATE0 0x05 +#define MAX98926_STATE1 0x06 +#define MAX98926_STATE2 0x07 +#define MAX98926_FLAG0 0x08 +#define MAX98926_FLAG1 0x09 +#define MAX98926_FLAG2 0x0A +#define MAX98926_IRQ_ENABLE0 0x0B +#define MAX98926_IRQ_ENABLE1 0x0C +#define MAX98926_IRQ_ENABLE2 0x0D +#define MAX98926_IRQ_CLEAR0 0x0E +#define MAX98926_IRQ_CLEAR1 0x0F +#define MAX98926_IRQ_CLEAR2 0x10 +#define MAX98926_MAP0 0x11 +#define MAX98926_MAP1 0x12 +#define MAX98926_MAP2 0x13 +#define MAX98926_MAP3 0x14 +#define MAX98926_MAP4 0x15 +#define MAX98926_MAP5 0x16 +#define MAX98926_MAP6 0x17 +#define MAX98926_MAP7 0x18 +#define MAX98926_MAP8 0x19 +#define MAX98926_DAI_CLK_MODE1 0x1A +#define MAX98926_DAI_CLK_MODE2 0x1B +#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98926_FORMAT 0x20 +#define MAX98926_TDM_SLOT_SELECT 0x21 +#define MAX98926_DOUT_CFG_VMON 0x22 +#define MAX98926_DOUT_CFG_IMON 0x23 +#define MAX98926_DOUT_CFG_VBAT 0x24 +#define MAX98926_DOUT_CFG_VBST 0x25 +#define MAX98926_DOUT_CFG_FLAG 0x26 +#define MAX98926_DOUT_HIZ_CFG1 0x27 +#define MAX98926_DOUT_HIZ_CFG2 0x28 +#define MAX98926_DOUT_HIZ_CFG3 0x29 +#define MAX98926_DOUT_HIZ_CFG4 0x2A +#define MAX98926_DOUT_DRV_STRENGTH 0x2B +#define MAX98926_FILTERS 0x2C +#define MAX98926_GAIN 0x2D +#define MAX98926_GAIN_RAMPING 0x2E +#define MAX98926_SPK_AMP 0x2F +#define MAX98926_THRESHOLD 0x30 +#define MAX98926_ALC_ATTACK 0x31 +#define MAX98926_ALC_ATTEN_RLS 0x32 +#define MAX98926_ALC_HOLD_RLS 0x33 +#define MAX98926_ALC_CONFIGURATION 0x34 +#define MAX98926_BOOST_CONVERTER 0x35 +#define MAX98926_BLOCK_ENABLE 0x36 +#define MAX98926_CONFIGURATION 0x37 +#define MAX98926_GLOBAL_ENABLE 0x38 +#define MAX98926_BOOST_LIMITER 0x3A +#define MAX98926_VERSION 0xFF + +#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1) + +#define MAX98926_PDM_CURRENT_MASK (1<<7) +#define MAX98926_PDM_CURRENT_SHIFT 7 +#define MAX98926_PDM_VOLTAGE_MASK (1<<3) +#define MAX98926_PDM_VOLTAGE_SHIFT 3 +#define MAX98926_PDM_CHANNEL_0_MASK (1<<2) +#define MAX98926_PDM_CHANNEL_0_SHIFT 2 +#define MAX98926_PDM_CHANNEL_1_MASK (1<<6) +#define MAX98926_PDM_CHANNEL_1_SHIFT 6 +#define MAX98926_PDM_CHANNEL_1_HIZ 5 +#define MAX98926_PDM_CHANNEL_0_HIZ 1 +#define MAX98926_PDM_SOURCE_0_SHIFT 0 +#define MAX98926_PDM_SOURCE_0_MASK (1<<0) +#define MAX98926_PDM_SOURCE_1_MASK (1<<4) +#define MAX98926_PDM_SOURCE_1_SHIFT 4 + +/* MAX98926 Register Bit Fields */ + +/* MAX98926_R002_LIVE_STATUS0 */ +#define MAX98926_THERMWARN_STATUS_MASK (1<<3) +#define MAX98926_THERMWARN_STATUS_SHIFT 3 +#define MAX98926_THERMWARN_STATUS_WIDTH 1 +#define MAX98926_THERMSHDN_STATUS_MASK (1<<1) +#define MAX98926_THERMSHDN_STATUS_SHIFT 1 +#define MAX98926_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98926_R003_LIVE_STATUS1 */ +#define MAX98926_SPKCURNT_STATUS_MASK (1<<5) +#define MAX98926_SPKCURNT_STATUS_SHIFT 5 +#define MAX98926_SPKCURNT_STATUS_WIDTH 1 +#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATUS_SHIFT 4 +#define MAX98926_WATCHFAIL_STATUS_WIDTH 1 +#define MAX98926_ALCINFH_STATUS_MASK (1<<3) +#define MAX98926_ALCINFH_STATUS_SHIFT 3 +#define MAX98926_ALCINFH_STATUS_WIDTH 1 +#define MAX98926_ALCACT_STATUS_MASK (1<<2) +#define MAX98926_ALCACT_STATUS_SHIFT 2 +#define MAX98926_ALCACT_STATUS_WIDTH 1 +#define MAX98926_ALCMUT_STATUS_MASK (1<<1) +#define MAX98926_ALCMUT_STATUS_SHIFT 1 +#define MAX98926_ALCMUT_STATUS_WIDTH 1 +#define MAX98926_ACLP_STATUS_MASK (1<<0) +#define MAX98926_ACLP_STATUS_SHIFT 0 +#define MAX98926_ACLP_STATUS_WIDTH 1 + +/* MAX98926_R004_LIVE_STATUS2 */ +#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATUS_SHIFT 6 +#define MAX98926_SLOTOVRN_STATUS_WIDTH 1 +#define MAX98926_INVALSLOT_STATUS_MASK (1<<5) +#define MAX98926_INVALSLOT_STATUS_SHIFT 5 +#define MAX98926_INVALSLOT_STATUS_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1 +#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATUS_SHIFT 3 +#define MAX98926_VBSTOVFL_STATUS_WIDTH 1 +#define MAX98926_VBATOVFL_STATUS_MASK (1<<2) +#define MAX98926_VBATOVFL_STATUS_SHIFT 2 +#define MAX98926_VBATOVFL_STATUS_WIDTH 1 +#define MAX98926_IMONOVFL_STATUS_MASK (1<<1) +#define MAX98926_IMONOVFL_STATUS_SHIFT 1 +#define MAX98926_IMONOVFL_STATUS_WIDTH 1 +#define MAX98926_VMONOVFL_STATUS_MASK (1<<0) +#define MAX98926_VMONOVFL_STATUS_SHIFT 0 +#define MAX98926_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98926_R005_STATE0 */ +#define MAX98926_THERMWARN_END_STATE_MASK (1<<3) +#define MAX98926_THERMWARN_END_STATE_SHIFT 3 +#define MAX98926_THERMWARN_END_STATE_WIDTH 1 +#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1 +#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1) +#define MAX98926_THERMSHDN_END_STATE_SHIFT 1 +#define MAX98926_THERMSHDN_END_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98926_R006_STATE1 */ +#define MAX98926_SPRCURNT_STATE_MASK (1<<5) +#define MAX98926_SPRCURNT_STATE_SHIFT 5 +#define MAX98926_SPRCURNT_STATE_WIDTH 1 +#define MAX98926_WATCHFAIL_STATE_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATE_SHIFT 4 +#define MAX98926_WATCHFAIL_STATE_WIDTH 1 +#define MAX98926_ALCINFH_STATE_MASK (1<<3) +#define MAX98926_ALCINFH_STATE_SHIFT 3 +#define MAX98926_ALCINFH_STATE_WIDTH 1 +#define MAX98926_ALCACT_STATE_MASK (1<<2) +#define MAX98926_ALCACT_STATE_SHIFT 2 +#define MAX98926_ALCACT_STATE_WIDTH 1 +#define MAX98926_ALCMUT_STATE_MASK (1<<1) +#define MAX98926_ALCMUT_STATE_SHIFT 1 +#define MAX98926_ALCMUT_STATE_WIDTH 1 +#define MAX98926_ALCP_STATE_MASK (1<<0) +#define MAX98926_ALCP_STATE_SHIFT 0 +#define MAX98926_ALCP_STATE_WIDTH 1 + +/* MAX98926_R007_STATE2 */ +#define MAX98926_SLOTOVRN_STATE_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATE_SHIFT 6 +#define MAX98926_SLOTOVRN_STATE_WIDTH 1 +#define MAX98926_INVALSLOT_STATE_MASK (1<<5) +#define MAX98926_INVALSLOT_STATE_SHIFT 5 +#define MAX98926_INVALSLOT_STATE_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATE_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATE_WIDTH 1 +#define MAX98926_VBSTOVFL_STATE_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATE_SHIFT 3 +#define MAX98926_VBSTOVFL_STATE_WIDTH 1 +#define MAX98926_VBATOVFL_STATE_MASK (1<<2) +#define MAX98926_VBATOVFL_STATE_SHIFT 2 +#define MAX98926_VBATOVFL_STATE_WIDTH 1 +#define MAX98926_IMONOVFL_STATE_MASK (1<<1) +#define MAX98926_IMONOVFL_STATE_SHIFT 1 +#define MAX98926_IMONOVFL_STATE_WIDTH 1 +#define MAX98926_VMONOVFL_STATE_MASK (1<<0) +#define MAX98926_VMONOVFL_STATE_SHIFT 0 +#define MAX98926_VMONOVFL_STATE_WIDTH 1 + +/* MAX98926_R008_FLAG0 */ +#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3) +#define MAX98926_THERMWARN_END_FLAG_SHIFT 3 +#define MAX98926_THERMWARN_END_FLAG_WIDTH 1 +#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2 +#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1) +#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1 +#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98926_R009_FLAG1 */ +#define MAX98926_SPKCURNT_FLAG_MASK (1<<5) +#define MAX98926_SPKCURNT_FLAG_SHIFT 5 +#define MAX98926_SPKCURNT_FLAG_WIDTH 1 +#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4) +#define MAX98926_WATCHFAIL_FLAG_SHIFT 4 +#define MAX98926_WATCHFAIL_FLAG_WIDTH 1 +#define MAX98926_ALCINFH_FLAG_MASK (1<<3) +#define MAX98926_ALCINFH_FLAG_SHIFT 3 +#define MAX98926_ALCINFH_FLAG_WIDTH 1 +#define MAX98926_ALCACT_FLAG_MASK (1<<2) +#define MAX98926_ALCACT_FLAG_SHIFT 2 +#define MAX98926_ALCACT_FLAG_WIDTH 1 +#define MAX98926_ALCMUT_FLAG_MASK (1<<1) +#define MAX98926_ALCMUT_FLAG_SHIFT 1 +#define MAX98926_ALCMUT_FLAG_WIDTH 1 +#define MAX98926_ALCP_FLAG_MASK (1<<0) +#define MAX98926_ALCP_FLAG_SHIFT 0 +#define MAX98926_ALCP_FLAG_WIDTH 1 + +/* MAX98926_R00A_FLAG2 */ +#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6) +#define MAX98926_SLOTOVRN_FLAG_SHIFT 6 +#define MAX98926_SLOTOVRN_FLAG_WIDTH 1 +#define MAX98926_INVALSLOT_FLAG_MASK (1<<5) +#define MAX98926_INVALSLOT_FLAG_SHIFT 5 +#define MAX98926_INVALSLOT_FLAG_WIDTH 1 +#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4) +#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4 +#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1 +#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3) +#define MAX98926_VBSTOVFL_FLAG_SHIFT 3 +#define MAX98926_VBSTOVFL_FLAG_WIDTH 1 +#define MAX98926_VBATOVFL_FLAG_MASK (1<<2) +#define MAX98926_VBATOVFL_FLAG_SHIFT 2 +#define MAX98926_VBATOVFL_FLAG_WIDTH 1 +#define MAX98926_IMONOVFL_FLAG_MASK (1<<1) +#define MAX98926_IMONOVFL_FLAG_SHIFT 1 +#define MAX98926_IMONOVFL_FLAG_WIDTH 1 +#define MAX98926_VMONOVFL_FLAG_MASK (1<<0) +#define MAX98926_VMONOVFL_FLAG_SHIFT 0 +#define MAX98926_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98926_R00B_IRQ_ENABLE0 */ +#define MAX98926_THERMWARN_END_EN_MASK (1<<3) +#define MAX98926_THERMWARN_END_EN_SHIFT 3 +#define MAX98926_THERMWARN_END_EN_WIDTH 1 +#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_EN_SHIFT 2 +#define MAX98926_THERMWARN_BGN_EN_WIDTH 1 +#define MAX98926_THERMSHDN_END_EN_MASK (1<<1) +#define MAX98926_THERMSHDN_END_EN_SHIFT 1 +#define MAX98926_THERMSHDN_END_EN_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98926_R00C_IRQ_ENABLE1 */ +#define MAX98926_SPKCURNT_EN_MASK (1<<5) +#define MAX98926_SPKCURNT_EN_SHIFT 5 +#define MAX98926_SPKCURNT_EN_WIDTH 1 +#define MAX98926_WATCHFAIL_EN_MASK (1<<4) +#define MAX98926_WATCHFAIL_EN_SHIFT 4 +#define MAX98926_WATCHFAIL_EN_WIDTH 1 +#define MAX98926_ALCINFH_EN_MASK (1<<3) +#define MAX98926_ALCINFH_EN_SHIFT 3 +#define MAX98926_ALCINFH_EN_WIDTH 1 +#define MAX98926_ALCACT_EN_MASK (1<<2) +#define MAX98926_ALCACT_EN_SHIFT 2 +#define MAX98926_ALCACT_EN_WIDTH 1 +#define MAX98926_ALCMUT_EN_MASK (1<<1) +#define MAX98926_ALCMUT_EN_SHIFT 1 +#define MAX98926_ALCMUT_EN_WIDTH 1 +#define MAX98926_ALCP_EN_MASK (1<<0) +#define MAX98926_ALCP_EN_SHIFT 0 +#define MAX98926_ALCP_EN_WIDTH 1 + +/* MAX98926_R00D_IRQ_ENABLE2 */ +#define MAX98926_SLOTOVRN_EN_MASK (1<<6) +#define MAX98926_SLOTOVRN_EN_SHIFT 6 +#define MAX98926_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_INVALSLOT_EN_MASK (1<<5) +#define MAX98926_INVALSLOT_EN_SHIFT 5 +#define MAX98926_INVALSLOT_EN_WIDTH 1 +#define MAX98926_SLOTCNFLT_EN_MASK (1<<4) +#define MAX98926_SLOTCNFLT_EN_SHIFT 4 +#define MAX98926_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_VBSTOVFL_EN_MASK (1<<3) +#define MAX98926_VBSTOVFL_EN_SHIFT 3 +#define MAX98926_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_VBATOVFL_EN_MASK (1<<2) +#define MAX98926_VBATOVFL_EN_SHIFT 2 +#define MAX98926_VBATOVFL_EN_WIDTH 1 +#define MAX98926_IMONOVFL_EN_MASK (1<<1) +#define MAX98926_IMONOVFL_EN_SHIFT 1 +#define MAX98926_IMONOVFL_EN_WIDTH 1 +#define MAX98926_VMONOVFL_EN_MASK (1<<0) +#define MAX98926_VMONOVFL_EN_SHIFT 0 +#define MAX98926_VMONOVFL_EN_WIDTH 1 + +/* MAX98926_R00E_IRQ_CLEAR0 */ +#define MAX98926_THERMWARN_END_CLR_MASK (1<<3) +#define MAX98926_THERMWARN_END_CLR_SHIFT 3 +#define MAX98926_THERMWARN_END_CLR_WIDTH 1 +#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2 +#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1) +#define MAX98926_THERMSHDN_END_CLR_SHIFT 1 +#define MAX98926_THERMSHDN_END_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98926_R00F_IRQ_CLEAR1 */ +#define MAX98926_SPKCURNT_CLR_MASK (1<<5) +#define MAX98926_SPKCURNT_CLR_SHIFT 5 +#define MAX98926_SPKCURNT_CLR_WIDTH 1 +#define MAX98926_WATCHFAIL_CLR_MASK (1<<4) +#define MAX98926_WATCHFAIL_CLR_SHIFT 4 +#define MAX98926_WATCHFAIL_CLR_WIDTH 1 +#define MAX98926_ALCINFH_CLR_MASK (1<<3) +#define MAX98926_ALCINFH_CLR_SHIFT 3 +#define MAX98926_ALCINFH_CLR_WIDTH 1 +#define MAX98926_ALCACT_CLR_MASK (1<<2) +#define MAX98926_ALCACT_CLR_SHIFT 2 +#define MAX98926_ALCACT_CLR_WIDTH 1 +#define MAX98926_ALCMUT_CLR_MASK (1<<1) +#define MAX98926_ALCMUT_CLR_SHIFT 1 +#define MAX98926_ALCMUT_CLR_WIDTH 1 +#define MAX98926_ALCP_CLR_MASK (1<<0) +#define MAX98926_ALCP_CLR_SHIFT 0 +#define MAX98926_ALCP_CLR_WIDTH 1 + +/* MAX98926_R010_IRQ_CLEAR2 */ +#define MAX98926_SLOTOVRN_CLR_MASK (1<<6) +#define MAX98926_SLOTOVRN_CLR_SHIFT 6 +#define MAX98926_SLOTOVRN_CLR_WIDTH 1 +#define MAX98926_INVALSLOT_CLR_MASK (1<<5) +#define MAX98926_INVALSLOT_CLR_SHIFT 5 +#define MAX98926_INVALSLOT_CLR_WIDTH 1 +#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4) +#define MAX98926_SLOTCNFLT_CLR_SHIFT 4 +#define MAX98926_SLOTCNFLT_CLR_WIDTH 1 +#define MAX98926_VBSTOVFL_CLR_MASK (1<<3) +#define MAX98926_VBSTOVFL_CLR_SHIFT 3 +#define MAX98926_VBSTOVFL_CLR_WIDTH 1 +#define MAX98926_VBATOVFL_CLR_MASK (1<<2) +#define MAX98926_VBATOVFL_CLR_SHIFT 2 +#define MAX98926_VBATOVFL_CLR_WIDTH 1 +#define MAX98926_IMONOVFL_CLR_MASK (1<<1) +#define MAX98926_IMONOVFL_CLR_SHIFT 1 +#define MAX98926_IMONOVFL_CLR_WIDTH 1 +#define MAX98926_VMONOVFL_CLR_MASK (1<<0) +#define MAX98926_VMONOVFL_CLR_SHIFT 0 +#define MAX98926_VMONOVFL_CLR_WIDTH 1 + +/* MAX98926_R011_MAP0 */ +#define MAX98926_ER_THERMWARN_EN_MASK (1<<7) +#define MAX98926_ER_THERMWARN_EN_SHIFT 7 +#define MAX98926_ER_THERMWARN_EN_WIDTH 1 +#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4) +#define MAX98926_ER_THERMWARN_MAP_SHIFT 4 +#define MAX98926_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98926_R012_MAP1 */ +#define MAX98926_ER_ALCMUT_EN_MASK (1<<7) +#define MAX98926_ER_ALCMUT_EN_SHIFT 7 +#define MAX98926_ER_ALCMUT_EN_WIDTH 1 +#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCMUT_MAP_SHIFT 4 +#define MAX98926_ER_ALCMUT_MAP_WIDTH 3 +#define MAX98926_ER_ALCP_EN_MASK (1<<3) +#define MAX98926_ER_ALCP_EN_SHIFT 3 +#define MAX98926_ER_ALCP_EN_WIDTH 1 +#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCP_MAP_SHIFT 0 +#define MAX98926_ER_ALCP_MAP_WIDTH 3 + +/* MAX98926_R013_MAP2 */ +#define MAX98926_ER_ALCINFH_EN_MASK (1<<7) +#define MAX98926_ER_ALCINFH_EN_SHIFT 7 +#define MAX98926_ER_ALCINFH_EN_WIDTH 1 +#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCINFH_MAP_SHIFT 4 +#define MAX98926_ER_ALCINFH_MAP_WIDTH 3 +#define MAX98926_ER_ALCACT_EN_MASK (1<<3) +#define MAX98926_ER_ALCACT_EN_SHIFT 3 +#define MAX98926_ER_ALCACT_EN_WIDTH 1 +#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCACT_MAP_SHIFT 0 +#define MAX98926_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98926_R014_MAP3 */ +#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7) +#define MAX98926_ER_SPKCURNT_EN_SHIFT 7 +#define MAX98926_ER_SPKCURNT_EN_WIDTH 1 +#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4 +#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98926_R015_MAP4 */ +/* RESERVED */ + +/* MAX98926_R016_MAP5 */ +#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7) +#define MAX98926_ER_IMONOVFL_EN_SHIFT 7 +#define MAX98926_ER_IMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4 +#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VMONOVFL_EN_SHIFT 3 +#define MAX98926_ER_VMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98926_R017_MAP6 */ +#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7) +#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7 +#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4 +#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VBATOVFL_EN_SHIFT 3 +#define MAX98926_ER_VBATOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98926_R018_MAP7 */ +#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7) +#define MAX98926_ER_INVALSLOT_EN_SHIFT 7 +#define MAX98926_ER_INVALSLOT_EN_WIDTH 1 +#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4 +#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3 +#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3) +#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3 +#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0 +#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98926_R019_MAP8 */ +#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3) +#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3 +#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0 +#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98926_R01A_DAI_CLK_MODE1 */ +#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6) +#define MAX98926_DAI_CLK_SOURCE_SHIFT 6 +#define MAX98926_DAI_CLK_SOURCE_WIDTH 1 +#define MAX98926_MDLL_MULT_MASK (0x0F<<0) +#define MAX98926_MDLL_MULT_SHIFT 0 +#define MAX98926_MDLL_MULT_WIDTH 4 + +#define MAX98926_MDLL_MULT_MCLKx8 6 +#define MAX98926_MDLL_MULT_MCLKx16 8 + +/* MAX98926_R01B_DAI_CLK_MODE2 */ +#define MAX98926_DAI_SR_MASK (0x0F<<4) +#define MAX98926_DAI_SR_SHIFT 4 +#define MAX98926_DAI_SR_WIDTH 4 +#define MAX98926_DAI_MAS_MASK (1<<3) +#define MAX98926_DAI_MAS_SHIFT 3 +#define MAX98926_DAI_MAS_WIDTH 1 +#define MAX98926_DAI_BSEL_MASK (0x07<<0) +#define MAX98926_DAI_BSEL_SHIFT 0 +#define MAX98926_DAI_BSEL_WIDTH 3 + +#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT) + +/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */ +#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_MSBS_SHIFT 0 +#define MAX98926_DAI_M_MSBS_WIDTH 8 + +/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */ +#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_LSBS_SHIFT 0 +#define MAX98926_DAI_M_LSBS_WIDTH 8 + +/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */ +#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0) +#define MAX98926_DAI_N_MSBS_SHIFT 0 +#define MAX98926_DAI_N_MSBS_WIDTH 7 + +/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */ +#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_N_LSBS_SHIFT 0 +#define MAX98926_DAI_N_LSBS_WIDTH 8 + +/* MAX98926_R020_FORMAT */ +#define MAX98926_DAI_CHANSZ_MASK (0x03<<6) +#define MAX98926_DAI_CHANSZ_SHIFT 6 +#define MAX98926_DAI_CHANSZ_WIDTH 2 +#define MAX98926_DAI_INTERLEAVE_MASK (1<<5) +#define MAX98926_DAI_INTERLEAVE_SHIFT 5 +#define MAX98926_DAI_INTERLEAVE_WIDTH 1 +#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4 +#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1 +#define MAX98926_DAI_WCI_MASK (1<<3) +#define MAX98926_DAI_WCI_SHIFT 3 +#define MAX98926_DAI_WCI_WIDTH 1 +#define MAX98926_DAI_BCI_MASK (1<<2) +#define MAX98926_DAI_BCI_SHIFT 2 +#define MAX98926_DAI_BCI_WIDTH 1 +#define MAX98926_DAI_DLY_MASK (1<<1) +#define MAX98926_DAI_DLY_SHIFT 1 +#define MAX98926_DAI_DLY_WIDTH 1 +#define MAX98926_DAI_TDM_MASK (1<<0) +#define MAX98926_DAI_TDM_SHIFT 0 +#define MAX98926_DAI_TDM_WIDTH 1 + +#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT) + +/* MAX98926_R021_TDM_SLOT_SELECT */ +#define MAX98926_DAI_DO_EN_MASK (1<<7) +#define MAX98926_DAI_DO_EN_SHIFT 7 +#define MAX98926_DAI_DO_EN_WIDTH 1 +#define MAX98926_DAI_DIN_EN_MASK (1<<6) +#define MAX98926_DAI_DIN_EN_SHIFT 6 +#define MAX98926_DAI_DIN_EN_WIDTH 1 +#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3) +#define MAX98926_DAI_INR_SOURCE_SHIFT 3 +#define MAX98926_DAI_INR_SOURCE_WIDTH 3 +#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0) +#define MAX98926_DAI_INL_SOURCE_SHIFT 0 +#define MAX98926_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98926_R022_DOUT_CFG_VMON */ +#define MAX98926_DAI_VMON_EN_MASK (1<<5) +#define MAX98926_DAI_VMON_EN_SHIFT 5 +#define MAX98926_DAI_VMON_EN_WIDTH 1 +#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VMON_SLOT_SHIFT 0 +#define MAX98926_DAI_VMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT) + +/* MAX98926_R023_DOUT_CFG_IMON */ +#define MAX98926_DAI_IMON_EN_MASK (1<<5) +#define MAX98926_DAI_IMON_EN_SHIFT 5 +#define MAX98926_DAI_IMON_EN_WIDTH 1 +#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_IMON_SLOT_SHIFT 0 +#define MAX98926_DAI_IMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT) + +/* MAX98926_R024_DOUT_CFG_VBAT */ +#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0 +#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5 + +/* MAX98926_R025_DOUT_CFG_VBST */ +#define MAX98926_DAI_VBST_EN_MASK (1<<5) +#define MAX98926_DAI_VBST_EN_SHIFT 5 +#define MAX98926_DAI_VBST_EN_WIDTH 1 +#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VBST_SLOT_SHIFT 0 +#define MAX98926_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98926_R026_DOUT_CFG_FLAG */ +#define MAX98926_DAI_FLAG_EN_MASK (1<<5) +#define MAX98926_DAI_FLAG_EN_SHIFT 5 +#define MAX98926_DAI_FLAG_EN_WIDTH 1 +#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_FLAG_SLOT_SHIFT 0 +#define MAX98926_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98926_R027_DOUT_HIZ_CFG1 */ +#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98926_R028_DOUT_HIZ_CFG2 */ +#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98926_R029_DOUT_HIZ_CFG3 */ +#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98926_R02A_DOUT_HIZ_CFG4 */ +#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98926_R02B_DOUT_DRV_STRENGTH */ +#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0) +#define MAX98926_DAI_OUT_DRIVE_SHIFT 0 +#define MAX98926_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98926_R02C_FILTERS */ +#define MAX98926_ADC_DITHER_EN_MASK (1<<7) +#define MAX98926_ADC_DITHER_EN_SHIFT 7 +#define MAX98926_ADC_DITHER_EN_WIDTH 1 +#define MAX98926_IV_DCB_EN_MASK (1<<6) +#define MAX98926_IV_DCB_EN_SHIFT 6 +#define MAX98926_IV_DCB_EN_WIDTH 1 +#define MAX98926_DAC_DITHER_EN_MASK (1<<4) +#define MAX98926_DAC_DITHER_EN_SHIFT 4 +#define MAX98926_DAC_DITHER_EN_WIDTH 1 +#define MAX98926_DAC_FILTER_MODE_MASK (1<<3) +#define MAX98926_DAC_FILTER_MODE_SHIFT 3 +#define MAX98926_DAC_FILTER_MODE_WIDTH 1 +#define MAX98926_DAC_HPF_MASK (0x07<<0) +#define MAX98926_DAC_HPF_SHIFT 0 +#define MAX98926_DAC_HPF_WIDTH 3 +#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT) + +/* MAX98926_R02D_GAIN */ +#define MAX98926_DAC_IN_SEL_MASK (0x03<<5) +#define MAX98926_DAC_IN_SEL_SHIFT 5 +#define MAX98926_DAC_IN_SEL_WIDTH 2 +#define MAX98926_SPK_GAIN_MASK (0x1F<<0) +#define MAX98926_SPK_GAIN_SHIFT 0 +#define MAX98926_SPK_GAIN_WIDTH 5 + +#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT) + +/* MAX98926_R02E_GAIN_RAMPING */ +#define MAX98926_SPK_RMP_EN_MASK (1<<1) +#define MAX98926_SPK_RMP_EN_SHIFT 1 +#define MAX98926_SPK_RMP_EN_WIDTH 1 +#define MAX98926_SPK_ZCD_EN_MASK (1<<0) +#define MAX98926_SPK_ZCD_EN_SHIFT 0 +#define MAX98926_SPK_ZCD_EN_WIDTH 1 + +/* MAX98926_R02F_SPK_AMP */ +#define MAX98926_SPK_MODE_MASK (1<<0) +#define MAX98926_SPK_MODE_SHIFT 0 +#define MAX98926_SPK_MODE_WIDTH 1 +#define MAX98926_INSELECT_MODE_MASK (1<<1) +#define MAX98926_INSELECT_MODE_SHIFT 1 +#define MAX98926_INSELECT_MODE_WIDTH 1 + +/* MAX98926_R030_THRESHOLD */ +#define MAX98926_ALC_EN_MASK (1<<5) +#define MAX98926_ALC_EN_SHIFT 5 +#define MAX98926_ALC_EN_WIDTH 1 +#define MAX98926_ALC_TH_MASK (0x1F<<0) +#define MAX98926_ALC_TH_SHIFT 0 +#define MAX98926_ALC_TH_WIDTH 5 + +/* MAX98926_R031_ALC_ATTACK */ +#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4) +#define MAX98926_ALC_ATK_STEP_SHIFT 4 +#define MAX98926_ALC_ATK_STEP_WIDTH 4 +#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0) +#define MAX98926_ALC_ATK_RATE_SHIFT 0 +#define MAX98926_ALC_ATK_RATE_WIDTH 3 + +/* MAX98926_R032_ALC_ATTEN_RLS */ +#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define MAX98926_ALC_MAX_ATTEN_SHIFT 4 +#define MAX98926_ALC_MAX_ATTEN_WIDTH 4 +#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0) +#define MAX98926_ALC_RLS_RATE_SHIFT 0 +#define MAX98926_ALC_RLS_RATE_WIDTH 3 + +/* MAX98926_R033_ALC_HOLD_RLS */ +#define MAX98926_ALC_RLS_TGR_MASK (1<<0) +#define MAX98926_ALC_RLS_TGR_SHIFT 0 +#define MAX98926_ALC_RLS_TGR_WIDTH 1 + +/* MAX98926_R034_ALC_CONFIGURATION */ +#define MAX98926_ALC_MUTE_EN_MASK (1<<7) +#define MAX98926_ALC_MUTE_EN_SHIFT 7 +#define MAX98926_ALC_MUTE_EN_WIDTH 1 +#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4) +#define MAX98926_ALC_MUTE_DLY_SHIFT 4 +#define MAX98926_ALC_MUTE_DLY_WIDTH 3 +#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0) +#define MAX98926_ALC_RLS_DBT_SHIFT 0 +#define MAX98926_ALC_RLS_DBT_WIDTH 3 + +/* MAX98926_R035_BOOST_CONVERTER */ +#define MAX98926_BST_SYNC_MASK (1<<7) +#define MAX98926_BST_SYNC_SHIFT 7 +#define MAX98926_BST_SYNC_WIDTH 1 +#define MAX98926_BST_PHASE_MASK (0x03<<4) +#define MAX98926_BST_PHASE_SHIFT 4 +#define MAX98926_BST_PHASE_WIDTH 2 +#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0) +#define MAX98926_BST_SKIP_MODE_SHIFT 0 +#define MAX98926_BST_SKIP_MODE_WIDTH 2 + +/* MAX98926_R036_BLOCK_ENABLE */ +#define MAX98926_BST_EN_MASK (1<<7) +#define MAX98926_BST_EN_SHIFT 7 +#define MAX98926_BST_EN_WIDTH 1 +#define MAX98926_WATCH_EN_MASK (1<<6) +#define MAX98926_WATCH_EN_SHIFT 6 +#define MAX98926_WATCH_EN_WIDTH 1 +#define MAX98926_CLKMON_EN_MASK (1<<5) +#define MAX98926_CLKMON_EN_SHIFT 5 +#define MAX98926_CLKMON_EN_WIDTH 1 +#define MAX98926_SPK_EN_MASK (1<<4) +#define MAX98926_SPK_EN_SHIFT 4 +#define MAX98926_SPK_EN_WIDTH 1 +#define MAX98926_ADC_VBST_EN_MASK (1<<3) +#define MAX98926_ADC_VBST_EN_SHIFT 3 +#define MAX98926_ADC_VBST_EN_WIDTH 1 +#define MAX98926_ADC_VBAT_EN_MASK (1<<2) +#define MAX98926_ADC_VBAT_EN_SHIFT 2 +#define MAX98926_ADC_VBAT_EN_WIDTH 1 +#define MAX98926_ADC_IMON_EN_MASK (1<<1) +#define MAX98926_ADC_IMON_EN_SHIFT 1 +#define MAX98926_ADC_IMON_EN_WIDTH 1 +#define MAX98926_ADC_VMON_EN_MASK (1<<0) +#define MAX98926_ADC_VMON_EN_SHIFT 0 +#define MAX98926_ADC_VMON_EN_WIDTH 1 + +/* MAX98926_R037_CONFIGURATION */ +#define MAX98926_BST_VOUT_MASK (0x0F<<4) +#define MAX98926_BST_VOUT_SHIFT 4 +#define MAX98926_BST_VOUT_WIDTH 4 +#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2) +#define MAX98926_THERMWARN_LEVEL_SHIFT 2 +#define MAX98926_THERMWARN_LEVEL_WIDTH 2 +#define MAX98926_WATCH_TIME_MASK (0x03<<0) +#define MAX98926_WATCH_TIME_SHIFT 0 +#define MAX98926_WATCH_TIME_WIDTH 2 + +/* MAX98926_R038_GLOBAL_ENABLE */ +#define MAX98926_EN_MASK (1<<7) +#define MAX98926_EN_SHIFT 7 +#define MAX98926_EN_WIDTH 1 + +/* MAX98926_R03A_BOOST_LIMITER */ +#define MAX98926_BST_ILIM_MASK (0xF<<4) +#define MAX98926_BST_ILIM_SHIFT 4 +#define MAX98926_BST_ILIM_WIDTH 4 + +/* MAX98926_R0FF_VERSION */ +#define MAX98926_REV_ID_MASK (0xFF<<0) +#define MAX98926_REV_ID_SHIFT 0 +#define MAX98926_REV_ID_WIDTH 8 + +struct max98926_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int ch_size; + unsigned int interleave_mode; +}; +#endif -- cgit v1.2.3 From 99081589c5ad590e9828ae9febc384612f241164 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 28 Jan 2016 16:43:36 +0800 Subject: ASoC: rt5616: trivial: fix the typo This patch try to fix the trivial typo. Run "scripts/checkpatch.pl -f --subjective xxx" The enable more subjective tests. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 376 +++++++++++++++++++++++----------------------- 1 file changed, 191 insertions(+), 185 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 1c10d8ed39d2..d4bdf9f6f0af 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -53,6 +53,7 @@ static const struct reg_sequence init_list[] = { {RT5616_PR_BASE + 0x21, 0x4040}, {RT5616_PR_BASE + 0x23, 0x0004}, }; + #define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5616_reg[] = { @@ -162,9 +163,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -190,9 +190,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -307,45 +306,45 @@ static unsigned int bst_tlv[] = { static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1, - RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 175, 0, dac_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 175, 0, dac_vol_tlv), /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT1, 8, 0, bst_tlv), + RT5616_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT2, 8, 0, bst_tlv), + RT5616_BST_SFT2, 8, 0, bst_tlv), /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL, - RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, - 31, 1, in_vol_tlv), + RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, + 31, 1, in_vol_tlv), /* ADC Digital Volume Control */ SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 127, 0, adc_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 127, 0, adc_vol_tlv), /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL, - RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, - 3, 0, adc_bst_tlv), + RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), }; static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) + struct snd_soc_dapm_widget *sink) { unsigned int val; @@ -462,20 +461,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = { }; static int rt5616_adc_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); break; default: @@ -486,7 +485,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w, } static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -494,54 +493,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* depop parameters */ snd_soc_update_bits(codec, RT5616_DEPOP_M2, - RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); + RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | - RT5616_HP_CB_MASK, RT5616_HP_CP_PU | - RT5616_HP_SG_DIS | RT5616_HP_CB_PU); + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, RT5616_HP_CP_PU | + RT5616_HP_SG_DIS | RT5616_HP_CB_PU); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_HP_DCC_INT1, 0x9f00); + RT5616_HP_DCC_INT1, 0x9f00); /* headphone amp power on */ snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); + RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); snd_soc_update_bits(codec, RT5616_PWR_VOL, - RT5616_PWR_HV_L | RT5616_PWR_HV_R, - RT5616_PWR_HV_L | RT5616_PWR_HV_R); + RT5616_PWR_HV_L | RT5616_PWR_HV_R, + RT5616_PWR_HV_L | RT5616_PWR_HV_R); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, RT5616_PWR_HP_L | - RT5616_PWR_HP_R | RT5616_PWR_HA); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, RT5616_PWR_HP_L | + RT5616_PWR_HP_R | RT5616_PWR_HA); msleep(50); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_CHARGE_PUMP, - RT5616_PM_HP_MASK, RT5616_PM_HP_HV); + RT5616_PM_HP_MASK, RT5616_PM_HP_HV); snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, - RT5616_HP_CO_EN | RT5616_HP_SG_EN); + RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, + RT5616_HP_CO_EN | RT5616_HP_SG_EN); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); /* headphone amp power down */ snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK | - RT5616_HP_CO_MASK | RT5616_HP_CP_MASK | - RT5616_HP_SG_MASK | RT5616_HP_CB_MASK, - RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | - RT5616_HP_CO_DIS | RT5616_HP_CP_PD | - RT5616_HP_SG_EN | RT5616_HP_CB_PD); + RT5616_SMT_TRIG_MASK | + RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK | + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, + RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | + RT5616_HP_CO_DIS | RT5616_HP_CP_PD | + RT5616_HP_SG_EN | RT5616_HP_CB_PD); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, 0); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, 0); break; default: return 0; @@ -551,7 +551,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, } static int rt5616_hp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -559,57 +559,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* headphone unmute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); + RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK, RT5616_RSTN_EN); + RT5616_RSTN_MASK, RT5616_RSTN_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); msleep(100); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); msleep(20); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); break; case SND_SOC_DAPM_PRE_PMD: /* headphone mute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK, RT5616_HP_SG_EN); + RT5616_HP_SG_MASK, RT5616_HP_SG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK, RT5616_RSTP_EN); + RT5616_RSTP_MASK, RT5616_RSTP_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); msleep(90); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); msleep(30); break; @@ -621,24 +621,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, } static int rt5616_lout_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, RT5616_PWR_LM); + RT5616_PWR_LM, RT5616_PWR_LM); snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, 0); + RT5616_PWR_LM, 0); break; default: @@ -649,19 +649,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); + RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, 0); + RT5616_PWR_BST1_OP2, 0); break; default: @@ -672,19 +672,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); + RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, 0); + RT5616_PWR_BST2_OP2, 0); break; default: @@ -696,13 +696,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2, - RT5616_PWR_PLL_BIT, 0, NULL, 0), + RT5616_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1, - RT5616_PWR_LDO_BIT, 0, NULL, 0), + RT5616_PWR_LDO_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2, - RT5616_PWR_MB1_BIT, 0, NULL, 0), + RT5616_PWR_MB1_BIT, 0, NULL, 0), /* Input Lines */ SND_SOC_DAPM_INPUT("MIC1"), @@ -714,45 +714,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Boost */ SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2, - RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2, - RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), /* Input Volume */ SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* REC Mixer */ SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0, - rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), + rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0, - rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), + rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), /* ADCs */ SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), /* ADC Mixer */ SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2, - RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)), + rt5616_sto1_adc_l_mix, + ARRAY_SIZE(rt5616_sto1_adc_l_mix)), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)), + rt5616_sto1_adc_r_mix, + ARRAY_SIZE(rt5616_sto1_adc_r_mix)), /* Digital Interface */ SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1, - RT5616_PWR_I2S1_BIT, 0, NULL, 0), + RT5616_PWR_I2S1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -770,68 +772,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Output Side */ /* DAC mixer before sound effect */ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), + rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), + rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2, - RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), /* DAC Mixer */ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)), + rt5616_sto_dac_l_mix, + ARRAY_SIZE(rt5616_sto_dac_l_mix)), SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)), + rt5616_sto_dac_r_mix, + ARRAY_SIZE(rt5616_sto_dac_r_mix)), /* DACs */ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_L1_BIT, 0), + RT5616_PWR_DAC_L1_BIT, 0), SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_R1_BIT, 0), + RT5616_PWR_DAC_R1_BIT, 0), /* OUT Mixer */ SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT, - 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), + 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT, - 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), + 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), /* Output Volume */ SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL, - RT5616_PWR_OV_L_BIT, 0, NULL, 0), + RT5616_PWR_OV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL, - RT5616_PWR_OV_R_BIT, 0, NULL, 0), + RT5616_PWR_OV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL, - RT5616_PWR_HV_L_BIT, 0, NULL, 0), + RT5616_PWR_HV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL, - RT5616_PWR_HV_R_BIT, 0, NULL, 0), + RT5616_PWR_HV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* HPO/LOUT/Mono Mixer */ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, - rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), + rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, - rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), + rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0, - rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), + rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), /* Output Lines */ SND_SOC_DAPM_OUTPUT("HPOL"), @@ -950,7 +954,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = { }; static int rt5616_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; @@ -977,7 +982,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", rt5616->bclk[dai->id], rt5616->lrck[dai->id]); dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", - bclk_ms, pre_div, dai->id); + bclk_ms, pre_div, dai->id); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -998,10 +1003,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, mask_clk = RT5616_I2S_PD1_MASK; val_clk = pre_div << RT5616_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_DL_MASK, val_len); + RT5616_I2S_DL_MASK, val_len); snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk); - return 0; } @@ -1050,15 +1054,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | - RT5616_I2S_DF_MASK, reg_val); - + RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | + RT5616_I2S_DF_MASK, reg_val); return 0; } static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) + int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1078,8 +1081,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; } + snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, reg_val); + RT5616_SCLK_SRC_MASK, reg_val); rt5616->sysclk = freq; rt5616->sysclk_src = clk_id; @@ -1089,7 +1093,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, } static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out) + unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1106,19 +1110,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, rt5616->pll_in = 0; rt5616->pll_out = 0; snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK); + RT5616_SCLK_SRC_MASK, + RT5616_SCLK_SRC_MCLK); return 0; } switch (source) { case RT5616_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_MCLK); break; case RT5616_PLL1_S_BCLK1: case RT5616_PLL1_S_BCLK2: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_BCLK1); break; default: dev_err(codec->dev, "Unknown PLL source %d\n", source); @@ -1136,10 +1143,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, pll_code.n_code, pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL1, - pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); + pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL2, - (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT | - pll_code.m_bp << RT5616_PLL_M_BP_SFT); + (pll_code.m_bp ? 0 : pll_code.m_code) << + RT5616_PLL_M_SFT | + pll_code.m_bp << RT5616_PLL_M_BP_SFT); rt5616->pll_in = freq_in; rt5616->pll_out = freq_out; @@ -1149,22 +1157,23 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, } static int rt5616_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) + enum snd_soc_bias_level level) { switch (level) { case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_D_MISC, - RT5616_D_GATE_EN, RT5616_D_GATE_EN); + RT5616_D_GATE_EN, + RT5616_D_GATE_EN); } break; @@ -1222,7 +1231,6 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) - struct snd_soc_dai_ops rt5616_aif_dai_ops = { .hw_params = rt5616_hw_params, .set_fmt = rt5616_set_dai_fmt, @@ -1296,15 +1304,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match); #endif static int rt5616_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct rt5616_priv *rt5616; unsigned int val; int ret; rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv), - GFP_KERNEL); - if (rt5616 == NULL) + GFP_KERNEL); + if (!rt5616) return -ENOMEM; i2c_set_clientdata(i2c, rt5616); @@ -1326,14 +1334,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, } regmap_write(rt5616->regmap, RT5616_RESET, 0); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); ret = regmap_register_patch(rt5616->regmap, init_list, ARRAY_SIZE(init_list)); @@ -1341,11 +1349,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); + RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616, - rt5616_dai, ARRAY_SIZE(rt5616_dai)); - + rt5616_dai, ARRAY_SIZE(rt5616_dai)); } static int rt5616_i2c_remove(struct i2c_client *i2c) @@ -1361,7 +1368,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client) regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8); regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8); - } static struct i2c_driver rt5616_i2c_driver = { -- cgit v1.2.3 From 76d3204eaa9364a662f0fe0f075dfe61e6bd14fe Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 28 Jan 2016 16:43:37 +0800 Subject: ASoC: rt5616: add the mclk for the codec driver This patch adds the code to enable the clock to the CODEC driver if it needs the clock enabled. In some case, We need to claim the clock which is driving the codec so that when we enable clock gating, we continue to clock the codec when needed. if mclk provided, to enable and disable the clock source. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index d4bdf9f6f0af..fdca63694b40 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,7 @@ struct rt5616_priv { struct snd_soc_codec *codec; struct delayed_work patch_work; struct regmap *regmap; + struct clk *mclk; int sysclk; int sysclk_src; @@ -1159,7 +1161,34 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5616_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { + + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5616->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5616->mclk); + } else { + ret = clk_prepare_enable(rt5616->mclk); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5616_PWR_ANLG1, @@ -1198,6 +1227,11 @@ static int rt5616_probe(struct snd_soc_codec *codec) { struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + /* Check if MCLK provided */ + rt5616->mclk = devm_clk_get(codec->dev, "mclk"); + if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + rt5616->codec = codec; return 0; -- cgit v1.2.3 From f1188b898ec5aa2e94da8fb998648a2738f4fb3b Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Thu, 28 Jan 2016 16:54:20 +0000 Subject: ASoC: pcm3168a: Call clk_set_rate in pcm3168a_set_dai_sysclk Call clk_set_rate in pcm3168a_set_dai_sysclk Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 44b268aa4dd8..0c7248ab6a37 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -299,10 +299,15 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec); + int ret; if (freq > PCM1368A_MAX_SYSCLK) return -EINVAL; + ret = clk_set_rate(pcm3168a->scki, freq); + if (ret) + return ret; + pcm3168a->sysclk = freq; return 0; -- cgit v1.2.3 From 0f9aa09d669b29ae536d51631bf035ad44ae0551 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:52:57 +0000 Subject: ASoC: wm5110: Fix up snd_soc_register_platform error path Whilst there is nothing wrong with the error path here it looks a little bit odd, this patches makes the code a little more idiomatic. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 6088d30962a9..83760b72aa03 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2366,7 +2366,7 @@ static int wm5110_probe(struct platform_device *pdev) ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform); if (ret < 0) { dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); - goto error; + return ret; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, @@ -2376,7 +2376,6 @@ static int wm5110_probe(struct platform_device *pdev) snd_soc_unregister_platform(&pdev->dev); } -error: return ret; } -- cgit v1.2.3 From e6d00f3403c439b8d1e6f9f09d5ae3048aa726e4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:52:58 +0000 Subject: ASoC: wm_adsp: Use more generic naming for regions and caps definitions The region definition will be shared by most firmwares so give this a more generic name and whilst we are there improve the naming of the voice control capabilities array as well. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 33806d487b8a..0392c58dfc5d 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -328,7 +328,7 @@ struct wm_adsp_buffer_region_def { unsigned int size_offset; }; -static struct wm_adsp_buffer_region_def ez2control_regions[] = { +static struct wm_adsp_buffer_region_def default_regions[] = { { .mem_type = WMFW_ADSP2_XM, .base_offset = HOST_BUFFER_FIELD(X_buf_base), @@ -353,7 +353,7 @@ struct wm_adsp_fw_caps { struct wm_adsp_buffer_region_def *region_defs; }; -static const struct wm_adsp_fw_caps ez2control_caps[] = { +static const struct wm_adsp_fw_caps ctrl_caps[] = { { .id = SND_AUDIOCODEC_BESPOKE, .desc = { @@ -362,8 +362,8 @@ static const struct wm_adsp_fw_caps ez2control_caps[] = { .num_sample_rates = 1, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .num_regions = ARRAY_SIZE(ez2control_regions), - .region_defs = ez2control_regions, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, }, }; @@ -382,8 +382,8 @@ static const struct { [WM_ADSP_FW_CTRL] = { .file = "ctrl", .compr_direction = SND_COMPRESS_CAPTURE, - .num_caps = ARRAY_SIZE(ez2control_caps), - .caps = ez2control_caps, + .num_caps = ARRAY_SIZE(ctrl_caps), + .caps = ctrl_caps, }, [WM_ADSP_FW_ASR] = { .file = "asr" }, [WM_ADSP_FW_TRACE] = { .file = "trace" }, -- cgit v1.2.3 From 7ce4283ca405d459c186960ed39d41ec0e6fb2bf Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:52:59 +0000 Subject: ASoC: wm_adsp: Add debug audio trace firmware The audio trace firmware allows the capture of arbitrary streams of audio from the DSP and commonly used for debugging other firmwares. This patch adds support for this firwmare into the ADSP driver. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 0392c58dfc5d..a81f568a9660 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -367,6 +367,24 @@ static const struct wm_adsp_fw_caps ctrl_caps[] = { }, }; +static const struct wm_adsp_fw_caps trace_caps[] = { + { + .id = SND_AUDIOCODEC_BESPOKE, + .desc = { + .max_ch = 8, + .sample_rates = { + 4000, 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, 88200, + 96000, 176400, 192000 + }, + .num_sample_rates = 15, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, + }, +}; + static const struct { const char *file; int compr_direction; @@ -386,7 +404,12 @@ static const struct { .caps = ctrl_caps, }, [WM_ADSP_FW_ASR] = { .file = "asr" }, - [WM_ADSP_FW_TRACE] = { .file = "trace" }, + [WM_ADSP_FW_TRACE] = { + .file = "trace", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(trace_caps), + .caps = trace_caps, + }, [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, [WM_ADSP_FW_MISC] = { .file = "misc" }, }; -- cgit v1.2.3 From d82d767f0e61da5f1f872d40358904eb316af264 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:53:02 +0000 Subject: ASoC: wm_adsp: Move setting of DSP speed into CODEC specific code The ADSP code should be agnostic of which CODEC it runs upon, currently there is only one remaining part of the implementation that doesn't follow this. When the DSP is booted on ADSP2 we read ARIZONA_SYSTEM_CLOCK_1 and use that to set the initial speed for the DSP clock. This patch factors that out into CODEC specific code, leaving the ADSP code entirely CODEC agnostic. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l24.c | 23 +++++++++++++++++++++-- sound/soc/codecs/wm5102.c | 6 +++--- sound/soc/codecs/wm5110.c | 27 +++++++++++++++++++++++---- sound/soc/codecs/wm_adsp.c | 40 +++++++++++++++------------------------- sound/soc/codecs/wm_adsp.h | 8 +++----- 5 files changed, 65 insertions(+), 39 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index dc5ae7f7a1bd..9dc77a4da35d 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = { cs47l24_dsp3_regions, }; +static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + return wm_adsp2_early_event(w, kcontrol, event, v); +} + static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); @@ -405,8 +424,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), -WM_ADSP2("DSP2", 1), -WM_ADSP2("DSP3", 2), +WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev), SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 64637d1cf4e5..7c0860df3204 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -619,7 +619,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - unsigned int v; + unsigned int v = 0; int ret; switch (event) { @@ -654,7 +654,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, break; } - return wm_adsp2_early_event(w, kcontrol, event); + return wm_adsp2_early_event(w, kcontrol, event, v); } static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, @@ -1408,7 +1408,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), -WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), +WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev), SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 83760b72aa03..b47bc4418e30 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -191,6 +191,25 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + return wm_adsp2_early_event(w, kcontrol, event, v); +} + static const struct reg_sequence wm5110_no_dre_left_enable[] = { { 0x3024, 0xE410 }, { 0x3025, 0x0056 }, @@ -1179,10 +1198,10 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), -WM_ADSP2("DSP1", 0), -WM_ADSP2("DSP2", 1), -WM_ADSP2("DSP3", 2), -WM_ADSP2("DSP4", 3), +WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev), +WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev), +WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev), +WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev), SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index a81f568a9660..76ab52d2c670 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -32,9 +32,6 @@ #include #include -#include - -#include "arizona.h" #include "wm_adsp.h" #define adsp_crit(_dsp, fmt, ...) \ @@ -2146,30 +2143,9 @@ static void wm_adsp2_boot_work(struct work_struct *work) struct wm_adsp, boot_work); int ret; - unsigned int val; mutex_lock(&dsp->pwr_lock); - /* - * For simplicity set the DSP clock rate to be the - * SYSCLK rate rather than making it configurable. - */ - ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); - goto err_mutex; - } - val = (val & ARIZONA_SYSCLK_FREQ_MASK) - >> ARIZONA_SYSCLK_FREQ_SHIFT; - - ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, val); - if (ret != 0) { - adsp_err(dsp, "Failed to set clock rate: %d\n", ret); - goto err_mutex; - } - ret = wm_adsp2_ena(dsp); if (ret != 0) goto err_mutex; @@ -2209,8 +2185,21 @@ err_mutex: mutex_unlock(&dsp->pwr_lock); } +static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) +{ + int ret; + + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CLOCKING, + ADSP2_CLK_SEL_MASK, + freq << ADSP2_CLK_SEL_SHIFT); + if (ret != 0) + adsp_err(dsp, "Failed to set clock rate: %d\n", ret); +} + int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event, + unsigned int freq) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); @@ -2220,6 +2209,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + wm_adsp2_set_dspclk(dsp, freq); queue_work(system_unbound_wq, &dsp->boot_work); break; default: diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 1a928ec54741..b61cb57e600f 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -80,7 +80,7 @@ struct wm_adsp { SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) -#define WM_ADSP2_E(wname, num, event_fn) \ +#define WM_ADSP2(wname, num, event_fn) \ { .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ @@ -88,9 +88,6 @@ struct wm_adsp { .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } -#define WM_ADSP2(wname, num) \ - WM_ADSP2_E(wname, num, wm_adsp2_early_event) - extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); @@ -100,7 +97,8 @@ int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); + struct snd_kcontrol *kcontrol, int event, + unsigned int freq); int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v1.2.3 From 1b347b6891335a30131899dd16ec6ca843c578f0 Mon Sep 17 00:00:00 2001 From: Jacob Siverskog Date: Fri, 22 Jan 2016 13:39:53 +0100 Subject: ASoC: pcm179x: Split into core and SPI parts The pcm179x family supports both SPI and I2C for configuration. This patch splits the driver into core and SPI parts, in preparation for I2C support. Signed-off-by: Jacob Siverskog Reviewed-by: Johan Hovold Acked-by: Michael Trimarchi Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 11 +++++-- sound/soc/codecs/Makefile | 2 ++ sound/soc/codecs/pcm179x-spi.c | 72 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/pcm179x.c | 52 +++++++----------------------- sound/soc/codecs/pcm179x.h | 5 +++ 5 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 sound/soc/codecs/pcm179x-spi.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..ae720f1b15e8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -87,7 +87,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ML26124 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C - select SND_SOC_PCM179X if SPI_MASTER + select SND_SOC_PCM179X_SPI if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER @@ -527,8 +527,15 @@ config SND_SOC_PCM1681 depends on I2C config SND_SOC_PCM179X - tristate "Texas Instruments PCM179X CODEC" + tristate + +config SND_SOC_PCM179X_SPI + tristate "Texas Instruments PCM179X CODEC (SPI)" depends on SPI_MASTER + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an SPI bus. config SND_SOC_PCM3008 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d44f7d347183..56e94d88693f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -81,6 +81,7 @@ snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o +snd-soc-pcm179x-spi-objs := pcm179x-spi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o @@ -285,6 +286,7 @@ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o +obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c new file mode 100644 index 000000000000..da924d444083 --- /dev/null +++ b/sound/soc/codecs/pcm179x-spi.c @@ -0,0 +1,72 @@ +/* + * PCM179X ASoC SPI driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "pcm179x.h" + +static int pcm179x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&spi->dev, regmap); +} + +static int pcm179x_spi_remove(struct spi_device *spi) +{ + return pcm179x_common_exit(&spi->dev); +} + +static const struct of_device_id pcm179x_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); + +static const struct spi_device_id pcm179x_spi_ids[] = { + { "pcm179x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids); + +static struct spi_driver pcm179x_spi_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_spi_ids, + .probe = pcm179x_spi_probe, + .remove = pcm179x_spi_remove, +}; + +module_spi_driver(pcm179x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM179X SPI driver"); +MODULE_AUTHOR("Michael Trimarchi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index a56c7b767d90..8f20f70af23e 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -29,7 +28,6 @@ #include #include #include -#include #include "pcm179x.h" @@ -194,13 +192,7 @@ static struct snd_soc_dai_driver pcm179x_dai = { .ops = &pcm179x_dai_ops, }; -static const struct of_device_id pcm179x_of_match[] = { - { .compatible = "ti,pcm1792a", }, - { } -}; -MODULE_DEVICE_TABLE(of, pcm179x_of_match); - -static const struct regmap_config pcm179x_regmap = { +const struct regmap_config pcm179x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 23, @@ -209,6 +201,7 @@ static const struct regmap_config pcm179x_regmap = { .writeable_reg = pcm179x_writeable_reg, .readable_reg = pcm179x_accessible_reg, }; +EXPORT_SYMBOL_GPL(pcm179x_regmap_config); static struct snd_soc_codec_driver soc_codec_dev_pcm179x = { .controls = pcm179x_controls, @@ -219,52 +212,29 @@ static struct snd_soc_codec_driver soc_codec_dev_pcm179x = { .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes), }; -static int pcm179x_spi_probe(struct spi_device *spi) +int pcm179x_common_init(struct device *dev, struct regmap *regmap) { struct pcm179x_private *pcm179x; - int ret; - pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private), + pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private), GFP_KERNEL); if (!pcm179x) return -ENOMEM; - spi_set_drvdata(spi, pcm179x); - - pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap); - if (IS_ERR(pcm179x->regmap)) { - ret = PTR_ERR(pcm179x->regmap); - dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); - return ret; - } + pcm179x->regmap = regmap; + dev_set_drvdata(dev, pcm179x); - return snd_soc_register_codec(&spi->dev, + return snd_soc_register_codec(dev, &soc_codec_dev_pcm179x, &pcm179x_dai, 1); } +EXPORT_SYMBOL_GPL(pcm179x_common_init); -static int pcm179x_spi_remove(struct spi_device *spi) +int pcm179x_common_exit(struct device *dev) { - snd_soc_unregister_codec(&spi->dev); + snd_soc_unregister_codec(dev); return 0; } - -static const struct spi_device_id pcm179x_spi_ids[] = { - { "pcm179x", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids); - -static struct spi_driver pcm179x_codec_driver = { - .driver = { - .name = "pcm179x", - .of_match_table = of_match_ptr(pcm179x_of_match), - }, - .id_table = pcm179x_spi_ids, - .probe = pcm179x_spi_probe, - .remove = pcm179x_spi_remove, -}; - -module_spi_driver(pcm179x_codec_driver); +EXPORT_SYMBOL_GPL(pcm179x_common_exit); MODULE_DESCRIPTION("ASoC PCM179X driver"); MODULE_AUTHOR("Michael Trimarchi "); diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h index c6fdc062a497..c4eea4df915b 100644 --- a/sound/soc/codecs/pcm179x.h +++ b/sound/soc/codecs/pcm179x.h @@ -24,4 +24,9 @@ #define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE) +extern const struct regmap_config pcm179x_regmap_config; + +int pcm179x_common_init(struct device *dev, struct regmap *regmap); +int pcm179x_common_exit(struct device *dev); + #endif -- cgit v1.2.3 From 70e3e56e6c3d499be47322037ed4a42314a8e509 Mon Sep 17 00:00:00 2001 From: Jacob Siverskog Date: Fri, 22 Jan 2016 13:39:54 +0100 Subject: ASoC: pcm179x: Add I2C interface driver The PCM179x family supports both SPI and I2C. This patch adds support for the I2C interface. Signed-off-by: Jacob Siverskog Reviewed-by: Johan Hovold Acked-by: Michael Trimarchi Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/pcm179x.txt | 11 +++- sound/soc/codecs/Kconfig | 9 +++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/pcm179x-i2c.c | 73 ++++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/pcm179x-i2c.c (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/pcm179x.txt b/Documentation/devicetree/bindings/sound/pcm179x.txt index 4ae70d3462d6..436c2b247693 100644 --- a/Documentation/devicetree/bindings/sound/pcm179x.txt +++ b/Documentation/devicetree/bindings/sound/pcm179x.txt @@ -1,6 +1,6 @@ Texas Instruments pcm179x DT bindings -This driver supports the SPI bus. +This driver supports both the I2C and SPI bus. Required properties: @@ -9,6 +9,11 @@ Required properties: For required properties on SPI, please consult Documentation/devicetree/bindings/spi/spi-bus.txt +Required properties on I2C: + + - reg: the I2C address + + Examples: codec_spi: 1792a@0 { @@ -16,3 +21,7 @@ Examples: spi-max-frequency = <600000>; }; + codec_i2c: 1792a@4c { + compatible = "ti,pcm1792a"; + reg = <0x4c>; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ae720f1b15e8..3a8fcf13200f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -87,6 +87,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ML26124 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C + select SND_SOC_PCM179X_I2C if I2C select SND_SOC_PCM179X_SPI if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C @@ -529,6 +530,14 @@ config SND_SOC_PCM1681 config SND_SOC_PCM179X tristate +config SND_SOC_PCM179X_I2C + tristate "Texas Instruments PCM179X CODEC (I2C)" + depends on I2C + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an I2C bus. + config SND_SOC_PCM179X_SPI tristate "Texas Instruments PCM179X CODEC (SPI)" depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 56e94d88693f..9acd777a7a98 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -81,6 +81,7 @@ snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o +snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o snd-soc-pcm179x-spi-objs := pcm179x-spi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o @@ -286,6 +287,7 @@ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o +obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c new file mode 100644 index 000000000000..4118106abb8d --- /dev/null +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -0,0 +1,73 @@ +/* + * PCM179X ASoC I2C driver + * + * Copyright (c) Teenage Engineering AB 2016 + * + * Jacob Siverskog + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "pcm179x.h" + +static int pcm179x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&client->dev, regmap); +} + +static int pcm179x_i2c_remove(struct i2c_client *client) +{ + return pcm179x_common_exit(&client->dev); +} + +static const struct of_device_id pcm179x_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); + +static const struct i2c_device_id pcm179x_i2c_ids[] = { + { "pcm179x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids); + +static struct i2c_driver pcm179x_i2c_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_i2c_ids, + .probe = pcm179x_i2c_probe, + .remove = pcm179x_i2c_remove, +}; + +module_i2c_driver(pcm179x_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM179X I2C driver"); +MODULE_AUTHOR("Jacob Siverskog "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e0236f541e68aa2187a9a402bf74bec6f007eb84 Mon Sep 17 00:00:00 2001 From: Jacob Siverskog Date: Fri, 22 Jan 2016 13:39:55 +0100 Subject: ASoC: pcm179x: Support continuous rates According to the PCM179x data sheets sampling frequencies between 10 kHz and 200 kHz are supported. Specify support in the driver. Tested with PCM1791A. References: http://www.ti.com/lit/ds/symlink/pcm1791a.pdf http://www.ti.com/lit/ds/symlink/pcm1792a.pdf http://www.ti.com/lit/ds/symlink/pcm1795.pdf http://www.ti.com/lit/ds/symlink/pcm1796.pdf Signed-off-by: Jacob Siverskog Reviewed-by: Johan Hovold Acked-by: Michael Trimarchi Signed-off-by: Mark Brown --- sound/soc/codecs/pcm179x.c | 4 +++- sound/soc/codecs/pcm179x.h | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index 8f20f70af23e..06a66579ca6d 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -187,7 +187,9 @@ static struct snd_soc_dai_driver pcm179x_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = PCM1792A_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 10000, + .rate_max = 200000, .formats = PCM1792A_FORMATS, }, .ops = &pcm179x_dai_ops, }; diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h index c4eea4df915b..11e331268aae 100644 --- a/sound/soc/codecs/pcm179x.h +++ b/sound/soc/codecs/pcm179x.h @@ -17,10 +17,6 @@ #ifndef __PCM179X_H__ #define __PCM179X_H__ -#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ - SNDRV_PCM_RATE_192000) - #define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE) -- cgit v1.2.3 From b0639bd2428d61a13b69d2ff247cfceae90b8366 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:47:12 +0000 Subject: ASoC: soc-pcm: copy dpcm->hw_params and call be_hw_params_fixup every time Current DPCM doesn't copy dpcm->hw_params and doesn't call be_hw_params if some FE are connected. But 2nd or later FE might want to know BE hw_params. This patch solves this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e898b427be7e..be2fd5e46182 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1866,18 +1866,6 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; - /* only allow hw_params() if no connected FEs are running */ - if (!snd_soc_dpcm_can_be_params(fe, be, stream)) - continue; - - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) - continue; - - dev_dbg(be->dev, "ASoC: hw_params BE %s\n", - dpcm->fe->dai_link->name); - /* copy params for each dpcm */ memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, sizeof(struct snd_pcm_hw_params)); @@ -1894,6 +1882,18 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) } } + /* only allow hw_params() if no connected FEs are running */ + if (!snd_soc_dpcm_can_be_params(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) + continue; + + dev_dbg(be->dev, "ASoC: hw_params BE %s\n", + dpcm->fe->dai_link->name); + ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); if (ret < 0) { dev_err(dpcm->be->dev, -- cgit v1.2.3 From 6590faaba4008d84806bf4b570db2e885e3d9e99 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 16 Dec 2015 13:42:41 +0800 Subject: ALSA: hda - hdmi begin to support dynamic PCM assignment Begin to support dynamic PCM assignment to pin in hdmi audio driver. This means PCM will not be statically bound with pin. When there is a monitor connected, the corresponding pin will try to find a proper PCM to bind. When the monitor is disconnected, the corresponding pin will unbind the PCM. This helps to reduce the PCM number when there are many pins (device entries in DP MST mode) and only a few of them work at the same time. This patch adds the pcm member in struct hdmi_spec_per_pin. When PCM is dynamically bound to the pin, the member pcm will pointer to the corresponding pcm_rec[] in hdmi_spec, which means the hda_pcm is bound to the pin. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 426a29a1c19b..aa7bd3f58129 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -86,6 +86,7 @@ struct hdmi_spec_per_pin { struct delayed_work work; struct snd_kcontrol *eld_ctl; struct snd_jack *acomp_jack; /* jack via audio component */ + struct hda_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ int channels; /* current number of channels */ @@ -144,7 +145,7 @@ struct hdmi_spec { struct hdmi_ops ops; bool dyn_pin_out; - + bool dyn_pcm_assign; /* * Non-generic VIA/NVIDIA specific */ @@ -389,13 +390,16 @@ static int hinfo_to_pin_index(struct hda_codec *codec, struct hda_pcm_stream *hinfo) { struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; int pin_idx; - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) - if (get_pcm_rec(spec, pin_idx)->stream == hinfo) + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + per_pin = get_pin(spec, pin_idx); + if (per_pin->pcm && per_pin->pcm->stream == hinfo) return pin_idx; + } - codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo); + codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo); return -EINVAL; } @@ -2116,6 +2120,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; int pin_idx; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { @@ -2125,6 +2130,10 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; + if (!spec->dyn_pcm_assign) { + per_pin = get_pin(spec, pin_idx); + per_pin->pcm = info; + } spec->pcm_rec[pin_idx] = info; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; -- cgit v1.2.3 From 42b2987079eca0238b576c08af1144ed5bd52188 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 16 Dec 2015 13:42:42 +0800 Subject: ALSA: hda - hdmi playback without monitor in dynamic pcm bind mode Pulseaudio requires open pcm successfully when probing. This patch handles playback without monitor in dynamic pcm assignment mode. It tries to open/prepare/close pcm successfully even there is no pin bound to the PCM. On the meantime, it will try to find a proper converter for the PCM. As pcm is This patch introduces a pcm_lock in struct hdmi_spec. This lock is used to protect: 1. the variables in struct hdmi_spec; 2. other variables shared for dynamic pcm assignment mode 3. device entry selection. As each device entry is represented by a separate struct struct hdmi_spec_per_pin, the lock in per_pin is not enough. Please see details below. MST audio device entry operation: 1. select device entry on the pin 2. operate on the pin nid Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 171 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index aa7bd3f58129..ea1e90aa299c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -139,6 +139,7 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ struct hda_pcm *pcm_rec[16]; + struct mutex pcm_lock; unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; @@ -1341,6 +1342,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, return 0; } +/* Try to find an available converter + * If pin_idx is less then zero, just try to find an available converter. + * Otherwise, try to find an available converter and get the cvt mux index + * of the pin. + */ static int hdmi_choose_cvt(struct hda_codec *codec, int pin_idx, int *cvt_id, int *mux_id) { @@ -1349,7 +1355,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec, struct hdmi_spec_per_cvt *per_cvt = NULL; int cvt_idx, mux_idx = 0; - per_pin = get_pin(spec, pin_idx); + /* pin_idx < 0 means no pin will be bound to the converter */ + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); /* Dynamically assign converter to stream */ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { @@ -1358,6 +1368,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec, /* Must not already be assigned */ if (per_cvt->assigned) continue; + if (per_pin == NULL) + break; /* Must be in pin's mux's list of converters */ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) @@ -1370,9 +1382,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec, /* No free converters */ if (cvt_idx == spec->num_cvts) - return -ENODEV; + return -EBUSY; - per_pin->mux_idx = mux_idx; + if (per_pin != NULL) + per_pin->mux_idx = mux_idx; if (cvt_id) *cvt_id = cvt_idx; @@ -1398,6 +1411,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec, mux_idx); } +/* get the mux index for the converter of the pins + * converter's mux index is the same for all pins on Intel platform + */ +static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, + hda_nid_t cvt_nid) +{ + int i; + + for (i = 0; i < spec->num_cvts; i++) + if (spec->cvt_nids[i] == cvt_nid) + return i; + return -EINVAL; +} + /* Intel HDMI workaround to fix audio routing issue: * For some Intel display codecs, pins share the same connection list. * So a conveter can be selected by multiple pins and playback on any of these @@ -1449,6 +1476,69 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, } } +/* A wrapper of intel_not_share_asigned_cvt() */ +static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, + hda_nid_t pin_nid, hda_nid_t cvt_nid) +{ + int mux_idx; + struct hdmi_spec *spec = codec->spec; + + if (!is_haswell_plus(codec) && !is_valleyview_plus(codec)) + return; + + /* On Intel platform, the mapping of converter nid to + * mux index of the pins are always the same. + * The pin nid may be 0, this means all pins will not + * share the converter. + */ + mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); + if (mux_idx >= 0) + intel_not_share_assigned_cvt(codec, pin_nid, mux_idx); +} + +/* called in hdmi_pcm_open when no pin is assigned to the PCM + * in dyn_pcm_assign mode. + */ +static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int cvt_idx; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int err; + + err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); + if (err) + return err; + + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 1; + hinfo->nid = per_cvt->cvt_nid; + + intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); + + /* todo: setup spdif ctls assign */ + + /* Initially set the converter's capabilities */ + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; + + /* Store the updated parameters */ + runtime->hw.channels_min = hinfo->channels_min; + runtime->hw.channels_max = hinfo->channels_max; + runtime->hw.formats = hinfo->formats; + runtime->hw.rates = hinfo->rates; + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + return 0; +} + /* * HDA PCM callbacks */ @@ -1465,19 +1555,36 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, int err; /* Validate hinfo */ + mutex_lock(&spec->pcm_lock); pin_idx = hinfo_to_pin_index(codec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) - return -EINVAL; - per_pin = get_pin(spec, pin_idx); - eld = &per_pin->sink_eld; + if (!spec->dyn_pcm_assign) { + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); + return -EINVAL; + } + } else { + /* no pin is assigned to the PCM + * PA need pcm open successfully when probe + */ + if (pin_idx < 0) { + err = hdmi_pcm_open_no_pin(hinfo, codec, substream); + mutex_unlock(&spec->pcm_lock); + return err; + } + } err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); - if (err < 0) + if (err < 0) { + mutex_unlock(&spec->pcm_lock); return err; + } per_cvt = get_cvt(spec, cvt_idx); /* Claim converter */ per_cvt->assigned = 1; + + + per_pin = get_pin(spec, pin_idx); per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid; @@ -1498,6 +1605,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, hinfo->formats = per_cvt->formats; hinfo->maxbps = per_cvt->maxbps; + eld = &per_pin->sink_eld; /* Restrict capabilities by ELD if this isn't disabled */ if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); @@ -1506,10 +1614,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_cvt->assigned = 0; hinfo->nid = 0; snd_hda_spdif_ctls_unassign(codec, pin_idx); + mutex_unlock(&spec->pcm_lock); return -ENODEV; } } + mutex_unlock(&spec->pcm_lock); /* Store the updated parameters */ runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -1854,13 +1964,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, { hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; - int pin_idx = hinfo_to_pin_index(codec, hinfo); - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + hda_nid_t pin_nid; struct snd_pcm_runtime *runtime = substream->runtime; bool non_pcm; int pinctl; + int err; + + mutex_lock(&spec->pcm_lock); + pin_idx = hinfo_to_pin_index(codec, hinfo); + if (spec->dyn_pcm_assign && pin_idx < 0) { + /* when dyn_pcm_assign and pcm is not bound to a pin + * skip pin setup and return 0 to make audio playback + * be ongoing + */ + intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + snd_hda_codec_setup_stream(codec, cvt_nid, + stream_tag, 0, format); + mutex_unlock(&spec->pcm_lock); + return 0; + } + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); + return -EINVAL; + } + per_pin = get_pin(spec, pin_idx); + pin_nid = per_pin->pin_nid; if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { /* Verify pin:cvt selections to avoid silent audio after S3. * After S3, the audio driver restores pin:cvt selections @@ -1885,7 +2016,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); - if (spec->dyn_pin_out) { pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -1894,7 +2024,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, pinctl | PIN_OUT); } - return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + err = spec->ops.setup_stream(codec, cvt_nid, pin_nid, + stream_tag, format); + mutex_unlock(&spec->pcm_lock); + return err; } static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1925,9 +2058,17 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_cvt->assigned = 0; hinfo->nid = 0; + mutex_lock(&spec->pcm_lock); pin_idx = hinfo_to_pin_index(codec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) + if (spec->dyn_pcm_assign && pin_idx < 0) { + mutex_unlock(&spec->pcm_lock); + return 0; + } + + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); return -EINVAL; + } per_pin = get_pin(spec, pin_idx); if (spec->dyn_pin_out) { @@ -1947,6 +2088,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin->setup = false; per_pin->channels = 0; mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); } return 0; @@ -2461,6 +2603,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) return -ENOMEM; spec->ops = generic_standard_hdmi_ops; + mutex_init(&spec->pcm_lock); codec->spec = spec; hdmi_array_init(spec, 4); -- cgit v1.2.3 From 2bf3c85a5b167a6c87d9d7d655af85d85d51769b Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 16 Dec 2015 13:42:43 +0800 Subject: ALSA: hda - hdmi operate spdif based on pcm Currently, the driver operates the spdif based on pin. This is ok for the current driver as pcm is statically bound to the pin. However, if the driver uses dynamically pcm assignment, this will cause confusion for user space. The patch changes spdif operation from pin based to pcm based. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ea1e90aa299c..91c716541f61 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -140,6 +140,7 @@ struct hdmi_spec { struct snd_array pins; /* struct hdmi_spec_per_pin */ struct hda_pcm *pcm_rec[16]; struct mutex pcm_lock; + int pcm_used; /* counter of pcm_rec[] */ unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; @@ -387,6 +388,20 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } +static int hinfo_to_pcm_index(struct hda_codec *codec, + struct hda_pcm_stream *hinfo) +{ + struct hdmi_spec *spec = codec->spec; + int pcm_idx; + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) + if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) + return pcm_idx; + + codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo); + return -EINVAL; +} + static int hinfo_to_pin_index(struct hda_codec *codec, struct hda_pcm_stream *hinfo) { @@ -1548,13 +1563,17 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, { struct hdmi_spec *spec = codec->spec; struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, mux_idx = 0; + int pin_idx, cvt_idx, pcm_idx, mux_idx = 0; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; struct hdmi_spec_per_cvt *per_cvt = NULL; int err; /* Validate hinfo */ + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) + return -EINVAL; + mutex_lock(&spec->pcm_lock); pin_idx = hinfo_to_pin_index(codec, hinfo); if (!spec->dyn_pcm_assign) { @@ -1596,7 +1615,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (is_haswell_plus(codec) || is_valleyview_plus(codec)) intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); - snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); + snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); /* Initially set the converter's capabilities */ hinfo->channels_min = per_cvt->channels_min; @@ -1613,7 +1632,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, !hinfo->rates || !hinfo->formats) { per_cvt->assigned = 0; hinfo->nid = 0; - snd_hda_spdif_ctls_unassign(codec, pin_idx); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); mutex_unlock(&spec->pcm_lock); return -ENODEV; } @@ -2043,12 +2062,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - int cvt_idx, pin_idx; + int cvt_idx, pin_idx, pcm_idx; struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; int pinctl; if (hinfo->nid) { + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (snd_BUG_ON(pcm_idx < 0)) + return -EINVAL; cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); if (snd_BUG_ON(cvt_idx < 0)) return -EINVAL; @@ -2079,7 +2101,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, pinctl & ~PIN_OUT); } - snd_hda_spdif_ctls_unassign(codec, pin_idx); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); mutex_lock(&per_pin->lock); per_pin->chmap_set = false; @@ -2277,6 +2299,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) per_pin->pcm = info; } spec->pcm_rec[pin_idx] = info; + spec->pcm_used++; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; @@ -2353,6 +2376,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) HDA_PCM_TYPE_HDMI); if (err < 0) return err; + /* pin number is the same with pcm number so far */ snd_hda_spdif_ctls_unassign(codec, pin_idx); /* add control for ELD Bytes */ -- cgit v1.2.3 From a76056f2e57ec2825d6ae4be016596dd37300750 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 16 Dec 2015 16:48:15 +0800 Subject: ALSA: hda - hdmi dynamically bind PCM to pin when monitor hotplug Dynamically bind/unbind the PCM to pin when HDMI/DP monitor hotplug. When monitor is connected, find a proper PCM for the monitor. When monitor is disconnected, unbind the PCM from the pin. The binding policy (use Intel platform as example) is: 1. Try to use the legacy pin-pcm mapping for the device entry 0 of the pin. 2. If step 1 fails, try to bind pin to the backup PCMs. For example, on Intel platform, if DP MST is enabled, 5 PCMs will be created. PCM 3, PCM 7, PCM 8 are supposed to be used by device entry 0 of pin 5, pin 6 and pin 7. PCM 9 and PCM 10 are the backup PCMs. 3. If step 2 fails, try to find any PCM to bind to the pin. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 82 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 91c716541f61..6cf25eedf482 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -75,6 +75,8 @@ struct hdmi_spec_per_cvt { struct hdmi_spec_per_pin { hda_nid_t pin_nid; + /* pin idx, different device entries on the same pin use the same idx */ + int pin_nid_idx; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; int mux_idx; @@ -87,6 +89,7 @@ struct hdmi_spec_per_pin { struct snd_kcontrol *eld_ctl; struct snd_jack *acomp_jack; /* jack via audio component */ struct hda_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ + int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ int channels; /* current number of channels */ @@ -140,6 +143,8 @@ struct hdmi_spec { struct snd_array pins; /* struct hdmi_spec_per_pin */ struct hda_pcm *pcm_rec[16]; struct mutex pcm_lock; + /* pcm_bitmap means which pcms have been assigned to pins*/ + unsigned long pcm_bitmap; int pcm_used; /* counter of pcm_rec[] */ unsigned int channels_max; /* max over all cvts */ @@ -1673,6 +1678,60 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) return 0; } +static int hdmi_find_pcm_slot(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int i; + + /* try the prefer PCM */ + if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap)) + return per_pin->pin_nid_idx; + + /* have a second try; check the "reserved area" over num_pins */ + for (i = spec->num_pins; i < spec->pcm_used; i++) { + if (!test_bit(i, &spec->pcm_bitmap)) + return i; + } + + /* the last try; check the empty slots in pins */ + for (i = 0; i < spec->num_pins; i++) { + if (!test_bit(i, &spec->pcm_bitmap)) + return i; + } + return -EBUSY; +} + +static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be attached to the pin */ + if (per_pin->pcm) + return; + idx = hdmi_find_pcm_slot(spec, per_pin); + if (idx == -ENODEV) + return; + per_pin->pcm_idx = idx; + per_pin->pcm = spec->pcm_rec[idx]; + set_bit(idx, &spec->pcm_bitmap); +} + +static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be detached from the pin */ + if (!per_pin->pcm) + return; + idx = per_pin->pcm_idx; + per_pin->pcm_idx = -1; + per_pin->pcm = NULL; + if (idx >= 0 && idx < spec->pcm_used) + clear_bit(idx, &spec->pcm_bitmap); +} + /* update per_pin ELD from the given new ELD; * setup info frame and notification accordingly */ @@ -1681,9 +1740,17 @@ static void update_eld(struct hda_codec *codec, struct hdmi_eld *eld) { struct hdmi_eld *pin_eld = &per_pin->sink_eld; + struct hdmi_spec *spec = codec->spec; bool old_eld_valid = pin_eld->eld_valid; bool eld_changed; + if (spec->dyn_pcm_assign) { + if (eld->eld_valid) + hdmi_attach_hda_pcm(spec, per_pin); + else + hdmi_detach_hda_pcm(spec, per_pin); + } + if (eld->eld_valid) snd_hdmi_show_eld(codec, &eld->info); @@ -1827,13 +1894,19 @@ static void sync_eld_via_acomp(struct hda_codec *codec, static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { struct hda_codec *codec = per_pin->codec; + struct hdmi_spec *spec = codec->spec; + int ret; + mutex_lock(&spec->pcm_lock); if (codec_has_acomp(codec)) { sync_eld_via_acomp(codec, per_pin); - return false; /* don't call snd_hda_jack_report_sync() */ + ret = false; /* don't call snd_hda_jack_report_sync() */ } else { - return hdmi_present_sense_via_verbs(per_pin, repoll); + ret = hdmi_present_sense_via_verbs(per_pin, repoll); } + mutex_unlock(&spec->pcm_lock); + + return ret; } static void hdmi_repoll_eld(struct work_struct *work) @@ -1877,6 +1950,11 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) per_pin->pin_nid = pin_nid; per_pin->non_pcm = false; + if (spec->dyn_pcm_assign) + per_pin->pcm_idx = -1; + else + per_pin->pcm_idx = pin_idx; + per_pin->pin_nid_idx = pin_idx; err = hdmi_read_pin_conn(codec, pin_idx); if (err < 0) -- cgit v1.2.3 From ac98379a751e37ba9e5622f9519d9469498572c1 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 16 Dec 2015 16:48:16 +0800 Subject: ALSA: hda - hdmi setup pin when monitor hotplug in pcm dynamic assignment mode Setup pin configuration when monitor is hotplugged in pcm dynamic assignment if the PCM is in open state. When monitor is disconnect, The pin will be reset. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 87 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 6cf25eedf482..796d737366ae 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -146,6 +146,11 @@ struct hdmi_spec { /* pcm_bitmap means which pcms have been assigned to pins*/ unsigned long pcm_bitmap; int pcm_used; /* counter of pcm_rec[] */ + /* bitmap shows whether the pcm is opened in user space + * bit 0 means the first playback PCM (PCM3); + * bit 1 means the second playback PCM, and so on. + */ + unsigned long pcm_in_use; unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; @@ -1525,10 +1530,14 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, { struct hdmi_spec *spec = codec->spec; struct snd_pcm_runtime *runtime = substream->runtime; - int cvt_idx; + int cvt_idx, pcm_idx; struct hdmi_spec_per_cvt *per_cvt = NULL; int err; + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) + return -EINVAL; + err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); if (err) return err; @@ -1539,6 +1548,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); + set_bit(pcm_idx, &spec->pcm_in_use); /* todo: setup spdif ctls assign */ /* Initially set the converter's capabilities */ @@ -1607,7 +1617,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* Claim converter */ per_cvt->assigned = 1; - + set_bit(pcm_idx, &spec->pcm_in_use); per_pin = get_pin(spec, pin_idx); per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid; @@ -1732,6 +1742,71 @@ static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, clear_bit(idx, &spec->pcm_bitmap); } +static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid) +{ + int mux_idx; + + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == cvt_nid) + break; + return mux_idx; +} + +static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid); + +static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hda_codec *codec = per_pin->codec; + struct hda_pcm *pcm; + struct hda_pcm_stream *hinfo; + struct snd_pcm_substream *substream; + int mux_idx; + bool non_pcm; + + if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) + pcm = spec->pcm_rec[per_pin->pcm_idx]; + else + return; + if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) + return; + + /* hdmi audio only uses playback and one substream */ + hinfo = pcm->stream; + substream = pcm->pcm->streams[0].substream; + + per_pin->cvt_nid = hinfo->nid; + + mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid); + if (mux_idx < per_pin->num_mux_nids) + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid); + + non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid); + if (substream->runtime) + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; + per_pin->mux_idx = mux_idx; + + hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); +} + +static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) + snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx); + + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); + + per_pin->setup = false; + per_pin->channels = 0; +} + /* update per_pin ELD from the given new ELD; * setup info frame and notification accordingly */ @@ -1745,10 +1820,13 @@ static void update_eld(struct hda_codec *codec, bool eld_changed; if (spec->dyn_pcm_assign) { - if (eld->eld_valid) + if (eld->eld_valid) { hdmi_attach_hda_pcm(spec, per_pin); - else + hdmi_pcm_setup_pin(spec, per_pin); + } else { + hdmi_pcm_reset_pin(spec, per_pin); hdmi_detach_hda_pcm(spec, per_pin); + } } if (eld->eld_valid) @@ -2159,6 +2237,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, hinfo->nid = 0; mutex_lock(&spec->pcm_lock); + clear_bit(pcm_idx, &spec->pcm_in_use); pin_idx = hinfo_to_pin_index(codec, hinfo); if (spec->dyn_pcm_assign && pin_idx < 0) { mutex_unlock(&spec->pcm_lock); -- cgit v1.2.3 From 2bea241a03ab67fc841c626c25f09108ffda7a35 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 12 Jan 2016 11:13:26 +0800 Subject: ALSA: hda - add hdmi_pcm to manage hdmi pcm related features Use struct hdmi_pcm wrapper for hdmi pcm management. All PCM related features, like jack, will be put in this structure. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 796d737366ae..d922fe833a31 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -88,7 +88,7 @@ struct hdmi_spec_per_pin { struct delayed_work work; struct snd_kcontrol *eld_ctl; struct snd_jack *acomp_jack; /* jack via audio component */ - struct hda_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ + struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ @@ -134,6 +134,11 @@ struct hdmi_ops { int (*chmap_validate)(int ca, int channels, unsigned char *chmap); }; +struct hdmi_pcm { + struct hda_pcm *pcm; + struct snd_jack *jack; +}; + struct hdmi_spec { int num_cvts; struct snd_array cvts; /* struct hdmi_spec_per_cvt */ @@ -141,7 +146,7 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hda_pcm *pcm_rec[16]; + struct hdmi_pcm pcm_rec[16]; struct mutex pcm_lock; /* pcm_bitmap means which pcms have been assigned to pins*/ unsigned long pcm_bitmap; @@ -383,7 +388,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) #define get_cvt(spec, idx) \ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx]) +/* obtain hdmi_pcm object assigned to idx */ +#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) +/* obtain hda_pcm object assigned to idx */ +#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) { @@ -421,7 +429,8 @@ static int hinfo_to_pin_index(struct hda_codec *codec, for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { per_pin = get_pin(spec, pin_idx); - if (per_pin->pcm && per_pin->pcm->stream == hinfo) + if (per_pin->pcm && + per_pin->pcm->pcm->stream == hinfo) return pin_idx; } @@ -1723,7 +1732,7 @@ static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, if (idx == -ENODEV) return; per_pin->pcm_idx = idx; - per_pin->pcm = spec->pcm_rec[idx]; + per_pin->pcm = get_hdmi_pcm(spec, idx); set_bit(idx, &spec->pcm_bitmap); } @@ -1766,7 +1775,7 @@ static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, bool non_pcm; if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) - pcm = spec->pcm_rec[per_pin->pcm_idx]; + pcm = get_pcm_rec(spec, per_pin->pcm_idx); else return; if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) @@ -2030,8 +2039,10 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) per_pin->non_pcm = false; if (spec->dyn_pcm_assign) per_pin->pcm_idx = -1; - else + else { + per_pin->pcm = get_hdmi_pcm(spec, pin_idx); per_pin->pcm_idx = pin_idx; + } per_pin->pin_nid_idx = pin_idx; err = hdmi_read_pin_conn(codec, pin_idx); @@ -2441,7 +2452,6 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; int pin_idx; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { @@ -2451,11 +2461,8 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; - if (!spec->dyn_pcm_assign) { - per_pin = get_pin(spec, pin_idx); - per_pin->pcm = info; - } - spec->pcm_rec[pin_idx] = info; + + spec->pcm_rec[pin_idx].pcm = info; spec->pcm_used++; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; @@ -2538,7 +2545,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add control for ELD Bytes */ err = hdmi_create_eld_ctl(codec, pin_idx, - get_pcm_rec(spec, pin_idx)->device); + get_pcm_rec(spec, pin_idx)->device); if (err < 0) return err; @@ -2553,7 +2560,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) struct snd_kcontrol *kctl; int i; - pcm = spec->pcm_rec[pin_idx]; + pcm = get_pcm_rec(spec, pin_idx); if (!pcm || !pcm->pcm) break; err = snd_pcm_add_chmap_ctls(pcm->pcm, @@ -2859,7 +2866,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "HDMI 0"); if (!info) return -ENOMEM; - spec->pcm_rec[0] = info; + spec->pcm_rec[0].pcm = info; info->pcm_type = HDA_PCM_TYPE_HDMI; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; *pstr = spec->pcm_playback; -- cgit v1.2.3 From 25e4abb33df3aafa7d1efba8f82f9178268efab1 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 12 Jan 2016 11:13:27 +0800 Subject: ALSA: hda - hdmi jack created based on pcm Jack is created based on pcm. Apply the acomp jack rule to dyn_pcm_assign. For dyn_pcm_assign: Driver does not use hda_jack. It operates snd_jack directly. snd_jack pointer will be stored in spec->pcm.jack instead of the current spec->acomp_jack. When pcm is assigned to pin, jack will be assigned to pin automatically. For !dyn_pcm_assign: Driver continues using hda_jack for less impact on the old cases. Pcm is statically assigned to pin. So is jack. spec->pcm.jack saves the snd_jack pointer created in hda_jack. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 93 +++++++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index d922fe833a31..c8b11dadbd2a 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -87,7 +87,6 @@ struct hdmi_spec_per_pin { struct mutex lock; struct delayed_work work; struct snd_kcontrol *eld_ctl; - struct snd_jack *acomp_jack; /* jack via audio component */ struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; @@ -1948,6 +1947,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; + struct snd_jack *jack = NULL; int size; mutex_lock(&per_pin->lock); @@ -1971,8 +1971,17 @@ static void sync_eld_via_acomp(struct hda_codec *codec, eld->eld_size = 0; } + /* pcm_idx >=0 before update_eld() means it is in monitor + * disconnected event. Jack must be fetched before update_eld() + */ + if (per_pin->pcm_idx >= 0) + jack = spec->pcm_rec[per_pin->pcm_idx].jack; update_eld(codec, per_pin, eld); - snd_jack_report(per_pin->acomp_jack, + if (jack == NULL && per_pin->pcm_idx >= 0) + jack = spec->pcm_rec[per_pin->pcm_idx].jack; + if (jack == NULL) + goto unlock; + snd_jack_report(jack, eld->monitor_present ? SND_JACK_AVOUT : 0); unlock: mutex_unlock(&per_pin->lock); @@ -2476,15 +2485,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) return 0; } -static void free_acomp_jack_priv(struct snd_jack *jack) +static void free_hdmi_jack_priv(struct snd_jack *jack) { - struct hdmi_spec_per_pin *per_pin = jack->private_data; + struct hdmi_pcm *pcm = jack->private_data; - per_pin->acomp_jack = NULL; + pcm->jack = NULL; } -static int add_acomp_jack_kctl(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, +static int add_hdmi_jack_kctl(struct hda_codec *codec, + struct hdmi_spec *spec, + int pcm_idx, const char *name) { struct snd_jack *jack; @@ -2494,45 +2504,68 @@ static int add_acomp_jack_kctl(struct hda_codec *codec, true, false); if (err < 0) return err; - per_pin->acomp_jack = jack; - jack->private_data = per_pin; - jack->private_free = free_acomp_jack_priv; + + spec->pcm_rec[pcm_idx].jack = jack; + jack->private_data = &spec->pcm_rec[pcm_idx]; + jack->private_free = free_hdmi_jack_priv; return 0; } -static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) +static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) { char hdmi_str[32] = "HDMI/DP"; struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - int pcmdev = get_pcm_rec(spec, pin_idx)->device; + struct hdmi_spec_per_pin *per_pin; + struct hda_jack_tbl *jack; + int pcmdev = get_pcm_rec(spec, pcm_idx)->device; bool phantom_jack; + int ret; if (pcmdev > 0) sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - if (codec_has_acomp(codec)) - return add_acomp_jack_kctl(codec, per_pin, hdmi_str); + + if (spec->dyn_pcm_assign) + return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str); + + /* for !dyn_pcm_assign, we still use hda_jack for compatibility */ + /* if !dyn_pcm_assign, it must be non-MST mode. + * This means pcms and pins are statically mapped. + * And pcm_idx is pin_idx. + */ + per_pin = get_pin(spec, pcm_idx); phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid); if (phantom_jack) strncat(hdmi_str, " Phantom", sizeof(hdmi_str) - strlen(hdmi_str) - 1); - - return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, - phantom_jack); + ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, + phantom_jack); + if (ret < 0) + return ret; + jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); + if (jack == NULL) + return 0; + /* assign jack->jack to pcm_rec[].jack to + * align with dyn_pcm_assign mode + */ + spec->pcm_rec[pcm_idx].jack = jack->jack; + return 0; } static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err; - int pin_idx; + int pin_idx, pcm_idx; - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - err = generic_hdmi_build_jack(codec, pin_idx); + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + err = generic_hdmi_build_jack(codec, pcm_idx); if (err < 0) return err; + } + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); err = snd_hda_create_dig_out_ctls(codec, per_pin->pin_nid, @@ -2631,18 +2664,25 @@ static void hdmi_array_free(struct hdmi_spec *spec) static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int pin_idx; + int pin_idx, pcm_idx; if (codec_has_acomp(codec)) snd_hdac_i915_register_notifier(NULL); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); eld_proc_free(per_pin); - if (per_pin->acomp_jack) - snd_device_free(codec->card, per_pin->acomp_jack); + } + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + if (spec->pcm_rec[pcm_idx].jack == NULL) + continue; + if (spec->dyn_pcm_assign) + snd_device_free(codec->card, + spec->pcm_rec[pcm_idx].jack); + else + spec->pcm_rec[pcm_idx].jack = NULL; } if (spec->i915_bound) @@ -2844,6 +2884,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) init_channel_allocations(); + WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); return 0; } -- cgit v1.2.3 From b09887f89159a6883a4e1837bbe1698167def91e Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Fri, 29 Jan 2016 13:53:27 +0800 Subject: ALSA: hda - hdmi create spdif ctl based on pcm SPDIF ctl should be based on pcm. Each spdif ctl controls one pcm state. This patch creates spdif based on pcm and no longer based on pin. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index c8b11dadbd2a..275b68afb575 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2257,6 +2257,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, hinfo->nid = 0; mutex_lock(&spec->pcm_lock); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); clear_bit(pcm_idx, &spec->pcm_in_use); pin_idx = hinfo_to_pin_index(codec, hinfo); if (spec->dyn_pcm_assign && pin_idx < 0) { @@ -2278,8 +2279,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, pinctl & ~PIN_OUT); } - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - mutex_lock(&per_pin->lock); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); @@ -2562,19 +2561,29 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) err = generic_hdmi_build_jack(codec, pcm_idx); if (err < 0) return err; - } - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - err = snd_hda_create_dig_out_ctls(codec, + /* create the spdif for each pcm + * pin will be bound when monitor is connected + */ + if (spec->dyn_pcm_assign) + err = snd_hda_create_dig_out_ctls(codec, + 0, spec->cvt_nids[0], + HDA_PCM_TYPE_HDMI); + else { + struct hdmi_spec_per_pin *per_pin = + get_pin(spec, pcm_idx); + err = snd_hda_create_dig_out_ctls(codec, per_pin->pin_nid, per_pin->mux_nids[0], HDA_PCM_TYPE_HDMI); + } if (err < 0) return err; - /* pin number is the same with pcm number so far */ - snd_hda_spdif_ctls_unassign(codec, pin_idx); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + } + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); /* add control for ELD Bytes */ err = hdmi_create_eld_ctl(codec, pin_idx, -- cgit v1.2.3 From 79289e24194a9d099bf18f200894832c5760cd83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jan 2016 11:33:34 +0100 Subject: ALSA: usb-audio: Refer to chip->usb_id for quirks and MIDI creation This is a preliminary patch for the later change to allow a better quirk ID management. In the current USB-audio code, there are a few places looking at usb_device idVendor and idProduct fields directly even though we have already a static member in snd_usb_audio.usb_id. This patch modifies such codes to refer to the latter field. For achieving this, two slightly intensive changes have been done: - The snd_usb_audio object is set/reset via dev_getdrv() for the given USB device; it's needed for minimizing the changes for some existing quirks that take only usb_device object. - __snd_usbmidi_create() is introduced to receive the pre-given usb_id argument. The exported snd_usbmidi_create() is unchanged by calling this new function internally. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 9 ++++++--- sound/usb/midi.c | 15 +++++++++------ sound/usb/midi.h | 14 ++++++++++++-- sound/usb/quirks.c | 36 +++++++++++++++++++++--------------- sound/usb/quirks.h | 3 ++- 5 files changed, 50 insertions(+), 27 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 1f09d9591276..2c0269014b85 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -171,8 +171,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = snd_usbmidi_create(chip->card, iface, - &chip->midi_list, NULL); + int err = __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, NULL, + chip->usb_id); if (err < 0) { dev_err(&dev->dev, "%u:%d: cannot create sequencer device\n", @@ -311,6 +312,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); + dev_set_drvdata(&chip->dev->dev, NULL); kfree(chip); return 0; } @@ -484,7 +486,7 @@ static int usb_audio_probe(struct usb_interface *intf, if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; - err = snd_usb_apply_boot_quirk(dev, intf, quirk); + err = snd_usb_apply_boot_quirk(dev, intf, quirk, id); if (err < 0) return err; @@ -503,6 +505,7 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } chip = usb_chip[i]; + dev_set_drvdata(&dev->dev, chip); atomic_inc(&chip->active); /* avoid autopm */ break; } diff --git a/sound/usb/midi.c b/sound/usb/midi.c index cc39f63299ef..b79875ebec1e 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume); /* * Creates and registers everything needed for a MIDI streaming interface. */ -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface *iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk) +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id) { struct snd_usb_midi *umidi; struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; @@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card, spin_lock_init(&umidi->disc_lock); init_rwsem(&umidi->disc_rwsem); mutex_init(&umidi->mutex); - umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), + if (!usb_id) + usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); + umidi->usb_id = usb_id; setup_timer(&umidi->error_timer, snd_usbmidi_error_timer, (unsigned long)umidi); @@ -2464,4 +2467,4 @@ int snd_usbmidi_create(struct snd_card *card, list_add_tail(&umidi->list, midi_list); return 0; } -EXPORT_SYMBOL(snd_usbmidi_create); +EXPORT_SYMBOL(__snd_usbmidi_create); diff --git a/sound/usb/midi.h b/sound/usb/midi.h index ad8a3211f8e7..5e25a3fd6c1d 100644 --- a/sound/usb/midi.h +++ b/sound/usb/midi.h @@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info { /* for QUIRK_MIDI_AKAI, data is NULL */ -int snd_usbmidi_create(struct snd_card *card, +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); + +static inline int snd_usbmidi_create(struct snd_card *card, struct usb_interface *iface, struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk) +{ + return __snd_usbmidi_create(card, iface, midi_list, quirk, 0); +} + void snd_usbmidi_input_stop(struct list_head *p); void snd_usbmidi_input_start(struct list_head *p); void snd_usbmidi_disconnect(struct list_head *p); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a75d9ce7d77a..16364d94f17b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -446,8 +446,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, const struct snd_usb_audio_quirk *quirk = chip->usb_id == USB_ID(0x0582, 0x002b) ? &ua700_quirk : &uaxx_quirk; - return snd_usbmidi_create(chip->card, iface, - &chip->midi_list, quirk); + return __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, quirk, + chip->usb_id); } if (altsd->bNumEndpoints != 1) @@ -974,11 +975,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk, + unsigned int id) { - u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - switch (id) { case USB_ID(0x041e, 0x3000): /* SB Extigy needs special boot-up sequence */ @@ -1182,7 +1181,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) * "Playback Design" products send bogus feedback data at the start * of the stream. Ignore them. */ - if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba && ep->type == SND_USB_ENDPOINT_TYPE_SYNC) ep->skip_packets = 4; @@ -1201,11 +1200,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) void snd_usb_set_interface_quirk(struct usb_device *dev) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 50ms delay after setting the * USB interface. */ - switch (le16_to_cpu(dev->descriptor.idVendor)) { + switch (USB_ID_VENDOR(chip->usb_id)) { case 0x23ba: /* Playback Design */ case 0x0644: /* TEAC Corp. */ mdelay(50); @@ -1213,15 +1216,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev) } } +/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); @@ -1229,23 +1237,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, * "TEAC Corp." products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) && + if (USB_ID_VENDOR(chip->usb_id) == 0x0644 && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ - if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct))) + if (is_marantz_denon_dac(chip->usb_id) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Zoom R16/24 needs a tiny delay here, otherwise requests like * get/set frequency return as failed despite actually succeeding. */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && - (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + if (chip->usb_id == USB_ID(0x1686, 0x00dd) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(1); } @@ -1262,7 +1268,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, unsigned int sample_bytes) { /* Playback Designs */ - if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) { + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) { switch (fp->altsetting) { case 1: fp->dsd_dop = true; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 2cd71ed1201f..192ff5ce9452 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt); -- cgit v1.2.3 From e2703363316278cd4a8880671d38f783f7de18ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jan 2016 14:39:12 +0100 Subject: ALSA: usb-audio: Add quirk_alias option This patch adds a new option "quirk_alias" to snd-usb-audio driver for allowing user to pass the quirk alias list. A quirk alias consists of a string form like 0123abcd:5678beef, which makes to apply a quirk to a device with USB ID 0123:abcd treated as if it were 5678:beef. This feature is useful to test an existing quirk, typically for a newer model of the same vendor, without patching / rebuilding the kernel driver. The current implementation is fairly simplistic: since there is no API for matching a usb_device_id to the given ID pair, it has an open code to loop over the id table and matches only with vendor:product pair. So far, this is OK, as all existing entries are with vendor:product pairs, indeed. Once when we have another matching entry, however, we'd need to update get_alias_quirk() as well. Note that this option is provided only for testing / development. If you want to have a proper support, contact to upstream for adding the matching quirk in the driver code statically. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 2c0269014b85..3fc63583a537 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -82,6 +82,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; +static char *quirk_alias[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); @@ -100,6 +101,8 @@ MODULE_PARM_DESC(ignore_ctl_error, "Ignore errors from USB controller for mixer interfaces."); module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); +module_param_array(quirk_alias, charp, NULL, 0444); +MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -457,6 +460,48 @@ static int snd_usb_audio_create(struct usb_interface *intf, return 0; } +/* look for a matching quirk alias id */ +static bool get_alias_id(struct usb_device *dev, unsigned int *id) +{ + int i; + unsigned int src, dst; + + for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) { + if (!quirk_alias[i] || + sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 || + src != *id) + continue; + dev_info(&dev->dev, + "device (%04x:%04x): applying quirk alias %04x:%04x\n", + USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id), + USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst)); + *id = dst; + return true; + } + + return false; +} + +static struct usb_device_id usb_audio_ids[]; /* defined below */ + +/* look for the corresponding quirk */ +static const struct snd_usb_audio_quirk * +get_alias_quirk(struct usb_device *dev, unsigned int id) +{ + const struct usb_device_id *p; + + for (p = usb_audio_ids; p->match_flags; p++) { + /* FIXME: this checks only vendor:product pair in the list */ + if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) == + USB_DEVICE_ID_MATCH_DEVICE && + p->idVendor == USB_ID_VENDOR(id) && + p->idProduct == USB_ID_PRODUCT(id)) + return (const struct snd_usb_audio_quirk *)p->driver_info; + } + + return NULL; +} + /* * probe the active usb device * @@ -483,6 +528,8 @@ static int usb_audio_probe(struct usb_interface *intf, ifnum = get_iface_desc(alts)->bInterfaceNumber; id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + if (get_alias_id(dev, &id)) + quirk = get_alias_quirk(dev, id); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; -- cgit v1.2.3 From 4e26ad80cbd3a6b0d606201892e81a9a9c6864ce Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 29 Jan 2016 01:47:12 +0000 Subject: ASoC: rt5616: Add support sample rate to 192KHz Reference the TRM, the ALC5616 support one 24bit/8KHz ~ 192KHz I2S/PCM Interface for stereo DAC and stereo ADC. Signed-off-by: Xing Zheng Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index fdca63694b40..3704d0716c04 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1261,7 +1261,7 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define rt5616_resume NULL #endif -#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -- cgit v1.2.3 From 31889507fd84a6a1c8dce36a214a070cec1fc559 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:53:00 +0000 Subject: ASoC: wm5110: Add support for audio trace firmware Mainly this adds DAI links for the audio trace, however, it is also necessary to update the data IRQ handler to check more cores. We have the handler check every core so it should not be necessary to update this function if more compressed firmwares are added in the future. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.h | 2 +- sound/soc/codecs/wm5110.c | 43 +++++++++++++++++++++++++++++++++++++++---- sound/soc/codecs/wm_adsp.c | 1 - 3 files changed, 40 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 8b6adb5419bb..0c64a5731513 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -57,7 +57,7 @@ #define ARIZONA_CLK_98MHZ 5 #define ARIZONA_CLK_147MHZ 6 -#define ARIZONA_MAX_DAI 8 +#define ARIZONA_MAX_DAI 10 #define ARIZONA_MAX_ADSP 4 #define ARIZONA_DVFS_SR1_RQ 0x001 diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index b47bc4418e30..92b6a6a564df 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1828,6 +1828,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Voice Control DSP", NULL, "DSP3" }, { "Voice Control DSP", NULL, "SYSCLK" }, + { "Audio Trace DSP", NULL, "DSP1" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -2171,6 +2174,27 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .formats = WM5110_FORMATS, }, }, + { + .name = "wm5110-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "wm5110-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + }, }; static int wm5110_open(struct snd_compr_stream *stream) @@ -2182,6 +2206,8 @@ static int wm5110_open(struct snd_compr_stream *stream) if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) { n_adsp = 2; + } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) { + n_adsp = 0; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", @@ -2194,12 +2220,21 @@ static int wm5110_open(struct snd_compr_stream *stream) static irqreturn_t wm5110_adsp2_irq(int irq, void *data) { - struct wm5110_priv *florida = data; - int ret; + struct wm5110_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int serviced = 0; + int i, ret; + + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + } - ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]); - if (ret == -ENODEV) + if (!serviced) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); return IRQ_NONE; + } return IRQ_HANDLED; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 76ab52d2c670..653dbff81fee 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2823,7 +2823,6 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) mutex_lock(&dsp->pwr_lock); if (!buf) { - adsp_err(dsp, "Spurious buffer IRQ\n"); ret = -ENODEV; goto out; } -- cgit v1.2.3 From 69fa80584f69554275ef1257d41b90f4e0d417fc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 21 Jan 2016 17:53:01 +0000 Subject: ASoC: wm5102: Add support for the audio trace firmware wm5102 also supports the audio trace firmware, this patch adds support for this into the wm5102 driver. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 7c0860df3204..aa1e96ab4bd2 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1599,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "Slim2 Capture", NULL, "SYSCLK" }, { "Slim3 Capture", NULL, "SYSCLK" }, + { "Audio Trace DSP", NULL, "DSP1" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -1864,14 +1867,67 @@ static struct snd_soc_dai_driver wm5102_dai[] = { }, .ops = &arizona_simple_dai_ops, }, + { + .name = "wm5102-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "wm5102-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + }, }; +static int wm5102_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + + return wm_adsp_compr_open(&priv->core.adsp[0], stream); +} + +static irqreturn_t wm5102_adsp2_irq(int irq, void *data) +{ + struct wm5102_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]); + if (ret == -ENODEV) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + static int wm5102_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5102_adsp2_irq, + priv); + if (ret != 0) { + dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); if (ret) return ret; @@ -1946,6 +2002,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), }; +static struct snd_compr_ops wm5102_compr_ops = { + .open = wm5102_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static struct snd_soc_platform_driver wm5102_compr_platform = { + .compr_ops = &wm5102_compr_ops, +}; + static int wm5102_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -2005,12 +2075,25 @@ static int wm5102_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, + ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, wm5102_dai, ARRAY_SIZE(wm5102_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + snd_soc_unregister_platform(&pdev->dev); + } + + return ret; } static int wm5102_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 7eec55d7a2f2932b3b4a799bc5d0d6718e1ee9e2 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 22 Jan 2016 13:49:59 +0000 Subject: ASoC: cs47l24: Add voice control compressed stream This patch adds firmware compressed stream capture support and DAI hookups for voice control firmware (based on the wm5110 implementation.) Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l24.c | 98 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 9dc77a4da35d..b8d3cd031d7d 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -798,6 +798,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "AIF2 Capture", NULL, "SYSCLK" }, { "AIF3 Capture", NULL, "SYSCLK" }, + { "Voice Control DSP", NULL, "DSP3" }, + { "Voice Control DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -992,12 +995,68 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .symmetric_rates = 1, .symmetric_samplebits = 1, }, + { + .name = "cs47l24-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "cs47l24-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, }; +static int cs47l24_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct arizona *arizona = priv->core.arizona; + int n_adsp; + + if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { + n_adsp = 2; + } else { + dev_err(arizona->dev, + "No suitable compressed stream for DAI '%s'\n", + rtd->codec_dai->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream); +} + +static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) +{ + struct cs47l24_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]); + if (ret == -ENODEV) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + static int cs47l24_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; priv->core.arizona->dapm = dapm; @@ -1006,6 +1065,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l24_adsp2_irq, + priv); + if (ret != 0) { + dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); if (ret) goto err_adsp2_codec_probe; @@ -1033,13 +1100,14 @@ err_adsp2_codec_probe: static int cs47l24_codec_remove(struct snd_soc_codec *codec) { struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); - + struct arizona *arizona = priv->core.arizona; wm_adsp2_codec_remove(&priv->core.adsp[1], codec); wm_adsp2_codec_remove(&priv->core.adsp[2], codec); priv->core.arizona->dapm = NULL; + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); return 0; } @@ -1076,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), }; +static struct snd_compr_ops cs47l24_compr_ops = { + .open = cs47l24_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static struct snd_soc_platform_driver cs47l24_compr_platform = { + .compr_ops = &cs47l24_compr_ops, +}; static int cs47l24_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -1139,12 +1220,25 @@ static int cs47l24_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, + ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); + return ret; + } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + snd_soc_unregister_platform(&pdev->dev); + } + + return ret; } static int cs47l24_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 3a9686c4ba7eec9201c6e8793705d32f24748771 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 1 Feb 2016 15:22:34 +0000 Subject: ASoC: wm_adsp: Add missing const from region definitions Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 653dbff81fee..28d3851c5063 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -325,7 +325,7 @@ struct wm_adsp_buffer_region_def { unsigned int size_offset; }; -static struct wm_adsp_buffer_region_def default_regions[] = { +static const struct wm_adsp_buffer_region_def default_regions[] = { { .mem_type = WMFW_ADSP2_XM, .base_offset = HOST_BUFFER_FIELD(X_buf_base), @@ -347,7 +347,7 @@ struct wm_adsp_fw_caps { u32 id; struct snd_codec_desc desc; int num_regions; - struct wm_adsp_buffer_region_def *region_defs; + const struct wm_adsp_buffer_region_def *region_defs; }; static const struct wm_adsp_fw_caps ctrl_caps[] = { -- cgit v1.2.3 From da2b33589013b4cb3dad163f50fae060896cb8b6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 2 Feb 2016 16:41:36 +0000 Subject: ASoC: wm_adsp: Return sample rate in wm_adsp_compr_pointer We should return a valid sample rate from the pointer callback, this patch adds this into the driver. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 33806d487b8a..68b85ee67586 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -295,6 +295,8 @@ struct wm_adsp_compr { u32 *raw_buf; unsigned int copied_total; + + unsigned int sample_rate; }; #define WM_ADSP_DATA_WORD_SIZE 3 @@ -2471,6 +2473,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream, if (!compr->raw_buf) return -ENOMEM; + compr->sample_rate = params->codec.sample_rate; + return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); @@ -2911,6 +2915,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, tstamp->copied_total = compr->copied_total; tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; + tstamp->sampling_rate = compr->sample_rate; out: mutex_unlock(&dsp->pwr_lock); -- cgit v1.2.3 From 022f344b41a54a995d90450726842518e91aec29 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Feb 2016 10:48:34 +0800 Subject: ALSA: hda - build chmap kctl based on pcm in hdmi audio Build chmap kctl based on pcm. The first chmap kctl will be mapped to the first pcm, and so on. When a monitor is connected to a pin, the chmap kctl can find the pin and the monitor through the pcm index. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 275b68afb575..1a46f45de736 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -437,6 +437,20 @@ static int hinfo_to_pin_index(struct hda_codec *codec, return -EINVAL; } +static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec, + int pcm_idx) +{ + int i; + struct hdmi_spec_per_pin *per_pin; + + for (i = 0; i < spec->num_pins; i++) { + per_pin = get_pin(spec, i); + if (per_pin->pcm_idx == pcm_idx) + return per_pin; + } + return NULL; +} + static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) { struct hdmi_spec *spec = codec->spec; @@ -2398,10 +2412,16 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - int pin_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + int pcm_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); int i; + if (!per_pin) { + for (i = 0; i < spec->channels_max; i++) + ucontrol->value.integer.value[i] = 0; + return 0; + } + for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; @@ -2413,13 +2433,19 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - int pin_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + int pcm_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; struct snd_pcm_substream *substream; unsigned char chmap[8]; int i, err, ca, prepared = 0; + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!per_pin) + return 0; + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); if (!substream || !substream->runtime) @@ -2596,18 +2622,18 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) } /* add channel maps */ - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { struct hda_pcm *pcm; struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int i; - pcm = get_pcm_rec(spec, pin_idx); + pcm = get_pcm_rec(spec, pcm_idx); if (!pcm || !pcm->pcm) break; err = snd_pcm_add_chmap_ctls(pcm->pcm, SNDRV_PCM_STREAM_PLAYBACK, - NULL, 0, pin_idx, &chmap); + NULL, 0, pcm_idx, &chmap); if (err < 0) return err; /* override handlers */ -- cgit v1.2.3 From 4a6180ea7399b945cd380dc63e2e8118f9b432d3 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 3 Feb 2016 19:53:24 +0800 Subject: ASoC: rt5514: add rt5514 codec driver This is the initial codec driver for rt5514. The codec includes a low power DSP for voice wake up. The register address is incremental by 4 in the DSP memory map. In order to recover the codec settings in the codec mode and manipulate the DSP mode for voice wake up, we use the multi-level register map. One is for ALSA API in codec mode that can be recovered by cache before recording. Another is for DSP related settings that can be accessed with 32bit address of the DSP in the application of voice wake up. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5514.txt | 25 + sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5514.c | 982 +++++++++++++++++++++ sound/soc/codecs/rt5514.h | 252 ++++++ 5 files changed, 1267 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rt5514.txt create mode 100644 sound/soc/codecs/rt5514.c create mode 100644 sound/soc/codecs/rt5514.h (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt new file mode 100644 index 000000000000..e24436fc5ea9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5514.txt @@ -0,0 +1,25 @@ +RT5514 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : "realtek,rt5514". + +- reg : The I2C address of the device. + +Pins on the device (for linking into audio routes) for RT5514: + + * DMIC1L + * DMIC1R + * DMIC2L + * DMIC2R + * AMICL + * AMICR + +Example: + +codec: rt5514@57 { + compatible = "realtek,rt5514"; + reg = <0x57>; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..dabd479bf1f0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C + select SND_SOC_RT5514 if I2C select SND_SOC_RT5616 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -565,6 +566,7 @@ config SND_SOC_PCM512x_SPI config SND_SOC_RL6231 tristate + default y if SND_SOC_RT5514=y default y if SND_SOC_RT5616=y default y if SND_SOC_RT5640=y default y if SND_SOC_RT5645=y @@ -572,6 +574,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5659=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m default m if SND_SOC_RT5640=m default m if SND_SOC_RT5645=m @@ -595,6 +598,9 @@ config SND_SOC_RT298 tristate depends on I2C +config SND_SOC_RT5514 + tristate + config SND_SOC_RT5616 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d44f7d347183..79f95dd4ed68 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -92,6 +92,7 @@ snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o snd-soc-rt298-objs := rt298.o +snd-soc-rt5514-objs := rt5514.o snd-soc-rt5616-objs := rt5616.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -296,6 +297,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o +obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c new file mode 100644 index 000000000000..879bf60f4965 --- /dev/null +++ b/sound/soc/codecs/rt5514.c @@ -0,0 +1,982 @@ +/* + * rt5514.c -- RT5514 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5514.h" + +static const struct reg_sequence rt5514_i2c_patch[] = { + {0x1800101c, 0x00000000}, + {0x18001100, 0x0000031f}, + {0x18001104, 0x00000007}, + {0x18001108, 0x00000000}, + {0x1800110c, 0x00000000}, + {0x18001110, 0x00000000}, + {0x18001114, 0x00000001}, + {0x18001118, 0x00000000}, + {0x18002f08, 0x00000006}, + {0x18002f00, 0x00055149}, + {0x18002f00, 0x0005514b}, + {0x18002f00, 0x00055149}, + {0xfafafafa, 0x00000001}, + {0x18002f10, 0x00000001}, + {0x18002f10, 0x00000000}, + {0x18002f10, 0x00000001}, + {0xfafafafa, 0x00000001}, + {0x18002000, 0x000010ec}, + {0xfafafafa, 0x00000000}, +}; + +static const struct reg_sequence rt5514_patch[] = { + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, +}; + +static const struct reg_default rt5514_reg[] = { + {RT5514_RESET, 0x00000000}, + {RT5514_PWR_ANA1, 0x00808880}, + {RT5514_PWR_ANA2, 0x00220000}, + {RT5514_I2S_CTRL1, 0x00000330}, + {RT5514_I2S_CTRL2, 0x20000000}, + {RT5514_VAD_CTRL6, 0xc00007d2}, + {RT5514_EXT_VAD_CTRL, 0x80000080}, + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_PAD_CTRL1, 0x00804000}, + {RT5514_DMIC_DATA_CTRL, 0x00000005}, + {RT5514_DIG_SOURCE_CTRL, 0x00000002}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_DOWNFILTER2_CTRL1, 0x0000882f}, + {RT5514_PLL_SOURCE_CTRL, 0x00000004}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_CLK_CTRL2, 0x00000000}, + {RT5514_PLL3_CALIB_CTRL1, 0x00400200}, + {RT5514_PLL3_CALIB_CTRL5, 0x40220012}, + {RT5514_DELAY_BUF_CTRL1, 0x7fff006a}, + {RT5514_DELAY_BUF_CTRL3, 0x00000000}, + {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL3, 0x00000362}, + {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL3, 0x00000362}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, + {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, + {RT5514_ANA_CTRL_ADC21, 0x00001180}, + {RT5514_ANA_CTRL_ADC22, 0x0000aaa8}, + {RT5514_ANA_CTRL_ADC23, 0x00151427}, + {RT5514_ANA_CTRL_MICBST, 0x00002000}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, + {RT5514_ANA_CTRL_INBUF, 0x00000143}, + {RT5514_ANA_CTRL_VREF, 0x00008d50}, + {RT5514_ANA_CTRL_PLL3, 0x0000000e}, + {RT5514_ANA_CTRL_PLL1_1, 0x00000000}, + {RT5514_ANA_CTRL_PLL1_2, 0x00030220}, + {RT5514_DMIC_LP_CTRL, 0x00000000}, + {RT5514_MISC_CTRL_DSP, 0x00000000}, + {RT5514_DSP_CTRL1, 0x00055149}, + {RT5514_DSP_CTRL3, 0x00000006}, + {RT5514_DSP_CTRL4, 0x00000001}, + {RT5514_VENDOR_ID1, 0x00000001}, + {RT5514_VENDOR_ID2, 0x10ec5514}, +}; + +static bool rt5514_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_RESET: + case RT5514_PWR_ANA1: + case RT5514_PWR_ANA2: + case RT5514_I2S_CTRL1: + case RT5514_I2S_CTRL2: + case RT5514_VAD_CTRL6: + case RT5514_EXT_VAD_CTRL: + case RT5514_DIG_IO_CTRL: + case RT5514_PAD_CTRL1: + case RT5514_DMIC_DATA_CTRL: + case RT5514_DIG_SOURCE_CTRL: + case RT5514_SRC_CTRL: + case RT5514_DOWNFILTER2_CTRL1: + case RT5514_PLL_SOURCE_CTRL: + case RT5514_CLK_CTRL1: + case RT5514_CLK_CTRL2: + case RT5514_PLL3_CALIB_CTRL1: + case RT5514_PLL3_CALIB_CTRL5: + case RT5514_DELAY_BUF_CTRL1: + case RT5514_DELAY_BUF_CTRL3: + case RT5514_DOWNFILTER0_CTRL1: + case RT5514_DOWNFILTER0_CTRL2: + case RT5514_DOWNFILTER0_CTRL3: + case RT5514_DOWNFILTER1_CTRL1: + case RT5514_DOWNFILTER1_CTRL2: + case RT5514_DOWNFILTER1_CTRL3: + case RT5514_ANA_CTRL_LDO10: + case RT5514_ANA_CTRL_LDO18_16: + case RT5514_ANA_CTRL_ADC12: + case RT5514_ANA_CTRL_ADC21: + case RT5514_ANA_CTRL_ADC22: + case RT5514_ANA_CTRL_ADC23: + case RT5514_ANA_CTRL_MICBST: + case RT5514_ANA_CTRL_ADCFED: + case RT5514_ANA_CTRL_INBUF: + case RT5514_ANA_CTRL_VREF: + case RT5514_ANA_CTRL_PLL3: + case RT5514_ANA_CTRL_PLL1_1: + case RT5514_ANA_CTRL_PLL1_2: + case RT5514_DMIC_LP_CTRL: + case RT5514_MISC_CTRL_DSP: + case RT5514_DSP_CTRL1: + case RT5514_DSP_CTRL3: + case RT5514_DSP_CTRL4: + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_i2c_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case RT5514_DSP_MAPPING | RT5514_RESET: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA1: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA2: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2: + case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6: + case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL: + case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_SRC_CTRL: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2: + case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL: + case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); + +static const struct snd_kcontrol_new rt5514_snd_controls[] = { + SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, + RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), + SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1, + RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), + SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, + RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), +}; + +/* ADC Mixer*/ +static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +/* DMIC Source */ +static const char * const rt5514_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum); + +/** + * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic. + * + * @rate: base clock rate. + * + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. + */ +static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate) +{ + int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(div); i++) { + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) + return i; + } + + dev_warn(codec->dev, "Base clock rate %d is too high\n", rate); + return -EINVAL; +} + +static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int idx; + + idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk); + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1, + RT5514_CLK_DMIC_OUT_SEL_MASK, + idx << RT5514_CLK_DMIC_OUT_SEL_SFT); + + return idx; +} + +static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1) + return 1; + else + return 0; +} + +static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + SND_SOC_DAPM_INPUT("AMICL"), + SND_SOC_DAPM_INPUT("AMICR"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1, + RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1, + RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO21_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2, + RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2, + RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2, + RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0, + NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto2_dmic_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)), + + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { + { "DMIC1", NULL, "DMIC1L" }, + { "DMIC1", NULL, "DMIC1R" }, + { "DMIC2", NULL, "DMIC2L" }, + { "DMIC2", NULL, "DMIC2R" }, + + { "DMIC1L", NULL, "DMIC CLK" }, + { "DMIC1R", NULL, "DMIC CLK" }, + { "DMIC2L", NULL, "DMIC CLK" }, + { "DMIC2R", NULL, "DMIC CLK" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXR", "ADC Switch", "AMICR" }, + + { "ADC Power", NULL, "LDO18 IN" }, + { "ADC Power", NULL, "LDO18 ADC" }, + { "ADC Power", NULL, "LDO21" }, + { "ADC Power", NULL, "BG LDO18 IN" }, + { "ADC Power", NULL, "BG LDO21" }, + { "ADC Power", NULL, "BG MBIAS" }, + { "ADC Power", NULL, "MBIAS" }, + { "ADC Power", NULL, "VREF2" }, + { "ADC Power", NULL, "VREF1" }, + + { "ADCL Power", NULL, "LDO16L" }, + { "ADCL Power", NULL, "ADC1L" }, + { "ADCL Power", NULL, "BSTL2" }, + { "ADCL Power", NULL, "BSTL" }, + { "ADCL Power", NULL, "ADCFEDL" }, + + { "ADCR Power", NULL, "LDO16R" }, + { "ADCR Power", NULL, "ADC1R" }, + { "ADCR Power", NULL, "BSTR2" }, + { "ADCR Power", NULL, "BSTR" }, + { "ADCR Power", NULL, "ADCFEDR" }, + + { "AMICL", NULL, "ADC CLK" }, + { "AMICL", NULL, "ADC Power" }, + { "AMICL", NULL, "ADCL Power" }, + { "AMICR", NULL, "ADC CLK" }, + { "AMICR", NULL, "ADC Power" }, + { "AMICR", NULL, "ADCR Power" }, + + { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" }, + { "PLL1", NULL, "PLL1 LDO" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXR", "ADC Switch", "AMICR" }, + + { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" }, + { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "AIF1TX", NULL, "Stereo1 ADC MIX"}, + { "AIF1TX", NULL, "Stereo2 ADC MIX"}, +}; + +static int rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt5514->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt5514->bclk = rt5514->lrck * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5514->bclk, rt5514->lrck); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len = RT5514_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len = RT5514_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len = RT5514_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK, + val_len); + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK, + pre_div << RT5514_CLK_SYS_DIV_OUT_SFT | + pre_div << RT5514_SEL_ADC_OSR_SFT); + + return 0; +} + +static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_NB_IF: + reg_val |= RT5514_I2S_LR_INV; + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5514_I2S_BP_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: + reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5514_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5514_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5514_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, + RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK, + reg_val); + + return 0; +} + +static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src) + return 0; + + switch (clk_id) { + case RT5514_SCLK_S_MCLK: + reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT5514_SCLK_S_PLL1: + reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, reg_val); + + rt5514->sysclk = freq; + rt5514->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5514->pll_in = 0; + rt5514->pll_out = 0; + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, + RT5514_CLK_SYS_PRE_SEL_MCLK); + + return 0; + } + + if (source == rt5514->pll_src && freq_in == rt5514->pll_in && + freq_out == rt5514->pll_out) + return 0; + + switch (source) { + case RT5514_PLL1_S_MCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK); + break; + + case RT5514_PLL1_S_BCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK); + break; + + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1, + pll_code.k_code << RT5514_PLL_K_SFT | + pll_code.n_code << RT5514_PLL_N_SFT | + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT); + regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2, + RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT); + + rt5514->pll_in = freq_in; + rt5514->pll_out = freq_out; + rt5514->pll_src = source; + + return 0; +} + +static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= RT5514_TDM_MODE; + + if (slots == 4) + val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH; + + + switch (slot_width) { + case 20: + val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20; + break; + + case 24: + val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24; + break; + + case 32: + val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32; + break; + + case 16: + default: + break; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE | + RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK | + RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val); + + return 0; +} + +static int rt5514_probe(struct snd_soc_codec *codec) +{ + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + rt5514->codec = codec; + + return 0; +} + +static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5514_aif_dai_ops = { + .hw_params = rt5514_hw_params, + .set_fmt = rt5514_set_dai_fmt, + .set_sysclk = rt5514_set_dai_sysclk, + .set_pll = rt5514_set_dai_pll, + .set_tdm_slot = rt5514_set_tdm_slot, +}; + +struct snd_soc_dai_driver rt5514_dai[] = { + { + .name = "rt5514-aif1", + .id = 0, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT5514_STEREO_RATES, + .formats = RT5514_FORMATS, + }, + .ops = &rt5514_aif_dai_ops, + } +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5514 = { + .probe = rt5514_probe, + .idle_bias_off = true, + .controls = rt5514_snd_controls, + .num_controls = ARRAY_SIZE(rt5514_snd_controls), + .dapm_widgets = rt5514_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), + .dapm_routes = rt5514_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), +}; + +static const struct regmap_config rt5514_i2c_regmap = { + .name = "i2c", + .reg_bits = 32, + .val_bits = 32, + + .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2, + .readable_reg = rt5514_i2c_readable_register, + + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config rt5514_regmap = { + .reg_bits = 16, + .val_bits = 32, + + .max_register = RT5514_VENDOR_ID2, + .volatile_reg = rt5514_volatile_register, + .readable_reg = rt5514_readable_register, + .reg_read = rt5514_i2c_read, + .reg_write = rt5514_i2c_write, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5514_reg, + .num_reg_defaults = ARRAY_SIZE(rt5514_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5514_i2c_id[] = { + { "rt5514", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5514_of_match[] = { + { .compatible = "realtek,rt5514", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5514_of_match); +#endif + +static int rt5514_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5514_priv *rt5514; + int ret; + unsigned int val; + + rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv), + GFP_KERNEL); + if (rt5514 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5514); + + rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); + if (IS_ERR(rt5514->i2c_regmap)) { + ret = PTR_ERR(rt5514->i2c_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap); + if (IS_ERR(rt5514->regmap)) { + ret = PTR_ERR(rt5514->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + if (val != RT5514_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5514\n", val); + return -ENODEV; + } + + ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", + ret); + + ret = regmap_register_patch(rt5514->regmap, rt5514_patch, + ARRAY_SIZE(rt5514_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514, + rt5514_dai, ARRAY_SIZE(rt5514_dai)); +} + +static int rt5514_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5514_i2c_driver = { + .driver = { + .name = "rt5514", + .of_match_table = of_match_ptr(rt5514_of_match), + }, + .probe = rt5514_i2c_probe, + .remove = rt5514_i2c_remove, + .id_table = rt5514_i2c_id, +}; +module_i2c_driver(rt5514_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5514 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h new file mode 100644 index 000000000000..6ad8a612f659 --- /dev/null +++ b/sound/soc/codecs/rt5514.h @@ -0,0 +1,252 @@ +/* + * rt5514.h -- RT5514 ALSA SoC audio driver + * + * Copyright 2015 Realtek Microelectronics + * Author: Oder Chiou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5514_H__ +#define __RT5514_H__ + +#define RT5514_DEVICE_ID 0x10ec5514 + +#define RT5514_RESET 0x2000 +#define RT5514_PWR_ANA1 0x2004 +#define RT5514_PWR_ANA2 0x2008 +#define RT5514_I2S_CTRL1 0x2010 +#define RT5514_I2S_CTRL2 0x2014 +#define RT5514_VAD_CTRL6 0x2030 +#define RT5514_EXT_VAD_CTRL 0x206c +#define RT5514_DIG_IO_CTRL 0x2070 +#define RT5514_PAD_CTRL1 0x2080 +#define RT5514_DMIC_DATA_CTRL 0x20a0 +#define RT5514_DIG_SOURCE_CTRL 0x20a4 +#define RT5514_SRC_CTRL 0x20ac +#define RT5514_DOWNFILTER2_CTRL1 0x20d0 +#define RT5514_PLL_SOURCE_CTRL 0x2100 +#define RT5514_CLK_CTRL1 0x2104 +#define RT5514_CLK_CTRL2 0x2108 +#define RT5514_PLL3_CALIB_CTRL1 0x2110 +#define RT5514_PLL3_CALIB_CTRL5 0x2124 +#define RT5514_DELAY_BUF_CTRL1 0x2140 +#define RT5514_DELAY_BUF_CTRL3 0x2148 +#define RT5514_DOWNFILTER0_CTRL1 0x2190 +#define RT5514_DOWNFILTER0_CTRL2 0x2194 +#define RT5514_DOWNFILTER0_CTRL3 0x2198 +#define RT5514_DOWNFILTER1_CTRL1 0x21a0 +#define RT5514_DOWNFILTER1_CTRL2 0x21a4 +#define RT5514_DOWNFILTER1_CTRL3 0x21a8 +#define RT5514_ANA_CTRL_LDO10 0x2200 +#define RT5514_ANA_CTRL_LDO18_16 0x2204 +#define RT5514_ANA_CTRL_ADC12 0x2210 +#define RT5514_ANA_CTRL_ADC21 0x2214 +#define RT5514_ANA_CTRL_ADC22 0x2218 +#define RT5514_ANA_CTRL_ADC23 0x221c +#define RT5514_ANA_CTRL_MICBST 0x2220 +#define RT5514_ANA_CTRL_ADCFED 0x2224 +#define RT5514_ANA_CTRL_INBUF 0x2228 +#define RT5514_ANA_CTRL_VREF 0x222c +#define RT5514_ANA_CTRL_PLL3 0x2240 +#define RT5514_ANA_CTRL_PLL1_1 0x2260 +#define RT5514_ANA_CTRL_PLL1_2 0x2264 +#define RT5514_DMIC_LP_CTRL 0x2e00 +#define RT5514_MISC_CTRL_DSP 0x2e04 +#define RT5514_DSP_CTRL1 0x2f00 +#define RT5514_DSP_CTRL3 0x2f08 +#define RT5514_DSP_CTRL4 0x2f10 +#define RT5514_VENDOR_ID1 0x2ff0 +#define RT5514_VENDOR_ID2 0x2ff4 + +#define RT5514_DSP_MAPPING 0x18000000 + +/* RT5514_PWR_ANA1 (0x2004) */ +#define RT5514_POW_LDO18_IN (0x1 << 5) +#define RT5514_POW_LDO18_IN_BIT 5 +#define RT5514_POW_LDO18_ADC (0x1 << 4) +#define RT5514_POW_LDO18_ADC_BIT 4 +#define RT5514_POW_LDO21 (0x1 << 3) +#define RT5514_POW_LDO21_BIT 3 +#define RT5514_POW_BG_LDO18_IN (0x1 << 2) +#define RT5514_POW_BG_LDO18_IN_BIT 2 +#define RT5514_POW_BG_LDO21 (0x1 << 1) +#define RT5514_POW_BG_LDO21_BIT 1 + +/* RT5514_PWR_ANA2 (0x2008) */ +#define RT5514_POW_PLL1 (0x1 << 18) +#define RT5514_POW_PLL1_BIT 18 +#define RT5514_POW_PLL1_LDO (0x1 << 16) +#define RT5514_POW_PLL1_LDO_BIT 16 +#define RT5514_POW_BG_MBIAS (0x1 << 15) +#define RT5514_POW_BG_MBIAS_BIT 15 +#define RT5514_POW_MBIAS (0x1 << 14) +#define RT5514_POW_MBIAS_BIT 14 +#define RT5514_POW_VREF2 (0x1 << 13) +#define RT5514_POW_VREF2_BIT 13 +#define RT5514_POW_VREF1 (0x1 << 12) +#define RT5514_POW_VREF1_BIT 12 +#define RT5514_POWR_LDO16 (0x1 << 11) +#define RT5514_POWR_LDO16_BIT 11 +#define RT5514_POWL_LDO16 (0x1 << 10) +#define RT5514_POWL_LDO16_BIT 10 +#define RT5514_POW_ADC2 (0x1 << 9) +#define RT5514_POW_ADC2_BIT 9 +#define RT5514_POW_INPUT_BUF (0x1 << 8) +#define RT5514_POW_INPUT_BUF_BIT 8 +#define RT5514_POW_ADC1_R (0x1 << 7) +#define RT5514_POW_ADC1_R_BIT 7 +#define RT5514_POW_ADC1_L (0x1 << 6) +#define RT5514_POW_ADC1_L_BIT 6 +#define RT5514_POW2_BSTR (0x1 << 5) +#define RT5514_POW2_BSTR_BIT 5 +#define RT5514_POW2_BSTL (0x1 << 4) +#define RT5514_POW2_BSTL_BIT 4 +#define RT5514_POW_BSTR (0x1 << 3) +#define RT5514_POW_BSTR_BIT 3 +#define RT5514_POW_BSTL (0x1 << 2) +#define RT5514_POW_BSTL_BIT 2 +#define RT5514_POW_ADCFEDR (0x1 << 1) +#define RT5514_POW_ADCFEDR_BIT 1 +#define RT5514_POW_ADCFEDL (0x1 << 0) +#define RT5514_POW_ADCFEDL_BIT 0 + +/* RT5514_I2S_CTRL1 (0x2010) */ +#define RT5514_TDM_MODE (0x1 << 28) +#define RT5514_TDM_MODE_SFT 28 +#define RT5514_I2S_LR_MASK (0x1 << 26) +#define RT5514_I2S_LR_SFT 26 +#define RT5514_I2S_LR_NOR (0x0 << 26) +#define RT5514_I2S_LR_INV (0x1 << 26) +#define RT5514_I2S_BP_MASK (0x1 << 25) +#define RT5514_I2S_BP_SFT 25 +#define RT5514_I2S_BP_NOR (0x0 << 25) +#define RT5514_I2S_BP_INV (0x1 << 25) +#define RT5514_I2S_DF_MASK (0x7 << 16) +#define RT5514_I2S_DF_SFT 16 +#define RT5514_I2S_DF_I2S (0x0 << 16) +#define RT5514_I2S_DF_LEFT (0x1 << 16) +#define RT5514_I2S_DF_PCM_A (0x2 << 16) +#define RT5514_I2S_DF_PCM_B (0x3 << 16) +#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10) +#define RT5514_TDMSLOT_SEL_RX_SFT 10 +#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10) +#define RT5514_CH_LEN_RX_MASK (0x3 << 8) +#define RT5514_CH_LEN_RX_SFT 8 +#define RT5514_CH_LEN_RX_16 (0x0 << 8) +#define RT5514_CH_LEN_RX_20 (0x1 << 8) +#define RT5514_CH_LEN_RX_24 (0x2 << 8) +#define RT5514_CH_LEN_RX_32 (0x3 << 8) +#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6) +#define RT5514_TDMSLOT_SEL_TX_SFT 6 +#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6) +#define RT5514_CH_LEN_TX_MASK (0x3 << 4) +#define RT5514_CH_LEN_TX_SFT 4 +#define RT5514_CH_LEN_TX_16 (0x0 << 4) +#define RT5514_CH_LEN_TX_20 (0x1 << 4) +#define RT5514_CH_LEN_TX_24 (0x2 << 4) +#define RT5514_CH_LEN_TX_32 (0x3 << 4) +#define RT5514_I2S_DL_MASK (0x3 << 0) +#define RT5514_I2S_DL_SFT 0 +#define RT5514_I2S_DL_16 (0x0 << 0) +#define RT5514_I2S_DL_20 (0x1 << 0) +#define RT5514_I2S_DL_24 (0x2 << 0) +#define RT5514_I2S_DL_8 (0x3 << 0) + +/* RT5514_DIG_SOURCE_CTRL (0x20a4) */ +#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1) +#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1 +#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0) +#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0 + +/* RT5514_PLL_SOURCE_CTRL (0x2100) */ +#define RT5514_PLL_1_SEL_MASK (0x7 << 12) +#define RT5514_PLL_1_SEL_SFT 12 +#define RT5514_PLL_1_SEL_SCLK (0x3 << 12) +#define RT5514_PLL_1_SEL_MCLK (0x4 << 12) + +/* RT5514_CLK_CTRL1 (0x2104) */ +#define RT5514_CLK_AD_ANA1_EN (0x1 << 31) +#define RT5514_CLK_AD_ANA1_EN_BIT 31 +#define RT5514_CLK_AD1_EN (0x1 << 24) +#define RT5514_CLK_AD1_EN_BIT 24 +#define RT5514_CLK_AD0_EN (0x1 << 23) +#define RT5514_CLK_AD0_EN_BIT 23 +#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8) +#define RT5514_CLK_DMIC_OUT_SEL_SFT 8 + +/* RT5514_CLK_CTRL2 (0x2108) */ +#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8) +#define RT5514_CLK_SYS_DIV_OUT_SFT 8 +#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4) +#define RT5514_SEL_ADC_OSR_SFT 4 +#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0) +#define RT5514_CLK_SYS_PRE_SEL_SFT 0 +#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0) +#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0) + +/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */ +#define RT5514_AD_DMIC_MIX (0x1 << 11) +#define RT5514_AD_DMIC_MIX_BIT 11 +#define RT5514_AD_AD_MIX (0x1 << 10) +#define RT5514_AD_AD_MIX_BIT 10 +#define RT5514_AD_AD_MUTE (0x1 << 7) +#define RT5514_AD_AD_MUTE_BIT 7 +#define RT5514_AD_GAIN_MASK (0x7f << 0) +#define RT5514_AD_GAIN_SFT 0 + +/* RT5514_ANA_CTRL_MICBST (0x2220) */ +#define RT5514_SEL_BSTL_MASK (0xf << 4) +#define RT5514_SEL_BSTL_SFT 4 +#define RT5514_SEL_BSTR_MASK (0xf << 0) +#define RT5514_SEL_BSTR_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */ +#define RT5514_PLL_K_MAX 0x1f +#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16) +#define RT5514_PLL_K_SFT 16 +#define RT5514_PLL_N_MAX 0x1ff +#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7) +#define RT5514_PLL_N_SFT 4 +#define RT5514_PLL_M_MAX 0xf +#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0) +#define RT5514_PLL_M_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */ +#define RT5514_PLL_M_BP (0x1 << 2) +#define RT5514_PLL_M_BP_SFT 2 +#define RT5514_PLL_K_BP (0x1 << 1) +#define RT5514_PLL_K_BP_SFT 1 +#define RT5514_EN_LDO_PLL1 (0x1 << 0) +#define RT5514_EN_LDO_PLL1_BIT 0 + +#define RT5514_PLL_INP_MAX 40000000 +#define RT5514_PLL_INP_MIN 256000 + +/* System Clock Source */ +enum { + RT5514_SCLK_S_MCLK, + RT5514_SCLK_S_PLL1, +}; + +/* PLL1 Source */ +enum { + RT5514_PLL1_S_MCLK, + RT5514_PLL1_S_BCLK, +}; + +struct rt5514_priv { + struct snd_soc_codec *codec; + struct regmap *i2c_regmap, *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int pll_src; + int pll_in; + int pll_out; +}; + +#endif /* __RT5514_H__ */ -- cgit v1.2.3 From 089dfaf7f7f9b18e3dffb6d9ee8b37089025b952 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 30 Jan 2016 23:07:00 -0800 Subject: ASoC: fsl-asoc-card: Don't add DAPM routes for ASRC if it doesn't exist There are a pair of warnings when ASRC is absent in the DTB: fsl-asoc-card sound: ASoC: no source widget found for ASRC-Playback fsl-asoc-card sound: ASoC: Failed to add route ASRC-Playback -> direct -> CPU-Playback fsl-asoc-card sound: ASoC: no sink widget found for ASRC-Capture fsl-asoc-card sound: ASoC: Failed to add route CPU-Capture -> direct -> ASRC-Capture This is because the driver is still trying to add DAPM routes for ASRC even if it doesn't exist on that platform. The warnings are harmless but it might be annoying. So this patch drops the DAPM routes of ASRC when it's absent in the DAI link. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 562b3bd22d9a..3d40797e3bcc 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -99,19 +99,26 @@ struct fsl_asoc_card_priv { /** * This dapm route map exsits for DPCM link only. * The other routes shall go through Device Tree. + * + * Note: keep all ASRC routes in the second half + * to drop them easily for non-ASRC cases. */ static const struct snd_soc_dapm_route audio_map[] = { - {"CPU-Playback", NULL, "ASRC-Playback"}, + /* 1st half -- Normal DAPM routes */ {"Playback", NULL, "CPU-Playback"}, - {"ASRC-Capture", NULL, "CPU-Capture"}, {"CPU-Capture", NULL, "Capture"}, + /* 2nd half -- ASRC DAPM routes */ + {"CPU-Playback", NULL, "ASRC-Playback"}, + {"ASRC-Capture", NULL, "CPU-Capture"}, }; static const struct snd_soc_dapm_route audio_map_ac97[] = { - {"AC97 Playback", NULL, "ASRC-Playback"}, + /* 1st half -- Normal DAPM routes */ {"Playback", NULL, "AC97 Playback"}, - {"ASRC-Capture", NULL, "AC97 Capture"}, {"AC97 Capture", NULL, "Capture"}, + /* 2nd half -- ASRC DAPM routes */ + {"AC97 Playback", NULL, "ASRC-Playback"}, + {"ASRC-Capture", NULL, "AC97 Capture"}, }; /* Add all possible widgets into here without being redundant */ @@ -593,6 +600,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); + /* Drop the second half of DAPM routes -- ASRC */ + if (!asrc_pdev) + priv->card.num_dapm_routes /= 2; + memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); -- cgit v1.2.3 From 1f1e60c9cdd573be271130e44a23efed48e0578d Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Tue, 24 Nov 2015 10:18:27 +0800 Subject: ASoC: cs42xx8: fix the noise in the right dac channel with mono playback When playback mono wav with record in background, there will be some nosie in the right dac channel. It seems that the ADC data has been routed to the dac channel. The cs42888 have 8 dac channels, it's appropriate to mute the unused dac channels, and the noise will disappear. Steps to reproduce this issue: arecord -D hw:0,0 -f S16_LE -r 48000 -c 1 a.wav & aplay -Dhw:0,0 audio48k16M.wav Signed-off-by: Zidan Wang Signed-off-by: Mark Brown --- sound/soc/codecs/cs42xx8.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index d562e1b9a5d1..1179101b2b05 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -44,6 +44,7 @@ struct cs42xx8_priv { bool slave_mode; unsigned long sysclk; + u32 tx_channels; }; /* -127.5dB to 0dB with step of 0.5dB */ @@ -257,6 +258,9 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, u32 ratio = cs42xx8->sysclk / params_rate(params); u32 i, fm, val, mask; + if (tx) + cs42xx8->tx_channels = params_channels(params); + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { if (cs42xx8_ratios[i].ratio == ratio) break; @@ -283,9 +287,11 @@ static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + u8 dac_unmute = cs42xx8->tx_channels ? + ~((0x1 << cs42xx8->tx_channels) - 1) : 0; - regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE, - CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0); + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, + mute ? CS42XX8_DACMUTE_ALL : dac_unmute); return 0; } -- cgit v1.2.3 From d81221ff9406a3918084bfaed752894e82c5d44b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Feb 2016 16:29:01 +0000 Subject: ASoC: arizona: Add support for SNDRV_PCM_RATE_KNOT The Arizona CODECs support several rates that do not have simple defines in ALSA. This patch adds support for SNDRV_PCM_RATE_KNOT so that users can open stream at these rates. As part of this we should always set constraints in arizona_startup, currently we only set the constraints if we already have a clock to limit rates to that family of sample rates. This patch updates this to set a constraint of all rates supported by the chip if we do not already know which family of rates to limit to. Finally we also reduce the list of rates supported in the constraints to only include those that are supported on current parts. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 62 ++++++++++++---------------------------------- sound/soc/codecs/arizona.h | 2 ++ sound/soc/codecs/cs47l24.c | 2 +- sound/soc/codecs/wm5102.c | 2 +- sound/soc/codecs/wm5110.c | 2 +- sound/soc/codecs/wm8997.c | 2 +- sound/soc/codecs/wm8998.c | 2 +- 7 files changed, 23 insertions(+), 51 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 33143fe1de0b..a32cfb85f1ca 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = { 24576000, }; -static const unsigned int arizona_48k_rates[] = { - 12000, - 24000, - 48000, - 96000, - 192000, - 384000, - 768000, - 4000, - 8000, - 16000, - 32000, - 64000, - 128000, - 256000, - 512000, -}; - -static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = { - .count = ARRAY_SIZE(arizona_48k_rates), - .list = arizona_48k_rates, -}; - static const int arizona_44k1_bclk_rates[] = { -1, 44100, @@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = { 22579200, }; -static const unsigned int arizona_44k1_rates[] = { - 11025, - 22050, - 44100, - 88200, - 176400, - 352800, - 705600, -}; - -static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = { - .count = ARRAY_SIZE(arizona_44k1_rates), - .list = arizona_44k1_rates, -}; - -static int arizona_sr_vals[] = { +static const unsigned int arizona_sr_vals[] = { 0, 12000, 24000, @@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = { 512000, }; +#define ARIZONA_48K_RATE_MASK 0x0F003E +#define ARIZONA_44K1_RATE_MASK 0x003E00 +#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK) + +static const struct snd_pcm_hw_constraint_list arizona_constraint = { + .count = ARRAY_SIZE(arizona_sr_vals), + .list = arizona_sr_vals, +}; + static int arizona_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; - const struct snd_pcm_hw_constraint_list *constraint; unsigned int base_rate; if (!substream->runtime) @@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream, } if (base_rate == 0) - return 0; - - if (base_rate % 8000) - constraint = &arizona_44k1_constraint; + dai_priv->constraint.mask = ARIZONA_RATE_MASK; + else if (base_rate % 8000) + dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK; else - constraint = &arizona_48k_constraint; + dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK; return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - constraint); + &dai_priv->constraint); } static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, @@ -1911,6 +1880,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id) struct arizona_dai_priv *dai_priv = &priv->dai[id]; dai_priv->clk = ARIZONA_CLK_SYSCLK; + dai_priv->constraint = arizona_constraint; return 0; } diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 0c64a5731513..1ea8e4ecf8d4 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -68,6 +68,8 @@ struct wm_adsp; struct arizona_dai_priv { int clk; + + struct snd_pcm_hw_constraint_list constraint; }; struct arizona_priv { diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index b8d3cd031d7d..576087bda330 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -923,7 +923,7 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000 +#define CS47L24_RATES SNDRV_PCM_RATE_KNOT #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index aa1e96ab4bd2..a8b3e3f701f9 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1738,7 +1738,7 @@ static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM5102_RATES SNDRV_PCM_RATE_8000_192000 +#define WM5102_RATES SNDRV_PCM_RATE_KNOT #define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 92b6a6a564df..aa47955b68af 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2024,7 +2024,7 @@ static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM5110_RATES SNDRV_PCM_RATE_8000_192000 +#define WM5110_RATES SNDRV_PCM_RATE_KNOT #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index b4dba3a02aba..52d766efe14f 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -943,7 +943,7 @@ static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM8997_RATES SNDRV_PCM_RATE_8000_192000 +#define WM8997_RATES SNDRV_PCM_RATE_KNOT #define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 7719bc509e50..012396074a8a 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1170,7 +1170,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { { "DRC1 Signal Activity", NULL, "DRC1R" }, }; -#define WM8998_RATES SNDRV_PCM_RATE_8000_192000 +#define WM8998_RATES SNDRV_PCM_RATE_KNOT #define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -- cgit v1.2.3 From 27d6e7d1c96c9f51379e0feb972fec26029098bc Mon Sep 17 00:00:00 2001 From: Andreas Irestål Date: Thu, 4 Feb 2016 15:05:19 +0100 Subject: ASoC: adau17x1: Cache writes when core clock is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some configurations, the dai registers get written before the bias level is changed in the codec driver. This leads to a situation where an initial write to the serial port register gets ignored, and future writes may as well, since regmap thinks that the codec already holds the value. More specifically, configuring the codec as i2s master would in fact result in the codec running as slave, a situation where no i2s clocks are generated and hence no data is transferred. This change makes sure that regmap only caches writes when the core clock is disabled, and syncs regmap whenever enabling the core clock again. Signed-off-by: Andreas Irestål Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/adau1761.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 2f12477e539e..e7136b1956a3 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -456,13 +456,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: + regcache_cache_only(adau->regmap, false); regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) + regcache_sync(adau->regmap); break; case SND_SOC_BIAS_OFF: regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0); + regcache_cache_only(adau->regmap, true); break; } @@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; + /* Enable cache only mode as we could miss writes before bias level + * reaches standby and the core clock is enabled */ + regcache_cache_only(regmap, true); + return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1); } EXPORT_SYMBOL_GPL(adau1761_probe); -- cgit v1.2.3 From 436e056c4ba368f13a5709a5a4a7f26fc238a5a6 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Wed, 3 Feb 2016 13:18:20 +0000 Subject: ASoC: mxs-saif: check BUSY bit in hw_params() only if not mclk_in_use If something else, typically a codec, has enabled mclk, the BUSY bit may be set when hw_params() is called without this being an error. This check thus causes intermittent failures to configure the sound device when used in such a manner. Fix this by making the test conditional on !saif->mclk_in_use. Signed-off-by: Mans Rullgard Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index c866ade28ad0..cd04eb7ee753 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -408,7 +408,7 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, } stat = __raw_readl(saif->base + SAIF_STAT); - if (stat & BM_SAIF_STAT_BUSY) { + if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) { dev_err(cpu_dai->dev, "error: busy\n"); return -EBUSY; } -- cgit v1.2.3 From 57e756d30268a0373cce97165bd57331554f4bfd Mon Sep 17 00:00:00 2001 From: "Felipe F. Tonello" Date: Fri, 29 Jan 2016 11:01:00 +0000 Subject: ASoC: fsl-asoc-card: add cs4271 and cs4272 support add cs4271 and cs42727 support for fsl-asoc-card Signed-off-by: Felipe F. Tonello Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | 9 +++++++++ sound/soc/fsl/Kconfig | 4 ++-- sound/soc/fsl/fsl-asoc-card.c | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index ce55c0a6f757..943b36d1b81b 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -24,6 +24,9 @@ The compatible list for this generic sound card currently: "fsl,imx-audio-cs42888" + "fsl,imx-audio-cs427x" + (compatible with CS4271 and CS4272) + "fsl,imx-audio-wm8962" (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt) @@ -61,6 +64,12 @@ Optional properties: - audio-asrc : The phandle of ASRC. It can be absent if there's no need to add ASRC support via DPCM. +Optional unless SSI is selected as a CPU DAI: + + - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) + + - mux-ext-port : The external port of the i.MX audio muxer + Example: sound-cs42888 { compatible = "fsl,imx-audio-cs42888"; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 14dfdee05fd5..35aabf9dc503 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -292,8 +292,8 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_SSI help ALSA SoC Audio support with ASRC feature for Freescale SoCs that have - ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888 - and SGTL5000. + ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888, + CS4271, CS4272 and SGTL5000. Say Y if you want to add support for Freescale Generic ASoC Sound Card. endif # SND_IMX_SOC diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 3d40797e3bcc..dffd549a0e2a 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -28,6 +28,8 @@ #include "../codecs/wm8962.h" #include "../codecs/wm8960.h" +#define CS427x_SYSCLK_MCLK 0 + #define RX 0 #define TX 1 @@ -535,6 +537,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { + codec_dai_name = "cs4271-hifi"; + priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { codec_dai_name = "sgtl5000"; priv->codec_priv.mclk_id = SGTL5000_SYSCLK; @@ -692,6 +698,7 @@ fail: static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, + { .compatible = "fsl,imx-audio-cs427x", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8960", }, -- cgit v1.2.3 From 3bdff244a2bcbde37ec33bf3cdde4638049c6c38 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:10 +0800 Subject: ALSA: pcm: Add snd_pcm_rate_range_to_bits() This helper function can convert a given sample rate range to SNDRV_PCM_RATE_xxx bits. Signed-off-by: Mengdong Lin Acked-by: Takashi Iwai Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/pcm.h | 2 ++ sound/core/pcm_misc.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'sound') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b0be09279943..af1fb37c6b26 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1093,6 +1093,8 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, unsigned int rates_b); +unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min, + unsigned int rate_max); /** * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index ebe8444de6c6..53dc37357bca 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, return rates_a & rates_b; } EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect); + +/** + * snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit + * @rate_min: the minimum sample rate + * @rate_max: the maximum sample rate + * + * This function has an implicit assumption: the rates in the given range have + * only the pre-defined rates like 44100 or 16000. + * + * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range, + * or SNDRV_PCM_RATE_KNOT for an unknown range. + */ +unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min, + unsigned int rate_max) +{ + unsigned int rates = 0; + int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) { + if (snd_pcm_known_rates.list[i] >= rate_min + && snd_pcm_known_rates.list[i] <= rate_max) + rates |= 1 << i; + } + + if (!rates) + rates = SNDRV_PCM_RATE_KNOT; + + return rates; +} +EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits); -- cgit v1.2.3 From efd931d6c7fc49dc555150c600555df0fac7bf14 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 6 Feb 2016 11:25:49 +0100 Subject: ALSA: Remove deprecated AU1X00 AC97 driver The AU1X00 AC97 ALSA driver was deprecated in commit 7137c6bcb7ff ("ALSA: deprecate MIPS AU1X00 AC97 driver") in favor of the newer and better ASoC driver for the same hardware. This was almost 5 years ago and this driver has not been in use in the mainline kernel since, it should be safe to remove it at this point. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/mips/Kconfig | 12 - sound/mips/Makefile | 2 - sound/mips/au1x00.c | 734 ---------------------------------------------------- 3 files changed, 748 deletions(-) delete mode 100644 sound/mips/au1x00.c (limited to 'sound') diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig index 2153d31fb663..4a4705031cb9 100644 --- a/sound/mips/Kconfig +++ b/sound/mips/Kconfig @@ -23,17 +23,5 @@ config SND_SGI_HAL2 help Sound support for the SGI Indy and Indigo2 Workstation. - -config SND_AU1X00 - tristate "Au1x00 AC97 Port Driver (DEPRECATED)" - depends on MIPS_ALCHEMY - select SND_PCM - select SND_AC97_CODEC - help - ALSA Sound driver for the Au1x00's AC97 port. - - Newer drivers for ASoC are available, please do not use - this driver as it will be removed in the future. - endif # SND_MIPS diff --git a/sound/mips/Makefile b/sound/mips/Makefile index 861ec0a574b4..b977c44330d6 100644 --- a/sound/mips/Makefile +++ b/sound/mips/Makefile @@ -2,11 +2,9 @@ # Makefile for ALSA # -snd-au1x00-objs := au1x00.o snd-sgi-o2-objs := sgio2audio.o ad1843.o snd-sgi-hal2-objs := hal2.o # Toplevel Module Dependency -obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c deleted file mode 100644 index 1e30e8475431..000000000000 --- a/sound/mips/au1x00.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port - * - * Copyright 2004 Cooper Street Innovations Inc. - * Author: Charles Eidsness - * - * 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. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * - * 2004-09-09 Charles Eidsness -- Original verion -- based on - * sa11xx-uda1341.c ALSA driver and the - * au1000.c OSS driver. - * 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Charles Eidsness "); -MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}"); - -#define PLAYBACK 0 -#define CAPTURE 1 -#define AC97_SLOT_3 0x01 -#define AC97_SLOT_4 0x02 -#define AC97_SLOT_6 0x08 -#define AC97_CMD_IRQ 31 -#define READ 0 -#define WRITE 1 -#define READ_WAIT 2 -#define RW_DONE 3 - -struct au1000_period -{ - u32 start; - u32 relative_end; /*realtive to start of buffer*/ - struct au1000_period * next; -}; - -/*Au1000 AC97 Port Control Reisters*/ -struct au1000_ac97_reg { - u32 volatile config; - u32 volatile status; - u32 volatile data; - u32 volatile cmd; - u32 volatile cntrl; -}; - -struct audio_stream { - struct snd_pcm_substream *substream; - int dma; - spinlock_t dma_lock; - struct au1000_period * buffer; - unsigned int period_size; - unsigned int periods; -}; - -struct snd_au1000 { - struct snd_card *card; - struct au1000_ac97_reg volatile *ac97_ioport; - - struct resource *ac97_res_port; - spinlock_t ac97_lock; - struct snd_ac97 *ac97; - - struct snd_pcm *pcm; - struct audio_stream *stream[2]; /* playback & capture */ - int dmaid[2]; /* tx(0)/rx(1) DMA ids */ -}; - -/*--------------------------- Local Functions --------------------------------*/ -static void -au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots) -{ - u32 volatile ac97_config; - - spin_lock(&au1000->ac97_lock); - ac97_config = au1000->ac97_ioport->config; - ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK; - ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT); - au1000->ac97_ioport->config = ac97_config; - spin_unlock(&au1000->ac97_lock); -} - -static void -au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots) -{ - u32 volatile ac97_config; - - spin_lock(&au1000->ac97_lock); - ac97_config = au1000->ac97_ioport->config; - ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK; - ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT); - au1000->ac97_ioport->config = ac97_config; - spin_unlock(&au1000->ac97_lock); -} - - -static void -au1000_release_dma_link(struct audio_stream *stream) -{ - struct au1000_period * pointer; - struct au1000_period * pointer_next; - - stream->period_size = 0; - stream->periods = 0; - pointer = stream->buffer; - if (! pointer) - return; - do { - pointer_next = pointer->next; - kfree(pointer); - pointer = pointer_next; - } while (pointer != stream->buffer); - stream->buffer = NULL; -} - -static int -au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes, - unsigned int periods) -{ - struct snd_pcm_substream *substream = stream->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - struct au1000_period *pointer; - unsigned long dma_start; - int i; - - dma_start = virt_to_phys(runtime->dma_area); - - if (stream->period_size == period_bytes && - stream->periods == periods) - return 0; /* not changed */ - - au1000_release_dma_link(stream); - - stream->period_size = period_bytes; - stream->periods = periods; - - stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL); - if (! stream->buffer) - return -ENOMEM; - pointer = stream->buffer; - for (i = 0; i < periods; i++) { - pointer->start = (u32)(dma_start + (i * period_bytes)); - pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1); - if (i < periods - 1) { - pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL); - if (! pointer->next) { - au1000_release_dma_link(stream); - return -ENOMEM; - } - pointer = pointer->next; - } - } - pointer->next = stream->buffer; - return 0; -} - -static void -au1000_dma_stop(struct audio_stream *stream) -{ - if (snd_BUG_ON(!stream->buffer)) - return; - disable_dma(stream->dma); -} - -static void -au1000_dma_start(struct audio_stream *stream) -{ - if (snd_BUG_ON(!stream->buffer)) - return; - - init_dma(stream->dma); - if (get_dma_active_buffer(stream->dma) == 0) { - clear_dma_done0(stream->dma); - set_dma_addr0(stream->dma, stream->buffer->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - set_dma_addr1(stream->dma, stream->buffer->next->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - } else { - clear_dma_done1(stream->dma); - set_dma_addr1(stream->dma, stream->buffer->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - set_dma_addr0(stream->dma, stream->buffer->next->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - } - enable_dma_buffers(stream->dma); - start_dma(stream->dma); -} - -static irqreturn_t -au1000_dma_interrupt(int irq, void *dev_id) -{ - struct audio_stream *stream = (struct audio_stream *) dev_id; - struct snd_pcm_substream *substream = stream->substream; - - spin_lock(&stream->dma_lock); - switch (get_dma_buffer_done(stream->dma)) { - case DMA_D0: - stream->buffer = stream->buffer->next; - clear_dma_done0(stream->dma); - set_dma_addr0(stream->dma, stream->buffer->next->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - enable_dma_buffer0(stream->dma); - break; - case DMA_D1: - stream->buffer = stream->buffer->next; - clear_dma_done1(stream->dma); - set_dma_addr1(stream->dma, stream->buffer->next->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - enable_dma_buffer1(stream->dma); - break; - case (DMA_D0 | DMA_D1): - printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma); - au1000_dma_stop(stream); - au1000_dma_start(stream); - break; - case (~DMA_D0 & ~DMA_D1): - printk(KERN_ERR "DMA %d empty irq.\n",stream->dma); - } - spin_unlock(&stream->dma_lock); - snd_pcm_period_elapsed(substream); - return IRQ_HANDLED; -} - -/*-------------------------- PCM Audio Streams -------------------------------*/ - -static unsigned int rates[] = {8000, 11025, 16000, 22050}; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, -}; - -static struct snd_pcm_hardware snd_au1000_hw = -{ - .info = (SNDRV_PCM_INFO_INTERLEAVED | \ - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050), - .rate_min = 8000, - .rate_max = 22050, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = 128*1024, - .period_bytes_min = 32, - .period_bytes_max = 16*1024, - .periods_min = 8, - .periods_max = 255, - .fifo_size = 16, -}; - -static int -snd_au1000_playback_open(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[PLAYBACK]->substream = substream; - au1000->stream[PLAYBACK]->buffer = NULL; - substream->private_data = au1000->stream[PLAYBACK]; - substream->runtime->hw = snd_au1000_hw; - return (snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0); -} - -static int -snd_au1000_capture_open(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[CAPTURE]->substream = substream; - au1000->stream[CAPTURE]->buffer = NULL; - substream->private_data = au1000->stream[CAPTURE]; - substream->runtime->hw = snd_au1000_hw; - return (snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0); -} - -static int -snd_au1000_playback_close(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[PLAYBACK]->substream = NULL; - return 0; -} - -static int -snd_au1000_capture_close(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[CAPTURE]->substream = NULL; - return 0; -} - -static int -snd_au1000_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct audio_stream *stream = substream->private_data; - int err; - - err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - return au1000_setup_dma_link(stream, - params_period_bytes(hw_params), - params_periods(hw_params)); -} - -static int -snd_au1000_hw_free(struct snd_pcm_substream *substream) -{ - struct audio_stream *stream = substream->private_data; - au1000_release_dma_link(stream); - return snd_pcm_lib_free_pages(substream); -} - -static int -snd_au1000_playback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (runtime->channels == 1) - au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4); - else - au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); - snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - return 0; -} - -static int -snd_au1000_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (runtime->channels == 1) - au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4); - else - au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); - snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); - return 0; -} - -static int -snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct audio_stream *stream = substream->private_data; - int err = 0; - - spin_lock(&stream->dma_lock); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - au1000_dma_start(stream); - break; - case SNDRV_PCM_TRIGGER_STOP: - au1000_dma_stop(stream); - break; - default: - err = -EINVAL; - break; - } - spin_unlock(&stream->dma_lock); - return err; -} - -static snd_pcm_uframes_t -snd_au1000_pointer(struct snd_pcm_substream *substream) -{ - struct audio_stream *stream = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - long location; - - spin_lock(&stream->dma_lock); - location = get_dma_residue(stream->dma); - spin_unlock(&stream->dma_lock); - location = stream->buffer->relative_end - location; - if (location == -1) - location = 0; - return bytes_to_frames(runtime,location); -} - -static struct snd_pcm_ops snd_card_au1000_playback_ops = { - .open = snd_au1000_playback_open, - .close = snd_au1000_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_au1000_hw_params, - .hw_free = snd_au1000_hw_free, - .prepare = snd_au1000_playback_prepare, - .trigger = snd_au1000_trigger, - .pointer = snd_au1000_pointer, -}; - -static struct snd_pcm_ops snd_card_au1000_capture_ops = { - .open = snd_au1000_capture_open, - .close = snd_au1000_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_au1000_hw_params, - .hw_free = snd_au1000_hw_free, - .prepare = snd_au1000_capture_prepare, - .trigger = snd_au1000_trigger, - .pointer = snd_au1000_pointer, -}; - -static int -snd_au1000_pcm_new(struct snd_au1000 *au1000) -{ - struct snd_pcm *pcm; - int err; - unsigned long flags; - - if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0) - return err; - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_card_au1000_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_card_au1000_capture_ops); - - pcm->private_data = au1000; - pcm->info_flags = 0; - strcpy(pcm->name, "Au1000 AC97 PCM"); - - spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock); - spin_lock_init(&au1000->stream[CAPTURE]->dma_lock); - - flags = claim_dma_lock(); - au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0], - "AC97 TX", au1000_dma_interrupt, 0, - au1000->stream[PLAYBACK]); - if (au1000->stream[PLAYBACK]->dma < 0) { - release_dma_lock(flags); - return -EBUSY; - } - au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1], - "AC97 RX", au1000_dma_interrupt, 0, - au1000->stream[CAPTURE]); - if (au1000->stream[CAPTURE]->dma < 0){ - release_dma_lock(flags); - return -EBUSY; - } - /* enable DMA coherency in read/write DMA channels */ - set_dma_mode(au1000->stream[PLAYBACK]->dma, - get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC); - set_dma_mode(au1000->stream[CAPTURE]->dma, - get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC); - release_dma_lock(flags); - au1000->pcm = pcm; - return 0; -} - - -/*-------------------------- AC97 CODEC Control ------------------------------*/ - -static unsigned short -snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg) -{ - struct snd_au1000 *au1000 = ac97->private_data; - u32 volatile cmd; - u16 volatile data; - int i; - - spin_lock(&au1000->ac97_lock); -/* would rather use the interrupt than this polling but it works and I can't -get the interrupt driven case to work efficiently */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) - printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); - - cmd = (u32) reg & AC97C_INDEX_MASK; - cmd |= AC97C_READ; - au1000->ac97_ioport->cmd = cmd; - - /* now wait for the data */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) { - printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); - spin_unlock(&au1000->ac97_lock); - return 0; - } - - data = au1000->ac97_ioport->cmd & 0xffff; - spin_unlock(&au1000->ac97_lock); - - return data; - -} - - -static void -snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) -{ - struct snd_au1000 *au1000 = ac97->private_data; - u32 cmd; - int i; - - spin_lock(&au1000->ac97_lock); -/* would rather use the interrupt than this polling but it works and I can't -get the interrupt driven case to work efficiently */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) - printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n"); - - cmd = (u32) reg & AC97C_INDEX_MASK; - cmd &= ~AC97C_READ; - cmd |= ((u32) val << AC97C_WD_BIT); - au1000->ac97_ioport->cmd = cmd; - spin_unlock(&au1000->ac97_lock); -} - -/*------------------------------ Setup / Destroy ----------------------------*/ - -static void snd_au1000_free(struct snd_card *card) -{ - struct snd_au1000 *au1000 = card->private_data; - - if (au1000->stream[PLAYBACK]) { - if (au1000->stream[PLAYBACK]->dma >= 0) - free_au1000_dma(au1000->stream[PLAYBACK]->dma); - kfree(au1000->stream[PLAYBACK]); - } - - if (au1000->stream[CAPTURE]) { - if (au1000->stream[CAPTURE]->dma >= 0) - free_au1000_dma(au1000->stream[CAPTURE]->dma); - kfree(au1000->stream[CAPTURE]); - } - - if (au1000->ac97_res_port) { - /* put internal AC97 block into reset */ - if (au1000->ac97_ioport) { - au1000->ac97_ioport->cntrl = AC97C_RS; - iounmap(au1000->ac97_ioport); - au1000->ac97_ioport = NULL; - } - release_and_free_resource(au1000->ac97_res_port); - au1000->ac97_res_port = NULL; - } -} - -static struct snd_ac97_bus_ops ops = { - .write = snd_au1000_ac97_write, - .read = snd_au1000_ac97_read, -}; - -static int au1000_ac97_probe(struct platform_device *pdev) -{ - int err; - void __iomem *io; - struct resource *r; - struct snd_card *card; - struct snd_au1000 *au1000; - struct snd_ac97_bus *pbus; - struct snd_ac97_template ac97; - - err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE, - sizeof(struct snd_au1000), &card); - if (err < 0) - return err; - - au1000 = card->private_data; - au1000->card = card; - spin_lock_init(&au1000->ac97_lock); - - /* from here on let ALSA call the special freeing function */ - card->private_free = snd_au1000_free; - - /* TX DMA ID */ - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!r) { - err = -ENODEV; - snd_printk(KERN_INFO "no TX DMA platform resource!\n"); - goto out; - } - au1000->dmaid[0] = r->start; - - /* RX DMA ID */ - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!r) { - err = -ENODEV; - snd_printk(KERN_INFO "no RX DMA platform resource!\n"); - goto out; - } - au1000->dmaid[1] = r->start; - - au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), - GFP_KERNEL); - if (!au1000->stream[PLAYBACK]) { - err = -ENOMEM; - goto out; - } - au1000->stream[PLAYBACK]->dma = -1; - - au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream), - GFP_KERNEL); - if (!au1000->stream[CAPTURE]) { - err = -ENOMEM; - goto out; - } - au1000->stream[CAPTURE]->dma = -1; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - err = -ENODEV; - goto out; - } - - err = -EBUSY; - au1000->ac97_res_port = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!au1000->ac97_res_port) { - snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n"); - goto out; - } - - io = ioremap(r->start, resource_size(r)); - if (!io) - goto out; - - au1000->ac97_ioport = (struct au1000_ac97_reg *)io; - - /* configure pins for AC'97 - TODO: move to board_setup.c */ - au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); - - /* Initialise Au1000's AC'97 Control Block */ - au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE; - udelay(10); - au1000->ac97_ioport->cntrl = AC97C_CE; - udelay(10); - - /* Initialise External CODEC -- cold reset */ - au1000->ac97_ioport->config = AC97C_RESET; - udelay(10); - au1000->ac97_ioport->config = 0x0; - mdelay(5); - - /* Initialise AC97 middle-layer */ - err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus); - if (err < 0) - goto out; - - memset(&ac97, 0, sizeof(ac97)); - ac97.private_data = au1000; - err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97); - if (err < 0) - goto out; - - err = snd_au1000_pcm_new(au1000); - if (err < 0) - goto out; - - strcpy(card->driver, "Au1000-AC97"); - strcpy(card->shortname, "AMD Au1000-AC97"); - sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver"); - - err = snd_card_register(card); - if (err < 0) - goto out; - - printk(KERN_INFO "ALSA AC97: Driver Initialized\n"); - - platform_set_drvdata(pdev, card); - - return 0; - - out: - snd_card_free(card); - return err; -} - -static int au1000_ac97_remove(struct platform_device *pdev) -{ - return snd_card_free(platform_get_drvdata(pdev)); -} - -struct platform_driver au1000_ac97c_driver = { - .driver = { - .name = "au1000-ac97c", - .owner = THIS_MODULE, - }, - .probe = au1000_ac97_probe, - .remove = au1000_ac97_remove, -}; - -module_platform_driver(au1000_ac97c_driver); -- cgit v1.2.3 From 33420d66354134b9369d0984a6ca418752c781a6 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 5 Feb 2016 12:19:04 +0530 Subject: ASoC: Intel: Skylake: Update the delay check Delay check was using ternary operator, it can be simplified to simple if condition, so update it Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b6e6b61d10ec..cc9a5599ab9d 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -863,7 +863,9 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, else delay += hstream->bufsize; } - delay = (hstream->bufsize == delay) ? 0 : delay; + + if (hstream->bufsize == delay) + delay = 0; if (delay >= hstream->period_bytes) { dev_info(bus->dev, -- cgit v1.2.3 From 4fdf810fc5c82f57aa7c54ac9384f1201087724a Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Fri, 5 Feb 2016 12:19:05 +0530 Subject: ASoC: Intel: Skylake: Get node id using helper skl_setup_cpr_gateway_cfg() retrieves gateway settings which are required for copier module configuration. For upcoming DMA control IPC we need similar retrieval, so separate this piece into skl_get_node_id() helper which will be common for these functions Signed-off-by: Dharageswari R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 4629372d7c8e..60f9fe389887 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -238,9 +238,8 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use */ -static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, - struct skl_module_cfg *mconfig, - struct skl_cpr_cfg *cpr_mconfig) +static u32 skl_get_node_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) { union skl_connector_node_id node_id = {0}; union skl_ssp_dma_node ssp_node = {0}; @@ -289,13 +288,24 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, break; default: - cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + node_id.val = 0xFFFFFFFF; + break; + } + + return node_id.val; +} + +static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_cpr_cfg *cpr_mconfig) +{ + cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig); + + if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) { cpr_mconfig->cpr_feature_mask = 0; return; } - cpr_mconfig->gtw_cfg.node_id = node_id.val; - if (SKL_CONN_SOURCE == mconfig->hw_conn_type) cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs; else -- cgit v1.2.3 From 718a42b5eaddb3180e2589590732d61e047858fb Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Fri, 5 Feb 2016 12:19:06 +0530 Subject: ASoC: Intel: Skylake: Add skl_tplg_be_get_cpr_module() helper An I2S port can be connected to multiple BE pipes, get module config only for the active BE pipe. This helpers helps to do that and is used in subsequent patches Signed-off-by: Dharageswari R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 60 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 2 ++ 2 files changed, 62 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a294fee431f0..5c8661450349 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1091,6 +1091,66 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) return NULL; } +static struct skl_module_cfg *skl_get_mconfig_pb_cpr( + struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct skl_module_cfg *mconfig = NULL; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (w->endpoints[SND_SOC_DAPM_DIR_OUT] > 0) { + if (p->connect && + (p->sink->id == snd_soc_dapm_aif_out) && + p->source->priv) { + mconfig = p->source->priv; + return mconfig; + } + mconfig = skl_get_mconfig_pb_cpr(dai, p->source); + if (mconfig) + return mconfig; + } + } + return mconfig; +} + +static struct skl_module_cfg *skl_get_mconfig_cap_cpr( + struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct skl_module_cfg *mconfig = NULL; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (w->endpoints[SND_SOC_DAPM_DIR_IN] > 0) { + if (p->connect && + (p->source->id == snd_soc_dapm_aif_in) && + p->sink->priv) { + mconfig = p->sink->priv; + return mconfig; + } + mconfig = skl_get_mconfig_cap_cpr(dai, p->sink); + if (mconfig) + return mconfig; + } + } + return mconfig; +} + +struct skl_module_cfg * +skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + mconfig = skl_get_mconfig_pb_cpr(dai, w); + } else { + w = dai->capture_widget; + mconfig = skl_get_mconfig_cap_cpr(dai, w); + } + return mconfig; +} + static u8 skl_tplg_be_link_type(int dev_type) { int ret; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 9aa2a2b6598a..5f0707cf1f54 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -345,5 +345,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); +struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, + int stream); enum skl_bitdepth skl_get_bit_depth(int params); #endif -- cgit v1.2.3 From c115fa5ec06a647c5aeff95d73e56d488145ec2e Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Fri, 5 Feb 2016 12:19:07 +0530 Subject: ASoC: Intel: Skylake: Add MCLK configuration The SoC has MCLK output which is typically required by codecs. The MCLK is controlled by DSP FW, so driver can configure that by sending DMA_CONTROL IPC. The configuration for MCLK is present in the endpoint blob. So if block has this configuration, send IPC to DSP for MCLK configuration. This is done by new function skl_dsp_set_dma_control() which is invoked by BE prepare. Signed-off-by: Dharageswari R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 40 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-pcm.c | 18 +++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 25 +++++++++++++++++++++ 3 files changed, 83 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 60f9fe389887..737934cc620d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -317,6 +317,46 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, skl_copy_copier_caps(mconfig, cpr_mconfig); } +#define DMA_CONTROL_ID 5 + +int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + struct skl_dma_control *dma_ctrl; + struct skl_i2s_config_blob config_blob; + struct skl_ipc_large_config_msg msg = {0}; + int err = 0; + + + /* + * if blob size is same as capablity size, then no dma control + * present so return + */ + if (mconfig->formats_config.caps_size == sizeof(config_blob)) + return 0; + + msg.large_param_id = DMA_CONTROL_ID; + msg.param_data_size = sizeof(struct skl_dma_control) + + mconfig->formats_config.caps_size; + + dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL); + if (dma_ctrl == NULL) + return -ENOMEM; + + dma_ctrl->node_id = skl_get_node_id(ctx, mconfig); + + /* size in dwords */ + dma_ctrl->config_length = sizeof(config_blob) / 4; + + memcpy(dma_ctrl->config_data, mconfig->formats_config.caps, + mconfig->formats_config.caps_size); + + err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); + + kfree(dma_ctrl); + + return err; +} + static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index cc9a5599ab9d..092450ac1c78 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -206,6 +206,23 @@ static int skl_get_format(struct snd_pcm_substream *substream, return format_val; } +static int skl_be_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + if ((dai->playback_active > 1) || (dai->capture_active > 1)) + return 0; + + mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (mconfig == NULL) + return -EINVAL; + + return skl_dsp_set_dma_control(ctx, mconfig); +} + static int skl_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -588,6 +605,7 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = { static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { .hw_params = skl_be_hw_params, + .prepare = skl_be_prepare, }; static struct snd_soc_dai_ops skl_link_dai_ops = { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 5f0707cf1f54..de3c401284d9 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -113,6 +113,29 @@ struct skl_cpr_gtw_cfg { u32 config_data[1]; } __packed; +struct skl_i2s_config_blob { + u32 gateway_attrib; + u32 tdm_ts_group[8]; + u32 ssc0; + u32 ssc1; + u32 sscto; + u32 sspsp; + u32 sstsa; + u32 ssrsa; + u32 ssc2; + u32 sspsp2; + u32 ssc3; + u32 ssioc; + u32 mdivc; + u32 mdivr; +} __packed; + +struct skl_dma_control { + u32 node_id; + u32 config_length; + u32 config_data[1]; +} __packed; + struct skl_cpr_cfg { struct skl_base_cfg base_cfg; struct skl_audio_data_format out_fmt; @@ -313,6 +336,8 @@ static inline struct skl *get_skl_ctx(struct device *dev) int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); +int skl_dsp_set_dma_control(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_platform *platform, -- cgit v1.2.3 From cc6a4044bdeeb7a7769b7aedf9afe9e1464aadf0 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 5 Feb 2016 12:19:08 +0530 Subject: ASoC: Intel: Skylake: Allow module parameter set after bind Some modules require params to be set after the module is bound to all the pins connected. The module provider initializes set_param flag for such modules and we send params after binding. This is done by the function skl_tplg_set_module_bind_params() Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 72 ++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-tplg-interface.h | 3 +- 2 files changed, 74 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 5c8661450349..a7be03665dd4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -545,6 +545,66 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +/* + * Some modules require params to be set after the module is bound to + * all pins connected. + * + * The module provider initializes set_param flag for such modules and we + * send params after binding + */ +static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, + struct skl_module_cfg *mcfg, struct skl_sst *ctx) +{ + int i, ret; + struct skl_module_cfg *mconfig = w->priv; + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_algo_data *bc; + struct skl_specific_cfg *sp_cfg; + + /* + * check all out/in pins are in bind state. + * if so set the module param + */ + for (i = 0; i < mcfg->max_out_queue; i++) { + if (mcfg->m_out_pin[i].pin_state != SKL_PIN_BIND_DONE) + return 0; + } + + for (i = 0; i < mcfg->max_in_queue; i++) { + if (mcfg->m_in_pin[i].pin_state != SKL_PIN_BIND_DONE) + return 0; + } + + if (mconfig->formats_config.caps_size > 0 && + mconfig->formats_config.set_params == SKL_PARAM_BIND) { + sp_cfg = &mconfig->formats_config; + ret = skl_set_module_params(ctx, sp_cfg->caps, + sp_cfg->caps_size, + sp_cfg->param_id, mconfig); + if (ret < 0) + return ret; + } + + for (i = 0; i < w->num_kcontrols; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + + if (bc->set_params == SKL_PARAM_BIND) { + ret = skl_set_module_params(ctx, + (u32 *)bc->params, bc->max, + bc->param_id, mconfig); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, struct skl *skl, struct snd_soc_dapm_widget *src_w, @@ -579,11 +639,19 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, sink = p->sink; sink_mconfig = sink->priv; + if (src_mconfig->m_state == SKL_MODULE_UNINIT || + sink_mconfig->m_state == SKL_MODULE_UNINIT) + continue; + /* Bind source to sink, mixin is always source */ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); if (ret) return ret; + /* set module params after bind */ + skl_tplg_set_module_bind_params(src_w, src_mconfig, ctx); + skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx); + /* Start sinks pipe first */ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { if (sink_mconfig->pipe->conn_type != @@ -714,6 +782,10 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; + /* set module params after bind */ + skl_tplg_set_module_bind_params(source, src_mconfig, ctx); + skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx); + if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) ret = skl_run_pipe(ctx, sink_mconfig->pipe); } diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index c9ae010b3cc8..1db88a63ac17 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -144,7 +144,8 @@ enum module_pin_type { enum skl_module_param_type { SKL_PARAM_DEFAULT = 0, SKL_PARAM_INIT, - SKL_PARAM_SET + SKL_PARAM_SET, + SKL_PARAM_BIND }; struct skl_dfw_module_pin { -- cgit v1.2.3 From c5a76a246989c8155d372142089ccfc9dc022388 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 5 Feb 2016 12:19:09 +0530 Subject: ASoC: Intel: Skylake: Add shutdown callback While going to shutdown, we need to bring HW to clean state. This is done by cleaning up stream descriptor registers. This cleanup is already done by decoupling of stream and stopping the chip, so invoke these from shutdown handler. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 092705e73db4..06f4b2c13423 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -634,6 +634,31 @@ out_free: return err; } +static void skl_shutdown(struct pci_dev *pci) +{ + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *s; + struct hdac_ext_stream *stream; + struct skl *skl; + + if (ebus == NULL) + return; + + skl = ebus_to_skl(ebus); + + if (skl->init_failed) + return; + + snd_hdac_ext_stop_streams(ebus); + list_for_each_entry(s, &bus->stream_list, list) { + stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(ebus, stream, false); + } + + snd_hdac_bus_stop_chip(bus); +} + static void skl_remove(struct pci_dev *pci) { struct hdac_ext_bus *ebus = pci_get_drvdata(pci); @@ -677,6 +702,7 @@ static struct pci_driver skl_driver = { .id_table = skl_ids, .probe = skl_probe, .remove = skl_remove, + .shutdown = skl_shutdown, .driver = { .pm = &skl_pm, }, -- cgit v1.2.3 From 2d1419a329e7de825c03738ad2ac532153239388 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 5 Feb 2016 12:19:10 +0530 Subject: ASoC: Intel: Skylake: Fill BE blob with default params When we have a path that connects to DSP sink and source, we need to query the blob using the default params. So add a function to query the params for such path Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a7be03665dd4..216d7a8e5680 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -260,6 +260,64 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, multiplier; } +static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + int link_type, dir; + u32 ch, s_freq, s_fmt; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(ctx->dev); + + /* check if we already have blob */ + if (m_cfg->formats_config.caps_size > 0) + return 0; + + switch (m_cfg->dev_type) { + case SKL_DEVICE_DMIC: + link_type = NHLT_LINK_DMIC; + dir = 1; + s_freq = m_cfg->in_fmt[0].s_freq; + s_fmt = m_cfg->in_fmt[0].bit_depth; + ch = m_cfg->in_fmt[0].channels; + break; + + case SKL_DEVICE_I2S: + link_type = NHLT_LINK_SSP; + if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { + dir = 1; + s_freq = m_cfg->in_fmt[0].s_freq; + s_fmt = m_cfg->in_fmt[0].bit_depth; + ch = m_cfg->in_fmt[0].channels; + } else { + dir = 0; + s_freq = m_cfg->out_fmt[0].s_freq; + s_fmt = m_cfg->out_fmt[0].bit_depth; + ch = m_cfg->out_fmt[0].channels; + } + break; + + default: + return -EINVAL; + } + + /* update the blob based on virtual bus_id and default params */ + cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, + s_fmt, ch, s_freq, dir); + if (cfg) { + m_cfg->formats_config.caps_size = cfg->size; + m_cfg->formats_config.caps = (u32 *) &cfg->caps; + } else { + dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n", + m_cfg->vbus_id, link_type, dir); + dev_err(ctx->dev, "PCM: ch %d, freq %d, fmt %d\n", + ch, s_freq, s_fmt); + return -EIO; + } + + return 0; +} + static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, struct skl_sst *ctx) { @@ -433,6 +491,9 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return ret; } + /* update blob if blob is null for be with default value */ + skl_tplg_update_be_blob(w, ctx); + /* * apply fix/conversion to module params based on * FE/BE params -- cgit v1.2.3 From 0d5ee195b1091e6ebcc121c091ce8646e95a48b6 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:15 +0900 Subject: ALSA: dice: limit to current sampling transfer frequency ALSA PCM core has a functionality for rule of PCM substream parameters. Typically, when userspace opens PCM character devices, each driver adds its own rules to PCM substream according to design of hardware. When the userspace executes hw_params ioctl with favorite parameters, the actual parameters are calculated according to the rules and the given parameters. Then, the result is returned to userspace. Currently, ALSA Dice driver has the rule between channels and rates, while Dice interface design doesn't allow drivers to retrieve all of the combinations. Dice drivers are just allowed to get current sampling transfer frequency and the number of multi bit linear audio data channels in an data block of an AMDTP packet. This commit purges the rule, and limit PCM substreams to current sampling transfer frequency, following to the interface design. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-pcm.c | 170 ++++++++--------------------------------- 1 file changed, 31 insertions(+), 139 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 9b3431999fc8..0b6e6bbed4b8 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,99 +9,40 @@ #include "dice.h" -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *c = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *r = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(c, pcm_channels[mode])) - continue; - - rates.min = min(rates.min, rate); - rates.max = max(rates.max, rate); - } - - return snd_interval_refine(r, &rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *r = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *c = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(r, rate)) - continue; - - channels.min = min(channels.min, pcm_channels[mode]); - channels.max = max(channels.max, pcm_channels[mode]); - } - - return snd_interval_refine(c, &channels); -} - -static void limit_channels_and_rates(struct snd_dice *dice, - struct snd_pcm_runtime *runtime, - unsigned int *pcm_channels) +static int limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + struct amdtp_stream *stream) { struct snd_pcm_hardware *hw = &runtime->hw; - unsigned int i, rate, mode; + unsigned int rate; + __be32 reg[2]; + int err; - hw->channels_min = UINT_MAX; - hw->channels_max = 0; + /* + * Retrieve current Multi Bit Linear Audio data channel and limit to + * it. + */ + if (stream == &dice->tx_stream) { + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + reg, sizeof(reg)); + } + if (err < 0) + return err; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - hw->rates |= snd_pcm_rate_to_rate_bit(rate); + hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]); - if (pcm_channels[mode] == 0) - continue; - hw->channels_min = min(hw->channels_min, pcm_channels[mode]); - hw->channels_max = max(hw->channels_max, pcm_channels[mode]); - } + /* Retrieve current sampling transfer frequency and limit to it. */ + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; + hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); + + return 0; } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) @@ -122,7 +63,6 @@ static int init_hw_info(struct snd_dice *dice, struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; - unsigned int *pcm_channels; int err; hw->info = SNDRV_PCM_INFO_MMAP | @@ -135,37 +75,22 @@ static int init_hw_info(struct snd_dice *dice, if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; - pcm_channels = dice->tx_channels; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; - pcm_channels = dice->rx_channels; } - limit_channels_and_rates(dice, runtime, pcm_channels); - limit_period_and_buffer(hw); - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, substream, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + err = limit_channels_and_rates(dice, runtime, stream); if (err < 0) - goto end; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, substream, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - goto end; + return err; + limit_period_and_buffer(hw); - err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); -end: - return err; + return amdtp_am824_add_pcm_hw_constraints(stream, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - unsigned int source, rate; - bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -176,39 +101,6 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto err_locked; - switch (source) { - case CLOCK_SOURCE_AES1: - case CLOCK_SOURCE_AES2: - case CLOCK_SOURCE_AES3: - case CLOCK_SOURCE_AES4: - case CLOCK_SOURCE_AES_ANY: - case CLOCK_SOURCE_ADAT: - case CLOCK_SOURCE_TDIF: - case CLOCK_SOURCE_WC: - internal = false; - break; - default: - internal = true; - break; - } - - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ - if (!internal || - amdtp_stream_pcm_running(&dice->tx_stream) || - amdtp_stream_pcm_running(&dice->rx_stream)) { - err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) - goto err_locked; - substream->runtime->hw.rate_min = rate; - substream->runtime->hw.rate_max = rate; - } - snd_pcm_set_sync(substream); end: return err; -- cgit v1.2.3 From 1bc8e12d9480e072a7818e9cc51ff901662f2d22 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:16 +0900 Subject: ALSA: dice: limit stream to current sampling transfer frequency. In previous commit, ALSA Dice driver limits PCM substreams at current sampling transfer frequency and current number of Multi bit linear audio data channel. Thus, the driver has no need to start AMDTP streams at the other sampling transfer frequency except for current one. This is due to Dice interface design. This commit limits AMDTP stream at current sampling transfer frequency, according to the design. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a6a39f7ef58d..4f74e3ef58fd 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -99,6 +99,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, unsigned int rate) { struct fw_iso_resources *resources; + __be32 reg[2]; unsigned int i, mode, pcm_chs, midi_ports; bool double_pcm_frames; int err; @@ -108,14 +109,20 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, goto end; if (stream == &dice->tx_stream) { resources = &dice->tx_resources; - pcm_chs = dice->tx_channels[mode]; - midi_ports = dice->tx_midi_ports[mode]; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + reg, sizeof(reg)); } else { resources = &dice->rx_resources; - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + reg, sizeof(reg)); } + if (err < 0) + goto end; + + pcm_chs = be32_to_cpu(reg[0]); + midi_ports = be32_to_cpu(reg[1]); + /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -224,8 +231,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) } if (rate == 0) rate = curr_rate; - if (rate != curr_rate) - stop_stream(dice, master); + if (rate != curr_rate) { + err = -EINVAL; + goto end; + } if (!amdtp_stream_running(master)) { stop_stream(dice, slave); -- cgit v1.2.3 From b9022f4d2cb4660249036768f7d09442b213d12f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:17 +0900 Subject: ALSA: dice: add MIDI ports according to current number of MIDI substreams This commit changes the way to add ALSA MIDI ports. This driver read the number of multiplexed MIDI substreams from hardware register, then adds the same number of ALSA MIDI ports. This commit is based on my assumption that the number is fixed at all of supported sampling transfer frequency. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-midi.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 151b09f240f2..2461311e695a 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice, int snd_dice_create_midi(struct snd_dice *dice) { + __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; - unsigned int i, midi_in_ports, midi_out_ports; + unsigned int midi_in_ports, midi_out_ports; int err; - midi_in_ports = midi_out_ports = 0; - for (i = 0; i < 3; i++) { - midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); - midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); - } + /* + * Use the number of MIDI conformant data channel at current sampling + * transfer frequency. + */ + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_in_ports = be32_to_cpu(reg); + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_out_ports = be32_to_cpu(reg); if (midi_in_ports + midi_out_ports == 0) return 0; -- cgit v1.2.3 From c30076568d20bd6f7a4e021b19672a6b03266430 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:18 +0900 Subject: ALSA: dice: get the number of MBLA data channel at opening PCM substream This commit is a preparation to remove members related to channel cache for the number of channels for multi bit linear audio data and MIDI ports. This commit changes the way to get the number of multi bit linear audio data channel. It's directly retrieved by asynchronous transactions to some registers. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-pcm.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 0b6e6bbed4b8..a5c9b58655ef 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -294,17 +294,30 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; + __be32 reg; struct snd_pcm *pcm; - unsigned int i, capture, playback; + unsigned int capture, playback; int err; - capture = playback = 0; - for (i = 0; i < 3; i++) { - if (dice->tx_channels[i] > 0) - capture = 1; - if (dice->rx_channels[i] > 0) - playback = 1; - } + /* + * Check whether PCM substreams are required. + * + * TODO: in the case that any PCM substreams are not avail at a certain + * sampling transfer frequency? + */ + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + ®, sizeof(reg)); + if (err < 0) + return err; + if (be32_to_cpu(reg) > 0) + capture = 1; + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + ®, sizeof(reg)); + if (err < 0) + return err; + if (be32_to_cpu(reg) > 0) + playback = 1; err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) -- cgit v1.2.3 From 6f688268b3f4ba494bdf1f9755a3dfa4d91369f0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:19 +0900 Subject: ALSA: dice: purge generating channel cache Dice interface design doesn't allow drivers to read supported combination between sampling transfer frequencies and the number of Multi bit linear audio data channels. Due to the design, ALSA dice driver changes current sampling transfer frequency to generate cache of the combinations at device probing processing. Although, this idea is worse because ALSA dice driver changes the state of clock. This is not what users want when they save favorite configuration to the device in advance. Furthermore, there's a possibility that the format of data block is decided not only according to current sampling transfer frequency, but also the other factors, i.e. data format for digital interface. It's not good to generate channel cache according to the sampling transfer frequency only. This commit purges processing cache data and related structure members. As a result, users must set preferable sampling transfer frequency before using ALSA PCM applications, as long as they want to start any PCM substreams at the rate except for current one. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 24 ++------------ sound/firewire/dice/dice.c | 67 ++------------------------------------- sound/firewire/dice/dice.h | 7 ---- 3 files changed, 5 insertions(+), 93 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 4f74e3ef58fd..716db092d7c7 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -24,23 +24,6 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, - unsigned int *mode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (!(dice->clock_caps & BIT(i))) - continue; - if (snd_dice_rates[i] != rate) - continue; - - *mode = (i - 1) / 2; - return 0; - } - return -EINVAL; -} - static void release_resources(struct snd_dice *dice, struct fw_iso_resources *resources) { @@ -100,13 +83,10 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, { struct fw_iso_resources *resources; __be32 reg[2]; - unsigned int i, mode, pcm_chs, midi_ports; + unsigned int i, pcm_chs, midi_ports; bool double_pcm_frames; int err; - err = snd_dice_stream_get_rate_mode(dice, rate, &mode); - if (err < 0) - goto end; if (stream == &dice->tx_stream) { resources = &dice->tx_resources; err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, @@ -133,7 +113,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - double_pcm_frames = mode > 1; + double_pcm_frames = rate > 96000; if (double_pcm_frames) { rate /= 2; pcm_chs *= 2; diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index b91b3739c810..f7303a650ac2 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -57,65 +57,10 @@ static int check_dice_category(struct fw_unit *unit) return 0; } -static int highest_supported_mode_rate(struct snd_dice *dice, - unsigned int mode, unsigned int *rate) -{ - unsigned int i, m; - - for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { - *rate = snd_dice_rates[i - 1]; - if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) - continue; - if (mode == m) - break; - } - if (i == 0) - return -EINVAL; - - return 0; -} - -static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) -{ - __be32 values[2]; - unsigned int rate; - int err; - - if (highest_supported_mode_rate(dice, mode, &rate) < 0) { - dice->tx_channels[mode] = 0; - dice->tx_midi_ports[mode] = 0; - dice->rx_channels[mode] = 0; - dice->rx_midi_ports[mode] = 0; - return 0; - } - - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->tx_channels[mode] = be32_to_cpu(values[0]); - dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->rx_channels[mode] = be32_to_cpu(values[0]); - dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); - - return 0; -} - -static int dice_read_params(struct snd_dice *dice) +static int check_clock_caps(struct snd_dice *dice) { __be32 value; - int mode, err; + int err; /* some very old firmwares don't tell about their clock support */ if (dice->clock_caps > 0) { @@ -133,12 +78,6 @@ static int dice_read_params(struct snd_dice *dice) CLOCK_CAP_SOURCE_INTERNAL; } - for (mode = 2; mode >= 0; --mode) { - err = dice_read_mode_params(dice, mode); - if (err < 0) - return err; - } - return 0; } @@ -215,7 +154,7 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - err = dice_read_params(dice); + err = check_clock_caps(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 3d5ebebe61ea..c968f9887af6 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -56,10 +56,6 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; - unsigned int tx_channels[3]; - unsigned int rx_channels[3]; - unsigned int tx_midi_ports[3]; - unsigned int rx_midi_ports[3]; struct fw_address_handler notification_handler; int owner_generation; @@ -169,9 +165,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, - unsigned int rate, unsigned int *mode); - int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); -- cgit v1.2.3 From dfabc0eee1400b910d9e0f4bc55af8d2797927d2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 8 Feb 2016 22:54:20 +0900 Subject: ALSA: dice: ensure phase lock before starting streaming In former commits, probing process has no need to set sampling transfer frequency. Although it's OK to drop a function to change the frequency from this module, some models require it before streaming. This seems to be due to phase lock of clock source. This commit moves the function from transaction layer to stream layer, and rename it according to the purpose. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 34 +++++++++++++++++++-- sound/firewire/dice/dice-transaction.c | 54 ---------------------------------- sound/firewire/dice/dice.h | 1 - 3 files changed, 32 insertions(+), 57 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 716db092d7c7..e4938b0cddbe 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -10,6 +10,7 @@ #include "dice.h" #define CALLBACK_TIMEOUT 200 +#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ @@ -24,6 +25,35 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; +/* + * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE + * to GLOBAL_STATUS. Especially, just after powering on, these are different. + */ +static int ensure_phase_lock(struct snd_dice *dice) +{ + __be32 reg; + int err; + + err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; + + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); + + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; + + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) + return -ETIMEDOUT; + + return 0; +} + static void release_resources(struct snd_dice *dice, struct fw_iso_resources *resources) { @@ -222,10 +252,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) amdtp_stream_set_sync(sync_mode, master, slave); - err = snd_dice_transaction_set_rate(dice, rate); + err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, - "fail to set sampling rate\n"); + "fail to ensure phase lock\n"); goto end; } diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index a4ff4e0bc0af..76f9f72df2a9 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -9,8 +9,6 @@ #include "dice.h" -#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) - static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, u64 offset) { @@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) info, 4); } -static int set_clock_info(struct snd_dice *dice, - unsigned int rate, unsigned int source) -{ - unsigned int i; - __be32 info; - u32 mask; - u32 clock; - int err; - - err = get_clock_info(dice, &info); - if (err < 0) - return err; - - clock = be32_to_cpu(info); - if (source != UINT_MAX) { - mask = CLOCK_SOURCE_MASK; - clock &= ~mask; - clock |= source; - } - if (rate != UINT_MAX) { - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (snd_dice_rates[i] == rate) - break; - } - if (i == ARRAY_SIZE(snd_dice_rates)) - return -EINVAL; - - mask = CLOCK_RATE_MASK; - clock &= ~mask; - clock |= i << CLOCK_RATE_SHIFT; - } - info = cpu_to_be32(clock); - - if (completion_done(&dice->clock_accepted)) - reinit_completion(&dice->clock_accepted); - - err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, - &info, 4); - if (err < 0) - return err; - - if (wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) - return -ETIMEDOUT; - - return 0; -} - int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source) { @@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) end: return err; } -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) -{ - return set_clock_info(dice, rate, UINT_MAX); -} int snd_dice_transaction_set_enable(struct snd_dice *dice) { diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index c968f9887af6..423cdba99726 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -154,7 +154,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source); -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); int snd_dice_transaction_set_enable(struct snd_dice *dice); void snd_dice_transaction_clear_enable(struct snd_dice *dice); -- cgit v1.2.3 From c308abe45e2a7bc58e22d9b6d04dccf76a1b2012 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 9 Feb 2016 07:04:09 +0000 Subject: ASoC: rsnd: rsnd_ssi_is_multi_slave() macro uses rsnd_ssi_multi_slaves() b4c83b171 ("ASoC: rsnd: add Multi channel support") added Multi channel support, and current rsnd_ssi_is_multi_slave()'s check method is !SSI equals SSIM1/2/3. But, SSI parent also hit to this macro. Because of this reason, some stream which needs SSI parent clock can't work correctly. This patch uses rsnd_ssi_multi_slaves() to solve this issue. This issue was reported by Dung. Reported-by: Nguyen Viet Dung Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 803e9ae65915..592505a4bc13 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -94,7 +94,8 @@ struct rsnd_ssi { #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_ssi_mode_flags(p) ((p)->flags) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) -#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) +#define rsnd_ssi_is_multi_slave(mod, io) \ + (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -167,9 +168,6 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); enum rsnd_mod_type types[] = { RSND_MOD_SSIM1, RSND_MOD_SSIM2, @@ -177,16 +175,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) }; int i, mask; - switch (runtime->channels) { - case 2: /* Multi channel is not needed for Stereo */ - return 0; - case 6: - break; - default: - dev_err(dev, "unsupported channel\n"); - return 0; - } - mask = 0; for (i = 0; i < ARRAY_SIZE(types); i++) { mod = rsnd_io_to_mod(io, types[i]); -- cgit v1.2.3 From 9984d1b5835ca29fc7025186a891ee7398d21cc7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Feb 2016 11:53:30 +0100 Subject: ALSA: timer: Protect the whole snd_timer_close() with open race In order to make the open/close more robust, widen the register_mutex protection over the whole snd_timer_close() function. Also, the close procedure is slightly shuffled to be in the safer order, as well as a few code refactoring. Signed-off-by: Takashi Iwai --- sound/core/timer.c | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'sound') diff --git a/sound/core/timer.c b/sound/core/timer.c index dca817fc7894..b572a9bc31ad 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -318,25 +318,14 @@ int snd_timer_close(struct snd_timer_instance *timeri) if (snd_BUG_ON(!timeri)) return -ENXIO; + mutex_lock(®ister_mutex); + list_del(&timeri->open_list); + /* force to stop the timer */ snd_timer_stop(timeri); - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - /* wait, until the active callback is finished */ - spin_lock_irq(&slave_active_lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { - spin_unlock_irq(&slave_active_lock); - udelay(10); - spin_lock_irq(&slave_active_lock); - } - spin_unlock_irq(&slave_active_lock); - mutex_lock(®ister_mutex); - list_del(&timeri->open_list); - mutex_unlock(®ister_mutex); - } else { - timer = timeri->timer; - if (snd_BUG_ON(!timer)) - goto out; + timer = timeri->timer; + if (timer) { /* wait, until the active callback is finished */ spin_lock_irq(&timer->lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { @@ -345,11 +334,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) spin_lock_irq(&timer->lock); } spin_unlock_irq(&timer->lock); - mutex_lock(®ister_mutex); - list_del(&timeri->open_list); - if (list_empty(&timer->open_list_head) && - timer->hw.close) - timer->hw.close(timer); + /* remove slave links */ spin_lock_irq(&slave_active_lock); spin_lock(&timer->lock); @@ -363,18 +348,27 @@ int snd_timer_close(struct snd_timer_instance *timeri) } spin_unlock(&timer->lock); spin_unlock_irq(&slave_active_lock); - /* release a card refcount for safe disconnection */ - if (timer->card) - put_device(&timer->card->card_dev); - mutex_unlock(®ister_mutex); + + /* slave doesn't need to release timer resources below */ + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + timer = NULL; } - out: + if (timeri->private_free) timeri->private_free(timeri); kfree(timeri->owner); kfree(timeri); - if (timer) + + if (timer) { + if (list_empty(&timer->open_list_head) && timer->hw.close) + timer->hw.close(timer); + /* release a card refcount for safe disconnection */ + if (timer->card) + put_device(&timer->card->card_dev); module_put(timer->module); + } + + mutex_unlock(®ister_mutex); return 0; } -- cgit v1.2.3 From aec045b80d79bdc2cdd9b0003ff85b0470107e18 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 11 Feb 2016 20:18:37 +0900 Subject: ALSA: dice: change notification mask to detect lock status change With former patchset, ALSA dice driver doesn't change clock parameters anymore, while the driver still touch clock configuration for phase lock. Although the locking status is in Dice notification, the driver doesn't detect it. Usually, this causes no issues because in most case NOTIFY_LOCK_CHG notification transfers after NOTIFY_CLOCK_ACCEPTED notification, while it's better to detect locking status. This commit changes notification mask just to detect lock status change. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index 76f9f72df2a9..0f0350320ae8 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -156,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE); - if (bits & NOTIFY_CLOCK_ACCEPTED) + if (bits & NOTIFY_LOCK_CHG) complete(&dice->clock_accepted); wake_up(&dice->hwdep_wait); } -- cgit v1.2.3 From fbeac84dbe9e72d58b1377a0d1bc8a58f40ce31a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 11 Feb 2016 20:18:38 +0900 Subject: ALSA: dice: old firmware optimization for Dice notification As long as I tested, Dice-based models produced by TC Electronic with factory-configured settings transfer no notification within ensure_phase_lock(). On the other hand, with upgraded firmwares, it starts to transfer the notification. This seems to be a quirk of earlier firmwares. This commit ensures phase lock by reading a register after waiting for the notification. Even if it's timed-out, ensure_phase_lock() return success as long as the register has expected clock status. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index e4938b0cddbe..a64b3cc76bf1 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -31,7 +31,7 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { */ static int ensure_phase_lock(struct snd_dice *dice) { - __be32 reg; + __be32 reg, nominal; int err; err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, @@ -48,8 +48,19 @@ static int ensure_phase_lock(struct snd_dice *dice) return err; if (wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) - return -ETIMEDOUT; + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + /* + * Old versions of Dice firmware transfer no notification when + * the same clock status as current one is set. In this case, + * just check current clock status. + */ + err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS, + &nominal, sizeof(nominal)); + if (err < 0) + return err; + if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED)) + return -ETIMEDOUT; + } return 0; } -- cgit v1.2.3 From f65e0d299807d8a11812845c972493c3f9a18e10 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Feb 2016 12:47:03 +0100 Subject: ALSA: timer: Call notifier in the same spinlock snd_timer_notify1() is called outside the spinlock and it retakes the lock after the unlock. This is rather racy, and it's safer to move snd_timer_notify() call inside the main spinlock. The patch also contains a slight refactoring / cleanup of the code. Now all start/stop/continue/pause look more symmetric and a bit better readable. Signed-off-by: Takashi Iwai --- sound/core/timer.c | 220 +++++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 118 deletions(-) (limited to 'sound') diff --git a/sound/core/timer.c b/sound/core/timer.c index b572a9bc31ad..aa1b15c155d1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti, return 0; } -static int _snd_timer_stop(struct snd_timer_instance *timeri, int event); - /* * close a timer instance */ @@ -389,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) static void snd_timer_notify1(struct snd_timer_instance *ti, int event) { struct snd_timer *timer; - unsigned long flags; unsigned long resolution = 0; struct snd_timer_instance *ts; struct timespec tstamp; @@ -413,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) return; if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) return; - spin_lock_irqsave(&timer->lock, flags); list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) ts->ccallback(ts, event + 100, &tstamp, resolution); - spin_unlock_irqrestore(&timer->lock, flags); } -static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, - unsigned long sticks) +/* start/continue a master timer */ +static int snd_timer_start1(struct snd_timer_instance *timeri, + bool start, unsigned long ticks) { + struct snd_timer *timer; + int result; + unsigned long flags; + + timer = timeri->timer; + if (!timer) + return -EINVAL; + + spin_lock_irqsave(&timer->lock, flags); + if (timer->card && timer->card->shutdown) { + result = -ENODEV; + goto unlock; + } + if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | + SNDRV_TIMER_IFLG_START)) { + result = -EBUSY; + goto unlock; + } + + if (start) + timeri->ticks = timeri->cticks = ticks; + else if (!timeri->cticks) + timeri->cticks = 1; + timeri->pticks = 0; + list_move_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) goto __start_now; timer->flags |= SNDRV_TIMER_FLG_RESCHED; timeri->flags |= SNDRV_TIMER_IFLG_START; - return 1; /* delayed start */ + result = 1; /* delayed start */ } else { - timer->sticks = sticks; + if (start) + timer->sticks = ticks; timer->hw.start(timer); __start_now: timer->running++; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; - return 0; + result = 0; } + snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : + SNDRV_TIMER_EVENT_CONTINUE); + unlock: + spin_unlock_irqrestore(&timer->lock, flags); + return result; } -static int snd_timer_start_slave(struct snd_timer_instance *timeri) +/* start/continue a slave timer */ +static int snd_timer_start_slave(struct snd_timer_instance *timeri, + bool start) { unsigned long flags; @@ -454,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri) spin_lock(&timeri->timer->lock); list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : + SNDRV_TIMER_EVENT_CONTINUE); spin_unlock(&timeri->timer->lock); } spin_unlock_irqrestore(&slave_active_lock, flags); return 1; /* delayed start */ } -/* - * start the timer instance - */ -int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) -{ - struct snd_timer *timer; - int result = -EINVAL; - unsigned long flags; - - if (timeri == NULL || ticks < 1) - return -EINVAL; - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - result = snd_timer_start_slave(timeri); - if (result >= 0) - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); - return result; - } - timer = timeri->timer; - if (timer == NULL) - return -EINVAL; - if (timer->card && timer->card->shutdown) - return -ENODEV; - spin_lock_irqsave(&timer->lock, flags); - if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | - SNDRV_TIMER_IFLG_START)) { - result = -EBUSY; - goto unlock; - } - timeri->ticks = timeri->cticks = ticks; - timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, ticks); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); - if (result >= 0) - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); - return result; -} - -static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) +/* stop/pause a master timer */ +static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) { struct snd_timer *timer; + int result = 0; unsigned long flags; - if (snd_BUG_ON(!timeri)) - return -ENXIO; - - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - spin_lock_irqsave(&slave_active_lock, flags); - if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { - spin_unlock_irqrestore(&slave_active_lock, flags); - return -EBUSY; - } - if (timeri->timer) - spin_lock(&timeri->timer->lock); - timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; - list_del_init(&timeri->ack_list); - list_del_init(&timeri->active_list); - if (timeri->timer) - spin_unlock(&timeri->timer->lock); - spin_unlock_irqrestore(&slave_active_lock, flags); - goto __end; - } timer = timeri->timer; if (!timer) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START))) { - spin_unlock_irqrestore(&timer->lock, flags); - return -EBUSY; + result = -EBUSY; + goto unlock; } list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); - if (timer->card && timer->card->shutdown) { - spin_unlock_irqrestore(&timer->lock, flags); - return 0; + if (timer->card && timer->card->shutdown) + goto unlock; + if (stop) { + timeri->cticks = timeri->ticks; + timeri->pticks = 0; } if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && !(--timer->running)) { @@ -550,13 +528,49 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) } } timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); + snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : + SNDRV_TIMER_EVENT_CONTINUE); + unlock: spin_unlock_irqrestore(&timer->lock, flags); - __end: - if (event != SNDRV_TIMER_EVENT_RESOLUTION) - snd_timer_notify1(timeri, event); + return result; +} + +/* stop/pause a slave timer */ +static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) +{ + unsigned long flags; + + spin_lock_irqsave(&slave_active_lock, flags); + if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { + spin_unlock_irqrestore(&slave_active_lock, flags); + return -EBUSY; + } + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + if (timeri->timer) { + spin_lock(&timeri->timer->lock); + list_del_init(&timeri->ack_list); + list_del_init(&timeri->active_list); + snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : + SNDRV_TIMER_EVENT_CONTINUE); + spin_unlock(&timeri->timer->lock); + } + spin_unlock_irqrestore(&slave_active_lock, flags); return 0; } +/* + * start the timer instance + */ +int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) +{ + if (timeri == NULL || ticks < 1) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri, true); + else + return snd_timer_start1(timeri, true, ticks); +} + /* * stop the timer instance. * @@ -564,21 +578,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) */ int snd_timer_stop(struct snd_timer_instance *timeri) { - struct snd_timer *timer; - unsigned long flags; - int err; - - err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP); - if (err < 0) - return err; - timer = timeri->timer; - if (!timer) - return -EINVAL; - spin_lock_irqsave(&timer->lock, flags); - timeri->cticks = timeri->ticks; - timeri->pticks = 0; - spin_unlock_irqrestore(&timer->lock, flags); - return 0; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_stop_slave(timeri, true); + else + return snd_timer_stop1(timeri, true); } /* @@ -586,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri) */ int snd_timer_continue(struct snd_timer_instance *timeri) { - struct snd_timer *timer; - int result = -EINVAL; - unsigned long flags; - - if (timeri == NULL) - return result; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) - return snd_timer_start_slave(timeri); - timer = timeri->timer; - if (! timer) - return -EINVAL; - if (timer->card && timer->card->shutdown) - return -ENODEV; - spin_lock_irqsave(&timer->lock, flags); - if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { - result = -EBUSY; - goto unlock; - } - if (!timeri->cticks) - timeri->cticks = 1; - timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, timer->sticks); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); - return result; + return snd_timer_start_slave(timeri, false); + else + return snd_timer_start1(timeri, false, 0); } /* @@ -619,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri) */ int snd_timer_pause(struct snd_timer_instance * timeri) { - return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE); + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_stop_slave(timeri, false); + else + return snd_timer_stop1(timeri, false); } /* -- cgit v1.2.3 From 8faa268ab780a7379cfe54b882c6f6e0083233f2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 12 Feb 2016 09:30:17 -0800 Subject: ASoC: qcom: Don't specify LE device endianness This reverts commit 18560a4e3 (ASoC: qcom: Specify LE device endianness). The commit that caused us to specify LE device endianness here, 29bb45f25ff3 (regmap-mmio: Use native endianness for read/write, 2015-10-29), has been reverted in mainline so now when we specify LE it actively breaks big endian kernels because the byte swapping in regmap-mmio is incorrect. Let's revert this change because it will 1) fix the big endian kernels and 2) be redundant to specify LE because that will become the default soon. Signed-off-by: Stephen Boyd Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 00b6c9d039cf..e5101e0d2d37 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -355,7 +355,6 @@ static struct regmap_config lpass_cpu_regmap_config = { .readable_reg = lpass_cpu_regmap_readable, .volatile_reg = lpass_cpu_regmap_volatile, .cache_type = REGCACHE_FLAT, - .val_format_endian = REGMAP_ENDIAN_LITTLE, }; int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) -- cgit v1.2.3 From 64527e8a352968bda529f01df1c9dd5fe581ff04 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:28 +0800 Subject: ASoC: topology: Add FE DAIs dynamically Topology will create FE DAIs dynamically from the PCM objects, and register them to the component. A PCM topoplogy object describes a FE DAI and DAI link. Later patch will add FE DAI links as well. Change tplg load ops for DAI: - Only process a DAI. - Pass the DAI driver pointer to the component driver for extra initialization. Signed-off-by: Mengdong Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 15 ++--- include/sound/soc.h | 2 +- sound/soc/soc-topology.c | 129 +++++++++++++++++++++++++++++-------------- 3 files changed, 92 insertions(+), 54 deletions(-) (limited to 'sound') diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 5b68e3f5aa85..78813adca2a1 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -56,12 +56,6 @@ struct snd_soc_dobj_widget { unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ }; -/* dynamic PCM DAI object */ -struct snd_soc_dobj_pcm_dai { - struct snd_soc_tplg_pcm_dai *pd; - unsigned int count; -}; - /* generic dynamic object - all dynamic objects belong to this struct */ struct snd_soc_dobj { enum snd_soc_dobj_type type; @@ -71,7 +65,6 @@ struct snd_soc_dobj { union { struct snd_soc_dobj_control control; struct snd_soc_dobj_widget widget; - struct snd_soc_dobj_pcm_dai pcm_dai; }; void *private; /* core does not touch this */ }; @@ -126,10 +119,10 @@ struct snd_soc_tplg_ops { int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); - /* FE - used for any driver specific init */ - int (*pcm_dai_load)(struct snd_soc_component *, - struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe); - int (*pcm_dai_unload)(struct snd_soc_component *, + /* FE DAI - used for any driver specific init */ + int (*dai_load)(struct snd_soc_component *, + struct snd_soc_dai_driver *dai_drv); + int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 7afb72ceac56..02b4a215fd75 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -27,7 +27,6 @@ #include #include #include -#include /* * Convenience kcontrol builders @@ -404,6 +403,7 @@ struct snd_soc_jack_zone; struct snd_soc_jack_pin; #include #include +#include struct snd_soc_jack_gpio; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6963ba20991c..446ac9a93aef 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -330,12 +330,12 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, return 0; } -/* pass dynamic FEs configurations to component driver */ -static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg, - struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai) +/* pass DAI configurations to component driver for extra intialization */ +static int soc_tplg_dai_load(struct soc_tplg *tplg, + struct snd_soc_dai_driver *dai_drv) { - if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load) - return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai); + if (tplg->comp && tplg->ops && tplg->ops->dai_load) + return tplg->ops->dai_load(tplg->comp, dai_drv); return 0; } @@ -495,18 +495,21 @@ static void remove_widget(struct snd_soc_component *comp, /* widget w is freed by soc-dapm.c */ } -/* remove PCM DAI configurations */ -static void remove_pcm_dai(struct snd_soc_component *comp, +/* remove DAI configurations */ +static void remove_dai(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { + struct snd_soc_dai_driver *dai_drv = + container_of(dobj, struct snd_soc_dai_driver, dobj); + if (pass != SOC_TPLG_PASS_PCM_DAI) return; - if (dobj->ops && dobj->ops->pcm_dai_unload) - dobj->ops->pcm_dai_unload(comp, dobj); + if (dobj->ops && dobj->ops->dai_unload) + dobj->ops->dai_unload(comp, dobj); list_del(&dobj->list); - kfree(dobj); + kfree(dai_drv); } /* bind a kcontrol to it's IO handlers */ @@ -1544,18 +1547,79 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return 0; } -static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, +static int soc_tplg_dai_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + struct snd_soc_dai_driver *dai_drv; + struct snd_soc_pcm_stream *stream; + struct snd_soc_tplg_stream_caps *caps; + int ret; + + dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); + if (dai_drv == NULL) + return -ENOMEM; + + dai_drv->name = pcm->dai_name; + dai_drv->id = pcm->dai_id; + + if (pcm->playback) { + stream = &dai_drv->playback; + caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; + + stream->stream_name = kstrdup(caps->name, GFP_KERNEL); + stream->channels_min = caps->channels_min; + stream->channels_max = caps->channels_max; + stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, + caps->rate_max); + stream->formats = caps->formats; + } + + if (pcm->capture) { + stream = &dai_drv->capture; + caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; + + stream->stream_name = kstrdup(caps->name, GFP_KERNEL); + stream->channels_min = caps->channels_min; + stream->channels_max = caps->channels_max; + stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, + caps->rate_max); + stream->formats = caps->formats; + } + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_load(tplg, dai_drv); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); + kfree(dai_drv); + return ret; + } + + dai_drv->dobj.index = tplg->index; + dai_drv->dobj.ops = tplg->ops; + dai_drv->dobj.type = SND_SOC_DOBJ_PCM; + list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); + + /* register the DAI to the component */ + return snd_soc_register_dai(tplg->comp, dai_drv); +} + +static int soc_tplg_pcm_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + return soc_tplg_dai_create(tplg, pcm); +} + +static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_pcm_dai *pcm_dai; - struct snd_soc_dobj *dobj; + struct snd_soc_tplg_pcm *pcm; int count = hdr->count; - int ret; + int i; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; - pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_pcm), count, @@ -1565,31 +1629,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, return -EINVAL; } + /* create the FE DAIs and DAI links */ + for (i = 0; i < count; i++) { + soc_tplg_pcm_create(tplg, pcm); + pcm++; + } + dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; - dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); - if (dobj == NULL) - return -ENOMEM; - - /* Call the platform driver call back to register the dais */ - ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count); - if (ret < 0) { - dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n"); - goto err; - } - - dobj->type = get_dobj_type(hdr, NULL); - dobj->pcm_dai.count = count; - dobj->pcm_dai.pd = pcm_dai; - dobj->ops = tplg->ops; - dobj->index = tplg->index; - list_add(&dobj->list, &tplg->comp->dobj_list); return 0; - -err: - kfree(dobj); - return ret; } static int soc_tplg_manifest_load(struct soc_tplg *tplg, @@ -1681,9 +1730,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: - case SND_SOC_TPLG_TYPE_DAI_LINK: - case SND_SOC_TPLG_TYPE_CODEC_LINK: - return soc_tplg_pcm_dai_elems_load(tplg, hdr); + return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: @@ -1841,9 +1888,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) remove_widget(comp, dobj, pass); break; case SND_SOC_DOBJ_PCM: - case SND_SOC_DOBJ_DAI_LINK: - case SND_SOC_DOBJ_CODEC_LINK: - remove_pcm_dai(comp, dobj, pass); + remove_dai(comp, dobj, pass); break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", -- cgit v1.2.3 From acfc7d46cddcf71cf18772bbe8717b84eac5f672 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:37 +0800 Subject: ASoC: topology: Add FE DAI links dynamically Topology will also create FE DAI links dynamically from the PCM objects. These links will be removed when the component is removed and its topology info is unloaded. The component driver can implement link_load/unload ops for extra intialization (e.g. error check) and destruction. Signed-off-by: Mengdong Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 6 ++++ sound/soc/soc-topology.c | 70 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 78813adca2a1..b897b9d63161 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -125,6 +125,12 @@ struct snd_soc_tplg_ops { int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); + /* DAI link - used for any driver specific init */ + int (*link_load)(struct snd_soc_component *, + struct snd_soc_dai_link *link); + int (*link_unload)(struct snd_soc_component *, + struct snd_soc_dobj *); + /* callback to handle vendor bespoke data */ int (*vendor_load)(struct snd_soc_component *, struct snd_soc_tplg_hdr *); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 446ac9a93aef..0eb01e8155f1 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -340,6 +340,16 @@ static int soc_tplg_dai_load(struct soc_tplg *tplg, return 0; } +/* pass link configurations to component driver for extra intialization */ +static int soc_tplg_dai_link_load(struct soc_tplg *tplg, + struct snd_soc_dai_link *link) +{ + if (tplg->comp && tplg->ops && tplg->ops->link_load) + return tplg->ops->link_load(tplg->comp, link); + + return 0; +} + /* tell the component driver that all firmware has been loaded in this request */ static void soc_tplg_complete(struct soc_tplg *tplg) { @@ -512,6 +522,24 @@ static void remove_dai(struct snd_soc_component *comp, kfree(dai_drv); } +/* remove link configurations */ +static void remove_link(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_soc_dai_link *link = + container_of(dobj, struct snd_soc_dai_link, dobj); + + if (pass != SOC_TPLG_PASS_PCM_DAI) + return; + + if (dobj->ops && dobj->ops->link_unload) + dobj->ops->link_unload(comp, dobj); + + list_del(&dobj->list); + snd_soc_remove_dai_link(comp->card, link); + kfree(link); +} + /* bind a kcontrol to it's IO handlers */ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, struct snd_kcontrol_new *k, @@ -1603,10 +1631,47 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, return snd_soc_register_dai(tplg->comp, dai_drv); } +static int soc_tplg_link_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + struct snd_soc_dai_link *link; + int ret; + + link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); + if (link == NULL) + return -ENOMEM; + + link->name = pcm->pcm_name; + link->stream_name = pcm->pcm_name; + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_link_load(tplg, link); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); + kfree(link); + return ret; + } + + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + list_add(&link->dobj.list, &tplg->comp->dobj_list); + + snd_soc_add_dai_link(tplg->comp->card, link); + return 0; +} + +/* create a FE DAI and DAI link from the PCM object */ static int soc_tplg_pcm_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { - return soc_tplg_dai_create(tplg, pcm); + int ret; + + ret = soc_tplg_dai_create(tplg, pcm); + if (ret < 0) + return ret; + + return soc_tplg_link_create(tplg, pcm); } static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, @@ -1890,6 +1955,9 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) case SND_SOC_DOBJ_PCM: remove_dai(comp, dobj, pass); break; + case SND_SOC_DOBJ_DAI_LINK: + remove_link(comp, dobj, pass); + break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", dobj->type); -- cgit v1.2.3 From b8a54545b00cbf2aff743578c5c69aafdcde1d64 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:01 +0530 Subject: ASoC: hdac_hdmi: Add hotplug notification and read ELD This patch uses i915 component framework to register for hotplug notification. In the hotplug notification, driver reads pin sense and ELD by sending PIN_SENSE and ELD verbs over HDA bus. Once it identifies valid pin sense and valid ELD, store the ELD into the corresponding pin map buffer. Also read the monitor present sense during resume and ignore the ELD notify from graphics during PM as is done in legacy hda, commit 8ae743e82f0b ("ALSA: hda - Skip ELD notification during system suspend") Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 217 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 5a1ec0f7a1a6..fff225d6539c 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -34,6 +34,9 @@ #define HDA_MAX_CONNECTIONS 32 +#define ELD_MAX_SIZE 256 +#define ELD_FIXED_BYTES 20 + struct hdac_hdmi_cvt_params { unsigned int channels_min; unsigned int channels_max; @@ -48,11 +51,22 @@ struct hdac_hdmi_cvt { struct hdac_hdmi_cvt_params params; }; +struct hdac_hdmi_eld { + bool monitor_present; + bool eld_valid; + int eld_size; + char eld_buffer[ELD_MAX_SIZE]; +}; + struct hdac_hdmi_pin { struct list_head head; hda_nid_t nid; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + struct hdac_hdmi_eld eld; + struct hdac_ext_device *edev; + int repoll_count; + struct delayed_work work; }; struct hdac_hdmi_dai_pin_map { @@ -76,6 +90,80 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) return to_ehdac_device(hdac); } + /* HDMI ELD routines */ +static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec, + hda_nid_t nid, int byte_index) +{ + unsigned int val; + + val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD, + byte_index); + + dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n", + byte_index, val); + + return val; +} + +static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid) +{ + return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, + AC_DIPSIZE_ELD_BUF); +} + +/* + * This function queries the ELD size and ELD data and fills in the buffer + * passed by user + */ +static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) +{ + int i, size, ret = 0; + + /* + * ELD size is initialized to zero in caller function. If no errors and + * ELD is valid, actual eld_size is assigned. + */ + + size = hdac_hdmi_get_eld_size(codec, nid); + if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) { + dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size); + return -ERANGE; + } + + /* set ELD buffer */ + for (i = 0; i < size; i++) { + unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i); + /* + * Graphics driver might be writing to ELD buffer right now. + * Just abort. The caller will repoll after a while. + */ + if (!(val & AC_ELDD_ELD_VALID)) { + dev_err(&codec->dev, + "HDMI: invalid ELD data byte %d\n", i); + ret = -EINVAL; + goto error; + } + val &= AC_ELDD_ELD_DATA; + /* + * The first byte cannot be zero. This can happen on some DVI + * connections. Some Intel chips may also need some 250ms delay + * to return non-zero ELD data, even when the graphics driver + * correctly writes ELD content before setting ELD_valid bit. + */ + if (!val && !i) { + dev_err(&codec->dev, "HDMI: 0 ELD data\n"); + ret = -EINVAL; + goto error; + } + buf[i] = val; + } + + *eld_size = size; +error: + return ret; +} + static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format) @@ -246,7 +334,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; - int val; if (dai->id > 0) { dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); @@ -255,12 +342,14 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; - val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val); + if ((!dai_map->pin->eld.monitor_present) || + (!dai_map->pin->eld.eld_valid)) { + + dev_err(&hdac->hdac.dev, + "Failed: montior present? %d ELD valid?: %d\n", + dai_map->pin->eld.monitor_present, + dai_map->pin->eld.eld_valid); - if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) { - dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val); return -ENODEV; } @@ -410,6 +499,71 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); } +static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) +{ + struct hdac_ext_device *edev = pin->edev; + int val; + + if (!edev) + return; + + pin->repoll_count = repoll; + + pm_runtime_get_sync(&edev->hdac.dev); + val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + + dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n", + val, pin->nid); + + pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE); + pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV); + + if (!pin->eld.monitor_present || !pin->eld.eld_valid) { + + dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n", + __func__, pin->nid); + goto put_hdac_device; + } + + if (pin->eld.monitor_present && pin->eld.eld_valid) { + /* TODO: use i915 component for reading ELD later */ + if (hdac_hdmi_get_eld(&edev->hdac, pin->nid, + pin->eld.eld_buffer, + &pin->eld.eld_size) == 0) { + + print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, + pin->eld.eld_buffer, pin->eld.eld_size); + } else { + pin->eld.monitor_present = false; + pin->eld.eld_valid = false; + } + } + + /* + * Sometimes the pin_sense may present invalid monitor + * present and eld_valid. If ELD data is not valid, loop few + * more times to get correct pin sense and valid ELD. + */ + if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll) + schedule_delayed_work(&pin->work, msecs_to_jiffies(300)); + +put_hdac_device: + pm_runtime_put_sync(&edev->hdac.dev); +} + +static void hdac_hdmi_repoll_eld(struct work_struct *work) +{ + struct hdac_hdmi_pin *pin = + container_of(to_delayed_work(work), struct hdac_hdmi_pin, work); + + /* picked from legacy HDA driver */ + if (pin->repoll_count++ > 6) + pin->repoll_count = 0; + + hdac_hdmi_present_sense(pin, pin->repoll_count); +} + static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) { struct hdac_hdmi_priv *hdmi = edev->private_data; @@ -424,6 +578,9 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) list_add_tail(&pin->head, &hdmi->pin_list); hdmi->num_pin++; + pin->edev = edev; + INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); + return 0; } @@ -482,17 +639,65 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) return hdac_hdmi_init_dai_map(edev); } +static void hdac_hdmi_eld_notify_cb(void *aptr, int port) +{ + struct hdac_ext_device *edev = aptr; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pin *pin; + struct snd_soc_codec *codec = edev->scodec; + + /* Don't know how this mapping is derived */ + hda_nid_t pin_nid = port + 0x04; + + dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid); + + /* + * skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume. Also since the ELD and + * connection states are updated in anyway at the end of the resume, + * we can skip it when received during PM process. + */ + if (snd_power_get_state(codec->component.card->snd_card) != + SNDRV_CTL_POWER_D0) + return; + + if (atomic_read(&edev->hdac.in_pm)) + return; + + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid == pin_nid) + hdac_hdmi_present_sense(pin, 1); + } +} + +static struct i915_audio_component_audio_ops aops = { + .pin_eld_notify = hdac_hdmi_eld_notify_cb, +}; + static int hdmi_codec_probe(struct snd_soc_codec *codec) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); struct hdac_hdmi_priv *hdmi = edev->private_data; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); + struct hdac_hdmi_pin *pin; + int ret; edev->scodec = codec; create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); + aops.audio_ptr = edev; + ret = snd_hdac_i915_register_notifier(&aops); + if (ret < 0) { + dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n", + ret); + return ret; + } + + list_for_each_entry(pin, &hdmi->pin_list, head) + hdac_hdmi_present_sense(pin, 1); + /* Imp: Store the card pointer in hda_codec */ edev->card = dapm->card->snd_card; -- cgit v1.2.3 From 2428bca305e082885e08e7f3399db616daf3a51a Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:02 +0530 Subject: ASoC: hdac_hdmi: Apply constraints based on ELD Uses the drm ELD core framework to apply rate and channel Also compute the format to be set based on ELD. Even though the channel constraint is based on ELD, infoframe is set with stereo only. Multichannel support will be added later. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/hdac_hdmi.c | 51 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..8c86239cc3f2 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -490,6 +490,7 @@ config SND_SOC_GTM601 config SND_SOC_HDAC_HDMI tristate select SND_HDA_EXT_CORE + select SND_PCM_ELD select HDMI config SND_SOC_ICS43432 diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index fff225d6539c..d22fa6b66c80 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -22,10 +22,12 @@ #include #include #include +#include #include #include #include #include +#include #include "../../hda/local.h" #define AMP_OUT_MUTE 0xb080 @@ -90,6 +92,45 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) return to_ehdac_device(hdac); } +static unsigned int sad_format(const u8 *sad) +{ + return ((sad[0] >> 0x3) & 0x1f); +} + +static unsigned int sad_sample_bits_lpcm(const u8 *sad) +{ + return (sad[2] & 7); +} + +static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, + void *eld) +{ + u64 formats = SNDRV_PCM_FMTBIT_S16; + int i; + const u8 *sad, *eld_buf = eld; + + sad = drm_eld_sad(eld_buf); + if (!sad) + goto format_constraint; + + for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) { + if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */ + + /* + * the controller support 20 and 24 bits in 32 bit + * container so we set S32 + */ + if (sad_sample_bits_lpcm(sad) & 0x6) + formats |= SNDRV_PCM_FMTBIT_S32; + } + } + +format_constraint: + return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, + formats); + +} + /* HDMI ELD routines */ static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec, hda_nid_t nid, int byte_index) @@ -334,6 +375,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; + int ret; if (dai->id > 0) { dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); @@ -358,10 +400,13 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); + ret = hdac_hdmi_eld_limit_formats(substream->runtime, + dai_map->pin->eld.eld_buffer); + if (ret < 0) + return ret; - return 0; + return snd_pcm_hw_constraint_eld(substream->runtime, + dai_map->pin->eld.eld_buffer); } static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, -- cgit v1.2.3 From 211caab73b20dcc91ac103db0f68fe2388120d59 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:03 +0530 Subject: ASoC: hdac_hdmi: Enable DP1.2 and all converters/pins Skylake supports 3 pin and 3 converter widgets. But by default only one converter and pin widget are enabled. In skylake platform the DP port is on a different port which is not enabled by default. To enable playback on DP port, enable all pin and converter widget by sending a vendor VERB for a vendor widget to set required bits. As we are enabling the DP support enable the DP1.2 feature as well. Enabling DP1.2 and all widget changes are copied from patch_hdmi.c. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index d22fa6b66c80..b2e5e549b022 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -629,6 +629,46 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) return 0; } +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac) +{ + unsigned int vendor_param; + + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; +} + +static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac) +{ + unsigned int vendor_param; + + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + +} + /* * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets @@ -641,6 +681,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) struct hdac_hdmi_priv *hdmi = edev->private_data; int ret; + hdac_hdmi_skl_enable_all_pins(hdac); + hdac_hdmi_skl_enable_dp12(hdac); + num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid); if (!nid || num_nodes <= 0) { dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n"); -- cgit v1.2.3 From 17a42c4500b05a6af4c32eb8e9cfc44bab945d1f Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:04 +0530 Subject: ASoC: hdac_hdmi: create dais based on number of cvts After enabling all pins/cvts, Skylake HDMI codec enumerates 3 converters. Three independent streams can be supported with 3 cvts. This patch removes the static dai creation and creates dais based on the number of cvts queried. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 123 ++++++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 32 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index b2e5e549b022..c85deae68064 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -30,6 +30,8 @@ #include #include "../../hda/local.h" +#define NAME_SIZE 32 + #define AMP_OUT_MUTE 0xb080 #define AMP_OUT_UNMUTE 0xb000 #define PIN_OUT (AC_PINCTL_OUT_EN) @@ -669,11 +671,82 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac) } +static struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdac_hdmi_pcm_open, + .shutdown = hdac_hdmi_pcm_close, + .hw_params = hdac_hdmi_set_hw_params, + .prepare = hdac_hdmi_playback_prepare, + .hw_free = hdac_hdmi_playback_cleanup, +}; + +/* + * Each converter can support a stream independently. So a dai is created + * based on the number of converter queried. + */ +static int hdac_hdmi_create_dais(struct hdac_device *hdac, + struct snd_soc_dai_driver **dais, + struct hdac_hdmi_priv *hdmi, int num_dais) +{ + struct snd_soc_dai_driver *hdmi_dais; + struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE], dai_name[NAME_SIZE]; + int i = 0; + u32 rates, bps; + unsigned int rate_max = 384000, rate_min = 8000; + u64 formats; + int ret; + + hdmi_dais = devm_kzalloc(&hdac->dev, + (sizeof(*hdmi_dais) * num_dais), + GFP_KERNEL); + if (!hdmi_dais) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + ret = snd_hdac_query_supported_pcm(hdac, cvt->nid, + &rates, &formats, &bps); + if (ret) + return ret; + + sprintf(dai_name, "intel-hdmi-hifi%d", i+1); + hdmi_dais[i].name = devm_kstrdup(&hdac->dev, + dai_name, GFP_KERNEL); + + if (!hdmi_dais[i].name) + return -ENOMEM; + + snprintf(name, sizeof(name), "hifi%d", i+1); + hdmi_dais[i].playback.stream_name = + devm_kstrdup(&hdac->dev, name, GFP_KERNEL); + if (!hdmi_dais[i].playback.stream_name) + return -ENOMEM; + + /* + * Set caps based on capability queried from the converter. + * It will be constrained runtime based on ELD queried. + */ + hdmi_dais[i].playback.formats = formats; + hdmi_dais[i].playback.rates = rates; + hdmi_dais[i].playback.rate_max = rate_max; + hdmi_dais[i].playback.rate_min = rate_min; + hdmi_dais[i].playback.channels_min = 2; + hdmi_dais[i].playback.channels_max = 2; + hdmi_dais[i].ops = &hdmi_dai_ops; + + i++; + } + + *dais = hdmi_dais; + + return 0; +} + /* * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets */ -static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) +static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, + struct snd_soc_dai_driver **dais, int *num_dais) { hda_nid_t nid; int i, num_nodes; @@ -724,6 +797,15 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) if (!hdmi->num_pin || !hdmi->num_cvt) return -EIO; + ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); + if (ret) { + dev_err(&hdac->dev, "Failed to create dais with err: %d\n", + ret); + return ret; + } + + *num_dais = hdmi->num_cvt; + return hdac_hdmi_init_dai_map(edev); } @@ -814,38 +896,12 @@ static struct snd_soc_codec_driver hdmi_hda_codec = { .idle_bias_off = true, }; -static struct snd_soc_dai_ops hdmi_dai_ops = { - .startup = hdac_hdmi_pcm_open, - .shutdown = hdac_hdmi_pcm_close, - .hw_params = hdac_hdmi_set_hw_params, - .prepare = hdac_hdmi_playback_prepare, - .hw_free = hdac_hdmi_playback_cleanup, -}; - -static struct snd_soc_dai_driver hdmi_dais[] = { - { .name = "intel-hdmi-hif1", - .playback = { - .stream_name = "hif1", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - - }, - .ops = &hdmi_dai_ops, - }, -}; - static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) { struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; + struct snd_soc_dai_driver *hdmi_dais = NULL; + int num_dais = 0; int ret = 0; hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); @@ -859,13 +915,16 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); - ret = hdac_hdmi_parse_and_map_nid(edev); - if (ret < 0) + ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); + if (ret < 0) { + dev_err(&codec->dev, + "Failed in parse and map nid with err: %d\n", ret); return ret; + } /* ASoC specific initialization */ return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, - hdmi_dais, ARRAY_SIZE(hdmi_dais)); + hdmi_dais, num_dais); } static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) -- cgit v1.2.3 From 79f4e922b54771a4d41b1da67caf109ba92ed95a Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:05 +0530 Subject: ASoC: hdac_hdmi: Create widget/route based on nodes enumerated In skylake, HDMI codec enumerates 3 pins and 3 cvts. Stream can be routed from any cvt to any pin based on the connection list queried from the pin. This patch removes the static modelling of widget/route and creates it dynamically based on the codec widgets enumerated. Mux widgets are added to represent the map between a cvt and pin. Ideally the mux widgets should be created based on the connection list queried from the pin widget. But due to HW behavior, if an external display is not connected on a port, querying the connection list returns zero. So create mux widgets to map all the cvt to all pins. At runtime, playback support on a pin can be verified based on the connection list query. Few function in driver have additional arguments now to support this. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 241 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 219 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index c85deae68064..6fb44c4cc8c7 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -446,46 +446,241 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) return err; } -static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w, - enum snd_soc_dapm_type id, - const char *wname, const char *stream) +static int hdac_hdmi_fill_widget_info(struct device *dev, + struct snd_soc_dapm_widget *w, + enum snd_soc_dapm_type id, void *priv, + const char *wname, const char *stream, + struct snd_kcontrol_new *wc, int numkc) { w->id = id; - w->name = wname; + w->name = devm_kstrdup(dev, wname, GFP_KERNEL); + if (!w->name) + return -ENOMEM; + w->sname = stream; w->reg = SND_SOC_NOPM; w->shift = 0; - w->kcontrol_news = NULL; - w->num_kcontrols = 0; - w->priv = NULL; + w->kcontrol_news = wc; + w->num_kcontrols = numkc; + w->priv = priv; + + return 0; } static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, - const char *sink, const char *control, const char *src) + const char *sink, const char *control, const char *src, + int (*handler)(struct snd_soc_dapm_widget *src, + struct snd_soc_dapm_widget *sink)) { route->sink = sink; route->source = src; route->control = control; - route->connected = NULL; + route->connected = handler; } -static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm, - struct hdac_hdmi_dai_pin_map *dai_map) +/* + * Ideally the Mux inputs should be based on the num_muxs enumerated, but + * the display driver seem to be programming the connection list for the pin + * widget runtime. + * + * So programming all the possible inputs for the mux, the user has to take + * care of selecting the right one and leaving all other inputs selected to + * "NONE" + */ +static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin, + struct snd_soc_dapm_widget *widget, + const char *widget_name) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct snd_kcontrol_new *kc; + struct hdac_hdmi_cvt *cvt; + struct soc_enum *se; + char kc_name[NAME_SIZE]; + char mux_items[NAME_SIZE]; + /* To hold inputs to the Pin mux */ + char *items[HDA_MAX_CONNECTIONS]; + int i = 0; + int num_items = hdmi->num_cvt + 1; + + kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL); + if (!kc) + return -ENOMEM; + + se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + sprintf(kc_name, "Pin %d Input", pin->nid); + kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = 0; + kc->info = snd_soc_info_enum_double; + kc->put = snd_soc_dapm_put_enum_double; + kc->get = snd_soc_dapm_get_enum_double; + + se->reg = SND_SOC_NOPM; + + /* enum texts: ["NONE", "cvt #", "cvt #", ...] */ + se->items = num_items; + se->mask = roundup_pow_of_two(se->items) - 1; + + sprintf(mux_items, "NONE"); + items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + i++; + sprintf(mux_items, "cvt %d", cvt->nid); + items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + } + + se->texts = devm_kmemdup(&edev->hdac.dev, items, + (num_items * sizeof(char *)), GFP_KERNEL); + if (!se->texts) + return -ENOMEM; + + return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, + snd_soc_dapm_mux, &pin->nid, widget_name, + NULL, kc, 1); +} + +/* Add cvt <- input <- mux route map */ +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, + struct snd_soc_dapm_widget *widgets, + struct snd_soc_dapm_route *route, int rindex) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + const struct snd_kcontrol_new *kc; + struct soc_enum *se; + int mux_index = hdmi->num_cvt + hdmi->num_pin; + int i, j; + + for (i = 0; i < hdmi->num_pin; i++) { + kc = widgets[mux_index].kcontrol_news; + se = (struct soc_enum *)kc->private_value; + for (j = 0; j < hdmi->num_cvt; j++) { + hdac_hdmi_fill_route(&route[rindex], + widgets[mux_index].name, + se->texts[j + 1], + widgets[j].name, NULL); + + rindex++; + } + + mux_index++; + } +} + +/* + * Widgets are added in the below sequence + * Converter widgets for num converters enumerated + * Pin widgets for num pins enumerated + * Pin mux widgets to represent connenction list of pin widget + * + * Total widgets elements = num_cvt + num_pin + num_pin; + * + * Routes are added as below: + * pin mux -> pin (based on num_pins) + * cvt -> "Input sel control" -> pin_mux + * + * Total route elements: + * num_pins + (pin_muxes * num_cvt) + */ +static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) { - struct snd_soc_dapm_route route[1]; - struct snd_soc_dapm_widget widgets[2] = { {0} }; + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *route; + struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv; + char widget_name[NAME_SIZE]; + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_pin *pin; + int ret, i = 0, num_routes = 0; - memset(&route, 0, sizeof(route)); + if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) + return -EINVAL; - hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output, - "hif1 Output", NULL); - hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in, - "Coverter 1", "hif1"); + widgets = devm_kzalloc(dapm->dev, + (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)), + GFP_KERNEL); - hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1"); + if (!widgets) + return -ENOMEM; + + /* DAPM widgets to represent each converter widget */ + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + sprintf(widget_name, "Converter %d", cvt->nid); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_aif_in, &cvt->nid, + widget_name, dai_drv[i].playback.stream_name, NULL, 0); + if (ret < 0) + return ret; + i++; + } + + list_for_each_entry(pin, &hdmi->pin_list, head) { + sprintf(widget_name, "hif%d Output", pin->nid); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_output, &pin->nid, + widget_name, NULL, NULL, 0); + if (ret < 0) + return ret; + i++; + } + + /* DAPM widgets to represent the connection list to pin widget */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + sprintf(widget_name, "Pin %d Mux", pin->nid); + ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i], + widget_name); + if (ret < 0) + return ret; + i++; + + /* For cvt to pin_mux mapping */ + num_routes += hdmi->num_cvt; + + /* For pin_mux to pin mapping */ + num_routes++; + } + + route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), + GFP_KERNEL); + if (!route) + return -ENOMEM; + + i = 0; + /* Add pin <- NULL <- mux route map */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + int sink_index = i + hdmi->num_cvt; + int src_index = sink_index + hdmi->num_pin; + + hdac_hdmi_fill_route(&route[i], + widgets[sink_index].name, NULL, + widgets[src_index].name, NULL); + i++; + + } + + hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i); + + snd_soc_dapm_new_controls(dapm, widgets, + ((2 * hdmi->num_pin) + hdmi->num_cvt)); + + snd_soc_dapm_add_routes(dapm, route, num_routes); + snd_soc_dapm_new_widgets(dapm->card); + + return 0; - snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets)); - snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route)); } static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) @@ -855,7 +1050,9 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) edev->scodec = codec; - create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); + ret = create_fill_widget_route_map(dapm); + if (ret < 0) + return ret; aops.audio_ptr = edev; ret = snd_hdac_i915_register_notifier(&aops); -- cgit v1.2.3 From 4a3478debf36c0aa0cf0860daec245b13cd4448f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 12 Feb 2016 07:46:06 +0530 Subject: ASoC: hdac_hdmi: Add jack reporting Jack is created based on pcm devices enumerated, so we will create Jack as "HDMI/DP, pcm=x Jack". This style is expected by current usermode like PulseAudio and CRAS. This patch exports an API which can be used to register Jack based on PCM. This API also establishes the map between PCM and cvt. Further cvt to pin mapping is established with the help of usermode selection based on the board topology. During device probe as the PCMs may not be registered, initial pin sense don't report jack events. So, first time jack reporting is done during user selection of mux control. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 160 ++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/hdac_hdmi.h | 6 ++ 2 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 sound/soc/codecs/hdac_hdmi.h (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 6fb44c4cc8c7..839148688744 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -24,11 +24,13 @@ #include #include #include +#include #include #include #include #include #include "../../hda/local.h" +#include "hdac_hdmi.h" #define NAME_SIZE 32 @@ -52,6 +54,7 @@ struct hdac_hdmi_cvt_params { struct hdac_hdmi_cvt { struct list_head head; hda_nid_t nid; + const char *name; struct hdac_hdmi_cvt_params params; }; @@ -73,6 +76,14 @@ struct hdac_hdmi_pin { struct delayed_work work; }; +struct hdac_hdmi_pcm { + struct list_head head; + int pcm_id; + struct hdac_hdmi_pin *pin; + struct hdac_hdmi_cvt *cvt; + struct snd_jack *jack; +}; + struct hdac_hdmi_dai_pin_map { int dai_id; struct hdac_hdmi_pin *pin; @@ -83,8 +94,10 @@ struct hdac_hdmi_priv { struct hdac_hdmi_dai_pin_map dai_map[3]; struct list_head pin_list; struct list_head cvt_list; + struct list_head pcm_list; int num_pin; int num_cvt; + struct mutex pin_mutex; }; static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) @@ -478,6 +491,67 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, route->connected = handler; } +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = NULL; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pin == pin) + return pcm; + } + + return NULL; +} + +/* + * Based on user selection, map the PINs with the PCMs. + */ +static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_context *dapm = w->dapm; + struct hdac_hdmi_pin *pin = w->priv; + struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = NULL; + const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + if (ret < 0) + return ret; + + mutex_lock(&hdmi->pin_mutex); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pin == pin) + pcm->pin = NULL; + + /* + * Jack status is not reported during device probe as the + * PCMs are not registered by then. So report it here. + */ + if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) { + pcm->pin = pin; + if (pin->eld.monitor_present && pin->eld.eld_valid) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, SND_JACK_AVOUT); + } + mutex_unlock(&hdmi->pin_mutex); + return ret; + } + } + mutex_unlock(&hdmi->pin_mutex); + + return ret; +} + /* * Ideally the Mux inputs should be based on the num_muxs enumerated, but * the display driver seem to be programming the connection list for the pin @@ -520,7 +594,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kc->access = 0; kc->info = snd_soc_info_enum_double; - kc->put = snd_soc_dapm_put_enum_double; + kc->put = hdac_hdmi_set_pin_mux; kc->get = snd_soc_dapm_get_enum_double; se->reg = SND_SOC_NOPM; @@ -548,8 +622,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, return -ENOMEM; return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, - snd_soc_dapm_mux, &pin->nid, widget_name, - NULL, kc, 1); + snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1); } /* Add cvt <- input <- mux route map */ @@ -728,12 +801,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) { struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE]; cvt = kzalloc(sizeof(*cvt), GFP_KERNEL); if (!cvt) return -ENOMEM; cvt->nid = nid; + sprintf(name, "cvt %d", cvt->nid); + cvt->name = kstrdup(name, GFP_KERNEL); list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; @@ -744,6 +820,8 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) { struct hdac_ext_device *edev = pin->edev; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm; int val; if (!edev) @@ -758,13 +836,31 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n", val, pin->nid); + + mutex_lock(&hdmi->pin_mutex); pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE); pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV); + pcm = hdac_hdmi_get_pcm(edev, pin); + if (!pin->eld.monitor_present || !pin->eld.eld_valid) { dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n", __func__, pin->nid); + + /* + * PCMs are not registered during device probe, so don't + * report jack here. It will be done in usermode mux + * control select. + */ + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", pcm->pcm_id); + + snd_jack_report(pcm->jack, 0); + } + + mutex_unlock(&hdmi->pin_mutex); goto put_hdac_device; } @@ -774,14 +870,32 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) pin->eld.eld_buffer, &pin->eld.eld_size) == 0) { + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, SND_JACK_AVOUT); + } + print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, pin->eld.eld_buffer, pin->eld.eld_size); } else { pin->eld.monitor_present = false; pin->eld.eld_valid = false; + + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, 0); + } } } + mutex_unlock(&hdmi->pin_mutex); + /* * Sometimes the pin_sense may present invalid monitor * present and eld_valid. If ELD data is not valid, loop few @@ -1039,6 +1153,35 @@ static struct i915_audio_component_audio_ops aops = { .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) +{ + char jack_name[NAME_SIZE]; + struct snd_soc_codec *codec = dai->codec; + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&codec->component); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm; + + /* + * this is a new PCM device, create new pcm and + * add to the pcm list + */ + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + pcm->pcm_id = device; + pcm->cvt = hdmi->dai_map[dai->id].cvt; + + list_add_tail(&pcm->head, &hdmi->pcm_list); + + sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); + + return snd_jack_new(dapm->card->snd_card, jack_name, + SND_JACK_AVOUT, &pcm->jack, true, false); +} +EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); + static int hdmi_codec_probe(struct snd_soc_codec *codec) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); @@ -1111,6 +1254,8 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); + INIT_LIST_HEAD(&hdmi_priv->pcm_list); + mutex_init(&hdmi_priv->pin_mutex); ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); if (ret < 0) { @@ -1129,11 +1274,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; + struct hdac_hdmi_pcm *pcm, *pcm_next; snd_soc_unregister_codec(&edev->hdac.dev); + list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { + pcm->cvt = NULL; + pcm->pin = NULL; + list_del(&pcm->head); + kfree(pcm); + } + list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) { list_del(&cvt->head); + kfree(cvt->name); kfree(cvt); } diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h new file mode 100644 index 000000000000..8dfd1e0b57b3 --- /dev/null +++ b/sound/soc/codecs/hdac_hdmi.h @@ -0,0 +1,6 @@ +#ifndef __HDAC_HDMI_H__ +#define __HDAC_HDMI_H__ + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm); + +#endif /* __HDAC_HDMI_H__ */ -- cgit v1.2.3 From 148569fddbc6d3ce97236c4344068b1293e51838 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:07 +0530 Subject: ASoC: hdac_hdmi: Enable playback on all enumerated ports Last patch added infrastructure to render over all the ports, PCM<->cvt<- pin mapping and user selection of controls. But we still have restriction of playback on the default port alone, so remove that. This patch removes the hardcoding of cvt<->pin map from the dai. Cvt and pin for a dai are now derived from the already stored pcm list of device opened. We query connection list of a pin from codec to validate the cvt<->pin map. If connection list returns zero, then monitor is not connected so fail playback. Signed-off-by: Subhransu S. Prusty Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 174 ++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 42 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 839148688744..dcd1cb643145 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -40,6 +40,8 @@ #define HDA_MAX_CONNECTIONS 32 +#define HDA_MAX_CVTS 3 + #define ELD_MAX_SIZE 256 #define ELD_FIXED_BYTES 20 @@ -91,7 +93,7 @@ struct hdac_hdmi_dai_pin_map { }; struct hdac_hdmi_priv { - struct hdac_hdmi_dai_pin_map dai_map[3]; + struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; struct list_head pcm_list; @@ -384,44 +386,136 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, return 0; } +static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + int mux_idx; + struct hdac_hdmi_pin *pin = dai_map->pin; + + for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) { + if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) { + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, mux_idx); + break; + } + } + + if (mux_idx == pin->num_mux_nids) + return -EIO; + + /* Enable out path for this pin widget */ + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + return 0; +} + +static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, + struct hdac_hdmi_pin *pin) +{ + if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdac->hdac.dev, + "HDMI: pin %d wcaps %#x does not support connection list\n", + pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + return -EINVAL; + } + + pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + pin->mux_nids, HDA_MAX_CONNECTIONS); + if (pin->num_mux_nids == 0) + dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n", + pin->nid); + + dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n", + pin->num_mux_nids, pin->nid); + + return pin->num_mux_nids; +} + +/* + * Query pcm list and return pin widget to which stream is routed. + * + * Also query connection list of the pin, to validate the cvt to pin map. + * + * Same stream rendering to multiple pins simultaneously can be done + * possibly, but not supported for now in driver. So return the first pin + * connected. + */ +static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt( + struct hdac_ext_device *edev, + struct hdac_hdmi_priv *hdmi, + struct hdac_hdmi_cvt *cvt) +{ + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_pin *pin = NULL; + int ret, i; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->cvt == cvt) { + pin = pcm->pin; + break; + } + } + + if (pin) { + ret = hdac_hdmi_query_pin_connlist(edev, pin); + if (ret < 0) + return NULL; + + for (i = 0; i < pin->num_mux_nids; i++) { + if (pin->mux_nids[i] == cvt->nid) + return pin; + } + } + + return NULL; +} + static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_pin *pin; int ret; - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); - return -ENODEV; - } - dai_map = &hdmi->dai_map[dai->id]; - if ((!dai_map->pin->eld.monitor_present) || - (!dai_map->pin->eld.eld_valid)) { + cvt = dai_map->cvt; + pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt); + if (!pin) + return -EIO; + + if ((!pin->eld.monitor_present) || + (!pin->eld.eld_valid)) { dev_err(&hdac->hdac.dev, - "Failed: montior present? %d ELD valid?: %d\n", - dai_map->pin->eld.monitor_present, - dai_map->pin->eld.eld_valid); + "Failed: montior present? %d ELD valid?: %d for pin: %d\n", + pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); return -ENODEV; } - hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + dai_map->pin = pin; - snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + ret = hdac_hdmi_enable_pin(hdac, dai_map); + if (ret < 0) + return ret; ret = hdac_hdmi_eld_limit_formats(substream->runtime, - dai_map->pin->eld.eld_buffer); + pin->eld.eld_buffer); if (ret < 0) return ret; return snd_pcm_hw_constraint_eld(substream->runtime, - dai_map->pin->eld.eld_buffer); + pin->eld.eld_buffer); } static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, @@ -437,6 +531,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + dai_map->pin = NULL; } static int @@ -759,40 +855,34 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) { struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0]; + struct hdac_hdmi_dai_pin_map *dai_map; struct hdac_hdmi_cvt *cvt; - struct hdac_hdmi_pin *pin; + int dai_id = 0; - if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) + if (list_empty(&hdmi->cvt_list)) return -EINVAL; - /* - * Currently on board only 1 pin and 1 converter is enabled for - * simplification, more will be added eventually - * So using fixed map for dai_id:pin:cvt - */ - cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head); - pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head); - - dai_map->dai_id = 0; - dai_map->pin = pin; + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + dai_map = &hdmi->dai_map[dai_id]; + dai_map->dai_id = dai_id; + dai_map->cvt = cvt; - dai_map->cvt = cvt; + /* Enable transmission */ + snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, 1); - /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* Category Code (CC) to zero */ + snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, 0); - /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, 1); + dai_id++; - /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0); - - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_CONNECT_SEL, 0); + if (dai_id == HDA_MAX_CVTS) { + dev_warn(&edev->hdac.dev, + "Max dais supported: %d\n", dai_id); + break; + } + } return 0; } -- cgit v1.2.3 From 478f544e5b5538ccbcfdb18b4e9860a2f61ab921 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:09 +0530 Subject: ASoC: hdac_hdmi: Add infoframe support for dp audio For DP audio support, infoframe needs to be different. Based on get_conn_type result we pack either HDMI or DP infoframe. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 74 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index dcd1cb643145..36f12004ae9c 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -253,27 +253,75 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, AC_VERB_SET_HDMI_DIP_INDEX, val); } +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, hda_nid_t cvt_nid, hda_nid_t pin_nid) { uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; struct hdmi_audio_infoframe frame; - u8 *dip = (u8 *)&frame; + struct dp_audio_infoframe dp_ai; + struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_pin *pin; + u8 *dip; int ret; int i; + const u8 *eld_buf; + u8 conn_type; + int channels = 2; - hdmi_audio_infoframe_init(&frame); + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid == pin_nid) + break; + } - /* Default stereo for now */ - frame.channels = 2; + eld_buf = pin->eld.eld_buffer; + conn_type = drm_eld_get_conn_type(eld_buf); /* setup channel count */ snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1); + AC_VERB_SET_CVT_CHAN_COUNT, channels - 1); - ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (ret < 0) - return ret; + switch (conn_type) { + case DRM_ELD_CONN_TYPE_HDMI: + hdmi_audio_infoframe_init(&frame); + + /* Default stereo for now */ + frame.channels = channels; + + ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + dip = (u8 *)&frame; + break; + + case DRM_ELD_CONN_TYPE_DP: + memset(&dp_ai, 0, sizeof(dp_ai)); + dp_ai.type = 0x84; + dp_ai.len = 0x1b; + dp_ai.ver = 0x11 << 2; + dp_ai.CC02_CT47 = channels - 1; + dp_ai.CA = 0; + + dip = (u8 *)&dp_ai; + break; + + default: + dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n", + conn_type); + return -EIO; + } /* stop infoframe transmission */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); @@ -283,9 +331,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, /* Fill infoframe. Index auto-incremented */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(frame); i++) - snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { + for (i = 0; i < sizeof(frame); i++) + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + } else { + for (i = 0; i < sizeof(dp_ai); i++) + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + } /* Start infoframe */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); -- cgit v1.2.3 From cd890ade492784ae06dadd20e688ba1760d3c801 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 17 Feb 2016 09:42:13 -0200 Subject: ASoC: hdac_hdmi: Remove 'edev' NULL check Commit 4a3478debf36 ("ASoC: hdac_hdmi: Add jack reporting") leads to the following Smatch complaint: sound/soc/codecs/hdac_hdmi.c:971 hdac_hdmi_present_sense() warn: variable dereferenced before check 'edev' (see line 967) As explained by Subhransu S. Prusty: "This NULL check is not required, can be removed. edev object is expected to be present when the present_sense is called. This is called from places where this edev object is already available." So remove the evdev NULL check. Reported-by: Dan Carpenter Signed-off-by: Fabio Estevam Acked-by: Subhransu S. Prusty Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 36f12004ae9c..2884dbf0bc07 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -968,9 +968,6 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) struct hdac_hdmi_pcm *pcm; int val; - if (!edev) - return; - pin->repoll_count = repoll; pm_runtime_get_sync(&edev->hdac.dev); -- cgit v1.2.3 From cffee535f0b09ab0801cbec01a8ae81595aa99bc Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 17 Feb 2016 08:46:07 +0800 Subject: ASoC: max98926: Fix blr_clk_ratio calculation Current code calculates blr_clk_ratio before setting max98926->ch_size, fix it. Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max98926.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index 66884ebdf2bb..5245e107de30 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -383,8 +383,7 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream, int rate = params_rate(params), i; struct snd_soc_codec *codec = dai->codec; struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); - /* BCLK/LRCLK ratio calculation */ - int blr_clk_ratio = params_channels(params) * max98926->ch_size; + int blr_clk_ratio; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -414,6 +413,9 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* BCLK/LRCLK ratio calculation */ + blr_clk_ratio = params_channels(params) * max98926->ch_size; + switch (blr_clk_ratio) { case 32: regmap_update_bits(max98926->regmap, -- cgit v1.2.3 From e6a1b7e88046055989b8b59c1d648fda4e606f34 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 17 Feb 2016 18:26:27 +0530 Subject: ALSA: portman2x4 - use new parport device model Modify portman driver to use the new parallel port device model. The advantage of using the device model is that the device gets binded to the hardware, we get the feature of hotplug, we can bind/unbind the driver at runtime. The changes are in the way the driver gets registered with the parallel port subsystem and the temporary device to probe portman card is removed and portman_probe() is used in the probe callback. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai --- sound/drivers/portman2x4.c | 93 +++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 51 deletions(-) (limited to 'sound') diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 464385a480e4..362533fceec7 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -83,8 +83,6 @@ struct portman { struct snd_card *card; struct snd_rawmidi *rmidi; struct pardevice *pardev; - int pardev_claimed; - int open_count; int mode[PORTMAN_NUM_INPUT_PORTS]; struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS]; @@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata) spin_unlock(&pm->reg_lock); } -static int snd_portman_probe_port(struct parport *p) -{ - struct pardevice *pardev; - int res; - - pardev = parport_register_device(p, DRIVER_NAME, - NULL, NULL, NULL, - 0, NULL); - if (!pardev) - return -EIO; - - if (parport_claim(pardev)) { - parport_unregister_device(pardev); - return -EIO; - } - - res = portman_probe(p); - - parport_release(pardev); - parport_unregister_device(pardev); - - return res ? -EIO : 0; -} - static void snd_portman_attach(struct parport *p) { struct platform_device *device; @@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p) /* nothing to do here */ } +static int snd_portman_dev_probe(struct pardevice *pardev) +{ + if (strcmp(pardev->name, DRIVER_NAME)) + return -ENODEV; + + return 0; +} + static struct parport_driver portman_parport_driver = { - .name = "portman2x4", - .attach = snd_portman_attach, - .detach = snd_portman_detach + .name = "portman2x4", + .probe = snd_portman_dev_probe, + .match_port = snd_portman_attach, + .detach = snd_portman_detach, + .devmodel = true, }; /********************************************************************* @@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card) struct pardevice *pardev = pm->pardev; if (pardev) { - if (pm->pardev_claimed) - parport_release(pardev); + parport_release(pardev); parport_unregister_device(pardev); } @@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev) struct snd_card *card = NULL; struct portman *pm = NULL; int err; + struct pardev_cb portman_cb = { + .preempt = NULL, + .wakeup = NULL, + .irq_func = snd_portman_interrupt, /* ISR */ + .flags = PARPORT_DEV_EXCL, /* flags */ + }; p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); @@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev) if (!enable[dev]) return -ENOENT; - if ((err = snd_portman_probe_port(p)) < 0) - return err; - err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { @@ -759,23 +745,32 @@ static int snd_portman_probe(struct platform_device *pdev) sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); - pardev = parport_register_device(p, /* port */ - DRIVER_NAME, /* name */ - NULL, /* preempt */ - NULL, /* wakeup */ - snd_portman_interrupt, /* ISR */ - PARPORT_DEV_EXCL, /* flags */ - (void *)card); /* private */ + portman_cb.private = card; /* private */ + pardev = parport_register_dev_model(p, /* port */ + DRIVER_NAME, /* name */ + &portman_cb, /* callbacks */ + pdev->id); /* device number */ if (pardev == NULL) { snd_printd("Cannot register pardevice\n"); err = -EIO; goto __err; } + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto free_pardev; + } + err = portman_probe(p); + if (err) { + err = -EIO; + goto release_pardev; + } + if ((err = portman_create(card, pardev, &pm)) < 0) { snd_printd("Cannot create main component\n"); - parport_unregister_device(pardev); - goto __err; + goto release_pardev; } card->private_data = pm; card->private_free = snd_portman_card_private_free; @@ -785,14 +780,6 @@ static int snd_portman_probe(struct platform_device *pdev) goto __err; } - /* claim parport */ - if (parport_claim(pardev)) { - snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); - err = -EIO; - goto __err; - } - pm->pardev_claimed = 1; - /* init device */ if ((err = portman_device_init(pm)) < 0) goto __err; @@ -808,6 +795,10 @@ static int snd_portman_probe(struct platform_device *pdev) snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base); return 0; +release_pardev: + parport_release(pardev); +free_pardev: + parport_unregister_device(pardev); __err: snd_card_free(card); return err; -- cgit v1.2.3 From 805d132dcbbdbdf8339008f4f964490cf6ff3025 Mon Sep 17 00:00:00 2001 From: anish kumar Date: Wed, 17 Feb 2016 17:28:49 -0800 Subject: ASoC: Add max9867 codec driver Signed-off-by: anish kumar Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/max9867.txt | 17 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 1 + sound/soc/codecs/max9867.c | 537 +++++++++++++++++++++ sound/soc/codecs/max9867.h | 83 ++++ 5 files changed, 642 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/max9867.txt create mode 100755 sound/soc/codecs/max9867.c create mode 100755 sound/soc/codecs/max9867.h (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/max9867.txt b/Documentation/devicetree/bindings/sound/max9867.txt new file mode 100644 index 000000000000..394cd4eb17ec --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max9867.txt @@ -0,0 +1,17 @@ +max9867 codec + +This device supports I2C mode only. + +Required properties: + +- compatible : "maxim,max9867" +- reg : The chip select number on the I2C bus + +Example: + +&i2c { + max9867: max9867@0x18 { + compatible = "maxim,max9867"; + reg = <0x18>; + }; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ebaaa51823ef..f55a846efe54 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB + select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C select SND_SOC_MAX9850 if I2C @@ -517,6 +518,9 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate +config SND_SOC_MAX9867 + tristate + config SND_SOC_MAX98925 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 16c014a46ae8..b84fe9d75dfd 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -74,6 +74,7 @@ snd-soc-max98088-objs := max98088.o snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o +snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o snd-soc-max9850-objs := max9850.o diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c new file mode 100755 index 000000000000..b9d989f25515 --- /dev/null +++ b/sound/soc/codecs/max9867.c @@ -0,0 +1,537 @@ +/* + * max9867.c -- max9867 ALSA SoC Audio driver + * + * Copyright 2013-15 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "max9867.h" + +static const char *const max9867_spmode[] = { + "Stereo Diff", "Mono Diff", + "Stereo Cap", "Mono Cap", + "Stereo Single", "Mono Single", + "Stereo Single Fast", "Mono Single Fast" +}; +static const char *const max9867_sidetone_text[] = { + "None", "Left", "Right", "LeftRight", "LeftRightDiv2", +}; +static const char *const max9867_filter_text[] = {"IIR", "FIR"}; + +static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, + max9867_filter_text); +static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, + max9867_spmode); +static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6, + max9867_sidetone_text); +static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0); +static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1); +static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1); +static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1); +static const unsigned int max98088_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const struct snd_kcontrol_new max9867_snd_controls[] = { + SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, + MAX9867_RIGHTVOL, 0, 63, 1), + SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, + 0, 15, 1, max9860_capture_tlv), + SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv), + SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv), + SOC_ENUM("Digital Sidetone Src", max9867_sidetone), + SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1), + SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0), + SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1), + SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL, + 4, 15, 1, max9860_adc_left_tlv), + SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL, + 0, 15, 1, max9860_adc_right_tlv), + SOC_ENUM("Speaker Mode", max9867_spkmode), + SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), + SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0), + SOC_ENUM("DSP Filter", max9867_filter), +}; + +static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"}; + +static SOC_ENUM_SINGLE_DECL(max9867_mux_enum, + MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT, + max9867_mux); + +static const struct snd_kcontrol_new max9867_dapm_mux_controls = + SOC_DAPM_ENUM("Route", max9867_mux_enum); + +static const struct snd_kcontrol_new max9867_left_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0); +static const struct snd_kcontrol_new max9867_right_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0); +static const struct snd_kcontrol_new max9867_line_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1); + +static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0), + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HPOUT"), + + SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0), + SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &max9867_dapm_mux_controls), + + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1, + &max9867_left_dapm_control), + SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1, + &max9867_right_dapm_control), + SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0, + &max9867_line_dapm_control), + SND_SOC_DAPM_INPUT("LINE_IN"), +}; + +static const struct snd_soc_dapm_route max9867_audio_map[] = { + {"Left DAC", NULL, "DAI_OUT"}, + {"Right DAC", NULL, "DAI_OUT"}, + {"Output Mixer", NULL, "Left DAC"}, + {"Output Mixer", NULL, "Right DAC"}, + {"HPOUT", NULL, "Output Mixer"}, + + {"Left ADC", NULL, "DAI_IN"}, + {"Right ADC", NULL, "DAI_IN"}, + {"Input Mixer", NULL, "Left ADC"}, + {"Input Mixer", NULL, "Right ADC"}, + {"Input Mux", "Line", "Input Mixer"}, + {"Input Mux", "Mic", "Input Mixer"}, + {"Input Mux", "Mic_Line", "Input Mixer"}, + {"Right Line", "Switch", "Input Mux"}, + {"Left Line", "Switch", "Input Mux"}, + {"LINE_IN", NULL, "Left Line"}, + {"LINE_IN", NULL, "Right Line"}, +}; + +enum rates { + pcm_rate_8, pcm_rate_16, pcm_rate_24, + pcm_rate_32, pcm_rate_44, + pcm_rate_48, max_pcm_rate, +}; + +struct ni_div_rates { + u32 mclk; + u16 ni[max_pcm_rate]; +} ni_div[] = { + {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} }, + {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} }, + {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} }, + {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} }, + {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} }, + {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} }, + {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} }, + {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} }, +}; + +static inline int get_ni_value(int mclk, int rate) +{ + int i, ret = 0; + + /* find the closest rate index*/ + for (i = 0; i < ARRAY_SIZE(ni_div); i++) { + if (ni_div[i].mclk >= mclk) + break; + } + + switch (rate) { + case 8000: + return ni_div[i].ni[pcm_rate_8]; + case 16000: + return ni_div[i].ni[pcm_rate_16]; + case 32000: + return ni_div[i].ni[pcm_rate_32]; + case 44100: + return ni_div[i].ni[pcm_rate_44]; + case 48000: + return ni_div[i].ni[pcm_rate_48]; + default: + pr_err("%s wrong rate %d\n", __func__, rate); + ret = -EINVAL; + } + return ret; +} + +static int max9867_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + unsigned int ni_h, ni_l, value; + + value = get_ni_value(max9867->sysclk, params_rate(params)); + if (value < 0) + return value; + + ni_h = (0xFF00 & value) >> 8; + ni_l = 0x00FF & value; + /* set up the ni value */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_NI_HIGH_MASK, ni_h); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_NI_LOW_MASK, ni_l); + if (!max9867->master) { + /* + * digital pll locks on to any externally supplied LRCLK signal + * and also enable rapid lock mode. + */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_PLL, MAX9867_PLL); + } else { + unsigned long int bclk_rate, pclk_bclk_ratio; + int bclk_value; + + bclk_rate = params_rate(params) * 2 * + snd_pcm_format_width(params_format(params)); + pclk_bclk_ratio = max9867->pclk/bclk_rate; + switch (snd_pcm_format_width(params_format(params))) { + case 8: + case 16: + if (pclk_bclk_ratio == 2) + bclk_value = MAX9867_IFC1B_PCLK_2; + else if (pclk_bclk_ratio == 4) + bclk_value = MAX9867_IFC1B_PCLK_4; + else if (pclk_bclk_ratio == 8) + bclk_value = MAX9867_IFC1B_PCLK_8; + else if (pclk_bclk_ratio == 16) + bclk_value = MAX9867_IFC1B_PCLK_16; + else { + dev_err(codec->dev, "unsupported sampling rate\n"); + return -EINVAL; + } + break; + case 24: + bclk_value = MAX9867_IFC1B_24BIT; + break; + case 32: + bclk_value = MAX9867_IFC1B_32BIT; + break; + default: + dev_err(codec->dev, "unsupported sampling rate\n"); + return -EINVAL; + } + regmap_update_bits(max9867->regmap, MAX9867_IFC1B, + MAX9867_IFC1B_BCLK_MASK, bclk_value); + } + return 0; +} + +static int max9867_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); + return 0; +} + +static int max9867_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + if (mute) + regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK); + else + regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + MAX9867_DAC_MUTE_MASK, 0); + return 0; +} + +static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + int value = 0; + + /* Set the prescaler based on the master clock frequency*/ + if (freq >= 10000000 && freq <= 20000000) { + value |= MAX9867_PSCLK_10_20; + max9867->pclk = freq; + } else if (freq >= 20000000 && freq <= 40000000) { + value |= MAX9867_PSCLK_20_40; + max9867->pclk = freq/2; + } else if (freq >= 40000000 && freq <= 60000000) { + value |= MAX9867_PSCLK_40_60; + max9867->pclk = freq/4; + } else { + pr_err("bad clock frequency %d", freq); + return -EINVAL; + } + value = value << MAX9867_PSCLK_SHIFT; + max9867->sysclk = freq; + /* exact integer mode is not supported */ + value &= ~MAX9867_FREQ_MASK; + regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, + MAX9867_PSCLK_MASK, value); + return 0; +} + +static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + u8 iface1A = 0, iface1B = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + max9867->master = 1; + iface1A |= MAX9867_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + max9867->master = 0; + iface1A &= ~MAX9867_MASTER; + break; + default: + return -EINVAL; + } + + /* for i2s compatible mode */ + iface1A |= MAX9867_I2S_DLY; + /* SDOUT goes to hiz state after all data is transferred */ + iface1A |= MAX9867_SDOUT_HIZ; + + /* Clock inversion bits, BCI and WCI */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_IB_NF: + iface1A |= MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_NB_IF: + iface1A |= MAX9867_WCI_MODE; + break; + default: + return -EINVAL; + } + + ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); + ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); + return 0; +} + +static struct snd_soc_dai_ops max9867_dai_ops = { + .set_fmt = max9867_dai_set_fmt, + .set_sysclk = max9867_set_dai_sysclk, + .prepare = max9867_prepare, + .digital_mute = max9867_mute, + .hw_params = max9867_dai_hw_params, +}; + +#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +static struct snd_soc_dai_driver max9867_dai[] = { + { + .name = "max9867-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9867_RATES, + .formats = MAX9867_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9867_RATES, + .formats = MAX9867_FORMATS, + }, + .ops = &max9867_dai_ops, + } +}; + +#ifdef CONFIG_PM_SLEEP +static int max9867_suspend(struct device *dev) +{ + struct max9867_priv *max9867 = dev_get_drvdata(dev); + + /* Drop down to power saving mode when system is suspended */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK); + return 0; +} + +static int max9867_resume(struct device *dev) +{ + struct max9867_priv *max9867 = dev_get_drvdata(dev); + + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); + return 0; +} +#endif + +static int max9867_probe(struct snd_soc_codec *codec) +{ + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "max98090_probe\n"); + max9867->codec = codec; + return 0; +} + +static struct snd_soc_codec_driver max9867_codec = { + .probe = max9867_probe, + .controls = max9867_snd_controls, + .num_controls = ARRAY_SIZE(max9867_snd_controls), + .dapm_routes = max9867_audio_map, + .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), + .dapm_widgets = max9867_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), +}; + +static bool max9867_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9867_STATUS: + case MAX9867_JACKSTATUS: + case MAX9867_AUXHIGH: + case MAX9867_AUXLOW: + return true; + default: + return false; + } +} + +static struct reg_default max9867_reg[] = { + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0A, 0x00 }, + { 0x0B, 0x00 }, + { 0x0C, 0x00 }, + { 0x0D, 0x00 }, + { 0x0E, 0x00 }, + { 0x0F, 0x00 }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, +}; + +static struct regmap_config max9867_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX9867_REVISION, + .reg_defaults = max9867_reg, + .num_reg_defaults = ARRAY_SIZE(max9867_reg), + .volatile_reg = max9867_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max9867_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max9867_priv *max9867; + int ret = 0, reg; + + max9867 = devm_kzalloc(&i2c->dev, + sizeof(*max9867), GFP_KERNEL); + if (!max9867) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9867); + max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); + if (IS_ERR(max9867->regmap)) { + ret = PTR_ERR(max9867->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + ret = regmap_read(max9867->regmap, + MAX9867_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "device revision: %x\n", reg); + ret = snd_soc_register_codec(&i2c->dev, &max9867_codec, + max9867_dai, ARRAY_SIZE(max9867_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + return ret; + } + return ret; +} + +static int max9867_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max9867_i2c_id[] = { + { "max9867", 0 }, +}; + +static const struct of_device_id max9867_of_match[] = { + { .compatible = "maxim,max9867", }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); + +static const struct dev_pm_ops max9867_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume) +}; + +static struct i2c_driver max9867_i2c_driver = { + .driver = { + .name = "max9867", + .of_match_table = of_match_ptr(max9867_of_match), + .pm = &max9867_pm_ops, + }, + .probe = max9867_i2c_probe, + .remove = max9867_i2c_remove, + .id_table = max9867_i2c_id, +}; + +module_i2c_driver(max9867_i2c_driver); + +MODULE_AUTHOR("anish kumar "); +MODULE_DESCRIPTION("ALSA SoC MAX9867 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h new file mode 100755 index 000000000000..65590b4ad62a --- /dev/null +++ b/sound/soc/codecs/max9867.h @@ -0,0 +1,83 @@ +/* + * max9867.h -- MAX9867 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX9867_H +#define _MAX9867_H + +/* MAX9867 register space */ + +#define MAX9867_STATUS 0x00 +#define MAX9867_JACKSTATUS 0x01 +#define MAX9867_AUXHIGH 0x02 +#define MAX9867_AUXLOW 0x03 +#define MAX9867_INTEN 0x04 +#define MAX9867_SYSCLK 0x05 +#define MAX9867_FREQ_MASK 0xF +#define MAX9867_PSCLK_SHIFT 0x4 +#define MAX9867_PSCLK_WIDTH 0x2 +#define MAX9867_PSCLK_MASK (0x03< Date: Thu, 18 Feb 2016 01:24:36 +0900 Subject: ALSA: oxfw: use workqueue instead of tasklet for scs1x This commit replaces tasklet with workqueue for scs1x functionality of ALSA oxfw driver. This driver transfers MIDI message specific for SCS.1m and SCS.1d. This task is currently done in software IRQ context of tasklet. In a view of system, this context is limited resources and some important drivers (at least, more important than ALSA oxfw driver) use the context as its bottom-harf. If the work to transfer MIDI messages is done within a time, it's better to use the other context for the work. Actually, with recent CPUs, the work will be scheduled within a time. This is a reason of this commit. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-scs1x.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index bb53eb35721b..f7ac1243ec94 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -26,7 +26,7 @@ struct fw_scs1x { u8 output_bytes; bool output_escaped; bool output_escape_high_nibble; - struct tasklet_struct tasklet; + struct work_struct work; wait_queue_head_t idle_wait; u8 buffer[HSS1394_MAX_PACKET_SIZE]; bool transaction_running; @@ -129,7 +129,7 @@ static void scs_write_callback(struct fw_card *card, int rcode, ; /* TODO: retry this packet */ scs->transaction_running = false; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } static bool is_valid_running_status(u8 status) @@ -165,9 +165,9 @@ static bool is_invalid_cmd(u8 status) status == 0xfd; } -static void scs_output_tasklet(unsigned long data) +static void scs_output_work(struct work_struct *work) { - struct fw_scs1x *scs = (struct fw_scs1x *)data; + struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work); struct snd_rawmidi_substream *stream; unsigned int i; u8 byte; @@ -311,7 +311,7 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) scs->output_idle = false; ACCESS_ONCE(scs->output) = stream; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } else { ACCESS_ONCE(scs->output) = NULL; } @@ -395,7 +395,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &midi_playback_ops); - tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs); + INIT_WORK(&scs->work, scs_output_work); init_waitqueue_head(&scs->idle_wait); scs->output_idle = true; -- cgit v1.2.3 From b14614502908beb5d898a665b40cfa91ad237f93 Mon Sep 17 00:00:00 2001 From: anish kumar Date: Thu, 18 Feb 2016 10:36:20 -0800 Subject: ASoC: max9867: Changed the if-else case to switch case Signed-off-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index b9d989f25515..5a8481bf20c2 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -212,16 +212,22 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream, switch (snd_pcm_format_width(params_format(params))) { case 8: case 16: - if (pclk_bclk_ratio == 2) + switch (pclk_bclk_ratio) { + case 2: bclk_value = MAX9867_IFC1B_PCLK_2; - else if (pclk_bclk_ratio == 4) + break; + case 4: bclk_value = MAX9867_IFC1B_PCLK_4; - else if (pclk_bclk_ratio == 8) + break; + case 8: bclk_value = MAX9867_IFC1B_PCLK_8; - else if (pclk_bclk_ratio == 16) + break; + case 16: bclk_value = MAX9867_IFC1B_PCLK_16; - else { - dev_err(codec->dev, "unsupported sampling rate\n"); + break; + default: + dev_err(codec->dev, + "unsupported sampling rate\n"); return -EINVAL; } break; -- cgit v1.2.3 From 3184270ec188a70df555ab08bca70c0e61704205 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Fri, 19 Feb 2016 15:42:06 +0800 Subject: ALSA: hda - hdmi get jack from hda_jack_tbl when not dyn_pcm_assign On Intel platform, if !dyn_pcm_assign, spec->pcm_rec[].jack is not NULL even after snd_hda_jack_tbl_clear() is called to free snd_jack. This may cause access invalid memory when calling snd_jack_report. Fixes: 25e4abb33df3 ('ALSA: hda - hdmi jack created based on pcm') Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index f4443b5fbf6e..541986f414cb 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1956,6 +1956,29 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, return ret; } +static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_jack *jack = NULL; + struct hda_jack_tbl *jack_tbl; + + /* if !dyn_pcm_assign, get jack from hda_jack_tbl + * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not + * NULL even after snd_hda_jack_tbl_clear() is called to + * free snd_jack. This may cause access invalid memory + * when calling snd_jack_report + */ + if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) + jack = spec->pcm_rec[per_pin->pcm_idx].jack; + else if (!spec->dyn_pcm_assign) { + jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); + if (jack_tbl) + jack = jack_tbl->jack; + } + return jack; +} + /* update ELD and jack state via audio component */ static void sync_eld_via_acomp(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) @@ -1989,11 +2012,10 @@ static void sync_eld_via_acomp(struct hda_codec *codec, /* pcm_idx >=0 before update_eld() means it is in monitor * disconnected event. Jack must be fetched before update_eld() */ - if (per_pin->pcm_idx >= 0) - jack = spec->pcm_rec[per_pin->pcm_idx].jack; + jack = pin_idx_to_jack(codec, per_pin); update_eld(codec, per_pin, eld); - if (jack == NULL && per_pin->pcm_idx >= 0) - jack = spec->pcm_rec[per_pin->pcm_idx].jack; + if (jack == NULL) + jack = pin_idx_to_jack(codec, per_pin); if (jack == NULL) goto unlock; snd_jack_report(jack, -- cgit v1.2.3 From 4b235c43deb8283802281a299cb730001a6ad1da Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 19 Feb 2016 11:42:34 +0530 Subject: ASoC: Intel: Skylake: Generate topology name for NHLT table header NHLT table [1] header has fields like oem_id, oem_table_id and oem_revision. Use that to load a unique topology binary specific to that platform NHLT Table is documented at: [1]: https://01.org/blogs/2016/intel-smart-sound-technology-audio-dsp Signed-off-by: Yang A Fang Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 34 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.c | 11 ++++++++--- sound/soc/intel/skylake/skl.c | 4 ++++ sound/soc/intel/skylake/skl.h | 3 +++ 4 files changed, 49 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 6e4b21cdb1bd..14d1916ea9f8 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -145,3 +145,37 @@ struct nhlt_specific_cfg return NULL; } + +static void skl_nhlt_trim_space(struct skl *skl) +{ + char *s = skl->tplg_name; + int cnt; + int i; + + cnt = 0; + for (i = 0; s[i]; i++) { + if (!isspace(s[i])) + s[cnt++] = s[i]; + } + + s[cnt] = '\0'; +} + +int skl_nhlt_update_topology_bin(struct skl *skl) +{ + struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct device *dev = bus->dev; + + dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", + nhlt->header.oem_id, nhlt->header.oem_table_id, + nhlt->header.oem_revision); + + snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", + skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, + nhlt->header.oem_revision, "-tplg.bin"); + + skl_nhlt_trim_space(skl); + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 216d7a8e5680..ba0d02d1613e 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1716,11 +1716,16 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl *skl = ebus_to_skl(ebus); - ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { dev_err(bus->dev, "tplg fw %s load failed with %d\n", - "dfw_sst.bin", ret); - return ret; + skl->tplg_name, ret); + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } } /* diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 06f4b2c13423..f89abb179efe 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -573,11 +573,15 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->pci_id = pci->device; + skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) goto out_free; + skl_nhlt_update_topology_bin(skl); + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4d18293b5537..1ca1911f1587 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -73,6 +73,8 @@ struct skl { struct list_head ppl_list; const char *fw_name; + char tplg_name[64]; + unsigned short pci_id; const struct firmware *tplg; int supend_active; @@ -96,6 +98,7 @@ void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); +int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); void skl_free_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); -- cgit v1.2.3 From d2240f0dad488c66e14c45762bd23999901f57a1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:13:13 +0000 Subject: ASoC: rsnd: rename RSND_DVC_CHANNELS to RSND_MAX_CHANNELS The channels number is not only for DVC. Let's rename it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- sound/soc/sh/rcar/dvc.c | 6 +++--- sound/soc/sh/rcar/rsnd.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 704ba7ae9eaf..f86d62730614 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -988,7 +988,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, int ch_size, u32 max) { - if (ch_size > RSND_DVC_CHANNELS) + if (ch_size > RSND_MAX_CHANNELS) return -EINVAL; _cfg->cfg.max = max; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 302c193f674d..d757f1316385 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -83,15 +83,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; int i; /* Enable Ramp */ if (dvc->ren.val) - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.cfg.max; else - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.val[i]; /* Enable Digital Volume */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index bbaf89b6de8a..61cb4aefdfd9 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -550,10 +550,10 @@ struct rsnd_kctrl_cfg { struct snd_kcontrol *kctrl; }; -#define RSND_DVC_CHANNELS 8 +#define RSND_MAX_CHANNELS 8 struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; }; struct rsnd_kctrl_cfg_s { -- cgit v1.2.3 From 0fbab951db17085fbb521b3a50550990b763bdf4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:13:45 +0000 Subject: ASoC: rsnd: setup SRC_ROUTE_MODE0 before SRC_SRCIR SRC_ROUTE_MODE0 determines whether to use SRC. Thus, it should be setup before SRC_SRCIR. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ab5f13155055..dab0954196b7 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -249,6 +249,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, break; } + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); @@ -258,7 +260,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_BSISR, bsisr); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); -- cgit v1.2.3 From 1120dbff2abd3dd9ca3f0736d0690b9592cdadb3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:14:09 +0000 Subject: ASoC: rsnd: indicates status failed SSI Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 592505a4bc13..a72ce284e34b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -140,7 +140,8 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, udelay(50); } - dev_warn(dev, "status check failed\n"); + dev_warn(dev, "%s[%d] status check failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } static int rsnd_ssi_irq(struct rsnd_mod *mod, -- cgit v1.2.3 From 26d34b11af6a344da6191beca2e2883f65d2597a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:14:37 +0000 Subject: ASoC: rsnd: SSI function parameter uses "mod" instead of "ssi" To reduce confusion, SSI uses "mod" instead of "ssi" as function parameter Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index a72ce284e34b..c5c4510afb2d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -188,14 +188,14 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } -static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, +static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); int slots = rsnd_get_slot_width(io); int j, ret; @@ -255,11 +255,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, +static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); if (!rsnd_rdai_is_clk_master(rdai)) @@ -277,11 +277,12 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, rsnd_adg_ssi_clk_stop(mod); } -static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, +static int rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own; u32 cr_mode; u32 wsr; @@ -317,7 +318,7 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, return -EINVAL; } - if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { + if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { @@ -356,14 +357,14 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, rsnd_mod_power_on(mod); - ret = rsnd_ssi_master_clk_start(ssi, io); + ret = rsnd_ssi_master_clk_start(mod, io); if (ret < 0) return ret; if (rsnd_ssi_is_parent(mod, io)) return 0; - ret = rsnd_ssi_config_init(ssi, io); + ret = rsnd_ssi_config_init(mod, io); if (ret < 0) return ret; @@ -389,7 +390,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; - rsnd_ssi_master_clk_stop(ssi, io); + rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); -- cgit v1.2.3 From 5bf5d8fc7f5a8a1e75413939e4bdb00ebc2d5610 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:16:04 +0000 Subject: ASoC: rsnd: fixup forever loop bug on SSI commit b5b442abd9 ("ASoC: rsnd: add .irq callback") added .irq support, and it cares both parent SSI and normal SSI. But it should care only normal SSI. Otherwise SSI might be forever loop if SSI is used as both parent SSI and normal SSI (= 2 users), and if under/over run error happen. Because irq disable do nothing in such case. This patch solve this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c5c4510afb2d..90c3f58821db 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -149,13 +149,12 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, struct rsnd_priv *priv, int enable) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 val = 0; if (rsnd_is_gen1(priv)) return 0; - if (ssi->usrcnt != 1) + if (rsnd_ssi_is_parent(mod, io)) return 0; if (enable) -- cgit v1.2.3 From c8e969a85ecb982dccf2ba13ba9aff9f1a68eab2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:16:43 +0000 Subject: ASoC: rsnd: add missing .irq callback for DMA commit b5b442abd9d5 ("ASoC: rsnd: add .irq callback") added .irq callback but SSI DMA is missing it. This patch adds it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 90c3f58821db..b1a29e28b251 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -694,6 +694,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; -- cgit v1.2.3 From 0dc6bf75023a42895962800020583c19e0b87159 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:17:18 +0000 Subject: ASoC: rsnd: tidyup SSI init/start sequence SSI want to have SSIWSR settings and SSICR settings without EN bit when init, and SSICR EN bit only when start timing. Otherwise, SSI output signal might be unstable. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index b1a29e28b251..120587270fe7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -276,7 +276,7 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, rsnd_adg_ssi_clk_stop(mod); } -static int rsnd_ssi_config_init(struct rsnd_mod *mod, +static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); @@ -313,8 +313,6 @@ static int rsnd_ssi_config_init(struct rsnd_mod *mod, case 32: cr_own |= DWL_24; break; - default: - return -EINVAL; } if (rsnd_ssi_is_dma_mode(mod)) { @@ -338,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_mod *mod, ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; +} - return 0; +static void rsnd_ssi_register_setup(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_write(mod, SSICR, ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode); /* without EN */ } /* @@ -360,12 +366,10 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (ret < 0) return ret; - if (rsnd_ssi_is_parent(mod, io)) - return 0; + if (!rsnd_ssi_is_parent(mod, io)) + rsnd_ssi_config_init(mod, io); - ret = rsnd_ssi_config_init(mod, io); - if (ret < 0) - return ret; + rsnd_ssi_register_setup(mod); /* clear error status */ rsnd_ssi_status_clear(mod); @@ -428,22 +432,14 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; - - cr = ssi->cr_own | - ssi->cr_clk | - ssi->cr_mode; - /* * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode */ - if (!rsnd_ssi_multi_slaves(io)) - cr |= EN; + if (rsnd_ssi_multi_slaves(io)) + return 0; - rsnd_mod_write(mod, SSICR, cr); - rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_bset(mod, SSICR, EN, EN); return 0; } -- cgit v1.2.3 From 098bd8911a5eacc3b70fdc09fa4084657511c584 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:17:52 +0000 Subject: ASoC: rsnd: Parent SSI attach is not needed if not clock master Parent SSI is needed if it is PIN sharing and clock master, otherwise, not needed. But, whether clockk master is judged on .set_fmt, thus, it can't call rsnd_ssi_parent_attach() on .probe. Now, .pcm_new will be called after .set_fmt, so this patch reuses it at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 120587270fe7..b5c6f0c274c3 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -545,12 +545,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * SSI PIO */ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) + struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + if (!__rsnd_ssi_is_pin_sharing(mod)) return; + if (!rsnd_rdai_is_clk_master(rdai)) + return; + switch (rsnd_mod_id(mod)) { case 1: case 2: @@ -565,6 +570,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, } } +static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + /* + * rsnd_rdai_is_clk_master() will be enabled after set_fmt, + * and, pcm_new will be called after it. + * This function reuse pcm_new at this point. + */ + rsnd_ssi_parent_attach(mod, io); + + return 0; +} + static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -580,7 +599,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, if (rsnd_ssi_is_multi_slave(mod, io)) return 0; - rsnd_ssi_parent_attach(mod, io, priv); + /* + * It can't judge ssi parent at this point + * see rsnd_ssi_pcm_new() + */ ret = rsnd_ssiu_attach(io, mod); if (ret < 0) @@ -602,6 +624,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -691,6 +714,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; -- cgit v1.2.3 From 615fb6c7b13b7f142f5f8e23e5f8593dd1e7b319 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:18:16 +0000 Subject: ASoC: rsnd: move rsnd_ssi_irq() position prepare for runtime judging for SSI work Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index b5c6f0c274c3..d46bc08ad977 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -144,27 +144,6 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, rsnd_mod_name(mod), rsnd_mod_id(mod)); } -static int rsnd_ssi_irq(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv, - int enable) -{ - u32 val = 0; - - if (rsnd_is_gen1(priv)) - return 0; - - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - if (enable) - val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; - - rsnd_mod_write(mod, SSI_INT_ENABLE, val); - - return 0; -} - u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; @@ -480,6 +459,27 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) +{ + u32 val = 0; + + if (rsnd_is_gen1(priv)) + return 0; + + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); + + return 0; +} + static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { -- cgit v1.2.3 From 4f5c634d58e71963d3c34a0a4af9ec71785f094f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:18:54 +0000 Subject: ASoC: rsnd: judge multi SSI in runtime Current rsnd supports multi SSI (maximum 4 SSI for 8ch), and, it should determine whether using each SSI or not in runtime. Current judgement is vague, and had broken by c308abe45e2("ASoC: rsnd: rsnd_ssi_is_multi_slave() macro uses rsnd_ssi_multi_slaves()") This patch makes clean it, and solve this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- sound/soc/sh/rcar/rsnd.h | 2 +- sound/soc/sh/rcar/ssi.c | 15 +++++++++++++-- sound/soc/sh/rcar/ssiu.c | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f86d62730614..7bc5b724fbf5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -230,7 +230,7 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) int chan = runtime->channels; /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 61cb4aefdfd9..5f613eb42614 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -599,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d46bc08ad977..32f1f5fb82b6 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -144,7 +144,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, rsnd_mod_name(mod), rsnd_mod_id(mod)); } -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) +static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; enum rsnd_mod_type types[] = { @@ -166,6 +166,17 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 mask = rsnd_ssi_multi_slaves(io); + + if (mask && (runtime->channels >= 6)) + return mask; + + return 0; +} + static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -415,7 +426,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode */ - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) return 0; rsnd_mod_bset(mod, SSICR, EN, EN); diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 11e55889b401..1b8ea0e09bc2 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io); + u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io); int use_busif = rsnd_ssi_use_busif(io); int id = rsnd_mod_id(mod); u32 mask1, val1; @@ -136,7 +136,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0x1); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0x1); return 0; @@ -151,7 +151,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0); return 0; -- cgit v1.2.3 From fd9adcfdc1434fdd4d0a14ddfe686449a6ffeeb3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:19:12 +0000 Subject: ASoC: rsnd: judge work SSI in runtime Current rsnd supports multi SSI (maximum 4 SSI for 8ch), and, it should determine whether using each SSI or not in runtime. All SSIs are not used even if there are 4 SSI in case of stereo. Current driver setups un-used SSI in such case. It is no problem, but not needed. This patch judges it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 32f1f5fb82b6..0979db8af8f9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -96,6 +96,8 @@ struct rsnd_ssi { #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) #define rsnd_ssi_is_multi_slave(mod, io) \ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) +#define rsnd_ssi_is_run_mods(mod, io) \ + (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -166,6 +168,16 @@ static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } +static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + + return rsnd_ssi_multi_slaves_runtime(io) | + 1 << rsnd_mod_id(ssi_mod) | + 1 << rsnd_mod_id(ssi_parent_mod); +} + u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -348,6 +360,9 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + ssi->usrcnt++; rsnd_mod_power_on(mod); @@ -374,6 +389,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + if (!ssi->usrcnt) { dev_err(dev, "%s[%d] usrcnt error\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); @@ -422,6 +440,9 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + /* * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode @@ -441,6 +462,9 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + /* * don't stop if not last user * see also @@ -483,6 +507,9 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, if (rsnd_ssi_is_parent(mod, io)) return 0; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + if (enable) val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; -- cgit v1.2.3 From 6793a3d70676c6f3d2ec0ce4e1e241617541702c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 17 Feb 2016 21:33:59 +0530 Subject: ASoC: hdac_hdmi: Fix possible memory leak in hw_params It's possible for hw_params to be called two times. So add NULL check to prevent memory leak. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2884dbf0bc07..d2a2e1882fdd 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -405,9 +405,13 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return -ENODEV; } - dd = kzalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - return -ENOMEM; + dd = snd_soc_dai_get_dma_data(dai, substream); + if (!dd) { + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + } + dd->format = snd_hdac_calc_stream_format(params_rate(hparams), params_channels(hparams), params_format(hparams), 24, 0); @@ -433,9 +437,11 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, AC_VERB_SET_STREAM_FORMAT, 0); dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dd); + if (dd) { + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dd); + } return 0; } -- cgit v1.2.3 From 54dfa1eaf4f8222811900922c67f539e60cc69c2 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 17 Feb 2016 21:34:00 +0530 Subject: ASoC: hdac_hdmi: Don't fail in dai startup to make userland happy In dai startup, driver was checking for ELD and would fail if no monitor is connected. This causes userland like PA, CRAS to be unhappy as they scan the device list at bootup. So move the ELD check to hw_params and fail if valid ELD is not found. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 48 ++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index d2a2e1882fdd..f12af62a2084 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -398,10 +398,20 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) { struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_pin *pin; struct hdac_ext_dma_params *dd; - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); + dai_map = &hdmi->dai_map[dai->id]; + pin = dai_map->pin; + + if (!pin) + return -ENODEV; + + if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) { + dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n", + pin->nid); return -ENODEV; } @@ -431,11 +441,6 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; - snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, - AC_VERB_SET_STREAM_FORMAT, 0); - dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); if (dd) { @@ -536,6 +541,11 @@ static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt( return NULL; } +/* + * This tries to get a valid pin and set the HW constraints based on the + * ELD. Even if a valid pin is not found return success so that device open + * doesn't fail. + */ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -550,17 +560,22 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, cvt = dai_map->cvt; pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt); + + /* + * To make PA and other userland happy. + * userland scans devices so returning error does not help. + */ if (!pin) - return -EIO; + return 0; if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) { - dev_err(&hdac->hdac.dev, + dev_warn(&hdac->hdac.dev, "Failed: montior present? %d ELD valid?: %d for pin: %d\n", pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); - return -ENODEV; + return 0; } dai_map->pin = pin; @@ -587,12 +602,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; - hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); + if (dai_map->pin) { + snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_STREAM_FORMAT, 0); + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); - snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, + snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - dai_map->pin = NULL; + dai_map->pin = NULL; + } } static int -- cgit v1.2.3 From aeaccef0bc0d364792ecc9455cb13d007fd7a371 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Wed, 17 Feb 2016 21:34:01 +0530 Subject: ASoC: hdac_hdmi: Fix to keep codec power active during enumeration. The codec power is turned OFF in the first explicit call to pm_runtime_suspend to keep the i915 refcount balanced. During regular operation, the power is turned ON/OFF in runtime PM handlers. Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f12af62a2084..57ab212b279b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1426,6 +1426,18 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) INIT_LIST_HEAD(&hdmi_priv->pcm_list); mutex_init(&hdmi_priv->pin_mutex); + /* + * Turned off in the runtime_suspend during the first explicit + * pm_runtime_suspend call. + */ + ret = snd_hdac_display_power(edev->hdac.bus, true); + if (ret < 0) { + dev_err(&edev->hdac.dev, + "Cannot turn on display power on i915 err: %d\n", + ret); + return ret; + } + ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); if (ret < 0) { dev_err(&codec->dev, -- cgit v1.2.3 From ab85f5b36ea3474622303a36293e63cf78f4a797 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 17 Feb 2016 21:34:02 +0530 Subject: ASoC: hdac_hdmi: Fix to reconfigure registers in runtime resume The register are reset during the D3 to D0 transition. So reconfigure them. Also as all DAIs are now supported, remove the dai id check in prepare. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 57ab212b279b..2445b4351efb 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -374,11 +374,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, struct hdac_ext_dma_params *dd; int ret; - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); - return -ENODEV; - } - dai_map = &hdmi->dai_map[dai->id]; dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); @@ -451,6 +446,18 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, return 0; } +static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + /* Enable transmission */ + snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, 1); + + /* Category Code (CC) to zero */ + snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, 0); +} + static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, struct hdac_hdmi_dai_pin_map *dai_map) { @@ -580,6 +587,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map->pin = pin; + hdac_hdmi_enable_cvt(hdac, dai_map); ret = hdac_hdmi_enable_pin(hdac, dai_map); if (ret < 0) return ret; @@ -949,14 +957,6 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) dai_map->dai_id = dai_id; dai_map->cvt = cvt; - /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, 1); - - /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0); - dai_id++; if (dai_id == HDA_MAX_CVTS) { @@ -1527,6 +1527,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) return err; } + hdac_hdmi_skl_enable_all_pins(&edev->hdac); + hdac_hdmi_skl_enable_dp12(&edev->hdac); + /* Power up afg */ if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) snd_hdac_codec_write(hdac, hdac->afg, 0, -- cgit v1.2.3 From aceb5d20d725fdc9c9d42522944baed3370b472c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 17 Feb 2016 21:34:05 +0530 Subject: ASoC: Intel: Skylake: Fix possible memory corruption in codec dai dma params Set required parameters for hdac_ext_dma_params object instead of skl_dma_params. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 092450ac1c78..bbb6c2870b7f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -475,7 +475,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct skl_dma_params *dma_params; + struct hdac_ext_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct skl_pipe_params p_params = {0}; @@ -487,11 +487,9 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); /* set the stream tag in the codec dai dma params */ - dma_params = (struct skl_dma_params *) - snd_soc_dai_get_dma_data(codec_dai, substream); + dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; - snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); -- cgit v1.2.3 From 6980c057ea1bca145e8fef39ddf541254f7c8747 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 17 Feb 2016 21:34:06 +0530 Subject: ASoC: Intel: Skylake: Add i915 enabling in skl probe The SKL also supports HDMI output so in probe we need to enable the HDMI using common i915 APIs to ensure it gets probed on the bus After S3 during the controller resequencing the codec domain need to be kept ON for successful reconfiguration of Codec. Once configured it will be turned OFF in codec driver. Signed-off-by: Vinod Koul Signed-off-by: Subhransu S. Prusty Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 803f95e40679..6e65085e8bc3 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -154,6 +154,7 @@ config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE select SND_SOC_TOPOLOGY + select SND_HDA_I915 select SND_SOC_INTEL_SST config SND_SOC_INTEL_SKL_RT286_MACH diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f89abb179efe..0d38bdb8321b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -28,6 +28,9 @@ #include #include #include "../common/sst-acpi.h" +#include +#include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -243,6 +246,16 @@ static int skl_resume(struct device *dev) struct hdac_bus *bus = ebus_to_hbus(ebus); int ret; + /* Turned OFF in HDMI codec driver after codec reconfiguration */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, true); + if (ret < 0) { + dev_err(bus->dev, + "Cannot turn on display power on i915\n"); + return ret; + } + } + /* * resume only when we are not in suspend active, otherwise need to * restore the device @@ -481,6 +494,27 @@ static int skl_create(struct pci_dev *pci, return 0; } +static int skl_i915_init(struct hdac_bus *bus) +{ + int err; + + /* + * The HDMI codec is in GPU so we need to ensure that it is powered + * up and ready for probe + */ + err = snd_hdac_i915_init(bus); + if (err < 0) + return err; + + err = snd_hdac_display_power(bus, true); + if (err < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + return err; + } + + return err; +} + static int skl_first_init(struct hdac_ext_bus *ebus) { struct skl *skl = ebus_to_skl(ebus); @@ -543,6 +577,12 @@ static int skl_first_init(struct hdac_ext_bus *ebus) /* initialize chip */ skl_init_pci(skl); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = skl_i915_init(bus); + if (err < 0) + return err; + } + skl_init_chip(bus, true); /* codec detection */ @@ -617,6 +657,14 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_unregister; + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + return err; + } + } + /*configure PM */ pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); @@ -671,6 +719,9 @@ static void skl_remove(struct pci_dev *pci) if (skl->tplg) release_firmware(skl->tplg); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_i915_exit(&ebus->bus); + if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); -- cgit v1.2.3 From 8cca87c073ac38c7866fb86205fc9ff312a3a575 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Wed, 17 Feb 2016 21:34:07 +0530 Subject: ASoC: Intel: Skylake: Add HDMI FE and BE cpu dais This patch enables HDMI support in passthrough mode in skylake. Skylake supports 3 pin widgets in HDMI codec, so add 3 FE and 3 BE dais Add required hw info, formats, rates to support HDMI. Signed-off-by: Subhransu S. Prusty Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 78 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bbb6c2870b7f..dab0900eef26 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -676,6 +676,51 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, +{ + .name = "HDMI1 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI1 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "HDMI2 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI2 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "HDMI3 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI3 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, /* BE CPU Dais */ { @@ -715,14 +760,41 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "iDisp Pin", + .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, .playback = { - .stream_name = "iDisp Tx", + .stream_name = "iDisp1 Tx", .channels_min = HDA_STEREO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "iDisp2 Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "iDisp2 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "iDisp3 Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "iDisp3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, }, }, { -- cgit v1.2.3 From 144a98835d2b4fc8a732628eaa5c98c573d58baf Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:17 +0000 Subject: ASoC: qcom: use snd_dma_alloc/free* apis There is no point in having local allocation functions when the driver can use snd_dma_alloc/free() apis. This patch replaces the local versions of the dma allocation apis with the snd_dma_alloc/free() apis. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 42 ++++++----------------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 4aeb8e1a7160..ec2414dc089a 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -439,39 +439,6 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } -static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = rt->platform->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr, - GFP_KERNEL); - if (!buf->area) { - dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n", - __func__); - return -ENOMEM; - } - buf->bytes = size; - - return 0; -} - -static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - - if (buf->area) { - dma_free_coherent(rt->dev, buf->bytes, buf->area, - buf->addr); - } - buf->area = NULL; -} - static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; @@ -483,6 +450,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) struct lpass_variant *v = drvdata->variant; int ret; struct lpass_pcm_data *data; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -499,7 +467,9 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) snd_soc_pcm_set_drvdata(soc_runtime, data); - ret = lpass_platform_alloc_buffer(substream, soc_runtime); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &substream->dma_buffer); if (ret) return ret; @@ -514,7 +484,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) return 0; err_buf: - lpass_platform_free_buffer(substream, soc_runtime); + snd_dma_free_pages(&substream->dma_buffer); return ret; } @@ -533,7 +503,7 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm) if (v->free_dma_channel) v->free_dma_channel(drvdata, data->rdma_ch); - lpass_platform_free_buffer(substream, soc_runtime); + snd_dma_free_pages(&substream->dma_buffer); } static struct snd_soc_platform_driver lpass_platform_driver = { -- cgit v1.2.3 From ffc1325e1d3bb809dbd8c0b7f9682a22e51a69c3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:23 +0000 Subject: ASoC: qcom: add wrdma register details to lpass_variant This patch adds wrdma related register offsets, wrdma channel start and shifts into lpass variant structure. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 0b63e2e5bcc9..024a771be4dc 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -71,12 +71,16 @@ struct lpass_variant { u32 rdma_reg_base; u32 rdma_reg_stride; u32 rdma_channels; + u32 wrdma_reg_base; + u32 wrdma_reg_stride; + u32 wrdma_channels; /** * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x **/ u32 rdmactl_audif_start; + u32 wrdma_channel_start; /* SOC specific intialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); -- cgit v1.2.3 From ec5b82878c36f8c7c6d0d85eacd5185633e6c91b Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:30 +0000 Subject: ASoC: qcom: rename rdmactl_audif_start to dmactrl_audif_start This patch renames rdmactl_audif_start to dmactrl_audif_start as this is common for both rdma and wrdma. Without this patch the name would be bit misleading to the readers. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 2 +- sound/soc/qcom/lpass-platform.c | 4 ++-- sound/soc/qcom/lpass.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 94efc01020c4..6cc7e72b6ff3 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -212,7 +212,7 @@ static struct lpass_variant apq8016_data = { .rdma_reg_base = 0x8400, .rdma_reg_stride = 0x1000, .rdma_channels = 2, - .rdmactl_audif_start = 1, + .dmactl_audif_start = 1, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .init = apq8016_lpass_init, diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index ec2414dc089a..7245c2ed4e8b 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -91,7 +91,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); unsigned int regval; int bitwidth; - int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; + int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -101,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF(rdma_port) | + LPAIF_RDMACTL_AUDINTF(dma_port) | LPAIF_RDMACTL_FIFOWM_8; switch (bitwidth) { diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 024a771be4dc..412399acfe3d 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -79,7 +79,7 @@ struct lpass_variant { * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x **/ - u32 rdmactl_audif_start; + u32 dmactl_audif_start; u32 wrdma_channel_start; /* SOC specific intialization like clocks */ int (*init)(struct platform_device *pdev); -- cgit v1.2.3 From 73c847b6d3670e4521cd3f603102332614d0b640 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:37 +0000 Subject: ASoC: qcom: pass direction to dma allocation This patch updates the internal dma allocation callbacks to take the stream direction so that it can allocate channels suitable for that stream direction. Before the capture support this was not necessary. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 3 ++- sound/soc/qcom/lpass-ipq806x.c | 2 +- sound/soc/qcom/lpass-platform.c | 3 ++- sound/soc/qcom/lpass.h | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 6cc7e72b6ff3..99061f42953e 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -133,7 +133,8 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { }, }; -static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction) { struct lpass_variant *v = drvdata->variant; int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 7a4167952711..119048c016ec 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -63,7 +63,7 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .ops = &asoc_qcom_lpass_cpu_dai_ops, }; -static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) { return IPQ806X_LPAIF_RDMA_CHAN_MI2S; } diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 7245c2ed4e8b..69c5ad8e2bd7 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -457,7 +457,8 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) return -ENOMEM; if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata); + data->rdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_PLAYBACK); if (IS_ERR_VALUE(data->rdma_ch)) return data->rdma_ch; diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 412399acfe3d..8475b609f8ce 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -84,7 +84,7 @@ struct lpass_variant { /* SOC specific intialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); - int (*alloc_dma_channel)(struct lpass_data *data); + int (*alloc_dma_channel)(struct lpass_data *data, int direction); int (*free_dma_channel)(struct lpass_data *data, int ch); /* SOC specific dais */ -- cgit v1.2.3 From 0a536400ad2ea72c80e6257fa71aa78d17bd43e3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:45 +0000 Subject: ASoC: qcom: ipq806x: add error in dma allocation. ipq806x is only ever tested for playback so return error in dma allocation if the stream direction is capture. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-ipq806x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 119048c016ec..29a35296d8da 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -65,7 +65,10 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) { - return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + else /* Capture currently not implemented */ + return -EINVAL; } static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) -- cgit v1.2.3 From 4d809fb12189aef9419f7337bb1767de85699003 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:51 +0000 Subject: ASoC: qcom: rename rdma_ch_bit_map to dma_ch_bit_map rdma_ch_bit_map can be reused for wrdma channel allocations as wrdma channel numbering start after rdma channel numbers. With capture support referring rdma_ch_bit_map for wrdma channel allocation is confusing, so renaming rdma_ch_bit_map to dma_ch_bit_map makes sense. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 6 +++--- sound/soc/qcom/lpass.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 99061f42953e..ca3bbd58c81a 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -137,20 +137,20 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, int direction) { struct lpass_variant *v = drvdata->variant; - int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, + int chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, v->rdma_channels); if (chan >= v->rdma_channels) return -EBUSY; - set_bit(chan, &drvdata->rdma_ch_bit_map); + set_bit(chan, &drvdata->dma_ch_bit_map); return chan; } static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) { - clear_bit(chan, &drvdata->rdma_ch_bit_map); + clear_bit(chan, &drvdata->dma_ch_bit_map); return 0; } diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 8475b609f8ce..30714ad1e138 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -50,7 +50,7 @@ struct lpass_data { struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ - unsigned long rdma_ch_bit_map; + unsigned long dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; -- cgit v1.2.3 From 0a14a1bf2e81a75c5b814a3904f971fb5a53c068 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:17:57 +0000 Subject: ASoC: qcom: ipq806x: add wrdma related register offsets This patch adds wrdma related register offsets to the lpass variant data of ipq806x. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-ipq806x.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 29a35296d8da..608c1a92af8a 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -86,6 +86,10 @@ static struct lpass_variant ipq806x_data = { .rdma_reg_base = 0x6000, .rdma_reg_stride = 0x1000, .rdma_channels = 4, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, .dai_driver = &ipq806x_lpass_cpu_dai_driver, .num_dai = 1, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, -- cgit v1.2.3 From 39ad0ecd1e26a4138bf299b98265fceab60553a6 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:06 +0000 Subject: ASoC: qcom: add mic related i2s control register defines This patch adds mic related bitmasks and offsets in the i2c control register. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-lpaif-reg.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 95e22f131052..65997945cc25 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -47,6 +47,28 @@ #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8) +#define LPAIF_I2SCTL_MICEN_SHIFT 8 +#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT) +#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT) + +#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4) +#define LPAIF_I2SCTL_MICMODE_SHIFT 4 +#define LPAIF_I2SCTL_MICMODE_NONE (0 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD0 (1 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD1 (2 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD2 (3 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD3 (4 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD01 (5 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD23 (6 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_6CH (7 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_8CH (8 << LPAIF_I2SCTL_MICMODE_SHIFT) + +#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3) +#define LPAIF_I2SCTL_MICMONO_SHIFT 3 +#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT) +#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT) + #define LPAIF_I2SCTL_WSSRC_MASK 0x0004 #define LPAIF_I2SCTL_WSSRC_SHIFT 2 #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) -- cgit v1.2.3 From 71aaa600787faffd72a9b674c9e5c9ded7cd9a82 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:14 +0000 Subject: ASoC: qcom: add wrdma register definitions This patch adds wrdma registers into the lpaif-reg.h. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-lpaif-reg.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 65997945cc25..760d21905f90 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -145,4 +145,15 @@ #define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) #define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) +#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \ + (v->wrdma_reg_base + (addr) + \ + v->wrdma_reg_stride * (chan - v->wrdma_channel_start)) + +#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) + #endif /* __LPASS_LPAIF_REG_H__ */ -- cgit v1.2.3 From ec9e0ec84476954f971feb5e0422b48cb25dde58 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:20 +0000 Subject: ASoC: qcom: add generic bit masks for RDMA and WRDMA This patch adds generic masks for accessing bits in rdma/wrdma registers. Doing this would simplify the driver and adding capture support would be much simpler. Also there is no point in having same bit masks for bits in both rdma and wrdma registers. This patch also deletes the RDMA specific bit masks and makes the code use the generic bit masks. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-lpaif-reg.h | 83 ++++++++++++++++++++++++---------------- sound/soc/qcom/lpass-platform.c | 54 ++++++++++++++------------ 2 files changed, 79 insertions(+), 58 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 760d21905f90..2240bc68e6ec 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -112,39 +112,6 @@ #define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan)) -#define LPAIF_RDMACTL_BURSTEN_MASK 0x800 -#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 -#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) -#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) - -#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 -#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 -#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) - -#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 -#define LPAIF_RDMACTL_AUDINTF_SHIFT 4 - -#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E -#define LPAIF_RDMACTL_FIFOWM_SHIFT 1 -#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) - -#define LPAIF_RDMACTL_ENABLE_MASK 0x1 -#define LPAIF_RDMACTL_ENABLE_SHIFT 0 -#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) -#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) - #define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \ (v->wrdma_reg_base + (addr) + \ v->wrdma_reg_stride * (chan - v->wrdma_channel_start)) @@ -156,4 +123,54 @@ #define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) +#define __LPAIF_DMA_REG(v, chan, dir, reg) \ + (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + LPAIF_RDMA##reg##_REG(v, chan) : \ + LPAIF_WRDMA##reg##_REG(v, chan) + +#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) +#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) +#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) +#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) +#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) +#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) + +#define LPAIF_DMACTL_BURSTEN_MASK 0x800 +#define LPAIF_DMACTL_BURSTEN_SHIFT 11 +#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT) +#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT) + +#define LPAIF_DMACTL_WPSCNT_MASK 0x700 +#define LPAIF_DMACTL_WPSCNT_SHIFT 8 +#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT) + +#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0 +#define LPAIF_DMACTL_AUDINTF_SHIFT 4 +#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT) + +#define LPAIF_DMACTL_FIFOWM_MASK 0x00E +#define LPAIF_DMACTL_FIFOWM_SHIFT 1 +#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT) + +#define LPAIF_DMACTL_ENABLE_MASK 0x1 +#define LPAIF_DMACTL_ENABLE_SHIFT 0 +#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT) +#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT) + +#define LPAIF_DMACTL_DYNCLK_MASK BIT(12) +#define LPAIF_DMACTL_DYNCLK_SHIFT 12 +#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT) +#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT) #endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 69c5ad8e2bd7..348e50bc8891 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -90,6 +90,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; + int dir = substream->stream; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; @@ -100,25 +101,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF(dma_port) | - LPAIF_RDMACTL_FIFOWM_8; + regval = LPAIF_DMACTL_BURSTEN_INCR4 | + LPAIF_DMACTL_AUDINTF(dma_port) | + LPAIF_DMACTL_FIFOWM_8; switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_THREE; + regval |= LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -130,19 +131,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, case 32: switch (channels) { case 1: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_SIX; + regval |= LPAIF_DMACTL_WPSCNT_SIX; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + regval |= LPAIF_DMACTL_WPSCNT_EIGHT; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -157,7 +158,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); + LPAIF_DMACTL_REG(v, pcm_data->rdma_ch, dir), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -194,6 +195,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; int ret, ch = pcm_data->rdma_ch; + int dir = substream->stream; ret = regmap_write(drvdata->lpaif_map, LPAIF_RDMABASE_REG(v, ch), @@ -205,7 +207,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABUFF_REG(v, ch), + LPAIF_DMABUFF_REG(v, ch, dir), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", @@ -214,7 +216,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMAPER_REG(v, ch), + LPAIF_DMAPER_REG(v, ch, dir), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", @@ -223,8 +225,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -243,6 +245,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; int ret, ch = pcm_data->rdma_ch; + int dir = substream->stream; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -269,9 +272,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -282,9 +285,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_OFF); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -315,9 +318,10 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; int ret, ch = pcm_data->rdma_ch; + int dir = substream->stream; ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), &base_addr); + LPAIF_DMABASE_REG(v, ch, dir), &base_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", __func__, ret); @@ -325,7 +329,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( } ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMACURR_REG(v, ch), &curr_addr); + LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", __func__, ret); -- cgit v1.2.3 From dad8061494d2a2ce6edc2ae5ab530b0d330b2685 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:27 +0000 Subject: ASoC: qcom: apq8016: set the correct max register for regmap Now that we are ready to access wrdma registers, set the max register and other regmap related configs to use correct values. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index e5101e0d2d37..91774fc87fcc 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -294,6 +294,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -327,6 +338,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -344,6 +368,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) if (reg == LPAIF_RDMACURR_REG(v, i)) return true; + for (i = 0; i < v->wrdma_channels; ++i) + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + return false; } @@ -398,8 +426,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR((void const __force *)drvdata->lpaif); } - lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, - variant->rdma_channels); + lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant, + variant->wrdma_channels + + variant->wrdma_channel_start); drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, &lpass_cpu_regmap_config); -- cgit v1.2.3 From fb5d11524eda5561f6dd8cc03f9dc778027ce907 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:33 +0000 Subject: ASoC: qcom: add mic support This patch adds mic support to the lpass driver, most of the driver is reused as it is, only the register level access is changed depending on te direction of the stream. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 113 +++++++++++++++++++-------- sound/soc/qcom/lpass-platform.c | 167 +++++++++++++++++++++++++++++----------- 2 files changed, 204 insertions(+), 76 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 91774fc87fcc..3cde9fb977fa 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (channels) { - case 1: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_MONO; - break; - case 2: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 4: - regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 6: - regval |= LPAIF_I2SCTL_SPKMODE_6CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 8: - regval |= LPAIF_I2SCTL_SPKMODE_8CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - default: - dev_err(dai->dev, "%s() invalid channels given: %u\n", - __func__, channels); - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_SPKMODE_6CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_SPKMODE_8CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } + } else { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_MICMODE_QUAD01; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_MICMODE_6CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_MICMODE_8CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } } ret = regmap_write(drvdata->lpaif_map, @@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; + unsigned int val, mask; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret = -EINVAL; + unsigned int val, mask; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_DISABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_DISABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_DISABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 348e50bc8891..6e8665430bd5 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -26,6 +26,7 @@ struct lpass_pcm_data { int rdma_ch; + int wrdma_ch; int i2s_port; }; @@ -90,10 +91,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; - int dir = substream->stream; + int ch, dir = substream->stream; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; + bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", @@ -158,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, pcm_data->rdma_ch, dir), regval); + LPAIF_DMACTL_REG(v, ch, dir), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -175,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; + unsigned int reg; int ret; - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch); + else + reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch); + + ret = regmap_write(drvdata->lpaif_map, reg, 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -194,11 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; - int dir = substream->stream; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), + LPAIF_DMABASE_REG(v, ch, dir), runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", @@ -244,8 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; - int dir = substream->stream; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -317,8 +336,12 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; - int ret, ch = pcm_data->rdma_ch; - int dir = substream->stream; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_read(drvdata->lpaif_map, LPAIF_DMABASE_REG(v, ch, dir), &base_addr); @@ -446,8 +469,7 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_pcm_substream *psubstream, *csubstream; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); @@ -460,55 +482,108 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) if (!data) return -ENOMEM; - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata, - SNDRV_PCM_STREAM_PLAYBACK); + data->i2s_port = cpu_dai->driver->id; + snd_soc_pcm_set_drvdata(soc_runtime, data); - if (IS_ERR_VALUE(data->rdma_ch)) - return data->rdma_ch; + psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (psubstream) { + if (v->alloc_dma_channel) + data->rdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_PLAYBACK); - drvdata->substream[data->rdma_ch] = substream; - data->i2s_port = cpu_dai->driver->id; + if (IS_ERR_VALUE(data->rdma_ch)) + return data->rdma_ch; - snd_soc_pcm_set_drvdata(soc_runtime, data); + drvdata->substream[data->rdma_ch] = psubstream; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - soc_runtime->platform->dev, - size, &substream->dma_buffer); - if (ret) - return ret; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &psubstream->dma_buffer); + if (ret) + goto playback_alloc_err; - ret = regmap_write(drvdata->lpaif_map, + ret = regmap_write(drvdata->lpaif_map, LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); - if (ret) { - dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", __func__, ret); - goto err_buf; + goto capture_alloc_err; + } + } + + csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (csubstream) { + if (v->alloc_dma_channel) + data->wrdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_CAPTURE); + + if (IS_ERR_VALUE(data->wrdma_ch)) + goto capture_alloc_err; + + drvdata->substream[data->wrdma_ch] = csubstream; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &csubstream->dma_buffer); + if (ret) + goto capture_alloc_err; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to wrdmactl reg: %d\n", + __func__, ret); + goto capture_reg_err; + } } return 0; -err_buf: - snd_dma_free_pages(&substream->dma_buffer); +capture_reg_err: + if (csubstream) + snd_dma_free_pages(&csubstream->dma_buffer); + +capture_alloc_err: + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + + playback_alloc_err: + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); - struct lpass_variant *v = drvdata->variant; - - drvdata->substream[data->rdma_ch] = NULL; - - if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->rdma_ch); - - snd_dma_free_pages(&substream->dma_buffer); + struct snd_soc_pcm_runtime *rt; + struct lpass_data *drvdata; + struct lpass_pcm_data *data; + struct lpass_variant *v; + struct snd_pcm_substream *substream; + int ch, i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + rt = substream->private_data; + data = snd_soc_pcm_get_drvdata(rt); + drvdata = snd_soc_platform_get_drvdata(rt->platform); + + ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ? data->rdma_ch + : data->wrdma_ch; + v = drvdata->variant; + drvdata->substream[ch] = NULL; + if (v->free_dma_channel) + v->free_dma_channel(drvdata, ch); + + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } } static struct snd_soc_platform_driver lpass_platform_driver = { -- cgit v1.2.3 From bbedefb9e8c350bc7e8e7dc4732ba1974789e3e9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:18:45 +0000 Subject: ASoC: qcom: apq8016-sbc: add mic support This patch add mic support on apq8016-sbc board aka db410c. Tested it with headset mic. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/apq8016_sbc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1efdf0088ecd..1289543c8fb2 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -30,6 +30,7 @@ struct apq8016_sbc_data { struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ }; +#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) #define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) #define MIC_CTRL_TLMM_SCLK_EN BIT(1) #define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) @@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) MIC_CTRL_TLMM_SCLK_EN, pdata->mic_iomux); break; + case MI2S_TERTIARY: + writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL | + MIC_CTRL_TLMM_SCLK_EN, + pdata->mic_iomux); + + break; default: dev_err(card->dev, "unsupported cpu dai configuration\n"); @@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) } link->platform_of_node = link->cpu_of_node; - /* For now we only support playback */ - link->playback_only = true; - ret = of_property_read_string(np, "link-name", &link->name); if (ret) { dev_err(card->dev, "error getting codec dai_link name\n"); -- cgit v1.2.3 From 6a9364ca6fa07ec215b0ba7c49d110b8236985d4 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 11 Feb 2016 12:19:30 +0000 Subject: ASoC: qcom: apq8016: add wrdma support This patch adds wrdma support in lpass-apq8016 by providing the register offsets and adding support in dma channel allocation callback. Signed-off-by: Srinivas Kandagatla Acked-by: Kenneth Westfield Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index ca3bbd58c81a..3eef0c37ba50 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -137,11 +137,23 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, int direction) { struct lpass_variant *v = drvdata->variant; - int chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + int chan = 0; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, v->rdma_channels); - if (chan >= v->rdma_channels) - return -EBUSY; + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } set_bit(chan, &drvdata->dma_ch_bit_map); @@ -214,6 +226,10 @@ static struct lpass_variant apq8016_data = { .rdma_reg_stride = 0x1000, .rdma_channels = 2, .dmactl_audif_start = 1, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 2, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .init = apq8016_lpass_init, -- cgit v1.2.3 From 2303b32f54289267adb75437cdeba50be92221f5 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Tue, 16 Feb 2016 22:27:19 +0100 Subject: ASoC: cht_bsw_rt5645: Enable jack detection Add missing DAPM pins and enable jack detection on those pins for Cherrytrail and Braswell. Signed-off-by: Carlo Caione Signed-off-by: Mark Brown --- sound/soc/intel/boards/cht_bsw_rt5645.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index e6cf800ab231..c7af2715b3f9 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -147,6 +147,17 @@ static const struct snd_kcontrol_new cht_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Ext Spk"), }; +static struct snd_soc_jack_pin cht_bsw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -202,9 +213,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) else jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; - ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, &ctx->jack, - NULL, 0); + cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); return ret; -- cgit v1.2.3 From f8260afa444b670016f22f2ba1440d9d2e74dcb6 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Mon, 8 Feb 2016 18:09:21 +0100 Subject: ASoC: sunxi: Add support for the SPDIF block The sun4i, sun5i and sun7i SoC families have an SPDIF block which is capable of playback and capture. This patch enables the playback of this block for the sun4i families. Signed-off-by: Marcus Cooper Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/Kconfig | 8 + sound/soc/sunxi/Makefile | 1 + sound/soc/sunxi/sun4i-spdif.c | 550 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 559 insertions(+) create mode 100644 sound/soc/sunxi/sun4i-spdif.c (limited to 'sound') diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 84c72ec6ad73..ae42294ef688 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -8,4 +8,12 @@ config SND_SUN4I_CODEC Select Y or M to add support for the Codec embedded in the Allwinner A10 and affiliated SoCs. +config SND_SUN4I_SPDIF + tristate "Allwinner A10 SPDIF Support" + depends on OF + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M to add support for the S/PDIF audio block in the Allwinner + A10 and affiliated SoCs. endmenu diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index ea8a08c881d6..8f5e889667f1 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o +obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c new file mode 100644 index 000000000000..0b04fb02125c --- /dev/null +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -0,0 +1,550 @@ +/* + * ALSA SoC SPDIF Audio Layer + * + * Copyright 2015 Andrea Venturi + * Copyright 2015 Marcus Cooper + * + * Based on the Allwinner SDK driver, released under the GPL. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUN4I_SPDIF_CTL (0x00) + #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ + #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) + #define SUN4I_SPDIF_CTL_GEN BIT(1) + #define SUN4I_SPDIF_CTL_RESET BIT(0) + +#define SUN4I_SPDIF_TXCFG (0x04) + #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) + #define SUN4I_SPDIF_TXCFG_ASS BIT(17) + #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16) + #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4) + #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4) + #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2) + #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2) + #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2) + #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2) + #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1) + #define SUN4I_SPDIF_TXCFG_TXEN BIT(0) + +#define SUN4I_SPDIF_RXCFG (0x08) + #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4) + #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3) + #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1) + #define SUN4I_SPDIF_RXCFG_RXEN BIT(0) + +#define SUN4I_SPDIF_TXFIFO (0x0C) + +#define SUN4I_SPDIF_RXFIFO (0x10) + +#define SUN4I_SPDIF_FCTL (0x14) + #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31) + #define SUN4I_SPDIF_FCTL_FTX BIT(17) + #define SUN4I_SPDIF_FCTL_FRX BIT(16) + #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8) + #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8) + #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3) + #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3) + #define SUN4I_SPDIF_FCTL_TXIM BIT(2) + #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) + #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) + +#define SUN4I_SPDIF_FSTA (0x18) + #define SUN4I_SPDIF_FSTA_TXE BIT(14) + #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) + #define SUN4I_SPDIF_FSTA_RXA BIT(6) + #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) + +#define SUN4I_SPDIF_INT (0x1C) + #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) + #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17) + #define SUN4I_SPDIF_INT_RXPARERREN BIT(16) + #define SUN4I_SPDIF_INT_TXDRQEN BIT(7) + #define SUN4I_SPDIF_INT_TXUIEN BIT(6) + #define SUN4I_SPDIF_INT_TXOIEN BIT(5) + #define SUN4I_SPDIF_INT_TXEIEN BIT(4) + #define SUN4I_SPDIF_INT_RXDRQEN BIT(2) + #define SUN4I_SPDIF_INT_RXOIEN BIT(1) + #define SUN4I_SPDIF_INT_RXAIEN BIT(0) + +#define SUN4I_SPDIF_ISTA (0x20) + #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18) + #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17) + #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16) + #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6) + #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5) + #define SUN4I_SPDIF_ISTA_TXESTA BIT(4) + #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1) + #define SUN4I_SPDIF_ISTA_RXASTA BIT(0) + +#define SUN4I_SPDIF_TXCNT (0x24) + +#define SUN4I_SPDIF_RXCNT (0x28) + +#define SUN4I_SPDIF_TXCHSTA0 (0x2C) + #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28) + #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24) + #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24) + #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20) + #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20) + #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16) + #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8) + #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6) + #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3) + #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2) + #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1) + #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0) + +#define SUN4I_SPDIF_TXCHSTA1 (0x30) + #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8) + #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4) + #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4) + #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1) + #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0) + +#define SUN4I_SPDIF_RXCHSTA0 (0x34) + #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28) + #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24) + #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20) + #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16) + #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8) + #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6) + #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3) + #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2) + #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1) + #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0) + +#define SUN4I_SPDIF_RXCHSTA1 (0x38) + #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8) + #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4) + #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1) + #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0) + +/* Defines for Sampling Frequency */ +#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0 +#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1 +#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2 +#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3 +#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4 +#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6 +#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8 +#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9 +#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa +#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc +#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe + +struct sun4i_spdif_dev { + struct platform_device *pdev; + struct clk *spdif_clk; + struct clk *apb_clk; + struct snd_soc_dai_driver cpu_dai_drv; + struct regmap *regmap; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) +{ + /* soft reset SPDIF */ + regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); + + /* flush TX FIFO */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, + SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX); + + /* clear TX counter */ + regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); +} + +static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream, + struct sun4i_spdif_dev *host) +{ + if (substream->runtime->channels == 1) + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_SINGLEMOD, + SUN4I_SPDIF_TXCFG_SINGLEMOD); + + /* SPDIF TX ENABLE */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN); + + /* DRQ ENABLE */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, + SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN); + + /* Global enable */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, + SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN); +} + +static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, + struct sun4i_spdif_dev *host) +{ + /* SPDIF TX DISABLE */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_TXEN, 0); + + /* DRQ DISABLE */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, + SUN4I_SPDIF_INT_TXDRQEN, 0); + + /* Global disable */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, + SUN4I_SPDIF_CTL_GEN, 0); +} + +static int sun4i_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + + sun4i_spdif_configure(host); + + return 0; +} + +static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + int ret = 0; + int fmt; + unsigned long rate = params_rate(params); + u32 mclk_div = 0; + unsigned int mclk = 0; + u32 reg_val; + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + struct platform_device *pdev = host->pdev; + + /* Add the PCM and raw data select interface */ + switch (params_channels(params)) { + case 1: /* PCM mode */ + case 2: + fmt = 0; + break; + case 4: /* raw data mode */ + fmt = SUN4I_SPDIF_TXCFG_NONAUDIO; + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; + break; + default: + return -EINVAL; + } + + switch (rate) { + case 22050: + case 44100: + case 88200: + case 176400: + mclk = 22579200; + break; + case 24000: + case 32000: + case 48000: + case 96000: + case 192000: + mclk = 24576000; + break; + default: + return -EINVAL; + } + + ret = clk_set_rate(host->spdif_clk, mclk); + if (ret < 0) { + dev_err(&pdev->dev, + "Setting SPDIF clock rate for %d Hz failed!\n", mclk); + return ret; + } + + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, + SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); + + switch (rate) { + case 22050: + case 24000: + mclk_div = 8; + break; + case 32000: + mclk_div = 6; + break; + case 44100: + case 48000: + mclk_div = 4; + break; + case 88200: + case 96000: + mclk_div = 2; + break; + case 176400: + case 192000: + mclk_div = 1; + break; + default: + return -EINVAL; + } + + reg_val = 0; + reg_val |= SUN4I_SPDIF_TXCFG_ASS; + reg_val |= fmt; /* set non audio and bit depth */ + reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE; + reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1); + regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val); + + return 0; +} + +static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + sun4i_snd_txctrl_on(substream, host); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sun4i_snd_txctrl_off(substream, host); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) +{ + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); + return 0; +} + +static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { + .startup = sun4i_spdif_startup, + .trigger = sun4i_spdif_trigger, + .hw_params = sun4i_spdif_hw_params, +}; + +static const struct regmap_config sun4i_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN4I_SPDIF_RXCHSTA1, +}; + +#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 + +#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ + SNDRV_PCM_FORMAT_S20_3LE | \ + SNDRV_PCM_FORMAT_S24_LE) + +static struct snd_soc_dai_driver sun4i_spdif_dai = { + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SUN4I_RATES, + .formats = SUN4I_FORMATS, + }, + .probe = sun4i_spdif_soc_dai_probe, + .ops = &sun4i_spdif_dai_ops, + .name = "spdif", +}; + +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("spdif-out"), +}; + +static const struct snd_soc_dapm_route dit_routes[] = { + { "spdif-out", NULL, "Playback" }, +}; + +static const struct of_device_id sun4i_spdif_of_match[] = { + { .compatible = "allwinner,sun4i-a10-spdif", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); + +static const struct snd_soc_component_driver sun4i_spdif_component = { + .name = "sun4i-spdif", +}; + +static int sun4i_spdif_runtime_suspend(struct device *dev) +{ + struct sun4i_spdif_dev *host = dev_get_drvdata(dev); + + clk_disable_unprepare(host->spdif_clk); + clk_disable_unprepare(host->apb_clk); + + return 0; +} + +static int sun4i_spdif_runtime_resume(struct device *dev) +{ + struct sun4i_spdif_dev *host = dev_get_drvdata(dev); + + clk_prepare_enable(host->spdif_clk); + clk_prepare_enable(host->apb_clk); + + return 0; +} + +static int sun4i_spdif_probe(struct platform_device *pdev) +{ + struct sun4i_spdif_dev *host; + struct resource *res; + int ret; + void __iomem *base; + + dev_dbg(&pdev->dev, "Entered %s\n", __func__); + + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->pdev = pdev; + + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); + host->cpu_dai_drv.name = dev_name(&pdev->dev); + + /* Get the addresses */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_spdif_regmap_config); + + /* Clocks */ + host->apb_clk = devm_clk_get(&pdev->dev, "apb"); + if (IS_ERR(host->apb_clk)) { + dev_err(&pdev->dev, "failed to get a apb clock.\n"); + return PTR_ERR(host->apb_clk); + } + + host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); + if (IS_ERR(host->spdif_clk)) { + dev_err(&pdev->dev, "failed to get a spdif clock.\n"); + ret = PTR_ERR(host->spdif_clk); + goto err_disable_apb_clk; + } + + host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO; + host->dma_params_tx.maxburst = 4; + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + platform_set_drvdata(pdev, host); + + ret = devm_snd_soc_register_component(&pdev->dev, + &sun4i_spdif_component, &sun4i_spdif_dai, 1); + if (ret) + goto err_disable_apb_clk; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = sun4i_spdif_runtime_resume(&pdev->dev); + if (ret) + goto err_unregister; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_suspend; + return 0; +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + sun4i_spdif_runtime_suspend(&pdev->dev); +err_unregister: + pm_runtime_disable(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); +err_disable_apb_clk: + clk_disable_unprepare(host->apb_clk); + return ret; +} + +static int sun4i_spdif_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + sun4i_spdif_runtime_suspend(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops sun4i_spdif_pm = { + SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, + sun4i_spdif_runtime_resume, NULL) +}; + +static struct platform_driver sun4i_spdif_driver = { + .driver = { + .name = "sun4i-spdif", + .of_match_table = of_match_ptr(sun4i_spdif_of_match), + .pm = &sun4i_spdif_pm, + }, + .probe = sun4i_spdif_probe, + .remove = sun4i_spdif_remove, +}; + +module_platform_driver(sun4i_spdif_driver); + +MODULE_AUTHOR("Marcus Cooper "); +MODULE_AUTHOR("Andrea Venturi "); +MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sun4i-spdif"); -- cgit v1.2.3 From 14a37ac1bf8a2ba9b19032195c371c7b0857ca87 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 20 Feb 2016 16:18:56 +0900 Subject: ALSA: bebob: simplify bus-reset handling At bus-reset, DM1000/DM1100/DM1500 chipsets transfer packets with discontinuous value in 'dbc' field of CIP header. In this case, packet streaming layer in firewire-lib module stops streaming and set XRUN to PCM substream. In ALSA, PCM applications are notified the XRUN status by the return value of ALSA PCM interface. They can recover this state by executing snd_pcm_prepare(), then PCM drivers' prepare handler is called, and start new PCM substream. For ALSA BeBoB driver, the handler establishes new connections and start new AMDTP streaming. Unfortunately, neither the PCM applications nor the driver know the reason of XRUN. The driver gets to know the reason when update handler is called by IEEE 1394 bus driver. As long as I tested, the order of below events are not fixed: * Detecting packet discontinuity in tasklet context of OHCI 1394 driver * Calling prepare handler in process context of ALSA PCM application * Calling update handler in kthread context of IEEE 1394 bus driver The unpredictable order is disadvantage for the driver to be compliant to CMP. In IEC 61883-1, new CMP establish operations should be done 1 sec (isoc_resource_delay) after bus-reset. Within 1 sec, CMP restore operations are allowed. For this reason, in former commit ('b6bc812327aa: ALSA: bebob/firewire-lib: Add a quirk for discontinuity at bus reset'), the process context is forced to wait for executing update handler. The process context wait for bus-reset up to 1 sec. This commit solves the issue, while causes more disadvantages. For PCM applications, calling snd_pcm_prepare() for recovering XRUN state takes more time and the driver got a bit complicated code, while the recovery is not always successful. As long as I tested, DM1000/DM1100/DM1500 and BeBoB firmware can allow drivers to establish new connections just after bus reset. Furthermore, any FCP transactions are handled correctly. Therefore, the driver don't need to wait for bus reset handler for starting new streaming. This commit removes the codes to reduce maintenance cost. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.c | 7 +++---- sound/firewire/bebob/bebob.h | 2 -- sound/firewire/bebob/bebob_stream.c | 32 ++------------------------------ 3 files changed, 5 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 091290d1f3ea..671179eaff1e 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -309,7 +309,10 @@ bebob_update(struct fw_unit *unit) return; fcp_bus_reset(bebob->unit); + + mutex_lock(&bebob->mutex); snd_bebob_stream_update_duplex(bebob); + mutex_unlock(&bebob->mutex); if (bebob->deferred_registration) { if (snd_card_register(bebob->card) < 0) { @@ -327,10 +330,6 @@ static void bebob_remove(struct fw_unit *unit) if (bebob == NULL) return; - /* Awake bus-reset waiters. */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(bebob->card); } diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4d8fcc78e747..c2e885c06473 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -88,8 +88,6 @@ struct snd_bebob { unsigned int midi_input_ports; unsigned int midi_output_ports; - /* for bus reset quirk */ - struct completion bus_reset; bool connected; struct amdtp_stream *master; diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 5022c9b97ddf..073ad6d8a76e 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) destroy_both_connections(bebob); goto end; } - /* See comments in next function */ - init_completion(&bebob->bus_reset); + bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; /* @@ -588,25 +587,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) struct amdtp_stream *master, *slave; enum cip_flags sync_mode; unsigned int curr_rate; - bool updated = false; int err = 0; - /* - * Normal BeBoB firmware has a quirk at bus reset to transmits packets - * with discontinuous value in dbc field. - * - * This 'struct completion' is used to call .update() at first to update - * connections/streams. Next following codes handle streaming error. - */ - if (amdtp_streaming_error(&bebob->tx_stream)) { - if (completion_done(&bebob->bus_reset)) - reinit_completion(&bebob->bus_reset); - - updated = (wait_for_completion_interruptible_timeout( - &bebob->bus_reset, - msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0); - } - mutex_lock(&bebob->mutex); /* Need no substreams */ @@ -642,8 +624,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) amdtp_stream_stop(master); if (amdtp_streaming_error(slave)) amdtp_stream_stop(slave); - if (!updated && - !amdtp_stream_running(master) && !amdtp_stream_running(slave)) + if (!amdtp_stream_running(master) && !amdtp_stream_running(slave)) break_both_connections(bebob); /* stop streams if rate is different */ @@ -774,9 +755,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) { - /* vs. XRUN recovery due to discontinuity at bus reset */ - mutex_lock(&bebob->mutex); - if ((cmp_connection_update(&bebob->in_conn) < 0) || (cmp_connection_update(&bebob->out_conn) < 0)) { amdtp_stream_pcm_abort(&bebob->rx_stream); @@ -788,12 +766,6 @@ void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) amdtp_stream_update(&bebob->rx_stream); amdtp_stream_update(&bebob->tx_stream); } - - /* wake up stream_start_duplex() */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - - mutex_unlock(&bebob->mutex); } /* -- cgit v1.2.3 From 2a71e701660d7aa9ce1a740588dbf13b975deb10 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 20 Feb 2016 16:18:57 +0900 Subject: ALSA: bebob: move mutex from function callee to callers Currently, critical section is protected by mutex in functions of fireworks_stream.c. Callers increments/decrements substreams counter before calling the functions. Moving mutex to the callers code allows to change type of the substream counter from atomic_t to unsigned int. This commit is a preparation for obsoleting usage of atomic_t for substream counter. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob_midi.c | 8 ++++++++ sound/firewire/bebob/bebob_pcm.c | 20 ++++++++++++++++---- sound/firewire/bebob/bebob_stream.c | 7 ------- 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 90d95be499b0..cb1b385b3551 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; + mutex_lock(&bebob->mutex); atomic_inc(&bebob->substreams_counter); err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; + mutex_lock(&bebob->mutex); atomic_inc(&bebob->substreams_counter); err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; + mutex_lock(&bebob->mutex); atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; @@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; + mutex_lock(&bebob->mutex); atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index ef224d6f5c24..2053d31376df 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); atomic_inc(&bebob->substreams_counter); + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); @@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); atomic_inc(&bebob->substreams_counter); + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); @@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); atomic_dec(&bebob->substreams_counter); + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); @@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); atomic_dec(&bebob->substreams_counter); + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 073ad6d8a76e..6809913110f4 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -589,8 +589,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) unsigned int curr_rate; int err = 0; - mutex_lock(&bebob->mutex); - /* Need no substreams */ if (atomic_read(&bebob->substreams_counter) == 0) goto end; @@ -722,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) } } end: - mutex_unlock(&bebob->mutex); return err; } @@ -738,8 +735,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) master = &bebob->tx_stream; } - mutex_lock(&bebob->mutex); - if (atomic_read(&bebob->substreams_counter) == 0) { amdtp_stream_pcm_abort(master); amdtp_stream_stop(master); @@ -749,8 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) break_both_connections(bebob); } - - mutex_unlock(&bebob->mutex); } void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) -- cgit v1.2.3 From 4fd6c6c7295a50aa9390a3131623456e0c05d8f9 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 20 Feb 2016 16:18:58 +0900 Subject: ALSA: bebob: change type of substream counter from atomic_t to unsigned int The counter is incremented/decremented in critical section protected with mutex. Therefore, no need to use atomic_t. This commit changes the type to unsigned int. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.h | 2 +- sound/firewire/bebob/bebob_midi.c | 8 ++++---- sound/firewire/bebob/bebob_pcm.c | 8 ++++---- sound/firewire/bebob/bebob_stream.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index c2e885c06473..4fe58e422133 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -95,7 +95,7 @@ struct snd_bebob { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - atomic_t substreams_counter; + unsigned int substreams_counter; struct snd_bebob_stream_formation tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index cb1b385b3551..868eb0decbec 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -18,7 +18,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&bebob->mutex); - atomic_inc(&bebob->substreams_counter); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); mutex_unlock(&bebob->mutex); if (err < 0) @@ -37,7 +37,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&bebob->mutex); - atomic_inc(&bebob->substreams_counter); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); mutex_unlock(&bebob->mutex); if (err < 0) @@ -51,7 +51,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) struct snd_bebob *bebob = substream->rmidi->private_data; mutex_lock(&bebob->mutex); - atomic_dec(&bebob->substreams_counter); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); mutex_unlock(&bebob->mutex); @@ -64,7 +64,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) struct snd_bebob *bebob = substream->rmidi->private_data; mutex_lock(&bebob->mutex); - atomic_dec(&bebob->substreams_counter); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); mutex_unlock(&bebob->mutex); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 2053d31376df..5d7b9343fa85 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -220,7 +220,7 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - atomic_inc(&bebob->substreams_counter); + bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } @@ -242,7 +242,7 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - atomic_inc(&bebob->substreams_counter); + bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } @@ -258,7 +258,7 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - atomic_dec(&bebob->substreams_counter); + bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } @@ -273,7 +273,7 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - atomic_dec(&bebob->substreams_counter); + bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 6809913110f4..8c687456a0e6 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -590,7 +590,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) int err = 0; /* Need no substreams */ - if (atomic_read(&bebob->substreams_counter) == 0) + if (bebob->substreams_counter == 0) goto end; err = get_sync_mode(bebob, &sync_mode); @@ -735,7 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) master = &bebob->tx_stream; } - if (atomic_read(&bebob->substreams_counter) == 0) { + if (bebob->substreams_counter == 0) { amdtp_stream_pcm_abort(master); amdtp_stream_stop(master); -- cgit v1.2.3 From 3800e6f944e929ddfcd9320a51e13a67fd1956ff Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 20 Feb 2016 16:18:59 +0900 Subject: ALSA: bebob: give up updating streams at bus reset handler DM1000/DM1100/DM1500 chipsets transfer packets with discontinue value in 'dbc' field of CIP header. For ALSA bebob driver, this makes its bus-reset handler meaningless, because the discontinuity is detected quite earlier than executing the handler. This commit gives up updating streams at the bus reset handler. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.c | 20 ++++++++++++++++---- sound/firewire/bebob/bebob.h | 1 - sound/firewire/bebob/bebob_stream.c | 15 --------------- 3 files changed, 16 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 671179eaff1e..3e4e0756e3fe 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -300,6 +300,22 @@ error: return err; } +/* + * This driver doesn't update streams in bus reset handler. + * + * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with + * discontinued counter at bus reset. This discontinuity is immediately + * detected in packet streaming layer, then it sets XRUN to PCM substream. + * + * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation. + * Then, they can recover the PCM substream by executing ioctl(2) with + * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers + * restart packet streaming. + * + * The above processing may be executed before this bus-reset handler is + * executed. When this handler updates streams with current isochronous + * channels, the streams already have the current ones. + */ static void bebob_update(struct fw_unit *unit) { @@ -310,10 +326,6 @@ bebob_update(struct fw_unit *unit) fcp_bus_reset(bebob->unit); - mutex_lock(&bebob->mutex); - snd_bebob_stream_update_duplex(bebob); - mutex_unlock(&bebob->mutex); - if (bebob->deferred_registration) { if (snd_card_register(bebob->card) < 0) { snd_bebob_stream_destroy_duplex(bebob); diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4fe58e422133..b50bb33d9d46 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -217,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 8c687456a0e6..77cbb02bff34 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -746,21 +746,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) } } -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) -{ - if ((cmp_connection_update(&bebob->in_conn) < 0) || - (cmp_connection_update(&bebob->out_conn) < 0)) { - amdtp_stream_pcm_abort(&bebob->rx_stream); - amdtp_stream_pcm_abort(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - break_both_connections(bebob); - } else { - amdtp_stream_update(&bebob->rx_stream); - amdtp_stream_update(&bebob->tx_stream); - } -} - /* * This function should be called before starting streams or after stopping * streams. -- cgit v1.2.3 From 99d7355914d3f97e4419675b938382d72aac0339 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 20 Feb 2016 16:41:01 +0900 Subject: ALSA: fireworks: serialize transactions to update connections at bus reset In IEC 61883-1, at bus-reset, applications can continue isochronous streaming by updating connections. In ALSA fireworks driver, the operation is executed in 'update' handler for bus driver. The connection resources are also changed in process contexts of PCM/MIDI applications. Therefore, bus-reset handling has race condition against connection. Current ALSA fireworks driver has a bug for the condition. This commit fixes the bug, by expand critical section with mutex. As a result, connection updating operation in bus-reset handler and connection changing operation in process context are serialized. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks.c | 3 +++ sound/firewire/fireworks/fireworks_stream.c | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index d5b19bc11e59..8f27b67503c8 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit) struct snd_efw *efw = dev_get_drvdata(&unit->device); snd_efw_transaction_bus_reset(efw->unit); + + mutex_lock(&efw->mutex); snd_efw_stream_update_duplex(efw); + mutex_unlock(&efw->mutex); } static void efw_remove(struct fw_unit *unit) diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 968a40a1beb2..425db8d88235 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_update_duplex(struct snd_efw *efw) { - if ((cmp_connection_update(&efw->out_conn) < 0) || - (cmp_connection_update(&efw->in_conn) < 0)) { - mutex_lock(&efw->mutex); + if (cmp_connection_update(&efw->out_conn) < 0 || + cmp_connection_update(&efw->in_conn) < 0) { stop_stream(efw, &efw->rx_stream); stop_stream(efw, &efw->tx_stream); - mutex_unlock(&efw->mutex); } else { amdtp_stream_update(&efw->rx_stream); amdtp_stream_update(&efw->tx_stream); -- cgit v1.2.3 From c7dae7c4c60693286298e300b3d140c8cff46daa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 19 Feb 2016 14:44:41 +0000 Subject: ASoC: wm_adsp: Avoid narrow race condition on compr pointer There is a very small window between then wm_adsp_compr_free gets call and when the DSP is actually powered down. If we get an IRQ from the DSP in this window then the wm_adsp_compr pointer will be NULL. This patch adds a check for this into the IRQ handler to avoid any issues when this happens. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 68b85ee67586..cb49a632462a 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2845,7 +2845,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) goto out; } - if (compr->stream) + if (compr && compr->stream) snd_compr_fragment_elapsed(compr->stream); out: -- cgit v1.2.3 From cc815c4b86d14630a7d4ca4f4c7d2c2acb88cb52 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 19 Feb 2016 14:44:42 +0000 Subject: ASoC: wm_adsp: Explicitly set the control access flags in all cases In the case where the firmware does not tell us the access flags for the control, we let ALSA select a default (READWRITE). But really we should be applying the volatile flag in this case, as we will read the control from the DSP if it is on in this case. This patch explicitly sets the access flags in all cases. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index cb49a632462a..ff0ce6ba1b69 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -857,6 +857,9 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } else { + kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; } ret = snd_soc_add_card_controls(dsp->card, -- cgit v1.2.3 From 7d00cd975a52e637da19548c1cc7d87a0a32b49c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 19 Feb 2016 14:44:43 +0000 Subject: ASoC: wm_adsp: Fixup some odd line wrapping Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ff0ce6ba1b69..0f6eeb199351 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -862,15 +862,13 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; } - ret = snd_soc_add_card_controls(dsp->card, - kcontrol, 1); + ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, - ctl->name); + ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); return 0; @@ -890,9 +888,7 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp) if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) continue; - ret = wm_coeff_read_control(ctl, - ctl->cache, - ctl->len); + ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -909,9 +905,7 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) if (!ctl->enabled) continue; if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = wm_coeff_write_control(ctl, - ctl->cache, - ctl->len); + ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -1507,8 +1501,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list: %d\n", - ret); + adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); kfree(alg); return ERR_PTR(ret); } @@ -2007,8 +2000,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, goto err_mutex; } - val = (val & dsp->sysclk_mask) - >> dsp->sysclk_shift; + val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_31, @@ -2101,8 +2093,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) /* Wait for the RAM to start, should be near instantaneous */ for (count = 0; count < 10; ++count) { - ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, - &val); + ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); if (ret != 0) return ret; -- cgit v1.2.3 From 4f8ea6d759f6ed5d448472143cef7681e319c958 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 19 Feb 2016 14:44:44 +0000 Subject: ASoC: wm_adsp: wm_coeff_{read|write}_control should use passed length wm_coeff_{read|write}_control were using the control length rather than the length parameter passed to them. This is not causing any issues as the two values are currently always the same, but this needs fixed to allow future improvements. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 0f6eeb199351..36687db5da61 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -721,19 +721,19 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); - scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); + scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_write(dsp->regmap, reg, scratch, - ctl->len); + len); if (ret) { adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", - ctl->len, reg, ret); + len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); + adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); kfree(scratch); @@ -780,20 +780,20 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); - scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); + scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(dsp->regmap, reg, scratch, len); if (ret) { adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", ctl->len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); + adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); - memcpy(buf, scratch, ctl->len); + memcpy(buf, scratch, len); kfree(scratch); return 0; -- cgit v1.2.3 From e354d86e551656668e879c833de9890b5053cc88 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 20 Feb 2016 10:56:58 +0800 Subject: ASoC: max98926: Constify max98926_reg and max98926_regmap Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max98926.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index 5245e107de30..8d14adae5cc5 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -40,7 +40,7 @@ static const char *const max98926_hpf_cutoff_txt[] = { "200Hz", "400Hz", "800Hz", }; -static struct reg_default max98926_reg[] = { +static const struct reg_default max98926_reg[] = { { 0x0B, 0x00 }, /* IRQ Enable0 */ { 0x0C, 0x00 }, /* IRQ Enable1 */ { 0x0D, 0x00 }, /* IRQ Enable2 */ @@ -506,7 +506,7 @@ static struct snd_soc_codec_driver soc_codec_dev_max98926 = { .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), }; -static struct regmap_config max98926_regmap = { +static const struct regmap_config max98926_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = MAX98926_VERSION, -- cgit v1.2.3 From ee06b6f0675907191656b11a8405a03c6d599ea9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 20 Feb 2016 10:52:32 +0800 Subject: ASoC: max9867: Add CONFIG_SND_SOC_MAX9867 to build Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b84fe9d75dfd..f35237d30f93 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -280,6 +280,7 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o +obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o -- cgit v1.2.3 From 1b36e4a2ab0cfcc322153337d2615bc6fa922567 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 20 Feb 2016 10:53:15 +0800 Subject: ASoC: max9867: Make max9867_i2c_id NULL terminated Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 5a8481bf20c2..70b54069ad2a 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -512,6 +512,7 @@ static int max9867_i2c_remove(struct i2c_client *client) static const struct i2c_device_id max9867_i2c_id[] = { { "max9867", 0 }, + { } }; static const struct of_device_id max9867_of_match[] = { -- cgit v1.2.3 From 250a99e70bf8628397901e10ad99a281dfc489d5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 20 Feb 2016 10:54:11 +0800 Subject: ASoC: max9867: Constify max9867_reg and max9867_regmap Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 70b54069ad2a..ab7a94db8db7 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -436,7 +436,7 @@ static bool max9867_volatile_register(struct device *dev, unsigned int reg) } } -static struct reg_default max9867_reg[] = { +static const struct reg_default max9867_reg[] = { { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x00 }, @@ -459,7 +459,7 @@ static struct reg_default max9867_reg[] = { { 0x17, 0x00 }, }; -static struct regmap_config max9867_regmap = { +static const struct regmap_config max9867_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = MAX9867_REVISION, -- cgit v1.2.3 From c3a900581e1137bdc2efc25325d99fcc9bb9ab1e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 14 Jan 2016 22:05:03 +0100 Subject: ALSA: hda - Use acpi_dev_present() Use shiny new acpi_dev_present() and remove all the boilerplate to search for a particular ACPI device. No functional change. Cf. 2d12b6b381ba ("ACPI / utils: Add acpi_dev_present()"). Signed-off-by: Lukas Wunner Acked-by: Hui Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/thinkpad_helper.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 0a4ad5feb82e..59ab6cee1ad8 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -10,23 +10,10 @@ static int (*led_set_func)(int, bool); static void (*old_vmaster_hook)(void *, int); -static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context, - void **rv) -{ - bool *found = context; - *found = true; - return AE_OK; -} - static bool is_thinkpad(struct hda_codec *codec) { - bool found = false; - if (codec->core.subsystem_id >> 16 != 0x17aa) - return false; - if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found) - return true; - found = false; - return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found; + return (codec->core.subsystem_id >> 16 == 0x17aa) && + (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068")); } static void update_tpacpi_mute_led(void *private_data, int enabled) -- cgit v1.2.3 From 6139b1b184d5f6257f692dc814dbcd47e4cf90f1 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 18 Jan 2016 20:07:44 +0100 Subject: ASoC: fsl_ssi: remove explicit register defaults There is no guarantee that on fsl_ssi module load SSI registers will have their power-on-reset values. In fact, if the driver is reloaded the values in registers will be whatever they were set to previously. However, the cache needs to be fully populated at probe time to avoid non-atomic allocations during register access. Special case here is imx21-class SSI, since according to datasheet it don't have SACC{ST,EN,DIS} regs. This fixes hard lockup on fsl_ssi module reload, at least in AC'97 mode. Fixes: 05cf237972fe ("ASoC: fsl_ssi: Add driver suspend and resume to support MEGA Fast") Signed-off-by: Maciej S. Szmigiero Tested-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 40dfd8a36484..ed8de1035cda 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val tx; }; -static const struct reg_default fsl_ssi_reg_defaults[] = { - {CCSR_SSI_SCR, 0x00000000}, - {CCSR_SSI_SIER, 0x00003003}, - {CCSR_SSI_STCR, 0x00000200}, - {CCSR_SSI_SRCR, 0x00000200}, - {CCSR_SSI_STCCR, 0x00040000}, - {CCSR_SSI_SRCCR, 0x00040000}, - {CCSR_SSI_SACNT, 0x00000000}, - {CCSR_SSI_STMSK, 0x00000000}, - {CCSR_SSI_SRMSK, 0x00000000}, - {CCSR_SSI_SACCEN, 0x00000000}, - {CCSR_SSI_SACCDIS, 0x00000000}, -}; - static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -190,8 +176,7 @@ static const struct regmap_config fsl_ssi_regconfig = { .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .reg_defaults = fsl_ssi_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1, .readable_reg = fsl_ssi_readable_reg, .volatile_reg = fsl_ssi_volatile_reg, .precious_reg = fsl_ssi_precious_reg, @@ -201,6 +186,7 @@ static const struct regmap_config fsl_ssi_regconfig = { struct fsl_ssi_soc_data { bool imx; + bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */ bool offline_config; u32 sisr_write_mask; }; @@ -303,6 +289,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { static struct fsl_ssi_soc_data fsl_ssi_imx21 = { .imx = true, + .imx21regs = true, .offline_config = true, .sisr_write_mask = 0, }; @@ -586,8 +573,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) */ regmap_write(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV); - regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); - regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + + /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ + if (!ssi_private->soc->imx21regs) { + regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); + regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + } /* * Enable SSI, Transmit and Receive. AC97 has to communicate with the @@ -1397,6 +1388,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource *res; void __iomem *iomem; char name[64]; + struct regmap_config regconfig = fsl_ssi_regconfig; of_id = of_match_device(fsl_ssi_ids, &pdev->dev); if (!of_id || !of_id->data) @@ -1444,15 +1436,25 @@ static int fsl_ssi_probe(struct platform_device *pdev) return PTR_ERR(iomem); ssi_private->ssi_phys = res->start; + if (ssi_private->soc->imx21regs) { + /* + * According to datasheet imx21-class SSI + * don't have SACC{ST,EN,DIS} regs. + */ + regconfig.max_register = CCSR_SSI_SRMSK; + regconfig.num_reg_defaults_raw = + CCSR_SSI_SRMSK / sizeof(uint32_t) + 1; + } + ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { ssi_private->has_ipg_clk_name = false; ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, - &fsl_ssi_regconfig); + ®config); } else { ssi_private->has_ipg_clk_name = true; ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, - "ipg", iomem, &fsl_ssi_regconfig); + "ipg", iomem, ®config); } if (IS_ERR(ssi_private->regs)) { dev_err(&pdev->dev, "Failed to init register map\n"); -- cgit v1.2.3 From edd7ea2d5486c12978fdc71c6efd493cbabeea60 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 22 Feb 2016 09:37:27 +0530 Subject: ASoC: Intel: Skylake: remove bogus comparison of an array with NULL dfw_ac->params is an array not a pointer. It will never be NULL. The check on ac->max appears sufficient. Signed-off-by: Alan Cox Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index ba0d02d1613e..c95bbce8ad5d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1657,8 +1657,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, if (!ac->params) return -ENOMEM; - if (dfw_ac->params) - memcpy(ac->params, dfw_ac->params, ac->max); + memcpy(ac->params, dfw_ac->params, ac->max); } be->dobj.private = ac; -- cgit v1.2.3 From 255edcdfab3d1a45b78d1dacd845d6da5fc64b03 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Mon, 22 Feb 2016 16:32:05 +0800 Subject: ASoC: rockchip: add bindings for rk3399 i2s Add devicetree bindings for i2s controller found on rk3399 processors from rockchip. It's helpful to add full set of compatible strings for serials of Rockchip SoCs (rk3066, rk3188, rk3288, rk3399). Acked-by: Rob Herring Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rockchip-i2s.txt | 1 + sound/soc/rockchip/rockchip_i2s.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt index b7f3a9325ebd..6e86d8aa29b4 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt @@ -9,6 +9,7 @@ Required properties: - "rockchip,rk3066-i2s": for rk3066 - "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188 - "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288 + - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399 - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the I2S interrupt. diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 6561c4cc2edd..531a6d201d2e 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -575,6 +575,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev) static const struct of_device_id rockchip_i2s_match[] = { { .compatible = "rockchip,rk3066-i2s", }, + { .compatible = "rockchip,rk3188-i2s", }, + { .compatible = "rockchip,rk3288-i2s", }, + { .compatible = "rockchip,rk3399-i2s", }, {}, }; -- cgit v1.2.3 From 3375cbe420a8987b30daf3c021bea6fb0b0fe344 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 22 Feb 2016 14:37:13 +0530 Subject: ASoC: topology: fix build warning We are getting build warning about: sound/soc/soc-topology.c:249:31: warning: 'get_dobj_type' defined but not used commit 64527e8a3529 has removed the only caller of get_dobj_type() but missed to remove the function which is now unused. And after removing get_dobj_type() we do not have any use of get_dobj_mixer_type(). Fixes: 64527e8a3529 ("ASoC: topology: Add FE DAIs dynamically") CC: Mengdong Lin Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 45 --------------------------------------------- 1 file changed, 45 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 0eb01e8155f1..07f61a67aef6 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -223,51 +223,6 @@ static int get_widget_id(int tplg_type) return -EINVAL; } -static enum snd_soc_dobj_type get_dobj_mixer_type( - struct snd_soc_tplg_ctl_hdr *control_hdr) -{ - if (control_hdr == NULL) - return SND_SOC_DOBJ_NONE; - - switch (control_hdr->ops.info) { - case SND_SOC_TPLG_CTL_VOLSW: - case SND_SOC_TPLG_CTL_VOLSW_SX: - case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - case SND_SOC_TPLG_CTL_RANGE: - case SND_SOC_TPLG_CTL_STROBE: - return SND_SOC_DOBJ_MIXER; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_CTL_ENUM_VALUE: - return SND_SOC_DOBJ_ENUM; - case SND_SOC_TPLG_CTL_BYTES: - return SND_SOC_DOBJ_BYTES; - default: - return SND_SOC_DOBJ_NONE; - } -} - -static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr, - struct snd_soc_tplg_ctl_hdr *control_hdr) -{ - switch (hdr->type) { - case SND_SOC_TPLG_TYPE_MIXER: - return get_dobj_mixer_type(control_hdr); - case SND_SOC_TPLG_TYPE_DAPM_GRAPH: - case SND_SOC_TPLG_TYPE_MANIFEST: - return SND_SOC_DOBJ_NONE; - case SND_SOC_TPLG_TYPE_DAPM_WIDGET: - return SND_SOC_DOBJ_WIDGET; - case SND_SOC_TPLG_TYPE_DAI_LINK: - return SND_SOC_DOBJ_DAI_LINK; - case SND_SOC_TPLG_TYPE_PCM: - return SND_SOC_DOBJ_PCM; - case SND_SOC_TPLG_TYPE_CODEC_LINK: - return SND_SOC_DOBJ_CODEC_LINK; - default: - return SND_SOC_DOBJ_NONE; - } -} - static inline void soc_bind_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, int index) { -- cgit v1.2.3 From 513cb31120fb5a5b02d9e42eb1d0a0bf05e227c5 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 22 Feb 2016 14:14:31 +0530 Subject: ASoC: fix memory leak If dai_link is already bound then we just returned and leaked rtd and rtd->codec_dais which were allocated by soc_new_pcm_runtime(). We do not need this newly allocated rtd to check if dai_link is already binded. Lets check first if it is already binded before allocating this memory. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 790ee2bf1a47..d2e62b159610 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -986,16 +986,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card, dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); - rtd = soc_new_pcm_runtime(card, dai_link); - if (!rtd) - return -ENOMEM; - if (soc_is_dai_link_bound(card, dai_link)) { dev_dbg(card->dev, "ASoC: dai link %s already bound\n", dai_link->name); return 0; } + rtd = soc_new_pcm_runtime(card, dai_link); + if (!rtd) + return -ENOMEM; + cpu_dai_component.name = dai_link->cpu_name; cpu_dai_component.of_node = dai_link->cpu_of_node; cpu_dai_component.dai_name = dai_link->cpu_dai_name; -- cgit v1.2.3 From b6b6e4d670c9285225dc826bf69620f69717ade0 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 22 Feb 2016 16:29:19 +0800 Subject: ASoC: topology: Fix setting of stream rates, rate_min and rate_max Directly set a stream's rates, rate_min and rate_max from the topology info. Also define set_stream_info to wrap setting of the stream info. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 07f61a67aef6..1cf94d7fb9f4 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1530,6 +1530,18 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return 0; } +static void set_stream_info(struct snd_soc_pcm_stream *stream, + struct snd_soc_tplg_stream_caps *caps) +{ + stream->stream_name = kstrdup(caps->name, GFP_KERNEL); + stream->channels_min = caps->channels_min; + stream->channels_max = caps->channels_max; + stream->rates = caps->rates; + stream->rate_min = caps->rate_min; + stream->rate_max = caps->rate_max; + stream->formats = caps->formats; +} + static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { @@ -1548,25 +1560,13 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, if (pcm->playback) { stream = &dai_drv->playback; caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; - - stream->stream_name = kstrdup(caps->name, GFP_KERNEL); - stream->channels_min = caps->channels_min; - stream->channels_max = caps->channels_max; - stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, - caps->rate_max); - stream->formats = caps->formats; + set_stream_info(stream, caps); } if (pcm->capture) { stream = &dai_drv->capture; caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; - - stream->stream_name = kstrdup(caps->name, GFP_KERNEL); - stream->channels_min = caps->channels_min; - stream->channels_max = caps->channels_max; - stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, - caps->rate_max); - stream->formats = caps->formats; + set_stream_info(stream, caps); } /* pass control to component driver for optional further init */ -- cgit v1.2.3 From ea2e5b96bd6e835052981b777253def60cfd905f Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 22 Feb 2016 15:56:54 +0800 Subject: ASoC: rockchip: i2s: add default values for registers this patch add default values for registers according description from TRM. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 531a6d201d2e..2f8e20416bd3 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) } } +static const struct reg_default rockchip_i2s_reg_defaults[] = { + {0x00, 0x0000000f}, + {0x04, 0x0000000f}, + {0x08, 0x00071f1f}, + {0x10, 0x001f0000}, + {0x14, 0x01f00000}, +}; + static const struct regmap_config rockchip_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = I2S_RXDR, + .reg_defaults = rockchip_i2s_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults), .writeable_reg = rockchip_i2s_wr_reg, .readable_reg = rockchip_i2s_rd_reg, .volatile_reg = rockchip_i2s_volatile_reg, -- cgit v1.2.3 From 6049af00fc2fac8d27f8bd064ff68b16991a80f7 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 22 Feb 2016 15:56:55 +0800 Subject: ASoC: rt5640: add master clock handling for rt5640 enable/disable master clock when codec is active or not. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5640.txt | 3 +++ sound/soc/codecs/rt5640.c | 31 ++++++++++++++++++++++ sound/soc/codecs/rt5640.h | 2 ++ 3 files changed, 36 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index 9e62f6eb348f..57fe64643050 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -12,6 +12,9 @@ Required properties: Optional properties: +- clocks: The phandle of the master clock to the CODEC +- clock-names: Should be "mclk" + - realtek,in1-differential - realtek,in2-differential - realtek,in3-differential diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 11d032cdc658..6cd84fb2196a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1949,7 +1949,33 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5640_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5640->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5640->mclk); + } else { + ret = clk_prepare_enable(rt5640->mclk); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_STANDBY: if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5640_PWR_ANLG1, @@ -2088,6 +2114,11 @@ static int rt5640_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + /* Check if MCLK provided */ + rt5640->mclk = devm_clk_get(codec->dev, "mclk"); + if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + rt5640->codec = codec; snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 83a7150ddc24..1761c3a98b76 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -12,6 +12,7 @@ #ifndef _RT5640_H #define _RT5640_H +#include #include /* Info */ @@ -2097,6 +2098,7 @@ struct rt5640_priv { struct snd_soc_codec *codec; struct rt5640_platform_data pdata; struct regmap *regmap; + struct clk *mclk; int sysclk; int sysclk_src; -- cgit v1.2.3 From 0f83f9296d5c91d08cf46cf1ba8a17fb870dedf0 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Fri, 19 Feb 2016 10:35:59 +0800 Subject: ASoC: mediatek: Add machine driver for ALC5650 codec This is the DPCM based machine driver with rt5650 Signed-off-by: Nicolas Boichat Signed-off-by: Koro Chen Signed-off-by: PC Liao Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mt8173-rt5650.txt | 15 ++ sound/soc/mediatek/Kconfig | 10 + sound/soc/mediatek/Makefile | 1 + sound/soc/mediatek/mt8173-rt5650.c | 236 +++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt8173-rt5650.txt create mode 100644 sound/soc/mediatek/mt8173-rt5650.c (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt new file mode 100644 index 000000000000..fe5a5ef1714d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt @@ -0,0 +1,15 @@ +MT8173 with RT5650 CODECS + +Required properties: +- compatible : "mediatek,mt8173-rt5650" +- mediatek,audio-codec: the phandles of rt5650 codecs +- mediatek,platform: the phandle of MT8173 ASoC platform + +Example: + + sound { + compatible = "mediatek,mt8173-rt5650"; + mediatek,audio-codec = <&rt5650>; + mediatek,platform = <&afe>; + }; + diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 976967675387..6c8808798c6b 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -17,6 +17,16 @@ config SND_SOC_MT8173_MAX98090 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT8173_RT5650 + tristate "ASoC Audio driver for MT8173 with RT5650 codec" + depends on SND_SOC_MEDIATEK && I2C + select SND_SOC_RT5645 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 audio codec. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MT8173_RT5650_RT5676 tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" depends on SND_SOC_MEDIATEK && I2C diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 75effbec438d..bcdcb062e73a 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o # Machine support obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c new file mode 100644 index 000000000000..bb09bb1b7f1c --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650.c @@ -0,0 +1,236 @@ +/* + * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Koro Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"DMIC L1", NULL, "Int Mic"}, + {"DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_ops = { + .hw_params = mt8173_rt5650_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_jack; + +static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650 Playback", + .stream_name = "rt5650 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650 Capture", + .stream_name = "rt5650 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_codecs, + .num_codecs = 1, + .init = mt8173_rt5650_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_rt5650_card = { + .name = "mtk-rt5650", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_dais), + .controls = mt8173_rt5650_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_controls), + .dapm_widgets = mt8173_rt5650_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets), + .dapm_routes = mt8173_rt5650_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes), +}; + +static int mt8173_rt5650_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_card; + struct device_node *platform_node; + int i, ret; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < card->num_links; i++) { + if (mt8173_rt5650_dais[i].platform_name) + continue; + mt8173_rt5650_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_rt5650_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match); + +static struct platform_driver mt8173_rt5650_driver = { + .driver = { + .name = "mtk-rt5650", + .of_match_table = mt8173_rt5650_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver"); +MODULE_AUTHOR("Koro Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650"); + -- cgit v1.2.3 From 94a573500d48e780e5b23d307d6e6b05e6af7d2a Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 22 Feb 2016 18:52:08 +0530 Subject: ALSA: mts64: use new parport device model Modify mts64 driver to use the new parallel port device model. The advantage of using the device model is that the driver gets binded to the hardware, we get the feature of hotplug, we can bind/unbind the driver at runtime. The changes are in the way the driver gets registered with the parallel port subsystem and the temporary device to probe mts64 card is removed and mts64_probe() is used in the probe callback. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai --- sound/drivers/mts64.c | 95 +++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 52 deletions(-) (limited to 'sound') diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 2a008a9ccf85..c76bd87071c6 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -65,8 +65,6 @@ struct mts64 { struct snd_card *card; struct snd_rawmidi *rmidi; struct pardevice *pardev; - int pardev_claimed; - int open_count; int current_midi_output_port; int current_midi_input_port; @@ -850,30 +848,6 @@ __out: spin_unlock(&mts->lock); } -static int snd_mts64_probe_port(struct parport *p) -{ - struct pardevice *pardev; - int res; - - pardev = parport_register_device(p, DRIVER_NAME, - NULL, NULL, NULL, - 0, NULL); - if (!pardev) - return -EIO; - - if (parport_claim(pardev)) { - parport_unregister_device(pardev); - return -EIO; - } - - res = mts64_probe(p); - - parport_release(pardev); - parport_unregister_device(pardev); - - return res; -} - static void snd_mts64_attach(struct parport *p) { struct platform_device *device; @@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p) /* nothing to do here */ } +static int snd_mts64_dev_probe(struct pardevice *pardev) +{ + if (strcmp(pardev->name, DRIVER_NAME)) + return -ENODEV; + + return 0; +} + static struct parport_driver mts64_parport_driver = { - .name = "mts64", - .attach = snd_mts64_attach, - .detach = snd_mts64_detach + .name = "mts64", + .probe = snd_mts64_dev_probe, + .match_port = snd_mts64_attach, + .detach = snd_mts64_detach, + .devmodel = true, }; /********************************************************************* @@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card) struct pardevice *pardev = mts->pardev; if (pardev) { - if (mts->pardev_claimed) - parport_release(pardev); + parport_release(pardev); parport_unregister_device(pardev); } @@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev) struct snd_card *card = NULL; struct mts64 *mts = NULL; int err; + struct pardev_cb mts64_cb = { + .preempt = NULL, + .wakeup = NULL, + .irq_func = snd_mts64_interrupt, /* ISR */ + .flags = PARPORT_DEV_EXCL, /* flags */ + }; p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); @@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev) return -ENODEV; if (!enable[dev]) return -ENOENT; - if ((err = snd_mts64_probe_port(p)) < 0) - return err; err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &card); @@ -960,23 +947,32 @@ static int snd_mts64_probe(struct platform_device *pdev) sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); - pardev = parport_register_device(p, /* port */ - DRIVER_NAME, /* name */ - NULL, /* preempt */ - NULL, /* wakeup */ - snd_mts64_interrupt, /* ISR */ - PARPORT_DEV_EXCL, /* flags */ - (void *)card); /* private */ - if (pardev == NULL) { + mts64_cb.private = card; /* private */ + pardev = parport_register_dev_model(p, /* port */ + DRIVER_NAME, /* name */ + &mts64_cb, /* callbacks */ + pdev->id); /* device number */ + if (!pardev) { snd_printd("Cannot register pardevice\n"); err = -EIO; goto __err; } + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto free_pardev; + } + err = mts64_probe(p); + if (err) { + err = -EIO; + goto release_pardev; + } + if ((err = snd_mts64_create(card, pardev, &mts)) < 0) { snd_printd("Cannot create main component\n"); - parport_unregister_device(pardev); - goto __err; + goto release_pardev; } card->private_data = mts; card->private_free = snd_mts64_card_private_free; @@ -986,14 +982,6 @@ static int snd_mts64_probe(struct platform_device *pdev) goto __err; } - /* claim parport */ - if (parport_claim(pardev)) { - snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); - err = -EIO; - goto __err; - } - mts->pardev_claimed = 1; - /* init device */ if ((err = mts64_device_init(p)) < 0) goto __err; @@ -1009,6 +997,10 @@ static int snd_mts64_probe(struct platform_device *pdev) snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base); return 0; +release_pardev: + parport_release(pardev); +free_pardev: + parport_unregister_device(pardev); __err: snd_card_free(card); return err; @@ -1024,7 +1016,6 @@ static int snd_mts64_remove(struct platform_device *pdev) return 0; } - static struct platform_driver snd_mts64_driver = { .probe = snd_mts64_probe, .remove = snd_mts64_remove, -- cgit v1.2.3 From 7ed49700c4d6959dbfdf174023a57fc15a97c9ab Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 22 Feb 2016 07:50:32 +0530 Subject: ASoC: hdac_hdmi: Fix to wait for D3 before powering off codec Powering off codec immediately after sending D3 verb may not set the node to D3 state. So wait for a confirmation response before shutting down codec. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2445b4351efb..aa953a5b57d4 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1486,6 +1486,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + unsigned long timeout; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1495,10 +1496,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return 0; /* Power down afg */ - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) { snd_hdac_codec_write(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + /* Wait till power state is set to D3 */ + timeout = jiffies + msecs_to_jiffies(1000); + while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3) + && time_before(jiffies, timeout)) { + + msleep(50); + } + } + err = snd_hdac_display_power(bus, false); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); -- cgit v1.2.3 From 571d507809812e1d9c06aa80be714bf39ece6b8e Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Mon, 22 Feb 2016 07:50:33 +0530 Subject: ASoC: hdac_hdmi: Add PM support The codec registers are reset during S3. So need to reconfigure all pins and DP1.2 feature again after resume from S3. Also reprogram the required registers if the S3 was triggered during playback. In suspended state ELD notify callback is not processed, So add ELD check for all pins as well. Also turn the codec power domain OFF which is kept ON during controller resequencing and codec reenumeration. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index aa953a5b57d4..7851dd204a49 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -601,6 +601,26 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, pin->eld.eld_buffer); } +static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdac->private_data; + int ret; + + dai_map = &hdmi->dai_map[dai->id]; + if (cmd == SNDRV_PCM_TRIGGER_RESUME) { + ret = hdac_hdmi_enable_pin(hdac, dai_map); + if (ret < 0) + return ret; + + return hdac_hdmi_playback_prepare(substream, dai); + } + + return 0; +} + static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1154,6 +1174,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = { .shutdown = hdac_hdmi_pcm_close, .hw_params = hdac_hdmi_set_hw_params, .prepare = hdac_hdmi_playback_prepare, + .trigger = hdac_hdmi_trigger, .hw_free = hdac_hdmi_playback_cleanup, }; @@ -1399,9 +1420,64 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_PM +static int hdmi_codec_resume(struct snd_soc_codec *codec) +{ + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pin *pin; + struct hdac_device *hdac = &edev->hdac; + struct hdac_bus *bus = hdac->bus; + int err; + unsigned long timeout; + + hdac_hdmi_skl_enable_all_pins(&edev->hdac); + hdac_hdmi_skl_enable_dp12(&edev->hdac); + + /* Power up afg */ + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) { + + snd_hdac_codec_write(hdac, hdac->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* Wait till power state is set to D0 */ + timeout = jiffies + msecs_to_jiffies(1000); + while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0) + && time_before(jiffies, timeout)) { + msleep(50); + } + } + + /* + * As the ELD notify callback request is not entertained while the + * device is in suspend state. Need to manually check detection of + * all pins here. + */ + list_for_each_entry(pin, &hdmi->pin_list, head) + hdac_hdmi_present_sense(pin, 1); + + /* + * Codec power is turned ON during controller resume. + * Turn it OFF here + */ + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, + "Cannot turn OFF display power on i915, err: %d\n", + err); + return err; + } + + return 0; +} +#else +#define hdmi_codec_resume NULL +#endif + static struct snd_soc_codec_driver hdmi_hda_codec = { .probe = hdmi_codec_probe, .remove = hdmi_codec_remove, + .resume = hdmi_codec_resume, .idle_bias_off = true, }; -- cgit v1.2.3 From fe0d128c57bf927a713159f60a18d9f315d4d91d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 17 Feb 2016 09:44:25 +0100 Subject: ALSA: jack: Allow building the jack layer without input device Since the recent integration of kctl jack and input jack layers, we can basically build the jack layer even without input devices. That is, the jack layer itself can be built with conditional to enable the input device support or not, while the users may enable always CONFIG_SND_JACK unconditionally. For achieving it, this patch changes the following: - A new Kconfig, CONFIG_SND_JACK_INPUT_DEV, was introduced to indicate whether the jack layer supports the input device, - A few items in snd_jack struct and relevant codes are conditionally built upon CONFIG_SND_JACK_INPUT_DEV, - The users of CONFIG_SND_JACK drop the messy dependency on CONFIG_INPUT. This change also automagically fixes a potential bug in HD-audio driver Arnd reported, where the NULL or uninitialized jack instance is dereferenced. Reported-by: Arnd Bergmann Acked-by: Arnd Bergmann Signed-off-by: Takashi Iwai --- include/sound/jack.h | 23 ++++++++++++++--------- sound/core/Kconfig | 9 ++++++--- sound/core/jack.c | 23 ++++++++++++++++++++--- sound/pci/Kconfig | 2 +- sound/pci/hda/Kconfig | 2 +- sound/soc/Kconfig | 2 +- 6 files changed, 43 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/include/sound/jack.h b/include/sound/jack.h index 23bede121c78..1e84bfb553cf 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -72,14 +72,16 @@ enum snd_jack_types { #define SND_JACK_SWITCH_TYPES 6 struct snd_jack { - struct input_dev *input_dev; struct list_head kctl_list; struct snd_card *card; + const char *id; +#ifdef CONFIG_SND_JACK_INPUT_DEV + struct input_dev *input_dev; int registered; int type; - const char *id; char name[100]; unsigned int key[6]; /* Keep in sync with definitions above */ +#endif /* CONFIG_SND_JACK_INPUT_DEV */ void *private_data; void (*private_free)(struct snd_jack *); }; @@ -89,10 +91,11 @@ struct snd_jack { int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack **jack, bool initial_kctl, bool phantom_jack); int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask); +#ifdef CONFIG_SND_JACK_INPUT_DEV void snd_jack_set_parent(struct snd_jack *jack, struct device *parent); int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, int keytype); - +#endif void snd_jack_report(struct snd_jack *jack, int status); #else @@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name return 0; } +static inline void snd_jack_report(struct snd_jack *jack, int status) +{ +} + +#endif + +#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV) static inline void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) { @@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack, { return 0; } - -static inline void snd_jack_report(struct snd_jack *jack, int status) -{ -} - -#endif +#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */ #endif diff --git a/sound/core/Kconfig b/sound/core/Kconfig index a2a1e24becc6..6d12ca9bcb80 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -24,12 +24,15 @@ config SND_RAWMIDI config SND_COMPRESS_OFFLOAD tristate -# To be effective this also requires INPUT - users should say: -# select SND_JACK if INPUT=y || INPUT=SND -# to avoid having to force INPUT on. config SND_JACK bool +# enable input device support in jack layer +config SND_JACK_INPUT_DEV + bool + depends on SND_JACK + default y if INPUT=y || INPUT=SND + config SND_SEQUENCER tristate "Sequencer support" select SND_TIMER diff --git a/sound/core/jack.c b/sound/core/jack.c index 7237acbdcbbc..f652e90efd7e 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -32,6 +32,7 @@ struct snd_jack_kctl { unsigned int mask_bits; /* only masked status bits are reported via kctl */ }; +#ifdef CONFIG_SND_JACK_INPUT_DEV static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_HEADPHONE_INSERT, SW_MICROPHONE_INSERT, @@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_VIDEOOUT_INSERT, SW_LINEIN_INSERT, }; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static int snd_jack_dev_disconnect(struct snd_device *device) { +#ifdef CONFIG_SND_JACK_INPUT_DEV struct snd_jack *jack = device->device_data; if (!jack->input_dev) @@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device) else input_free_device(jack->input_dev); jack->input_dev = NULL; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ return 0; } @@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device) return 0; } +#ifdef CONFIG_SND_JACK_INPUT_DEV static int snd_jack_dev_register(struct snd_device *device) { struct snd_jack *jack = device->device_data; @@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device) return err; } +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) { @@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack *jack; struct snd_jack_kctl *jack_kctl = NULL; int err; - int i; static struct snd_device_ops ops = { .dev_free = snd_jack_dev_free, +#ifdef CONFIG_SND_JACK_INPUT_DEV .dev_register = snd_jack_dev_register, .dev_disconnect = snd_jack_dev_disconnect, +#endif /* CONFIG_SND_JACK_INPUT_DEV */ }; if (initial_kctl) { @@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, /* don't creat input device for phantom jack */ if (!phantom_jack) { +#ifdef CONFIG_SND_JACK_INPUT_DEV + int i; + jack->input_dev = input_allocate_device(); if (jack->input_dev == NULL) { err = -ENOMEM; @@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, input_set_capability(jack->input_dev, EV_SW, jack_switch_types[i]); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); @@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, return 0; fail_input: +#ifdef CONFIG_SND_JACK_INPUT_DEV input_free_device(jack->input_dev); +#endif kfree(jack->id); kfree(jack); return err; } EXPORT_SYMBOL(snd_jack_new); +#ifdef CONFIG_SND_JACK_INPUT_DEV /** * snd_jack_set_parent - Set the parent device for a jack * @@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, jack->type |= type; jack->key[key] = keytype; - return 0; } EXPORT_SYMBOL(snd_jack_set_key); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ /** * snd_jack_report - Report the current status of a jack @@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key); void snd_jack_report(struct snd_jack *jack, int status) { struct snd_jack_kctl *jack_kctl; +#ifdef CONFIG_SND_JACK_INPUT_DEV int i; +#endif if (!jack) return; @@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status) snd_kctl_jack_report(jack->card, jack_kctl->kctl, status & jack_kctl->mask_bits); +#ifdef CONFIG_SND_JACK_INPUT_DEV if (!jack->input_dev) return; @@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status) } input_sync(jack->input_dev); - +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } EXPORT_SYMBOL(snd_jack_report); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 8f6594a7d37f..32151d8c6bb8 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -866,7 +866,7 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB select SND_PCM select SND_MPU401_UART - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK help Say Y here to include support for sound cards based on the Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX, diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e94cfd5c69f7..bb02c2d48fd5 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -4,7 +4,7 @@ config SND_HDA tristate select SND_PCM select SND_VMASTER - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select SND_HDA_CORE config SND_HDA_INTEL diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ea66ee3653f..182d92efc7c8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -6,7 +6,7 @@ menuconfig SND_SOC tristate "ALSA for SoC audio support" select SND_PCM select AC97_BUS if SND_SOC_AC97_BUS - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER ---help--- -- cgit v1.2.3 From fb087eaaef72061b2bb4e35b70ab43865f541cad Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 23 Feb 2016 16:33:37 +0800 Subject: ALSA: hda - hdmi eld control created based on pcm eld control is created based on pcm now. When monitor is connected, eld control will be bound to pin automatically. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 74 ++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 541986f414cb..21a2b28a2ceb 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -86,7 +86,6 @@ struct hdmi_spec_per_pin { struct hdmi_eld sink_eld; struct mutex lock; struct delayed_work work; - struct snd_kcontrol *eld_ctl; struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; @@ -136,6 +135,7 @@ struct hdmi_ops { struct hdmi_pcm { struct hda_pcm *pcm; struct snd_jack *jack; + struct snd_kcontrol *eld_ctl; }; struct hdmi_spec { @@ -471,17 +471,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; - int pin_idx; + int pcm_idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - pin_idx = kcontrol->private_value; - per_pin = get_pin(spec, pin_idx); + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + uinfo->count = 0; + mutex_unlock(&spec->pcm_lock); + return 0; + } eld = &per_pin->sink_eld; - - mutex_lock(&per_pin->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); return 0; } @@ -493,16 +498,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; - int pin_idx; + int pcm_idx; - pin_idx = kcontrol->private_value; - per_pin = get_pin(spec, pin_idx); + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + mutex_unlock(&spec->pcm_lock); + return 0; + } eld = &per_pin->sink_eld; - mutex_lock(&per_pin->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || eld->eld_size > ELD_MAX_SIZE) { - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); snd_BUG(); return -EINVAL; } @@ -512,7 +524,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); return 0; } @@ -525,7 +537,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = { .get = hdmi_eld_ctl_get, }; -static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, +static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx, int device) { struct snd_kcontrol *kctl; @@ -535,14 +547,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, kctl = snd_ctl_new1(&eld_bytes_ctl, codec); if (!kctl) return -ENOMEM; - kctl->private_value = pin_idx; + kctl->private_value = pcm_idx; kctl->id.device = device; - err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl); + /* no pin nid is associated with the kctl now + * tbd: associate pin nid to eld ctl later + */ + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; - get_pin(spec, pin_idx)->eld_ctl = kctl; + get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl; return 0; } @@ -1841,7 +1856,10 @@ static void update_eld(struct hda_codec *codec, struct hdmi_spec *spec = codec->spec; bool old_eld_valid = pin_eld->eld_valid; bool eld_changed; + int pcm_idx = -1; + /* for monitor disconnection, save pcm_idx firstly */ + pcm_idx = per_pin->pcm_idx; if (spec->dyn_pcm_assign) { if (eld->eld_valid) { hdmi_attach_hda_pcm(spec, per_pin); @@ -1851,6 +1869,11 @@ static void update_eld(struct hda_codec *codec, hdmi_detach_hda_pcm(spec, per_pin); } } + /* if pcm_idx == -1, it means this is in monitor connection event + * we can get the correct pcm_idx now. + */ + if (pcm_idx == -1) + pcm_idx = per_pin->pcm_idx; if (eld->eld_valid) snd_hdmi_show_eld(codec, &eld->info); @@ -1884,11 +1907,11 @@ static void update_eld(struct hda_codec *codec, hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } - if (eld_changed) + if (eld_changed && pcm_idx >= 0) snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, - &per_pin->eld_ctl->id); + &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); } /* update ELD and jack state via HD-audio verbs */ @@ -2629,17 +2652,16 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; snd_hda_spdif_ctls_unassign(codec, pcm_idx); - } - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); /* add control for ELD Bytes */ - err = hdmi_create_eld_ctl(codec, pin_idx, - get_pcm_rec(spec, pin_idx)->device); - + err = hdmi_create_eld_ctl(codec, pcm_idx, + get_pcm_rec(spec, pcm_idx)->device); if (err < 0) return err; + } + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hdmi_present_sense(per_pin, 0); } -- cgit v1.2.3 From 127a9cbb5860a7cf15f4d4703ab0454e4603e8ac Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Tue, 23 Feb 2016 07:45:14 +0100 Subject: ASoC: max9867: fix type of variable containing error codes value variable can contain error values and is compared with zero. Its type must be signed. The problem has been detected using coccinelle script scripts/coccinelle/tests/unsigned_lesser_than_zero.cocci Signed-off-by: Andrzej Hajda Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index ab7a94db8db7..4fb6fc7188c7 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -180,7 +180,8 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); - unsigned int ni_h, ni_l, value; + unsigned int ni_h, ni_l; + int value; value = get_ni_value(max9867->sysclk, params_rate(params)); if (value < 0) -- cgit v1.2.3 From 9dcc72efae313472d1d719e91b23e2bea3513789 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 23 Feb 2016 14:53:13 +0800 Subject: ASoC: max9867: Use params_width instead of open-coded Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 4fb6fc7188c7..f371a52fe29d 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -207,10 +207,9 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream, unsigned long int bclk_rate, pclk_bclk_ratio; int bclk_value; - bclk_rate = params_rate(params) * 2 * - snd_pcm_format_width(params_format(params)); + bclk_rate = params_rate(params) * 2 * params_width(params); pclk_bclk_ratio = max9867->pclk/bclk_rate; - switch (snd_pcm_format_width(params_format(params))) { + switch (params_width(params)) { case 8: case 16: switch (pclk_bclk_ratio) { -- cgit v1.2.3 From 2595b7fe1aa3a1dfc47e549b39f7c35c5ce8a56d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 23 Feb 2016 14:16:32 +0000 Subject: ASoC: arizona: Make logging of FLL calculations clearer The debug logging of FLL calculations was confusing. Values were printed in hex without indicating this by a leading 0x, and despite these normally being required in decimal. Also where the register value isn't the actual value (it s a power-of-two or 0 means 1, 1 means 2, ...) it was unclear whether the actual or register value was shown. This patch changes the log print so that all mathematical values are shown in decimal, all register values are shown in hex with a 0x prefix, and where the register value isn't the actual integer value the decimal integer value is shown in () after the hex register value. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index a32cfb85f1ca..dd926f4da409 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -2108,11 +2108,12 @@ static int arizona_calc_fll(struct arizona_fll *fll, return -EINVAL; } - arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", + arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n", cfg->n, cfg->theta, cfg->lambda); - arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", - cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); - arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain); + arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n", + cfg->fratio, ratio, cfg->outdiv, + cfg->refdiv, 1 << cfg->refdiv); + arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain); return 0; -- cgit v1.2.3 From d6dde63e90cf0d34ae61e885a5eb254c59eb3358 Mon Sep 17 00:00:00 2001 From: Andreas Irestål Date: Tue, 16 Feb 2016 13:56:42 +0100 Subject: ASoC: adau17x1: Correct typos in file headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Irestål Signed-off-by: Mark Brown --- include/linux/platform_data/adau17x1.h | 2 +- sound/soc/codecs/adau1761-i2c.c | 2 +- sound/soc/codecs/adau1761-spi.c | 2 +- sound/soc/codecs/adau1761.c | 2 +- sound/soc/codecs/adau1781.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h index a81766cae230..9db1b905df24 100644 --- a/include/linux/platform_data/adau17x1.h +++ b/include/linux/platform_data/adau17x1.h @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 codecs * * Copyright 2011-2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 348ccb17d3cc..a1b12671c307 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c index 8bc1fbd25fcc..188334928c23 100644 --- a/sound/soc/codecs/adau1761-spi.c +++ b/sound/soc/codecs/adau1761-spi.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index e7136b1956a3..b95d29dbd13d 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index fde9068550a6..bc1bb56dae63 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1781/ADAU1781 codec + * Driver for ADAU1381/ADAU1781 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen -- cgit v1.2.3 From aaf0f3aeeb6f9e5e874fccc8a0e56b00fea537c3 Mon Sep 17 00:00:00 2001 From: Andreas Irestål Date: Tue, 16 Feb 2016 13:56:44 +0100 Subject: ASoC: adau17x1: Add basic DT support for adau17x1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Irestål Signed-off-by: Mark Brown --- sound/soc/codecs/adau1761-i2c.c | 12 ++++++++++++ sound/soc/codecs/adau1761-spi.c | 12 ++++++++++++ sound/soc/codecs/adau1781-i2c.c | 10 ++++++++++ sound/soc/codecs/adau1781-spi.c | 10 ++++++++++ 4 files changed, 44 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index a1b12671c307..8de010f758cd 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -44,9 +44,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_i2c_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids); +#endif + static struct i2c_driver adau1761_i2c_driver = { .driver = { .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c index 188334928c23..d9171245bd9f 100644 --- a/sound/soc/codecs/adau1761-spi.c +++ b/sound/soc/codecs/adau1761-spi.c @@ -61,9 +61,21 @@ static const struct spi_device_id adau1761_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, adau1761_spi_id); +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_spi_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids); +#endif + static struct spi_driver adau1761_spi_driver = { .driver = { .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_spi_dt_ids), }, .probe = adau1761_spi_probe, .remove = adau1761_spi_remove, diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 0e32bba92339..06cbca84cf02 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -42,9 +42,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_i2c_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids); +#endif + static struct i2c_driver adau1781_i2c_driver = { .driver = { .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c index 33a73ff78de4..3d965a01b99c 100644 --- a/sound/soc/codecs/adau1781-spi.c +++ b/sound/soc/codecs/adau1781-spi.c @@ -59,9 +59,19 @@ static const struct spi_device_id adau1781_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, adau1781_spi_id); +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_spi_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids); +#endif + static struct spi_driver adau1781_spi_driver = { .driver = { .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_spi_dt_ids), }, .probe = adau1781_spi_probe, .remove = adau1781_spi_remove, -- cgit v1.2.3 From 4b2fe3822ae16436ed0aba379c500558f7846ce1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 24 Feb 2016 15:51:26 +0800 Subject: ASoC: rt298: enable IRQ for jack detection There are some registers needed for enabling rt298 IRQ missed on current driver. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 6 ++++++ sound/soc/codecs/rt298.h | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 30c6de62ae6c..e67ef5f8d4f3 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1225,6 +1225,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt298->regmap, RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); + + regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); + regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); + regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84); + regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2); + rt298->is_hp_in = -1; if (rt298->i2c->irq) { diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h index 31da16265f2b..d66f8847b676 100644 --- a/sound/soc/codecs/rt298.h +++ b/sound/soc/codecs/rt298.h @@ -34,6 +34,7 @@ #define RT298_HP_OUT 0x21 #define RT298_MIXER_IN1 0x22 #define RT298_MIXER_IN2 0x23 +#define RT298_INLINE_CMD 0x55 #define RT298_SET_PIN_SFT 6 #define RT298_SET_PIN_ENABLE 0x40 @@ -124,6 +125,12 @@ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0) #define RT298_PROC_COEF\ VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0) +#define RT298_UNSOLICITED_INLINE_CMD\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0) +#define RT298_UNSOLICITED_HP_OUT\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0) +#define RT298_UNSOLICITED_MIC1\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0) /* Index registers */ #define RT298_A_BIAS_CTRL1 0x01 @@ -148,6 +155,7 @@ #define RT298_DEPOP_CTRL2 0x67 #define RT298_DEPOP_CTRL3 0x68 #define RT298_DEPOP_CTRL4 0x69 +#define RT298_IRQ_FLAG_CTRL 0x7c /* SPDIF (0x06) */ #define RT298_SPDIF_SEL_SFT 0 -- cgit v1.2.3 From 0ce58cd6ce5e706c70c7b038ef5ddde0cc7d7385 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 24 Feb 2016 15:51:27 +0800 Subject: ASoC: rt298: Don't enable IRQ in i2c_probe The IRQ function should not be enabled before irq handler is registered. In fact, it is done in rt298_probe. So remove it from rt298_i2c_probe. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index e67ef5f8d4f3..f0e6c06e89ac 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1224,7 +1224,6 @@ static int rt298_i2c_probe(struct i2c_client *i2c, regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000); regmap_update_bits(rt298->regmap, RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); - regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); -- cgit v1.2.3 From 23905cd1ca48cbc852a670a3dbbfc5e7d694580f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 24 Feb 2016 11:27:38 +0530 Subject: ASoC: Intel: boards: Enable HDMI and DP on skl_rt286 machine Skylake platform has 3 pin widgets in HDMI codec. This patch adds 3 FE and BE dai links to support these in rt286 machine Also add jack initialization for each of the device. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/boards/skl_rt286.c | 111 +++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 6e65085e8bc3..9574b483f249 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -164,6 +164,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH select SND_SOC_INTEL_SKYLAKE select SND_SOC_RT286 select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for Skylake platforms with RT286 I2S audio codec. diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 2cbcbe412661..00b84c1ff07f 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -26,8 +26,20 @@ #include #include #include "../../codecs/rt286.h" +#include "../../codecs/hdac_hdmi.h" static struct snd_soc_jack skylake_headset; + +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + /* Headset jack detection DAPM pins */ static struct snd_soc_jack_pin skylake_headset_pins[] = { { @@ -53,6 +65,9 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("DMIC2", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SINK("WoV Sink"), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), }; static const struct snd_soc_dapm_route skylake_rt286_map[] = { @@ -72,6 +87,10 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { {"WoV Sink", NULL, "hwd_in sink"}, + {"HDMI1", NULL, "hif5 Output"}, + {"HDMI2", NULL, "hif6 Output"}, + {"HDMI3", NULL, "hif7 Output"}, + /* CODEC BE connections */ { "AIF1 Playback", NULL, "ssp0 Tx"}, { "ssp0 Tx", NULL, "codec0_out"}, @@ -84,8 +103,12 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hif1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, }; @@ -121,6 +144,13 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id); +} + static unsigned int rates[] = { 48000, }; @@ -249,7 +279,7 @@ static struct snd_soc_ops skylake_dmic_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_rt286_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -266,7 +296,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_playback = 1, .ops = &skylake_rt286_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -282,7 +312,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_capture = 1, .ops = &skylake_rt286_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "refcap", .cpu_dai_name = "Reference Pin", @@ -295,7 +325,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .nonatomic = 1, .dynamic = 1, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -308,6 +338,42 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, /* Back End DAI links */ { @@ -341,6 +407,39 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_capture = 1, .no_pcm = 1, }, + { + .name = "iDisp1", + .be_id = 2, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 3, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 4, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, }; /* skylake audio machine driver for SPT + RT286S */ -- cgit v1.2.3 From bc5f6ac9a66f90f8e16e568ebd463cbe44f46b81 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 24 Feb 2016 11:27:39 +0530 Subject: ASoC: Intel: boards: Enable HDMI and DP on nau88l2 machine This patch adds 2 HDMI FE and BE dai links and also initializes the jack for each device. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 120 +++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 9574b483f249..e29a7c5295a5 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -179,6 +179,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH select SND_SOC_NAU8825 select SND_SOC_SSM4567 select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + SSM4567. diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index c071812f31e5..ccc3d4a4f4b6 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -26,6 +26,7 @@ #include #include #include "../../codecs/nau8825.h" +#include "../../codecs/hdac_hdmi.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_SSM_CODEC_DAI "ssm4567-hifi" @@ -33,6 +34,16 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -131,8 +142,14 @@ static const struct snd_soc_dapm_route skylake_map[] = { /* DMIC */ { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hifi1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, }; @@ -202,6 +219,28 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; } +static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); +} + +static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); +} + + +static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); +} + static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; @@ -362,7 +401,7 @@ static struct snd_soc_ops skylaye_refcap_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -377,7 +416,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_playback = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -391,7 +430,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "Wake on Voice", .cpu_dai_name = "Reference Pin", @@ -405,7 +444,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylaye_refcap_ops, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -418,15 +457,45 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, - { - .name = "Skl HDMI Port", - .stream_name = "Hdmi", - .cpu_dai_name = "HDMI Pin", + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, .nonatomic = 1, .dynamic = 1, }, @@ -452,7 +521,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 0, + .be_id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -469,7 +538,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .be_id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -480,13 +549,36 @@ static struct snd_soc_dai_link skylake_dais[] = { .no_pcm = 1, }, { - .name = "iDisp", + .name = "iDisp1", .be_id = 3, - .cpu_dai_name = "iDisp Pin", + .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, + .init = skylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi3_init, + .dpcm_playback = 1, .no_pcm = 1, }, }; -- cgit v1.2.3 From 0ab338ff33762d50e5005d8d28b30b25f23aa934 Mon Sep 17 00:00:00 2001 From: Sathyanarayana Nujella Date: Wed, 24 Feb 2016 11:27:40 +0530 Subject: ASoC: Intel: boards: Enable HDMI and DP on SKL nau88l25_max98357 machine This patch adds 2 HDMI FE and BE dai links and also initializes the jack for each device. Signed-off-by: Sathyanarayana Nujella Signed-off-by: Fang, Yang A Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/boards/skl_nau88l25_max98357a.c | 119 +++++++++++++++++++++--- 2 files changed, 106 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e29a7c5295a5..cb968da5a308 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -194,6 +194,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH select SND_SOC_NAU8825 select SND_SOC_MAX98357A select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + MAX98357A. diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index ab7da9c304b2..33bf5286c948 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -22,6 +22,7 @@ #include #include #include "../../codecs/nau8825.h" +#include "../../codecs/hdac_hdmi.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_MAXIM_CODEC_DAI "HiFi" @@ -29,6 +30,16 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -124,8 +135,14 @@ static const struct snd_soc_dapm_route skylake_map[] = { /* DMIC */ { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hifi1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, }; @@ -176,6 +193,27 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; } +static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); +} + +static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); +} + +static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); +} + static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; @@ -318,7 +356,7 @@ static struct snd_soc_ops skylaye_refcap_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -333,7 +371,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_playback = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -347,7 +385,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "Wake on Voice", .cpu_dai_name = "Reference Pin", @@ -361,7 +399,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylaye_refcap_ops, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -374,15 +412,45 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, - { - .name = "Skl HDMI Port", - .stream_name = "Hdmi", - .cpu_dai_name = "HDMI Pin", + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, .nonatomic = 1, .dynamic = 1, }, @@ -407,7 +475,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 0, + .be_id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -424,7 +492,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .be_id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -435,13 +503,36 @@ static struct snd_soc_dai_link skylake_dais[] = { .no_pcm = 1, }, { - .name = "iDisp", + .name = "iDisp1", .be_id = 3, - .cpu_dai_name = "iDisp Pin", + .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, + .init = skylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi3_init, + .dpcm_playback = 1, .no_pcm = 1, }, }; -- cgit v1.2.3 From 5093f9287c83665b9227640aea8a8d980684246f Mon Sep 17 00:00:00 2001 From: Sathya Prakash M R Date: Wed, 24 Feb 2016 11:27:41 +0530 Subject: ASoC: Intel: boards: Add IV feedback route for nau88l25_ssm4567 machine SSM4567 supports IV feedback for feedback to the speaker protection algorithm Signed-off-by: Sathya Prakash M R Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index ccc3d4a4f4b6..68caf24f9fbd 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -133,6 +133,9 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "Right Playback", NULL, "ssp0 Tx"}, { "ssp0 Tx", NULL, "codec0_out"}, + /* IV feedback path */ + { "codec0_lp_in", NULL, "ssp0 Rx"}, + { "Playback", NULL, "ssp1 Tx"}, { "ssp1 Tx", NULL, "codec1_out"}, @@ -154,6 +157,14 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "Headset Mic", NULL, "Platform Clock" }, }; +static const struct snd_soc_pcm_stream skl_ssm4567_loop_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 4, + .channels_max = 4, +}; + static struct snd_soc_codec_conf ssm4567_codec_conf[] = { { .dev_name = "i2c-INT343B:00", @@ -499,6 +510,16 @@ static struct snd_soc_dai_link skylake_dais[] = { .nonatomic = 1, .dynamic = 1, }, + /* Codec-codec link */ + { + .name = "Skylake IV loop", + .stream_name = "SKL IV Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .codecs = ssm4567_codec_components, + .num_codecs = ARRAY_SIZE(ssm4567_codec_components), + .params = &skl_ssm4567_loop_params, + }, /* Back End DAI links */ { -- cgit v1.2.3 From aac5cc88b107c316fdd2846fdac777726b9f725d Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 24 Feb 2016 11:27:42 +0530 Subject: ASoC: Intel: boards: Remove WoV sink in skl_rt286 WoV module needs to be triggered with PCM open, so remove this as DAPM Sink and will be connected to FE Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 00b84c1ff07f..2016397a8e75 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -64,7 +64,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("DMIC2", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), SND_SOC_DAPM_SPK("HDMI3", NULL), @@ -85,8 +84,6 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { {"DMIC1 Pin", NULL, "DMIC2"}, {"DMic", NULL, "SoC DMIC"}, - {"WoV Sink", NULL, "hwd_in sink"}, - {"HDMI1", NULL, "hif5 Output"}, {"HDMI2", NULL, "hif6 Output"}, {"HDMI3", NULL, "hif7 Output"}, @@ -139,7 +136,6 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) rt286_mic_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return 0; } -- cgit v1.2.3 From 98ee1745c81e9cf9553dd311524f9610331e784f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 24 Feb 2016 11:27:43 +0530 Subject: ASoC: Intel: boards: Remove WoV sink in nau88125_ssm4567 machine WoV module needs to be triggered with PCM open, so remove this as DAPM Sink and will be connected to FE Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 68caf24f9fbd..a75c09343f84 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -103,7 +103,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_SPK("Left Speaker", NULL), SND_SOC_DAPM_SPK("Right Speaker", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), SND_SOC_DAPM_SPK("DP", NULL), SND_SOC_DAPM_SPK("HDMI", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, @@ -124,8 +123,6 @@ static const struct snd_soc_dapm_route skylake_map[] = { {"MIC", NULL, "Headset Mic"}, {"DMic", NULL, "SoC DMIC"}, - {"WoV Sink", NULL, "hwd_in sink"}, - {"HDMI", NULL, "hif5 Output"}, {"DP", NULL, "hif6 Output"}, /* CODEC BE connections */ @@ -225,7 +222,6 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) nau8825_enable_jack_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return ret; } -- cgit v1.2.3 From 8be3838af0e992846d4725fae152b818560c1068 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 24 Feb 2016 11:27:44 +0530 Subject: ASoC: Intel: boards: Remove WoV sink in nau88125_max98357a machine WoV module needs to be triggered with PCM open, so remove this as DAPM Sink and will be connected to FE Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_nau88l25_max98357a.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 33bf5286c948..72176b79a18d 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -98,7 +98,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Spk", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), SND_SOC_DAPM_SPK("DP", NULL), SND_SOC_DAPM_SPK("HDMI", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, @@ -118,7 +117,6 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "MIC", NULL, "Headset Mic" }, { "DMic", NULL, "SoC DMIC" }, - {"WoV Sink", NULL, "hwd_in sink"}, {"HDMI", NULL, "hif5 Output"}, {"DP", NULL, "hif6 Output"}, @@ -188,7 +186,6 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) nau8825_enable_jack_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return ret; } -- cgit v1.2.3 From b4c23ab1cbfedbebf9f7eecd1bd8a7989f388649 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 24 Feb 2016 09:26:32 +0900 Subject: ALSA: oxfw: retry MIDI transferring for scs1x at transaction failure Currently, ALSA oxfw driver has a TODO to retry MIDI transferring at transaction failure. This commit achieves it. Current implementation uses snd_rawmidi_transmit() to transfer messages, thus the target MIDI messages are not in buffer when transaction failure is detected. Although we cannot use a pair of snd_rawmidi_transmit_peek() and snd_ramwidi_transmit_ack(), the messages are still in scs1x specific structure and the data is available for retries. This commit adds a member to the structure for the length of buffered messages, and uses the value again at retries. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-scs1x.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index f7ac1243ec94..72446ac33ea7 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -31,6 +31,7 @@ struct fw_scs1x { u8 buffer[HSS1394_MAX_PACKET_SIZE]; bool transaction_running; struct fw_transaction transaction; + unsigned int transaction_bytes; struct fw_device *fw_dev; }; @@ -125,8 +126,8 @@ static void scs_write_callback(struct fw_card *card, int rcode, { struct fw_scs1x *scs = callback_data; - if (rcode == RCODE_GENERATION) - ; /* TODO: retry this packet */ + if (rcode != RCODE_GENERATION) + scs->transaction_bytes = 0; scs->transaction_running = false; schedule_work(&scs->work); @@ -183,6 +184,9 @@ static void scs_output_work(struct work_struct *work) return; } + if (scs->transaction_bytes > 0) + goto retry; + i = scs->output_bytes; for (;;) { if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { @@ -253,13 +257,16 @@ static void scs_output_work(struct work_struct *work) scs->output_bytes = 1; scs->output_escaped = false; + scs->transaction_bytes = i; +retry: scs->transaction_running = true; generation = scs->fw_dev->generation; smp_rmb(); /* node_id vs. generation */ fw_send_request(scs->fw_dev->card, &scs->transaction, TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, - scs->buffer, i, scs_write_callback, scs); + scs->buffer, scs->transaction_bytes, + scs_write_callback, scs); } static int midi_capture_open(struct snd_rawmidi_substream *stream) @@ -309,6 +316,7 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) scs->output_bytes = 1; scs->output_escaped = false; scs->output_idle = false; + scs->transaction_bytes = 0; ACCESS_ONCE(scs->output) = stream; schedule_work(&scs->work); -- cgit v1.2.3 From 956dea9e911253e34fa1773da07a6c4044e20817 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 24 Feb 2016 09:26:33 +0900 Subject: ALSA: oxfw: discontinue MIDI substream for scs1x at transaction failure With a previous commit, ALSA oxfw driver retries transferring MIDI messages at transaction failure for scs1x. On the other hand, there're fatal transaction error. Then, no MIDI messages reach to the unit anymore. In this case, MIDI substream should be terminated. This commit stops MIDI transmission after the fatal error occurs. Unfortunately, unlike ALSA PCM functionality, ALSA rawmidi core has no feature to discontinue MIDI substream runtime in kernel side, thus this commit just stops MIDI transmission without notifying it to userspace. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-scs1x.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index 72446ac33ea7..f897c9831077 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -32,6 +32,7 @@ struct fw_scs1x { bool transaction_running; struct fw_transaction transaction; unsigned int transaction_bytes; + bool error; struct fw_device *fw_dev; }; @@ -126,8 +127,13 @@ static void scs_write_callback(struct fw_card *card, int rcode, { struct fw_scs1x *scs = callback_data; - if (rcode != RCODE_GENERATION) - scs->transaction_bytes = 0; + if (!rcode_is_permanent_error(rcode)) { + /* Don't retry for this data. */ + if (rcode == RCODE_COMPLETE) + scs->transaction_bytes = 0; + } else { + scs->error = true; + } scs->transaction_running = false; schedule_work(&scs->work); @@ -178,7 +184,7 @@ static void scs_output_work(struct work_struct *work) return; stream = ACCESS_ONCE(scs->output); - if (!stream) { + if (!stream || scs->error) { scs->output_idle = true; wake_up(&scs->idle_wait); return; @@ -317,6 +323,7 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) scs->output_escaped = false; scs->output_idle = false; scs->transaction_bytes = 0; + scs->error = false; ACCESS_ONCE(scs->output) = stream; schedule_work(&scs->work); -- cgit v1.2.3 From 90966391005e69a8964350278917a87fe49adf0f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 25 Feb 2016 10:50:38 +0300 Subject: ASoC: max9867: silence and array overflow warning Smatch complains that we might reach the end of this loop without finding what we're looking for leading to a buffer overflow. Signed-off-by: Dan Carpenter Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index f371a52fe29d..2a22fddeb6af 100755 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -156,6 +156,8 @@ static inline int get_ni_value(int mclk, int rate) if (ni_div[i].mclk >= mclk) break; } + if (i == ARRAY_SIZE(ni_div)) + return -EINVAL; switch (rate) { case 8000: -- cgit v1.2.3 From 0150e8c053e585dbd3f8141fbb942015532527ca Mon Sep 17 00:00:00 2001 From: John Lin Date: Thu, 25 Feb 2016 13:18:26 +0800 Subject: ASoC: rt5645: improve headphone pop when system resumes from S3 When playing audio through headphone, headphone makes pop noise if system resumes from S3 to S0. We modify the sequence of writing register for avoiding pop sound. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c61d38b585fb..c3dc371a7ef3 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3029,13 +3029,18 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, RT5645_PWR_BG | RT5645_PWR_VREF2, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2); + mdelay(10); snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_FV1 | RT5645_PWR_FV2, RT5645_PWR_FV1 | RT5645_PWR_FV2); - if (rt5645->en_button_func && - snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) - queue_delayed_work(system_power_efficient_wq, - &rt5645->jack_detect_work, msecs_to_jiffies(0)); + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); + msleep(40); + if (rt5645->en_button_func) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, + msecs_to_jiffies(0)); + } break; case SND_SOC_BIAS_OFF: -- cgit v1.2.3 From 3524be4b21ef911ff37f7d401b743cfb4e30cf98 Mon Sep 17 00:00:00 2001 From: John Lin Date: Thu, 25 Feb 2016 13:18:27 +0800 Subject: ASoC: rt5645: extend delay time for headphone pop noise Headphone needs enough delay time before unmuting for avoiding pop sound. We extend the delay time to make sure headphone doesn't pop. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c3dc371a7ef3..df9fee7f1b42 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1674,7 +1674,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - msleep(70); + msleep(90); rt5645->hp_on = true; } else { /* depop parameters */ -- cgit v1.2.3 From aa3e838869d494c437c693b54d12fe02b96a0aa1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Feb 2016 23:02:04 +0100 Subject: ASoC: pxa: remove unused variable As pointed out by Zhangfei Gao, the sspa_div variable in brownstone_wm8994_hw_params() is completely unused, so as a cleanup following a prior patch, this removes both the variable and the division. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/pxa/brownstone.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 416ea646c3b1..ec522e94b0e2 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int freq_out, sspa_mclk, sysclk; - int sspa_div; if (params_rate(params) > 11025) { freq_out = params_rate(params) * 512; @@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, sysclk = params_rate(params) * 512; sspa_mclk = params_rate(params) * 64; } - sspa_div = freq_out / sspa_mclk; snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0); snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk); -- cgit v1.2.3 From 4d7cb66c8f9db37ffa2a72cc9b9233ae82e94d7e Mon Sep 17 00:00:00 2001 From: PC Liao Date: Tue, 23 Feb 2016 16:20:24 +0800 Subject: ASoC: mediatek: remove soft reset and add second I2S clock To improve I2S flow, this patch removes soft reset and adds second I2S clock to use. Signed-off-by: PC Liao Signed-off-by: Mark Brown --- sound/soc/mediatek/mtk-afe-pcm.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index 689c51f23d9f..3456bfab4617 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -283,20 +283,13 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) regmap_read(afe->regmap, AFE_I2S_CON2, &val); if (!!(val & AFE_I2S_CON2_EN) == enable) - return; /* must skip soft reset */ - - /* I2S soft reset begin */ - regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); + return; /* input */ regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); /* output */ regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); - - /* I2S soft reset end */ - udelay(1); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); } static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, @@ -365,6 +358,7 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, return 0; mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); return 0; @@ -384,6 +378,7 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); } static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, @@ -397,6 +392,9 @@ static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, mtk_afe_dais_set_clks(afe, afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, NULL, 0); + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256, + NULL, 0); /* config I2S */ ret = mtk_afe_set_i2s(afe, substream->runtime->rate); if (ret) -- cgit v1.2.3 From 84f3a52421a41d0b129b519da9f39fcfb336a840 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Thu, 25 Feb 2016 15:12:33 +0800 Subject: ASoC: mediatek: Add I03/I04 widgets and corresponding routes Add these widgets to allow another path from I2S input. Signed-off-by: Koro Chen Signed-off-by: PC Liao Signed-off-by: Mark Brown --- sound/soc/mediatek/mtk-afe-pcm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index 3456bfab4617..f1c58a2c12fb 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -40,6 +40,7 @@ #define AFE_CONN1 0x0024 #define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002c #define AFE_CONN7 0x0460 #define AFE_CONN8 0x0464 #define AFE_HDMI_CONN0 0x0390 @@ -63,6 +64,7 @@ #define AFE_HDMI_OUT_CUR 0x0378 #define AFE_HDMI_OUT_END 0x037c +#define AFE_ADDA_TOP_CON0 0x0120 #define AFE_ADDA2_TOP_CON0 0x0600 #define AFE_HDMI_OUT_CON0 0x0370 @@ -259,6 +261,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) return -EINVAL; /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1); regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); /* set input */ @@ -901,15 +904,19 @@ static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { }; static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), }; static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), }; static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { /* inter-connections */ + SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -932,12 +939,16 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { {"I2S Playback", NULL, "O04"}, {"VUL", NULL, "O09"}, {"VUL", NULL, "O10"}, + {"I03", NULL, "I2S Capture"}, + {"I04", NULL, "I2S Capture"}, {"I17", NULL, "I2S Capture"}, {"I18", NULL, "I2S Capture"}, { "O03", "I05 Switch", "I05" }, { "O04", "I06 Switch", "I06" }, { "O09", "I17 Switch", "I17" }, + { "O09", "I03 Switch", "I03" }, { "O10", "I18 Switch", "I18" }, + { "O10", "I04 Switch", "I04" }, }; static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { -- cgit v1.2.3 From 30ff5957c3f1887d04ca01d839dc382739e48bde Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 26 Feb 2016 19:39:57 +0200 Subject: ALSA: hda - Autosuspend controller after probe even if codecs are already suspended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit azx_probe_continue() uses pm_runtime_put_noidle() to drop the rpm usage_count, which means that if it's the last reference the autosuspend of the controller won't actually happen. So if the codecs autosuspend before the azx_probe_continue() drops the last reference we'll fail to autosuspend the controller. This does happen in practice, but not every time. As can be seen in [1] the controller autosuspend attempt fails due to the usage_count when suspending the codecs. A bit later we see the the contoller usage_count dropping to zero without further attempts at autosuspend. Fix the problem by using pm_runtime_put_autosuspend() instead, which will kick off the autosuspend of the controller even if the codecs are already asleep. As can be seen in [2] the controller autosuspend still fails while suspending the codecs, but later on we see another autosuspend attempt after dropping the usage_count to 0. I was also a bit worried that there might still be a race between the controller autosuspend and the rest of the code in azx_probe_continue(). So I also tried replacing the the put_noidle() with put_sync_suspend(). No explosions occurred, so I'm somewhat satisfied that there are no serious problems in this area. [1] kworker/1:2-122 [001] .... 63.661310: __pm_runtime_suspend: hdaudioC0D0 usage_count 0 kworker/1:2-122 [001] d..2 63.661316: rpm_suspend: hdaudioC0D0 flags-d cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/1:2-122 [001] d..1 63.661317: rpm_check_suspend_allowed: hdaudioC0D0 retval 0 kworker/1:2-122 [001] d..2 63.661332: rpm_return_int: rpm_suspend+0x406/0x5e8:hdaudioC0D0 ret=0 kworker/1:1-72 [001] d..2 63.661543: rpm_suspend: hdaudioC0D0 flags-a cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/1:1-72 [001] d..1 63.661544: rpm_check_suspend_allowed: hdaudioC0D0 retval 0 kworker/1:1-72 [001] .... 63.661545: hda_codec_runtime_suspend: hdaudioC0D0 suspend kworker/1:1-72 [001] d..2 63.661614: rpm_idle: 0000:00:03.0 flags-1 cnt-1 dep-0 auto-1 p-0 irq-0 child-0 kworker/1:1-72 [001] d..1 63.661615: rpm_check_suspend_allowed: 0000:00:03.0 usage_count 1 kworker/1:1-72 [001] d..1 63.661615: rpm_check_suspend_allowed: 0000:00:03.0 retval -11 kworker/1:1-72 [001] d..2 63.661616: rpm_return_int: rpm_idle+0x249/0x487:0000:00:03.0 ret=-11 kworker/1:1-72 [001] d..2 63.661616: rpm_return_int: rpm_suspend+0x406/0x5e8:hdaudioC0D0 ret=0 kworker/1:2-122 [001] d..2 63.664834: rpm_idle: hdaudioC0D0 flags-8 cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/1:2-122 [001] d..1 63.664835: rpm_check_suspend_allowed: hdaudioC0D0 retval 1 kworker/1:2-122 [001] d..2 63.664836: rpm_return_int: rpm_idle+0x249/0x487:hdaudioC0D0 ret=-11 kworker/1:2-122 [001] d..2 63.664841: rpm_idle: hdaudioC0D0 flags-8 cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/1:2-122 [001] d..1 63.664841: rpm_check_suspend_allowed: hdaudioC0D0 retval 1 kworker/1:2-122 [001] d..2 63.664841: rpm_return_int: rpm_idle+0x249/0x487:hdaudioC0D0 ret=-11 kworker/1:2-122 [001] .... 63.664842: azx_probe_continue: 0000:00:03.0 usage_count=0 [2] kworker/0:0-4 [000] .... 50.354567: __pm_runtime_suspend: hdaudioC0D0 usage_count 0 kworker/0:0-4 [000] d..2 50.354574: rpm_suspend: hdaudioC0D0 flags-d cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:0-4 [000] d..1 50.354575: rpm_check_suspend_allowed: hdaudioC0D0 retval 0 kworker/0:0-4 [000] d..2 50.354589: rpm_return_int: rpm_suspend+0x406/0x5e8:hdaudioC0D0 ret=0 kworker/0:2-135 [000] d..2 50.354809: rpm_suspend: hdaudioC0D0 flags-a cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:2-135 [000] d..1 50.354810: rpm_check_suspend_allowed: hdaudioC0D0 retval 0 kworker/0:2-135 [000] .... 50.354816: hda_codec_runtime_suspend: hdaudioC0D0 suspend kworker/0:2-135 [000] d..2 50.354908: rpm_idle: 0000:00:03.0 flags-1 cnt-1 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:2-135 [000] d..1 50.354909: rpm_check_suspend_allowed: 0000:00:03.0 usage_count 1 kworker/0:2-135 [000] d..1 50.354909: rpm_check_suspend_allowed: 0000:00:03.0 retval -11 kworker/0:2-135 [000] d..2 50.354909: rpm_return_int: rpm_idle+0x249/0x487:0000:00:03.0 ret=-11 kworker/0:2-135 [000] d..2 50.354910: rpm_return_int: rpm_suspend+0x406/0x5e8:hdaudioC0D0 ret=0 kworker/0:0-4 [000] d..2 50.373791: rpm_idle: hdaudioC0D0 flags-8 cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:0-4 [000] d..1 50.373792: rpm_check_suspend_allowed: hdaudioC0D0 retval 1 kworker/0:0-4 [000] d..2 50.373793: rpm_return_int: rpm_idle+0x249/0x487:hdaudioC0D0 ret=-11 kworker/0:0-4 [000] d..2 50.373797: rpm_idle: hdaudioC0D0 flags-8 cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:0-4 [000] d..1 50.373798: rpm_check_suspend_allowed: hdaudioC0D0 retval 1 kworker/0:0-4 [000] d..2 50.373798: rpm_return_int: rpm_idle+0x249/0x487:hdaudioC0D0 ret=-11 kworker/0:0-4 [000] .... 50.373799: __pm_runtime_suspend: 0000:00:03.0 usage_count 0 kworker/0:0-4 [000] d..2 50.373800: rpm_suspend: 0000:00:03.0 flags-d cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:0-4 [000] d..1 50.373800: rpm_check_suspend_allowed: 0000:00:03.0 retval 0 kworker/0:0-4 [000] d..2 50.373803: rpm_return_int: rpm_suspend+0x406/0x5e8:0000:00:03.0 ret=0 kworker/0:0-4 [000] d..2 50.385164: rpm_suspend: 0000:00:03.0 flags-a cnt-0 dep-0 auto-1 p-0 irq-0 child-0 kworker/0:0-4 [000] d..1 50.385165: rpm_check_suspend_allowed: 0000:00:03.0 retval 0 kworker/0:0-4 [000] .... 50.385174: azx_runtime_suspend: 0000:00:03.0 azx suspend releaseing power well kworker/0:0-4 [000] .... 50.385179: azx_runtime_suspend: 0000:00:03.0 azx suspend kworker/0:0-4 [000] d..2 50.386872: rpm_return_int: rpm_suspend+0x406/0x5e8:0000:00:03.0 ret=0 Signed-off-by: Ville Syrjälä Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4045dca3d699..41797108752f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2147,7 +2147,7 @@ static int azx_probe_continue(struct azx *chip) azx_add_card_list(chip); snd_hda_set_power_save(&chip->bus, power_save * 1000); if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) - pm_runtime_put_noidle(&pci->dev); + pm_runtime_put_autosuspend(&pci->dev); out_free: if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL -- cgit v1.2.3 From 1387e3eafa94837342b044b429b79830998009ac Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 27 Feb 2016 21:01:21 +0900 Subject: ALSA: dice: drop duplex streams synchronization to transfer own time stamps This commit drops implementation of duplex streams synchronization from ALSA dice driver, due to a reason of hardware design. This patch allows dice-based units to generate sounds correctly when isochronous packet streaming starts at first time. In IEC 61883-6:2005, CIP packetization layer for AM824 data format utilizes the value of SYT field in CIP header of received packet for a reference to phase lock loop. Figure 3 in clause 4.3 describes it. The value is an offset from cycle_time field of every cycle start packet from cycle master on IEEE 1394 bus. The time calculated with these two fields is called as 'presentation timestamp' which represents the time to play data included in the packet. Although, this idea includes some problems due to accuracy of timekeep in cycle master, accuracy of transmission of cycle start packet on the bus with the other units, accuracy of sampling clock in data transmitter side and accuracy of replay in data receiver side. In most case, these accuracies somewhat worse because there's no such ideal hardwares in this world. For the issues, ASICs for Dice include Jitter Elimination Technologies (JET) PLL. The PLL can handle several sources of clock and compensate it with high-precision internal clock source. The sequence of value in syt field of received AMDTP packets is one of the sources, therefore transmitters on IEEE 1394 bus should transfer it. On the other hand, current ALSA dice driver is programmed with a mode of duplex streams with synchronization. In this mode, the driver outputs packets after some incoming packets are handled, to re-use the value of SYT field in incoming packets to the value for outgoing packets. This mode is enabled when source signal of sampling clock is set to internal, and this is a major use case. Thus, in most cases, the unit receives no packets during a short time after packet streaming starts. As long as I experienced, this causes the units to generate no sounds at first time to receive packets. This issue occurs only with Dice II. I guess this is due to a quirk of the PLL. In short, the PLL cannot generate firm signals to ADCs/DACs or the other ICs when no packets are received in the beginning of packet streaming. While, on second time or later, the unit generates sound correctly. I guess that starting packet streaming at first time sets the PLL correctly. Well, still based on my hypothesis and no way to prove it, this commit drops duplex streams synchronization from this driver. At least, the PLL requires the sequence of value in SYT field of received AMDTP packets as one of source of clock signals with internal clock source. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 42 ++------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a64b3cc76bf1..df035b1dd44a 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -191,53 +191,17 @@ end: return err; } -static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) -{ - u32 source; - int err; - - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto end; - - switch (source) { - /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ - case CLOCK_SOURCE_ARX4: /* in 4th stream */ - case CLOCK_SOURCE_ARX3: /* in 3rd stream */ - case CLOCK_SOURCE_ARX2: /* in 2nd stream */ - err = -ENOSYS; - break; - case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ - *sync_mode = 0; - break; - default: - *sync_mode = CIP_SYNC_TO_DEVICE; - break; - } -end: - return err; -} - int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { struct amdtp_stream *master, *slave; unsigned int curr_rate; - enum cip_flags sync_mode; int err = 0; if (dice->substreams_counter == 0) goto end; - err = get_sync_mode(dice, &sync_mode); - if (err < 0) - goto end; - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master = &dice->tx_stream; - slave = &dice->rx_stream; - } else { - master = &dice->rx_stream; - slave = &dice->tx_stream; - } + master = &dice->rx_stream; + slave = &dice->tx_stream; /* Some packet queueing errors. */ if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) @@ -261,8 +225,6 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) stop_stream(dice, slave); snd_dice_transaction_clear_enable(dice); - amdtp_stream_set_sync(sync_mode, master, slave); - err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, -- cgit v1.2.3 From 0bbf7e025fc89ded4925daeaf3619722b13b12d4 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 29 Feb 2016 17:43:30 +0530 Subject: ALSA: mts64: fix NULL pointer dereference While registering pardev, the irq_func was also registered. As a result when we tried to probe for the card, an interrupt was generated and in the ISR we tried to dereference private_data. But private_data is still NULL as we have not yet done snd_mts64_create(). Lets probe for the card after mts64 is created. Reported-by: Fengguang Wu Fixes: 94a573500d48 ("ALSA: mts64: use new parport device model") Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai --- sound/drivers/mts64.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index c76bd87071c6..fd4d18df84d3 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -964,11 +964,6 @@ static int snd_mts64_probe(struct platform_device *pdev) err = -EIO; goto free_pardev; } - err = mts64_probe(p); - if (err) { - err = -EIO; - goto release_pardev; - } if ((err = snd_mts64_create(card, pardev, &mts)) < 0) { snd_printd("Cannot create main component\n"); @@ -976,6 +971,12 @@ static int snd_mts64_probe(struct platform_device *pdev) } card->private_data = mts; card->private_free = snd_mts64_card_private_free; + + err = mts64_probe(p); + if (err) { + err = -EIO; + goto __err; + } if ((err = snd_mts64_rawmidi_create(card)) < 0) { snd_printd("Creating Rawmidi component failed\n"); -- cgit v1.2.3 From dbc77eed6553362e08c1874ff30df640c96eca92 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 29 Feb 2016 22:53:46 +0530 Subject: ASoC: intel: remove unused variable The variable cmd_id is only assigned some value and is never used. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_ipc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 3dc7358828b3..8afa6fe7b0b0 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -318,7 +318,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, union ipc_header_high msg_high; u32 msg_low; struct ipc_dsp_hdr *dsp_hdr; - unsigned int cmd_id; msg_high = msg->mrfld_header.p.header_high; msg_low = msg->mrfld_header.p.header_low_payload; @@ -357,7 +356,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, return; /* Copy command id so that we can use to put sst to reset */ dsp_hdr = (struct ipc_dsp_hdr *)data; - cmd_id = dsp_hdr->cmd_id; dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id); if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, msg_high.part.drv_id, -- cgit v1.2.3 From d10a80de04a3a8c0d7c1567cbc0a8d2e1181c10a Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 1 Mar 2016 15:18:26 +0800 Subject: ALSA: hda - hdmi_find_pcm_slot return value bug fix hdmi_find_pcm_slot return -EBUSY when not no pcm slot found, not -ENODEV. So the caller should compare with -EBUSY. Fixes: a76056f2e57e ('ALSA: hda - hdmi dynamically bind PCM to pin when monitor hotplug') Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 21a2b28a2ceb..490931d020ee 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1758,7 +1758,7 @@ static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, if (per_pin->pcm) return; idx = hdmi_find_pcm_slot(spec, per_pin); - if (idx == -ENODEV) + if (idx == -EBUSY) return; per_pin->pcm_idx = idx; per_pin->pcm = get_hdmi_pcm(spec, idx); -- cgit v1.2.3 From c7c6c73604ed4018cf7c22575d336a448ef8ccf5 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 1 Mar 2016 07:59:10 +0530 Subject: ASoC: Intel: Skylake: correct direction in blob query The blob query for BE skl_tplg_update_be_blob() was not using right values for direction for blob query, so fix that. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c95bbce8ad5d..af7722ab7207 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -273,10 +273,11 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, if (m_cfg->formats_config.caps_size > 0) return 0; + dev_dbg(ctx->dev, "Applying default cfg blob\n"); switch (m_cfg->dev_type) { case SKL_DEVICE_DMIC: link_type = NHLT_LINK_DMIC; - dir = 1; + dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_cfg->in_fmt[0].s_freq; s_fmt = m_cfg->in_fmt[0].bit_depth; ch = m_cfg->in_fmt[0].channels; @@ -285,15 +286,15 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, case SKL_DEVICE_I2S: link_type = NHLT_LINK_SSP; if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { - dir = 1; - s_freq = m_cfg->in_fmt[0].s_freq; - s_fmt = m_cfg->in_fmt[0].bit_depth; - ch = m_cfg->in_fmt[0].channels; - } else { - dir = 0; + dir = SNDRV_PCM_STREAM_PLAYBACK; s_freq = m_cfg->out_fmt[0].s_freq; s_fmt = m_cfg->out_fmt[0].bit_depth; ch = m_cfg->out_fmt[0].channels; + } else { + dir = SNDRV_PCM_STREAM_CAPTURE; + s_freq = m_cfg->in_fmt[0].s_freq; + s_fmt = m_cfg->in_fmt[0].bit_depth; + ch = m_cfg->in_fmt[0].channels; } break; -- cgit v1.2.3 From 5188b95a60ce33d5445833f9b0a374a2ad3200f4 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 1 Mar 2016 07:59:11 +0530 Subject: ASoC: Intel: Skylake: Add IVI feedback path in nau88125_ssm4567 machine We can enable the IVI feedback path by connecting Left/Right speaker sensors (codec) to ssp0 Rx (soc-dsp). This way aDSP gets feedback data from codec to DSP algorithms Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index a75c09343f84..f6a672c55be9 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -132,6 +132,8 @@ static const struct snd_soc_dapm_route skylake_map[] = { /* IV feedback path */ { "codec0_lp_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Left Capture Sense" }, + { "ssp0 Rx", NULL, "Right Capture Sense" }, { "Playback", NULL, "ssp1 Tx"}, { "ssp1 Tx", NULL, "codec1_out"}, @@ -534,6 +536,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .ignore_pmdown_time = 1, .be_hw_params_fixup = skylake_ssp_fixup, .dpcm_playback = 1, + .dpcm_capture = 1, }, { /* SSP1 - Codec */ -- cgit v1.2.3 From fdd69c57370dbd9f81072b14aaeb90d80ab0b2f9 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 1 Mar 2016 07:59:12 +0530 Subject: ASoC: Intel: Skylake: remove IVI codec loop in skl_nau88l25_ssm4567 machine The IVI loop is removed in skl_nau88l25_ssm4567 machine as we enable this path thru DAPM graph using the PCM device and not thru loop Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index f6a672c55be9..5f1ca99ae9b0 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -156,14 +156,6 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "Headset Mic", NULL, "Platform Clock" }, }; -static const struct snd_soc_pcm_stream skl_ssm4567_loop_params = { - .formats = SNDRV_PCM_FMTBIT_S24_LE, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 4, - .channels_max = 4, -}; - static struct snd_soc_codec_conf ssm4567_codec_conf[] = { { .dev_name = "i2c-INT343B:00", @@ -508,16 +500,6 @@ static struct snd_soc_dai_link skylake_dais[] = { .nonatomic = 1, .dynamic = 1, }, - /* Codec-codec link */ - { - .name = "Skylake IV loop", - .stream_name = "SKL IV Loop", - .cpu_dai_name = "SSP0 Pin", - .platform_name = "0000:00:1f.3", - .codecs = ssm4567_codec_components, - .num_codecs = ARRAY_SIZE(ssm4567_codec_components), - .params = &skl_ssm4567_loop_params, - }, /* Back End DAI links */ { -- cgit v1.2.3 From b3fc95ad025683180628f6896d1f82d2cf5d0266 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:26:51 +0000 Subject: ASoC: rsnd: rsnd_write() / rsnd_bset() uses regmap _force_ function Some R-Car sound requests picky register access which needs *force* register write. Some status register needs to set 1 to clear status, but we might read 1 from its register. In such case, current regmap does nothing and driver will be forever loop To reduce code complexity, this patch uses regmap _force_ function for all register access. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 21 ++------------------- sound/soc/sh/rcar/rsnd.h | 2 -- 2 files changed, 2 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 271d29adac68..46c0ba7b6414 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -101,23 +101,6 @@ void rsnd_write(struct rsnd_priv *priv, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - if (!rsnd_is_accessible_reg(priv, gen, reg)) - return; - - regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); - - dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - rsnd_reg_name(gen, reg), reg, data); -} - -void rsnd_force_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - if (!rsnd_is_accessible_reg(priv, gen, reg)) return; @@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), - mask, data); + regmap_fields_force_update_bits(gen->regs[reg], + rsnd_mod_id(mod), mask, data); dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5f613eb42614..305cc086a0bc 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -182,8 +182,6 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) -#define rsnd_mod_force_write(m, r, d) \ - rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) -- cgit v1.2.3 From 0c71367d35115a7eb24483ffce2fad74f77dc4d0 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Thu, 25 Feb 2016 15:16:38 +0800 Subject: ASoC: mediatek: Add machine driver for rt5650 rt5514 codec This is the DPCM based machine driver with rt5650 and rt5514. Signed-off-by: Koro Chen Signed-off-by: PC Liao Signed-off-by: Mark Brown --- .../bindings/sound/mt8173-rt5650-rt5514.txt | 15 ++ sound/soc/mediatek/Kconfig | 28 ++- sound/soc/mediatek/Makefile | 3 +- sound/soc/mediatek/mt8173-rt5650-rt5514.c | 258 +++++++++++++++++++++ 4 files changed, 294 insertions(+), 10 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt create mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5514.c (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt new file mode 100644 index 000000000000..e8b3c80c6fff --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt @@ -0,0 +1,15 @@ +MT8173 with RT5650 RT5514 CODECS + +Required properties: +- compatible : "mediatek,mt8173-rt5650-rt5514" +- mediatek,audio-codec: the phandles of rt5650 and rt5514 codecs +- mediatek,platform: the phandle of MT8173 ASoC platform + +Example: + + sound { + compatible = "mediatek,mt8173-rt5650-rt5514"; + mediatek,audio-codec = <&rt5650 &rt5514>; + mediatek,platform = <&afe>; + }; + diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 6c8808798c6b..f7e789e97fbc 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -18,14 +18,25 @@ config SND_SOC_MT8173_MAX98090 If unsure select "N". config SND_SOC_MT8173_RT5650 - tristate "ASoC Audio driver for MT8173 with RT5650 codec" - depends on SND_SOC_MEDIATEK && I2C - select SND_SOC_RT5645 - help - This adds ASoC driver for Mediatek MT8173 boards - with the RT5650 audio codec. - Select Y if you have such device. - If unsure select "N". + tristate "ASoC Audio driver for MT8173 with RT5650 codec" + depends on SND_SOC_MEDIATEK && I2C + select SND_SOC_RT5645 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 audio codec. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8173_RT5650_RT5514 + tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs" + depends on SND_SOC_MEDIATEK && I2C + select SND_SOC_RT5645 + select SND_SOC_RT5514 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 and RT5514 codecs. + Select Y if you have such device. + If unsure select "N". config SND_SOC_MT8173_RT5650_RT5676 tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" @@ -37,4 +48,3 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N". - diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index bcdcb062e73a..d486860c0a88 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o # Machine support obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173-rt5650-rt5514.c new file mode 100644 index 000000000000..58e083642dea --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5514.c @@ -0,0 +1,258 @@ +/* + * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Koro Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Sub DMIC1L", NULL, "Int Mic"}, + {"Sub DMIC1R", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5514_ops = { + .hw_params = mt8173_rt5650_rt5514_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5514_jack; + +static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5514_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5514-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650_rt5514 Playback", + .stream_name = "rt5650_rt5514 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650_rt5514 Capture", + .stream_name = "rt5650_rt5514 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5514_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5514_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5514_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5514_card = { + .name = "mtk-rt5650-rt5514", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_rt5514_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais), + .codec_conf = mt8173_rt5650_rt5514_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf), + .controls = mt8173_rt5650_rt5514_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls), + .dapm_widgets = mt8173_rt5650_rt5514_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets), + .dapm_routes = mt8173_rt5650_rt5514_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes), +}; + +static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5514_card; + struct device_node *platform_node; + int i, ret; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < card->num_links; i++) { + if (mt8173_rt5650_rt5514_dais[i].platform_name) + continue; + mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_rt5514_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5514_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5514_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codec_conf[0].of_node = + mt8173_rt5650_rt5514_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5514", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match); + +static struct platform_driver mt8173_rt5650_rt5514_driver = { + .driver = { + .name = "mtk-rt5650-rt5514", + .of_match_table = mt8173_rt5650_rt5514_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5514_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_rt5514_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver"); +MODULE_AUTHOR("Koro Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5514"); + -- cgit v1.2.3 From 6d8044b4ff305238503edca7d75f3ab7410396ba Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Mar 2016 17:39:19 +0100 Subject: ASoC: sh: rcar: core: don't open code of_device_get_match_data() This change will also make Coverity happy by avoiding a theoretical NULL pointer dereference; yet another reason is to use the above helper function to tighten the code and make it more readable. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 21e13b3a356f..ba37b0d55b21 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1119,7 +1119,6 @@ static int rsnd_probe(struct platform_device *pdev) struct rsnd_priv *priv; struct device *dev = &pdev->dev; struct rsnd_dai *rdai; - const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); int (*probe_func[])(struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_dma_probe, @@ -1145,7 +1144,7 @@ static int rsnd_probe(struct platform_device *pdev) } priv->pdev = pdev; - priv->flags = (unsigned long)of_id->data; + priv->flags = (unsigned long)of_device_get_match_data(dev); spin_lock_init(&priv->lock); /* -- cgit v1.2.3 From 6a16c1765120abc8f58b2c5f275c95a33bb25572 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Mar 2016 17:39:20 +0100 Subject: ASoC: sh: rcar: rsrc-card: don't open code of_device_get_match_data() This change will also make Coverity happy by avoiding a theoretical NULL pointer dereference; yet another reason is to use the above helper function to tighten the code and make it more readable. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsrc-card.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 8a357fdf1077..545e94245e62 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -71,7 +71,6 @@ struct rsrc_card_priv { #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) static int rsrc_card_startup(struct snd_pcm_substream *substream) { @@ -246,7 +245,7 @@ static int rsrc_card_parse_links(struct device_node *np, struct device *dev = rsrc_priv_to_dev(priv); const struct rsrc_card_of_data *of_data; - of_data = rsrc_dev_to_of_data(dev); + of_data = of_device_get_match_data(dev); /* FE is dummy */ dai_link->cpu_of_node = NULL; @@ -396,7 +395,7 @@ static int rsrc_card_parse_of(struct device_node *node, struct rsrc_card_priv *priv, struct device *dev) { - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev); struct rsrc_card_dai *props; struct snd_soc_dai_link *links; int ret; -- cgit v1.2.3 From c706f2e55f04d25d212e34186521a38e8a6e459e Mon Sep 17 00:00:00 2001 From: Songjun Wu Date: Wed, 2 Mar 2016 12:15:25 +0100 Subject: ASoC: atmel_ssc_dai: distinguish the different SSC Cpu_dai id always equals 0, can't distinguish the different SSC. Use platform_device id to record and distinguish the different SSC. Signed-off-by: Songjun Wu Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/misc/atmel-ssc.c | 1 + sound/soc/atmel/atmel_ssc_dai.c | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index e11a0bd6c66e..0516ecda54d3 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -34,6 +34,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num) if (ssc->pdev->dev.of_node) { if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc") == ssc_num) { + ssc->pdev->id = ssc_num; ssc_valid = 1; break; } diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index ba8def5665c4..276897033639 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -285,7 +285,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; int ret; @@ -346,7 +347,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; @@ -392,7 +394,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; ssc_p->daifmt = fmt; return 0; @@ -404,7 +407,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { - struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; switch (div_id) { case ATMEL_SSC_CMR_DIV: @@ -445,7 +449,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - int id = dai->id; + struct platform_device *pdev = to_platform_device(dai->dev); + int id = pdev->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; @@ -772,7 +777,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, static int atmel_ssc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; @@ -795,7 +801,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, static int atmel_ssc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; @@ -824,11 +831,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream, static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); if (!cpu_dai->active) return 0; - ssc_p = &ssc_info[cpu_dai->id]; + ssc_p = &ssc_info[pdev->id]; /* Save the status register before disabling transmit and receive */ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); @@ -852,12 +860,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); u32 cr; if (!cpu_dai->active) return 0; - ssc_p = &ssc_info[cpu_dai->id]; + ssc_p = &ssc_info[pdev->id]; /* restore SSC register settings */ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); -- cgit v1.2.3 From 03367bf7fd30165bda3b037b3078cfa231ba33b7 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 2 Mar 2016 17:10:30 +0530 Subject: ALSA: portman2x4: fix NULL pointer dereference While registering pardev, the irq_func was also registered. As a result when we tried to probe for the card, an interrupt was generated and in the ISR we tried to dereference private_data. But private_data is still NULL as we have not yet done portman_create(). Lets probe for the device after card is created. Fixes: e6a1b7e88046 ("ALSA: portman2x4 - use new parport device model") Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai --- sound/drivers/portman2x4.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 362533fceec7..189e3e7028af 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -762,11 +762,6 @@ static int snd_portman_probe(struct platform_device *pdev) err = -EIO; goto free_pardev; } - err = portman_probe(p); - if (err) { - err = -EIO; - goto release_pardev; - } if ((err = portman_create(card, pardev, &pm)) < 0) { snd_printd("Cannot create main component\n"); @@ -774,6 +769,12 @@ static int snd_portman_probe(struct platform_device *pdev) } card->private_data = pm; card->private_free = snd_portman_card_private_free; + + err = portman_probe(p); + if (err) { + err = -EIO; + goto __err; + } if ((err = snd_portman_rawmidi_create(card)) < 0) { snd_printd("Creating Rawmidi component failed\n"); -- cgit v1.2.3 From 35383a24129a47a0b8365ed17d3dcd2f75175929 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 2 Mar 2016 09:54:57 -0800 Subject: ALSA: compress: allow writes in SNDRV_PCM_STATE_PREPARED state Allow writes in SNDRV_PCM_STATE_PREPARED state so that more than one buffer fragment can be written from user space before calling SNDRV_COMPRESS_START. Signed-off-by: Eric Laurent Acked-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 7fac3cae8abd..9e7c2f0831d7 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -286,6 +286,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, mutex_lock(&stream->device->lock); /* write is allowed when stream is running or has been steup */ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && + stream->runtime->state != SNDRV_PCM_STATE_PREPARED && stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { mutex_unlock(&stream->device->lock); return -EBADFD; -- cgit v1.2.3 From 862bca5d16c631d0a5d39a6f24857a5148970afb Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 4 Mar 2016 20:25:29 +0530 Subject: ALSA: compress: Add SNDRV_PCM_STATE_PREPARED state explanation Stream states were explained in the code comments but SNDRV_PCM_STATE_PREPARED was missed so add it Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 9e7c2f0831d7..a5b213e4cd7b 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -74,6 +74,9 @@ struct snd_compr_file { * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. + * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for + * playback only). User after setting up stream writes the data buffer + * before starting the stream. * SNDRV_PCM_STATE_RUNNING: When stream has been started and is * decoding/encoding and rendering/capturing data. * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done -- cgit v1.2.3 From 41eb94fd26f6c150b0460e01ecc84d643355f9b6 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 4 Mar 2016 20:25:30 +0530 Subject: ALSA: compress: fix some typos Found few typos while looking at code, so fix them Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index a5b213e4cd7b..94de9302193f 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -69,10 +69,10 @@ struct snd_compr_file { /* * a note on stream states used: - * we use follwing states in the compressed core + * we use following states in the compressed core * SNDRV_PCM_STATE_OPEN: When stream has been opened. * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by - * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this + * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for * playback only). User after setting up stream writes the data buffer -- cgit v1.2.3 From cdb1ee3f6c55b028cebc6af0280851bf89dbce2d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 4 Mar 2016 23:59:29 +0530 Subject: ALSA: compress: fix more typos More inspection of code revealed few more typos so fix them as well Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 94de9302193f..a9933c07a6bf 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -704,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) /* * We are called with lock held. So drop the lock while we wait for - * drain complete notfication from the driver + * drain complete notification from the driver * * It is expected that driver will notify the drain completion and then * stream will be moved to SETUP state, even if draining resulted in an @@ -759,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) return -EPERM; - /* you can signal next track isf this is intended to be a gapless stream + /* you can signal next track if this is intended to be a gapless stream * and current track metadata is set */ if (stream->metadata_set == false) -- cgit v1.2.3 From 288bc356a881fcdce05f9782736a6a3d9db359a9 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 4 Mar 2016 14:54:50 +0800 Subject: ASoC: rt5616: allow to build with CONFIG_SND_SOC_RT5616 This patch fixes that hasn't built the rt5616 driver with 'CONFIG_SND_SOC_RT5616=y' in .config. The tristate is the prompt on the 'make menuconfig', in other words, that can't show the prompt and select it if we don't say what's the tristate. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..1de259860bc9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -596,7 +596,7 @@ config SND_SOC_RT298 depends on I2C config SND_SOC_RT5616 - tristate + tristate "Realtek RT5616 CODEC" config SND_SOC_RT5631 tristate "Realtek ALC5631/RT5631 CODEC" -- cgit v1.2.3 From d8fc2198aab117a4bc16ee305caef19c4c7e7f5c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2016 16:59:06 +0100 Subject: ASoC: rockchip: use __maybe_unused to hide st_irq_syscfg_resume The rockchip spdif driver uses SIMPLE_DEV_PM_OPS to conditionally set its power management functions, but we get a warning about rk_spdif_runtime_resume being unused when CONFIG_PM is not set: sound/soc/rockchip/rockchip_spdif.c:67:12: error: 'rk_spdif_runtime_resume' defined but not used [-Werror=unused-function] This adds a __maybe_unused annotation so the compiler knows it can silently drop it instead of warning. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5a806da89f42..274599d4caeb 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -54,7 +54,7 @@ static const struct of_device_id rk_spdif_match[] = { }; MODULE_DEVICE_TABLE(of, rk_spdif_match); -static int rk_spdif_runtime_suspend(struct device *dev) +static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); @@ -64,7 +64,7 @@ static int rk_spdif_runtime_suspend(struct device *dev) return 0; } -static int rk_spdif_runtime_resume(struct device *dev) +static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); int ret; -- cgit v1.2.3 From e7a508f2b137092500fbad09d9713d3a1c5d4826 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 4 Mar 2016 16:27:14 +0530 Subject: ASoC: pcm3168a: remove unused variable While building we were getting build warning about: sound/soc/codecs/pcm3168a.c:403:21: warning: variable 'channels' set but not used The variable channels were being assigned some value but that was never reused. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 0c7248ab6a37..992a77edcd5d 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -400,13 +400,12 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); bool tx, master_mode; u32 val, mask, shift, reg; - unsigned int rate, channels, fmt, ratio, max_ratio; + unsigned int rate, fmt, ratio, max_ratio; int i, min_frame_size; snd_pcm_format_t format; rate = params_rate(params); format = params_format(params); - channels = params_channels(params); ratio = pcm3168a->sysclk / rate; -- cgit v1.2.3 From ab8773943ab3126d39eb41531c8e06aea2107aa1 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 4 Mar 2016 18:31:54 +0800 Subject: ASoC: rockchip: add bindings for spdif controller this patch add compatible for rk3366/rk3368/rk3399 spdif, these three spdifs share the same type. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rockchip-spdif.txt | 8 ++++++-- sound/soc/rockchip/rockchip_spdif.c | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt index e64dbdea7db9..11046429a118 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt @@ -7,8 +7,12 @@ a fibre cable. Required properties: - compatible: should be one of the following: - - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or - "rockchip,rk3066-spdif" + - "rockchip,rk3066-spdif" + - "rockchip,rk3188-spdif" + - "rockchip,rk3288-spdif" + - "rockchip,rk3366-spdif" + - "rockchip,rk3368-spdif" + - "rockchip,rk3399-spdif" - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the SPDIF interrupt. diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 274599d4caeb..100781e37848 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -28,6 +28,7 @@ enum rk_spdif_type { RK_SPDIF_RK3066, RK_SPDIF_RK3188, RK_SPDIF_RK3288, + RK_SPDIF_RK3366, }; #define RK3288_GRF_SOC_CON2 0x24c @@ -45,11 +46,17 @@ struct rk_spdif_dev { static const struct of_device_id rk_spdif_match[] = { { .compatible = "rockchip,rk3066-spdif", - .data = (void *) RK_SPDIF_RK3066 }, + .data = (void *)RK_SPDIF_RK3066 }, { .compatible = "rockchip,rk3188-spdif", - .data = (void *) RK_SPDIF_RK3188 }, + .data = (void *)RK_SPDIF_RK3188 }, { .compatible = "rockchip,rk3288-spdif", - .data = (void *) RK_SPDIF_RK3288 }, + .data = (void *)RK_SPDIF_RK3288 }, + { .compatible = "rockchip,rk3366-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3368-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3399-spdif", + .data = (void *)RK_SPDIF_RK3366 }, {}, }; MODULE_DEVICE_TABLE(of, rk_spdif_match); -- cgit v1.2.3 From cab473850226c3e41823453b5b80eb294dae6e0c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 3 Mar 2016 21:36:34 -0600 Subject: ASoC: Intel: common: filter ACPI devices with _STA return value BIOS vendors typically list multiple audio codecs in the DSDT table and enable the relevant one by changing the return value of the _STA method. With the current code, all devices are reported by acpi_dev_present(), regardless of the _STA return values. This causes errors on probe with the wrong machine driver being loaded. This patch essentially reverts 'commit 6f08cbdaac5a ("ASoC: Intel: Use acpi_dev_present()")' and adds code to force the evaluation of the _STA method. A better solution might be to make sure the ACPI subsystem only reports devices with a _STA value of 0xf but apparently it's problematic so dealing with this in the audio subsystem directly. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-match-acpi.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index c429e2226d40..cf2c27cd8651 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -16,14 +16,30 @@ #include "sst-acpi.h" +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + unsigned long long sta; + acpi_status status; + + *(bool *)context = true; + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + *(bool *)context = false; + + return AE_OK; +} + struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) { struct sst_acpi_mach *mach; + bool found = false; for (mach = machines; mach->id[0]; mach++) - if (acpi_dev_present(mach->id)) + if (ACPI_SUCCESS(acpi_get_devices(mach->id, + sst_acpi_mach_match, + &found, NULL)) && found) return mach; - return NULL; } EXPORT_SYMBOL_GPL(sst_acpi_find_machine); -- cgit v1.2.3 From 1fdb7c14a24689e9c7ec51aa16a3970a7ec0fdde Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 3 Mar 2016 21:36:36 -0600 Subject: ASoC: Intel: common: add translation from HID to codec-name BIOS vendors sometimes declare multiple devices in the DSDT table that all refer to the same HID. This is not very smart but not illegal as long as only one device reports present with the _STA method. The ACPI subsystem tracks each device with an extension, e.g. 10EC5640:00 and 10EC5640:01 In the ASoC machine driver, the DAI codec name needs to refer to the ACPI device that reported present, e.g. "i2c-10EC5640:01". The extension will vary depending on how the BIOS is written and which ACPI device is activated. This patch adds a translation function that provides the codec name from the ACPI HID to avoid any hard-coded values in the machine driver. Suggested-by: Mika Westerberg Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-acpi.h | 3 +++ sound/soc/intel/common/sst-match-acpi.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 3ee3b7ab5d03..4dcfb7e5ed70 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -14,6 +14,9 @@ #include +/* translation fron HID to I2C name, needed for DAI codec_name */ +const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); + /* acpi match */ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index cf2c27cd8651..2c0e7fca97db 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -16,6 +16,39 @@ #include "sst-acpi.h" +static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, + void *context, void **ret) +{ + struct acpi_device *adev; + const char *name = NULL; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (adev->status.present && adev->status.functional) { + name = acpi_dev_name(adev); + *(const char **)ret = name; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + const char *name = NULL; + acpi_status status; + + status = acpi_get_devices(hid, sst_acpi_find_name, NULL, + (void **)&name); + + if (ACPI_FAILURE(status) || name[0] == '\0') + return NULL; + + return name; +} +EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid); + static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, void *context, void **ret) { -- cgit v1.2.3 From ab738e4e3040b0985ab45780a688705f334e609b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 3 Mar 2016 21:36:37 -0600 Subject: ASoC: Intel: bytcr_rt5640: change quirk position No added functionality Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 9a1752df45a9..a06cd1f99190 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -32,6 +32,18 @@ #include "../atom/sst-atom-controls.h" #include "../common/sst-acpi.h" +enum { + BYT_RT5640_DMIC1_MAP, + BYT_RT5640_DMIC2_MAP, + BYT_RT5640_IN1_MAP, +}; + +#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_DMIC_EN BIT(16) + +static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN; + static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -70,18 +82,6 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { {"IN1P", NULL, "Internal Mic"}, }; -enum { - BYT_RT5640_DMIC1_MAP, - BYT_RT5640_DMIC2_MAP, - BYT_RT5640_IN1_MAP, -}; - -#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) -#define BYT_RT5640_DMIC_EN BIT(16) - -static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN; - static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -174,7 +174,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return ret; } - dmi_check_system(byt_rt5640_quirk_table); switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { case BYT_RT5640_IN1_MAP: custom_map = byt_rt5640_intmic_in1_map; @@ -351,6 +350,9 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) "%s%s%s", "i2c-", mach->id, ":00"); byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name; + /* check quirks before creating card */ + dmi_check_system(byt_rt5640_quirk_table); + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); if (ret_val) { -- cgit v1.2.3 From a232b96dcece4761793d39b8fb2c9d050df30357 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 3 Mar 2016 21:36:38 -0600 Subject: ASoC: Intel: bytcr_rt5640: use HID translation util Remove hard-coded generation of codec name, use translation routine to avoid issues with codec name not matching what the ACPI subsystem registered Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a06cd1f99190..032a2e753f0b 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -340,15 +340,31 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { int ret_val = 0; struct sst_acpi_mach *mach; + const char *i2c_name = NULL; + int i; + int dai_index; /* register the soc card */ byt_rt5640_card.dev = &pdev->dev; mach = byt_rt5640_card.dev->platform_data; + /* fix index of codec dai */ + dai_index = MERR_DPCM_COMPR + 1; + for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) { + if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) { + dai_index = i; + break; + } + } + /* fixup codec name based on HID */ - snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), - "%s%s%s", "i2c-", mach->id, ":00"); - byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name; + i2c_name = sst_acpi_find_name_from_hid(mach->id); + if (i2c_name != NULL) { + snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), + "%s%s", "i2c-", i2c_name); + + byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name; + } /* check quirks before creating card */ dmi_check_system(byt_rt5640_quirk_table); -- cgit v1.2.3 From fdf841937e4fa08e767dbe83f1c65696cfec67c9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 3 Mar 2016 21:36:39 -0600 Subject: ASoC: Intel: Atom: add support for CHT w/ RT5640 Some CHT-T platforms make use of the Realtek RT5640 codec. Make use of the machine driver developed for Baytrail. Tested on Tronsmart Ara X5. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_acpi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 4fce03fc1870..3bc4b63b2f9d 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -342,6 +342,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = { &chv_platform_data }, {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, + /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ + {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL, + &chv_platform_data }, + {}, }; -- cgit v1.2.3 From d7289565483c65094d0473555625a4acd89567d3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:51:12 +0000 Subject: ASoC: rsnd: don't call update callback if it was NULL Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ba37b0d55b21..3a3dc2ff18c9 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -927,7 +927,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } } - if (change) + if (change && cfg->update) cfg->update(cfg->io, mod); return change; -- cgit v1.2.3 From f90432fc6ec41c5bb1d7b75edc1f2bc58799ece3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:51:44 +0000 Subject: ASoC: rsrc-card: add convert channels support Renesas sound device has CTU (= Channel Transfer Unit), and sound card needs its support. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../bindings/sound/renesas,rsrc-card.txt | 1 + sound/soc/sh/rcar/rsrc-card.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt index 2b2caa281ce3..5abebf7898d9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -30,6 +30,7 @@ Optional subnode properties: - frame-inversion : bool property. Add this if the dai-link uses frame clock inversion. - convert-rate : platform specified sampling rate convert +- convert-channels : platform specified channel size convert - audio-prefix : see audio-routing - audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 545e94245e62..1bc7ecfc42a9 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -66,6 +66,7 @@ struct rsrc_card_priv { struct snd_soc_dai_link *dai_link; int dai_num; u32 convert_rate; + u32 convert_channels; }; #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) @@ -144,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); - if (!priv->convert_rate) - return 0; + if (priv->convert_rate) + rate->min = + rate->max = priv->convert_rate; - rate->min = rate->max = priv->convert_rate; + if (priv->convert_channels) + channels->min = + channels->max = priv->convert_channels; return 0; } @@ -436,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node, /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); - dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", - priv->snd_card.name ? priv->snd_card.name : "", - priv->convert_rate); + /* channels transfer */ + of_property_read_u32(node, "convert-channels", &priv->convert_channels); + + dev_dbg(dev, "New rsrc-audio-card: %s\n", + priv->snd_card.name ? priv->snd_card.name : ""); + dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate); + dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels); ret = rsrc_card_dai_link_of(node, priv); if (ret < 0) -- cgit v1.2.3 From 135bb7d5c7b3fa40cd0559b5500a50d4e3663f6e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:52:13 +0000 Subject: ASoC: rsnd: tidyup rsnd_ssiu_init_gen2() remove unnecessary variable Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssiu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 1b8ea0e09bc2..0d964a0a3e31 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -115,13 +115,12 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, } if (rsnd_ssi_use_busif(io)) { - u32 val = rsnd_get_dalign(mod, io); - rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | rsnd_get_adinr_chan(mod, io)); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); - rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN, + rsnd_get_dalign(mod, io)); } return 0; -- cgit v1.2.3 From 8a3a2211e97395694827f12552bbad7f2caf11ef Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:53:00 +0000 Subject: ASoC: rsnd: add CTU basic support This patch adds Renesas R-Car sound CTU (= Channel Transfer Unit) very basic support, but not yet enough feature at this point. Because CTU support needs more complex channel function for each modules. To avoid complex patch reviewing, this patch picked up very basic part only. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 1 + 2 files changed, 50 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index a10d0f7b73fa..784515afefaf 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -14,6 +14,7 @@ struct rsnd_ctu { struct rsnd_mod mod; + int channels; }; #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) @@ -23,6 +24,9 @@ struct rsnd_ctu { ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ i++) +#define rsnd_mod_to_ctu(_mod) \ + container_of((_mod), struct rsnd_ctu, mod) + #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) static void rsnd_ctu_activation(struct rsnd_mod *mod) @@ -37,6 +41,13 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } +int rsnd_ctu_converted_channel(struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + + return ctu->channels; +} + static int rsnd_ctu_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -116,11 +127,49 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, return 0; } +static int rsnd_ctu_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_params) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + struct snd_soc_pcm_runtime *fe = substream->private_data; + + /* + * CTU assumes that it is used under DPCM if user want to use + * channel transfer. Then, CTU should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + ctu->channels = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + int stream = substream->stream; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + be_params = &dpcm->hw_params; + if (params_channels(fe_params) != params_channels(be_params)) + ctu->channels = params_channels(be_params); + } + + dev_dbg(dev, "CTU convert channels %d\n", ctu->channels); + } + + return 0; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, + .hw_params = rsnd_ctu_hw_params, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 305cc086a0bc..bbc89bd1e918 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -638,6 +638,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") -- cgit v1.2.3 From eed76bb811cd143119b4bdeca88606685222e687 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:54:58 +0000 Subject: ASoC: rsnd: add rsnd_runtime_channel_xxx() Current SSI is supporting Normal SSI/Multi mode SSI/TDM mode SSI and its behavior is based on input channels. This input channel might be converted by CTU, and SSI needs to be Multi SSI mode / TDM SSI mode if 6ch input EX) 6ch input, CTU for 2ch, playback 6ch 6ch 2ch 2ch 2ch 2ch -> SRC -> CTU -> MIX -> DVC -> SSIU -> SSI EX) 6ch input, no CTU, Multi SSI, playback 6ch 6ch 6ch 6ch 6ch 2ch -> SRC -> CTU -> MIX -> DVC -> SSIU -> SSI0/SSI1/SSI2 Current driver is using rsnd_get_adinr_chan() / rsnd_get_slot_width() for this purpose, but it is complicated enough without meaning. This patch adds new rsnd_runtime_channel_xxx() which is caring CTU/Multi SSI. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 69 +++++++++++++++++++++++++++++------------------- sound/soc/sh/rcar/ctu.c | 2 +- sound/soc/sh/rcar/dvc.c | 2 +- sound/soc/sh/rcar/mix.c | 2 +- sound/soc/sh/rcar/rsnd.h | 8 ++++-- sound/soc/sh/rcar/src.c | 2 +- sound/soc/sh/rcar/ssi.c | 15 +++++------ sound/soc/sh/rcar/ssiu.c | 6 +++-- 8 files changed, 62 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 3a3dc2ff18c9..3351a701c60e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -224,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io) return rdai->slots_num; } -int rsnd_get_slot_width(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int chan = runtime->channels; - /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves_runtime(io)) + return runtime->channels; +} + +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) +{ + int chan = rsnd_runtime_channel_original(io); + struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); + + if (ctu_mod) { + u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + + if (converted_chan) + return converted_chan; + } + + return chan; +} + +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) +{ + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + /* Use Multi SSI */ + if (rsnd_runtime_is_ssi_multi(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ @@ -240,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) return chan; } +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +{ + int slots = rsnd_get_slot_num(io); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + return (chan >= 6) && (slots > 1); +} + +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +{ + return rsnd_runtime_channel_for_ssi(io) >= 6; +} + /* * ADINR function */ @@ -261,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return 0; } -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 chan = runtime->channels; - - switch (chan) { - case 1: - case 2: - case 4: - case 6: - case 8: - break; - default: - dev_warn(dev, "not supported channel\n"); - chan = 0; - break; - } - - return chan; -} - /* * DALIGN function */ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 784515afefaf..b326966ea407 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -60,7 +60,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, { rsnd_mod_write(mod, CTU_CTUIR, 1); - rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); rsnd_mod_write(mod, CTU_CPMDR, 0); rsnd_mod_write(mod, CTU_SCMDR, 0); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d757f1316385..93b11e1c5d7f 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -116,7 +116,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, u32 vrdbr = 0; adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_after_ctu(io); /* Enable Digital Volume, Zero Cross Mute Mode */ dvucr |= 0x101; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index e0e337ad4206..195fc7bb22af 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ - rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ rsnd_mod_write(mod, MIX_MIXMR, 0); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index bbc89bd1e918..ff53f96e5006 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -193,7 +193,6 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* @@ -356,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, void rsnd_set_slot(struct rsnd_dai *rdai, int slots, int slots_total); int rsnd_get_slot(struct rsnd_dai_stream *io); -int rsnd_get_slot_width(struct rsnd_dai_stream *io); int rsnd_get_slot_num(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); + /* * R-Car sound DAI */ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 03c6314871ff..8e1177aea6b1 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -205,7 +205,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, * SRC_ADINR */ adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_original(io); /* * SRC_IFSCR / SRC_IFSVR diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 0979db8af8f9..540489755367 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -180,11 +180,8 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 mask = rsnd_ssi_multi_slaves(io); - - if (mask && (runtime->channels >= 6)) - return mask; + if (rsnd_runtime_is_ssi_multi(io)) + return rsnd_ssi_multi_slaves(io); return 0; } @@ -198,7 +195,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); - int slots = rsnd_get_slot_width(io); + int chan = rsnd_runtime_channel_for_ssi(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, @@ -231,10 +228,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, /* * this driver is assuming that - * system word is 32bit x slots + * system word is 32bit x chan * see rsnd_ssi_init() */ - main_rate = rate * 32 * slots * ssi_clk_mul_table[j]; + main_rate = rate * 32 * chan * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { @@ -289,7 +286,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, u32 wsr; int is_tdm; - is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; + is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 0d964a0a3e31..6f9b388ec5a8 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - if (rsnd_get_slot_width(io) >= 6) { + if (rsnd_runtime_is_ssi_tdm(io)) { /* * TDM Extend Mode * see @@ -117,7 +117,9 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (rsnd_ssi_use_busif(io)) { rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io)); + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(mod, SSI_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); -- cgit v1.2.3 From ec2ac01afe001360b911e27c915579fd003339e8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:55:23 +0000 Subject: ASoC: rsnd: add CTU support This patch adds CTU (= Channel Transfer Unit) support on Renesas R-Car sound driver. It can Down/Up mixing and splitter. You need to check R-Car datasheet especially CTUn_CPMDR/CTUn_SV0xR/CTUn_SV1xR/CTUn_SV2xR/CTUn_SV3xR for setting parameter. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 234 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 197 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index b326966ea407..9dcc1f9db026 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -12,8 +12,74 @@ #define CTU_NAME_SIZE 16 #define CTU_NAME "ctu" +/* + * User needs to setup CTU by amixer, and its settings are + * based on below registers + * + * CTUn_CPMDR : amixser set "CTU Pass" + * CTUn_SV0xR : amixser set "CTU SV0" + * CTUn_SV1xR : amixser set "CTU SV1" + * CTUn_SV2xR : amixser set "CTU SV2" + * CTUn_SV3xR : amixser set "CTU SV3" + * + * [CTU Pass] + * 0000: default + * 0001: Connect input data of channel 0 + * 0010: Connect input data of channel 1 + * 0011: Connect input data of channel 2 + * 0100: Connect input data of channel 3 + * 0101: Connect input data of channel 4 + * 0110: Connect input data of channel 5 + * 0111: Connect input data of channel 6 + * 1000: Connect input data of channel 7 + * 1001: Connect calculated data by scale values of matrix row 0 + * 1010: Connect calculated data by scale values of matrix row 1 + * 1011: Connect calculated data by scale values of matrix row 2 + * 1100: Connect calculated data by scale values of matrix row 3 + * + * [CTU SVx] + * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] + * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] + * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] + * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] + * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * + * [SVxx] + * Plus Minus + * value time dB value time dB + * ----------------------------------------------------------------------- + * H'7F_FFFF 2 6 H'80_0000 2 6 + * ... + * H'40_0000 1 0 H'C0_0000 1 0 + * ... + * H'00_0001 2.38 x 10^-7 -132 + * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 + * + * + * Ex) Input ch -> Output ch + * 1ch -> 0ch + * 0ch -> 1ch + * + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 9,10 + * amixer set "CTU SV0" 0,4194304 + * amixer set "CTU SV1" 4194304,0 + * or + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 2,1 + */ + struct rsnd_ctu { struct rsnd_mod mod; + struct rsnd_kctrl_cfg_m pass; + struct rsnd_kctrl_cfg_m sv0; + struct rsnd_kctrl_cfg_m sv1; + struct rsnd_kctrl_cfg_m sv2; + struct rsnd_kctrl_cfg_m sv3; + struct rsnd_kctrl_cfg_s reset; int channels; }; @@ -58,51 +124,91 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + u32 cpmdr = 0; + u32 scmdr = 0; + int i; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + u32 val = ctu->pass.val[i]; + + cpmdr |= val << (28 - (i * 4)); + + if ((val > 0x8) && (scmdr < (val - 0x8))) + scmdr = val - 0x8; + } + rsnd_mod_write(mod, CTU_CTUIR, 1); rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); - rsnd_mod_write(mod, CTU_CPMDR, 0); - rsnd_mod_write(mod, CTU_SCMDR, 0); - rsnd_mod_write(mod, CTU_SV00R, 0); - rsnd_mod_write(mod, CTU_SV01R, 0); - rsnd_mod_write(mod, CTU_SV02R, 0); - rsnd_mod_write(mod, CTU_SV03R, 0); - rsnd_mod_write(mod, CTU_SV04R, 0); - rsnd_mod_write(mod, CTU_SV05R, 0); - rsnd_mod_write(mod, CTU_SV06R, 0); - rsnd_mod_write(mod, CTU_SV07R, 0); - - rsnd_mod_write(mod, CTU_SV10R, 0); - rsnd_mod_write(mod, CTU_SV11R, 0); - rsnd_mod_write(mod, CTU_SV12R, 0); - rsnd_mod_write(mod, CTU_SV13R, 0); - rsnd_mod_write(mod, CTU_SV14R, 0); - rsnd_mod_write(mod, CTU_SV15R, 0); - rsnd_mod_write(mod, CTU_SV16R, 0); - rsnd_mod_write(mod, CTU_SV17R, 0); - - rsnd_mod_write(mod, CTU_SV20R, 0); - rsnd_mod_write(mod, CTU_SV21R, 0); - rsnd_mod_write(mod, CTU_SV22R, 0); - rsnd_mod_write(mod, CTU_SV23R, 0); - rsnd_mod_write(mod, CTU_SV24R, 0); - rsnd_mod_write(mod, CTU_SV25R, 0); - rsnd_mod_write(mod, CTU_SV26R, 0); - rsnd_mod_write(mod, CTU_SV27R, 0); - - rsnd_mod_write(mod, CTU_SV30R, 0); - rsnd_mod_write(mod, CTU_SV31R, 0); - rsnd_mod_write(mod, CTU_SV32R, 0); - rsnd_mod_write(mod, CTU_SV33R, 0); - rsnd_mod_write(mod, CTU_SV34R, 0); - rsnd_mod_write(mod, CTU_SV35R, 0); - rsnd_mod_write(mod, CTU_SV36R, 0); - rsnd_mod_write(mod, CTU_SV37R, 0); + rsnd_mod_write(mod, CTU_CPMDR, cpmdr); + + rsnd_mod_write(mod, CTU_SCMDR, scmdr); + + if (scmdr > 0) { + rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); + rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); + rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); + rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); + rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); + rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); + rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); + rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); + } + if (scmdr > 1) { + rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); + rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); + rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); + rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); + rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); + rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); + rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); + rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); + } + if (scmdr > 2) { + rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); + rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); + rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); + rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); + rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); + rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); + rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); + rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); + } + if (scmdr > 3) { + rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); + rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); + rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); + rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); + rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); + rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); + rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); + rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); + } rsnd_mod_write(mod, CTU_CTUIR, 0); } +static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int i; + + if (!ctu->reset.val) + return; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + ctu->pass.val[i] = 0; + ctu->sv0.val[i] = 0; + ctu->sv1.val[i] = 0; + ctu->sv2.val[i] = 0; + ctu->sv3.val[i] = 0; + } + ctu->reset.val = 0; +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -164,12 +270,66 @@ static int rsnd_ctu_hw_params(struct rsnd_mod *mod, return 0; } +static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int ret; + + /* CTU Pass */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + NULL, + &ctu->pass, RSND_MAX_CHANNELS, + 0xC); + + /* ROW0 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + NULL, + &ctu->sv0, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW1 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + NULL, + &ctu->sv1, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW2 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + NULL, + &ctu->sv2, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW3 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + NULL, + &ctu->sv3, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* Reset */ + ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_ctu_value_reset, + &ctu->reset, 1); + + return ret; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, .hw_params = rsnd_ctu_hw_params, + .pcm_new = rsnd_ctu_pcm_new, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) -- cgit v1.2.3 From 05f5afd3f0d915537b67d3cfcb464e6836a65caf Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 2 Mar 2016 15:49:06 +0100 Subject: ASoC: mediatek: address dai link array entries by enum This should be more robust to future changes than adressing array entries by index number. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 50ba538eccb3..5c4c58c69c51 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -131,10 +131,17 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, }; +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, + DAI_LINK_INTERCODEC +}; + /* Digital audio interface glue - connects codec <---> CPU */ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { /* Front End DAI links */ - { + [DAI_LINK_PLAYBACK] = { .name = "rt5650_rt5676 Playback", .stream_name = "rt5650_rt5676 Playback", .cpu_dai_name = "DL1", @@ -144,7 +151,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_playback = 1, }, - { + [DAI_LINK_CAPTURE] = { .name = "rt5650_rt5676 Capture", .stream_name = "rt5650_rt5676 Capture", .cpu_dai_name = "VUL", @@ -156,7 +163,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { }, /* Back End DAI links */ - { + [DAI_LINK_CODEC_I2S] = { .name = "Codec", .cpu_dai_name = "I2S", .no_pcm = 1, @@ -170,7 +177,8 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, - { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec", .stream_name = "rt5650_rt5676 intercodec", .cpu_dai_name = "snd-soc-dummy-dai", @@ -240,7 +248,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_codec_conf[0].of_node = mt8173_rt5650_rt5676_codecs[1].of_node; - mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node; card->dev = &pdev->dev; -- cgit v1.2.3 From 4f2bf0ace0f96cc693002e1bbde967fa2356bc43 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Fri, 5 Feb 2016 16:32:14 +0100 Subject: ASoC: ads117x: Add device tree compatible string This patch adds the necessary device tree compatible string to allow DT probing. Signed-off-by: Florian Vaussard Signed-off-by: Mark Brown --- sound/soc/codecs/ads117x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index 1222282e93c3..c5be1bdc2c9a 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -20,6 +20,8 @@ #include #include +#include + #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) @@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev) return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id ads117x_dt_ids[] = { + { .compatible = "ti,ads1174" }, + { .compatible = "ti,ads1178" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads117x_dt_ids); +#endif + static struct platform_driver ads117x_codec_driver = { .driver = { .name = "ads117x-codec", + .of_match_table = of_match_ptr(ads117x_dt_ids), }, .probe = ads117x_probe, -- cgit v1.2.3 From e066ea2214f77e13e24344313243f9b3a182d3c4 Mon Sep 17 00:00:00 2001 From: Rajan Vaja Date: Thu, 11 Feb 2016 11:23:35 +0530 Subject: ASoC: dapm: Remove unneeded variable ret returning 0 This patch fixes coccicheck report: Unneeded variable: "ret". Return "0" on line 2817 Remove unneccesary variable ret created to return zero. Signed-off-by: Rajan Vaja Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5a2812fa8946..8dc395f15049 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2805,7 +2805,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { - int i, ret = 0; + int i; mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { @@ -2814,7 +2814,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, } mutex_unlock(&dapm->card->dapm_mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); -- cgit v1.2.3 From d7fcd13663fe43ad938fec1acd46ff196dddf914 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 7 Mar 2016 12:12:11 +0800 Subject: ASoC: rt5616: add missing mute control for HPVOL Add missing kcontrol for HPVOL mute control. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 3704d0716c04..f527b5b2817b 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -309,6 +309,8 @@ static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL, + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ -- cgit v1.2.3 From c467fc0e010b66069c0d5bb3e8e869adf267115f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 7 Mar 2016 15:09:37 +0800 Subject: ASoC: rt5640: Set PLL src according to source rt5640_set_dai_pll set pll source according to given source and dai id. However, the pll source should be set according to given source only. Signed-off-by: Jack Yu Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 6cd84fb2196a..863c190c5076 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1902,21 +1902,12 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK); break; case RT5640_PLL1_S_BCLK1: + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1); + break; case RT5640_PLL1_S_BCLK2: - dai_sel = get_sdp_info(codec, dai->id); - if (dai_sel < 0) { - dev_err(codec->dev, - "Failed to get sdp info: %d\n", dai_sel); - return -EINVAL; - } - if (dai_sel & RT5640_U_IF1) { - snd_soc_update_bits(codec, RT5640_GLB_CLK, - RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1); - } - if (dai_sel & RT5640_U_IF2) { - snd_soc_update_bits(codec, RT5640_GLB_CLK, - RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2); - } + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2); break; default: dev_err(codec->dev, "Unknown PLL source %d\n", source); -- cgit v1.2.3 From 57586fb76471c5fc521b2e9c3a4eae259b06d479 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 7 Mar 2016 15:09:38 +0800 Subject: ASoC: rt5640: add supplys for dac power The DAC1/2 power is for both DACs and related mixer/mux. Add SUPPLY type widgets to support it. Signed-off-by: Jack Yu Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 863c190c5076..af9b5f18f0e0 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1217,11 +1217,14 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0, rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)), /* DACs */ - SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1, - RT5640_PWR_DAC_L1_BIT, 0), - SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1, - RT5640_PWR_DAC_R1_BIT, 0), - + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_R1_BIT, 0, NULL, 0), /* SPK/OUT Mixer */ SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT, 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)), @@ -1298,9 +1301,9 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)), - SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT, + SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT, + SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT, @@ -1317,6 +1320,10 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = { rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)), SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1, RT5640_PWR_MA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_R2_BIT, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("MONOP"), SND_SOC_DAPM_OUTPUT("MONON"), @@ -1328,11 +1335,6 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)), - SND_SOC_DAPM_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1, - RT5640_PWR_DAC_L2_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1, - RT5640_PWR_DAC_R2_BIT, 0, NULL, 0), - SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT, 0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)), SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT, @@ -1493,8 +1495,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"}, {"DAC MIXL", "INF1 Switch", "IF1 DAC L"}, + {"DAC MIXL", NULL, "DAC L1 Power"}, {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"}, {"DAC MIXR", "INF1 Switch", "IF1 DAC R"}, + {"DAC MIXR", NULL, "DAC R1 Power"}, {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"}, {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"}, @@ -1507,8 +1511,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"DAC L1", NULL, "Stereo DAC MIXL"}, {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll}, + {"DAC L1", NULL, "DAC L1 Power"}, {"DAC R1", NULL, "Stereo DAC MIXR"}, {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll}, + {"DAC R1", NULL, "DAC R1 Power"}, {"SPK MIXL", "REC MIXL Switch", "RECMIXL"}, {"SPK MIXL", "INL Switch", "INL VOL"}, @@ -1595,8 +1601,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { {"DAC L2 Mux", "IF2", "IF2 DAC L"}, {"DAC L2 Mux", "Base L/R", "Audio DSP"}, - + {"DAC L2 Mux", NULL, "DAC L2 Power"}, {"DAC R2 Mux", "IF2", "IF2 DAC R"}, + {"DAC R2 Mux", NULL, "DAC R2 Power"}, {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, {"Stereo DAC MIXL", "ANC Switch", "ANC"}, @@ -1614,8 +1621,10 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { {"DAC L2", NULL, "Mono DAC MIXL"}, {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll}, + {"DAC L2", NULL, "DAC L2 Power"}, {"DAC R2", NULL, "Mono DAC MIXR"}, {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll}, + {"DAC R2", NULL, "DAC R2 Power"}, {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, {"SPK MIXR", "DAC R2 Switch", "DAC R2"}, @@ -1656,8 +1665,8 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = { {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"}, {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"}, - {"IF2 DAC L", NULL, "DAC L2 Filter"}, - {"IF2 DAC R", NULL, "DAC R2 Filter"}, + {"IF2 DAC L", NULL, "DAC L2 Power"}, + {"IF2 DAC R", NULL, "DAC R2 Power"}, }; static int get_sdp_info(struct snd_soc_codec *codec, int dai_id) -- cgit v1.2.3 From 61a219fe192412bca6c26657a4b0c99c67a3e7fc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:06:17 +0000 Subject: ASoC: rsnd: don't enable SRC Sync mode when Capture with CMD It can't use SRC Synchronous convert when Capture if it uses CMD, because no one provide out side clocks. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 8e1177aea6b1..541a478cf157 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -475,7 +475,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -490,9 +489,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, return 0; /* - * SRC In doesn't work if DVC was enabled + * It can't use SRC Synchronous convert + * when Capture if it uses CMD */ - if (dvc && !rsnd_io_is_play(io)) + if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) return 0; /* -- cgit v1.2.3 From b5ebbb3a16c631c776f6619dff8ee86da21cca6f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:06:46 +0000 Subject: ASoC: rsnd: SRC sync mode is not related to clock master mode SRC sync mode needs to control its clock (= for in/out). 1st but codec side clock Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 541a478cf157..ab974afe79eb 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -474,7 +474,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -482,12 +481,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, * enable SRC sync convert if possible */ - /* - * SRC sync convert needs clock master - */ - if (!rsnd_rdai_is_clk_master(rdai)) - return 0; - /* * It can't use SRC Synchronous convert * when Capture if it uses CMD -- cgit v1.2.3 From 1a9be9ee194131465d945981969b85045f20e6af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:07:29 +0000 Subject: ASoC: rsnd: SRC function parameter uses "mod" instead of "src" To reduce confusion, SRC uses "mod" instead of "src" as function parameter Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ab974afe79eb..d520690409a2 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -93,9 +93,10 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, } static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_src *src) + struct rsnd_mod *mod) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate; if (!runtime) @@ -120,18 +121,14 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct snd_pcm_runtime *runtime) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rsnd_src *src; unsigned int rate = 0; - if (src_mod) { - src = rsnd_mod_to_src(src_mod); - - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - rate = rsnd_src_convert_rate(io, src); - } + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + if (src_mod) + rate = rsnd_src_convert_rate(io, src_mod); if (!rate) rate = runtime->rate; @@ -179,7 +176,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); + u32 convert_rate = rsnd_src_convert_rate(io, mod); u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; -- cgit v1.2.3 From ab2049f9de72c901c07eec25eff74dae2e82df7e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:07:57 +0000 Subject: ASoC: rsnd: rename rsnd_enable_sync_convert() to rsnd_src_sync_is_enabled() rsnd_enable_sync_convert() is for checking, not for setting. In order to avoid confusion, this patch renamed it to rsnd_src_sync_is_enabled() Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index d520690409a2..1d5aedb50213 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -33,7 +33,7 @@ struct rsnd_src { #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_to_dma(src) ((src)->dma) #define rsnd_src_nr(priv) ((priv)->src_nr) -#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) @@ -102,7 +102,7 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return 0; - if (!rsnd_enable_sync_convert(src)) + if (!rsnd_src_sync_is_enabled(mod)) return src->convert_rate; convert_rate = src->sync.val; @@ -175,7 +175,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(io, mod); u32 ifscr, fsrate, adinr; u32 cr, route; @@ -222,7 +221,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (convert_rate) { route = 0x1; - if (rsnd_enable_sync_convert(src)) { + if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; route |= rsnd_io_is_play(io) ? (0x1 << 24) : (0x1 << 25); @@ -296,9 +295,9 @@ static int rsnd_src_irq(struct rsnd_mod *mod, /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) sys_int_val = sys_int_val & 0xffff; rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); @@ -318,7 +317,6 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; bool ret = false; @@ -327,9 +325,9 @@ static bool rsnd_src_error_occurred(struct rsnd_mod *mod) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) val0 = val0 & 0xffff; if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || @@ -343,7 +341,6 @@ static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val; /* @@ -351,7 +348,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, * * Enable SRC output if you want to use sync convert together with DVC */ - val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); -- cgit v1.2.3 From cbf1494fbcc80d363477af1efefb2380e7660a24 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:08:33 +0000 Subject: ASoC: rsnd: add rsnd_src_get_in/out_rate() SRC will convert rate, and then, CMD and SSI want to know its rate (= SRC.in / SRC.out) for each purpose. Current driver is supporting only Playback, but SRC+Capture support needs more flexibility. This patch adds rsnd_src_get_in/out_rate() for it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsnd.h | 10 +++++++--- sound/soc/sh/rcar/src.c | 20 +++++++++++++++++--- sound/soc/sh/rcar/ssi.c | 5 +++-- 3 files changed, 27 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index ff53f96e5006..4b77f33358fb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -627,9 +627,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv); int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime); + +#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) +#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in); + #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") #define rsnd_parse_connect_src(rdai, playback, capture) \ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 1d5aedb50213..d1a8741cc446 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -116,12 +116,26 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return convert_rate; } -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int rate = 0; + int is_play = rsnd_io_is_play(io); + + /* + * + * Playback + * runtime_rate -> [SRC] -> convert_rate + * + * Capture + * convert_rate -> [SRC] -> runtime_rate + */ + + if (is_play == is_in) + return runtime->rate; /* * return convert rate if SRC is used, diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 540489755367..5f848f054745 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -190,7 +190,6 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -201,7 +200,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; - unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); + unsigned int rate = rsnd_io_is_play(io) ? + rsnd_src_get_out_rate(priv, io) : + rsnd_src_get_in_rate(priv, io); if (!rsnd_rdai_is_clk_master(rdai)) return 0; -- cgit v1.2.3 From 0102eed57c47371023c03b3b0c564f33d5e94570 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:09:14 +0000 Subject: ASoC: rsnd: SRC TIMSEL support for Capture SRC has Sync/Async mode, and it can't use Sync mode when Capture with CMD. In Async mode, it needs to care about in/out SRC rate for settings, but current driver supporting Playback case only. This patch supports Capture case. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 204 ++++++++++++++++++++++++++--------------------- sound/soc/sh/rcar/rsnd.h | 8 +- sound/soc/sh/rcar/src.c | 26 +++--- 3 files changed, 128 insertions(+), 110 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d74e1ccc0f8f..f7e164c89f33 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } +static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int target_rate, + unsigned int *target_val, + unsigned int *target_en) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step; + unsigned int val, en; + unsigned int min, diff; + unsigned int sel_rate[] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(target_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return; + } + + *target_val = val; + if (target_en) + *target_en = en; +} + +static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate, + u32 *in, u32 *out, u32 *en) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int target_rate; + u32 *target_val; + u32 _in; + u32 _out; + u32 _en; + + /* default = SSI WS */ + _in = + _out = rsnd_adg_ssi_ws_timing_gen2(io); + + target_rate = 0; + target_val = NULL; + _en = 0; + if (runtime->rate != in_rate) { + target_rate = out_rate; + target_val = &_out; + } else if (runtime->rate != out_rate) { + target_rate = in_rate; + target_val = &_in; + } + + if (target_rate) + __rsnd_adg_get_timesel_ratio(priv, io, + target_rate, + target_val, &_en); + + if (in) + *in = _in; + if (out) + *out = _out; + if (en) + *en = _en; +} + int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, struct rsnd_dai_stream *io) { @@ -110,25 +212,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - u32 timsel) +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int is_play = rsnd_io_is_play(io); + u32 in, out; + u32 mask, en; int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; - u32 mask, ws; - u32 in, out; rsnd_mod_confirm_src(src_mod); - ws = rsnd_adg_ssi_ws_timing_gen2(io); - - in = (is_play) ? timsel : ws; - out = (is_play) ? ws : timsel; + rsnd_adg_get_timesel_ratio(priv, io, + in_rate, out_rate, + &in, &out, &en); in = in << shift; out = out << shift; @@ -157,91 +258,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, break; } - return 0; -} - -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int idx, sel, div, step, ret; - u32 val, en; - unsigned int min, diff; - unsigned int sel_rate [] = { - clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ - clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ - clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz, /* 0011: RBGA */ - adg->rbgb_rate_for_48khz, /* 0100: RBGB */ - }; - - rsnd_mod_confirm_src(src_mod); - - min = ~0; - val = 0; - en = 0; - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - idx = 0; - step = 2; - - if (!sel_rate[sel]) - continue; - - for (div = 2; div <= 98304; div += step) { - diff = abs(src_rate - sel_rate[sel] / div); - if (min > diff) { - val = (sel << 8) | idx; - min = diff; - en = 1 << (sel + 1); /* fixme */ - } - - /* - * step of 0_0000 / 0_0001 / 0_1101 - * are out of order - */ - if ((idx > 2) && (idx % 2)) - step *= 2; - if (idx == 0x1c) { - div += step; - step *= 2; - } - idx++; - } - } - - if (min == ~0) { - dev_err(dev, "no Input clock\n"); - return -EIO; - } - - ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); - if (ret < 0) { - dev_err(dev, "timsel error\n"); - return ret; - } - - rsnd_mod_bset(adg_mod, DIV_EN, en, en); - - dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + if (en) + rsnd_mod_bset(adg_mod, DIV_EN, en, en); return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io) -{ - u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - - rsnd_mod_confirm_src(src_mod); - - return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); -} - static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4b77f33358fb..fc89a67258ca 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -446,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate); -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io); + unsigned int in_rate, + unsigned int out_rate); int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index d1a8741cc446..15d6ffe8be74 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -189,7 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_src_convert_rate(io, mod); + u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; @@ -198,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return; + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!convert_rate) + if (fin == fout) ratio = 0; - else if (convert_rate > runtime->rate) - ratio = 100 * convert_rate / runtime->rate; + else if (fin > fout) + ratio = 100 * fin / fout; else - ratio = 100 * runtime->rate / convert_rate; + ratio = 100 * fout / fin; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); @@ -222,9 +225,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ ifscr = 0; fsrate = 0; - if (convert_rate) { + if (fin != fout) { ifscr = 1; - fsrate = 0x0400000 / convert_rate * runtime->rate; + fsrate = 0x0400000 / fout * fin; } /* @@ -232,7 +235,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ cr = 0x00011110; route = 0x0; - if (convert_rate) { + if (fin != fout) { route = 0x1; if (rsnd_src_sync_is_enabled(mod)) { @@ -274,12 +277,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - if (convert_rate) - rsnd_adg_set_convert_clk_gen2(mod, io, - runtime->rate, - convert_rate); - else - rsnd_adg_set_convert_timing_gen2(mod, io); + rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); } static int rsnd_src_irq(struct rsnd_mod *mod, -- cgit v1.2.3 From 09e59075496517206351cc28226ad7263a1a5c4f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:09:34 +0000 Subject: ASoC: rsnd: CMD TIMSEL support for Capture CMD.out should use same as SRC.out for TIMSEL settings, but it cares Playback case only. This patch fixup it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index f7e164c89f33..606399de684d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -202,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, int shift = (id % 2) ? 16 : 0; u32 mask, val; - val = rsnd_adg_ssi_ws_timing_gen2(io); + rsnd_adg_get_timesel_ratio(priv, io, + rsnd_src_get_in_rate(priv, io), + rsnd_src_get_out_rate(priv, io), + NULL, &val, NULL); val = val << shift; mask = 0xffff << shift; -- cgit v1.2.3 From 67b90cb84be8fde0e51f71834e15c32fbec08562 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:46 +0530 Subject: ALSA: hda - Create common chmap object chmap object represents multichannel capability and contains chmap ops. Legacy driver is updated to use this. With next set of patches chmap object is moved to common to be reused by other drivers (ex: skylake ASoC hdmi driver). Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 41 +++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 81 +++++++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 include/sound/hda_chmap.h (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h new file mode 100644 index 000000000000..9ab20f0274a3 --- /dev/null +++ b/include/sound/hda_chmap.h @@ -0,0 +1,41 @@ +/* + * For multichannel support + */ + +#ifndef __SOUND_HDA_CHMAP_H +#define __SOUND_HDA_CHMAP_H + +#include + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; +struct hdac_chmap; + +struct hdac_chmap_ops { + /* + * Helpers for producing the channel map TLVs. These can be overridden + * for devices that have non-standard mapping requirements. + */ + int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels); + void (*cea_alloc_to_tlv_chmap) + (struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); + + /* check that the user-given chmap is supported */ + int (*chmap_validate)(int ca, int channels, unsigned char *chmap); +}; + +struct hdac_chmap { + unsigned int channels_max; /* max over all cvts */ + struct hdac_chmap_ops ops; + struct hdac_device *hdac; +}; + +#endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index fe4141c43ddb..41f77ce0fa65 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -121,15 +122,6 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format); - /* Helpers for producing the channel map TLVs. These can be overridden - * for devices that have non-standard mapping requirements. */ - int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, - int channels); - void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels); - - /* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); }; struct hdmi_pcm { @@ -155,7 +147,6 @@ struct hdmi_spec { * bit 1 means the second playback PCM, and so on. */ unsigned long pcm_in_use; - unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; struct hdmi_ops ops; @@ -171,6 +162,8 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ struct i915_audio_component_audio_ops i915_audio_ops; bool i915_bound; /* was i915 bound in this driver? */ + + struct hdac_chmap chmap; }; #ifdef CONFIG_SND_HDA_I915 @@ -264,15 +257,6 @@ static int eld_speaker_allocation_bits[] = { [10] = FCH, }; -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - /* * ALSA sequence is: * @@ -2141,8 +2125,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->channels_min = 2; if (chans <= 16) { per_cvt->channels_max = chans; - if (chans > spec->channels_max) - spec->channels_max = chans; + if (chans > spec->chmap.channels_max) + spec->chmap.channels_max = chans; } err = snd_hda_query_supported_pcm(codec, cvt_nid, @@ -2368,15 +2352,17 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = spec->channels_max; + uinfo->count = chmap->channels_max; uinfo->value.integer.min = 0; uinfo->value.integer.max = SNDRV_CHMAP_LAST; return 0; } -static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2409,6 +2395,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; unsigned int __user *dst; int chs, count = 0; @@ -2418,13 +2405,14 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; - for (chs = 2; chs <= spec->channels_max; chs++) { + for (chs = 2; chs <= chmap->channels_max; chs++) { int i; struct cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); unsigned int tlv_chmap[8]; if (type < 0) @@ -2441,7 +2429,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; @@ -2458,12 +2446,13 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); int i; if (!per_pin) { - for (i = 0; i < spec->channels_max; i++) + for (i = 0; i < chmap->channels_max; i++) ucontrol->value.integer.value[i] = 0; return 0; } @@ -2479,6 +2468,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *hchmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; @@ -2514,8 +2504,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; - if (spec->ops.chmap_validate) { - err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -2806,6 +2796,9 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, +}; + +static const struct hdac_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, }; @@ -2912,6 +2905,8 @@ static int patch_generic_hdmi(struct hda_codec *codec) spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); + spec->chmap.ops = chmap_ops; + spec->chmap.hdac = &codec->core; codec->spec = spec; hdmi_array_init(spec, 4); @@ -3503,13 +3498,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0015 * - 0x10de0040 */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; - return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); + return chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, channels); } static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) @@ -3532,9 +3528,9 @@ static int patch_nvhdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true; - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; - spec->ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; return 0; } @@ -3893,8 +3889,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; } -static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, + int channels) { int c; @@ -4041,10 +4039,11 @@ static int patch_atihdmi(struct hda_codec *codec) if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; } /* ATI/AMD converters do not advertise all of their capabilities */ @@ -4056,7 +4055,7 @@ static int patch_atihdmi(struct hda_codec *codec) per_cvt->maxbps = max(per_cvt->maxbps, 24u); } - spec->channels_max = max(spec->channels_max, 8u); + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); return 0; } -- cgit v1.2.3 From 9b3dc8aa3fb1a5f38ca9501f20f6ea4dced10fa0 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:47 +0530 Subject: ALSA: hda - Register chmap obj as priv data instead of codec With this chmap object is added as private data and new ops are added to access driver specific chmap. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 6 +++ sound/pci/hda/patch_hdmi.c | 93 +++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 9ab20f0274a3..713b24526134 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -30,6 +30,12 @@ struct hdac_chmap_ops { /* check that the user-given chmap is supported */ int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + + void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap); + void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared); + bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx); }; struct hdac_chmap { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 41f77ce0fa65..345b83da46c5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -100,8 +100,6 @@ struct hdmi_spec_per_pin { #endif }; -struct cea_channel_speaker_allocation; - /* operations used by generic code that can be overridden by patches */ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, @@ -2350,9 +2348,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chmap->channels_max; @@ -2389,13 +2385,49 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *c WARN_ON(count != channels); } +static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + /* chmap is already set to 0 in caller */ + if (!per_pin) + return; + + memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); +} + +static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + mutex_lock(&per_pin->lock); + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); + if (prepared) + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); +} + +static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + return per_pin ? true:false; +} + static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; unsigned int __user *dst; int chs, count = 0; @@ -2444,21 +2476,17 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + unsigned char pcm_chmap[8]; int i; - if (!per_pin) { - for (i = 0; i < chmap->channels_max; i++) - ucontrol->value.integer.value[i] = 0; - return 0; - } + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; - for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) - ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; } @@ -2466,20 +2494,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *hchmap = &spec->chmap; + struct hdac_chmap *hchmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; struct snd_pcm_substream *substream; - unsigned char chmap[8]; + unsigned char chmap[8], per_pin_chmap[8]; int i, err, ca, prepared = 0; /* No monitor is connected in dyn_pcm_assign. * It's invalid to setup the chmap */ - if (!per_pin) + if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) return 0; ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); @@ -2499,7 +2524,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, memset(chmap, 0, sizeof(chmap)); for (i = 0; i < ARRAY_SIZE(chmap); i++) chmap[i] = ucontrol->value.integer.value[i]; - if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) return 0; ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) @@ -2509,12 +2536,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (err) return err; } - mutex_lock(&per_pin->lock); - per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, sizeof(chmap)); - if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->lock); + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); return 0; } @@ -2672,7 +2695,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; /* override handlers */ - chmap->private_data = codec; + chmap->private_data = &spec->chmap; kctl = chmap->kctl; for (i = 0; i < kctl->count; i++) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; @@ -2801,9 +2824,11 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { static const struct hdac_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, + .get_chmap = hdmi_get_chmap, + .set_chmap = hdmi_set_chmap, + .is_pcm_attached = is_hdmi_pcm_attached, }; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { -- cgit v1.2.3 From f302240da5d5cfc7366dc6994c523aa2aa17c122 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:48 +0530 Subject: ALSA: hda - Use hdac name space for CEA spk alloc structure Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 6 +++--- sound/pci/hda/patch_hdmi.c | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 713b24526134..d3f80e728593 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -7,7 +7,7 @@ #include -struct cea_channel_speaker_allocation { +struct hdac_cea_channel_speaker_allocation { int ca_index; int speakers[8]; @@ -23,9 +23,9 @@ struct hdac_chmap_ops { * for devices that have non-standard mapping requirements. */ int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels); + struct hdac_cea_channel_speaker_allocation *cap, int channels); void (*cea_alloc_to_tlv_chmap) - (struct cea_channel_speaker_allocation *cap, + (struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels); /* check that the user-given chmap is supported */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 345b83da46c5..12b0287784c8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -297,7 +297,7 @@ static int hdmi_channel_mapping[0x32][8] = { * The preceding ones have better chances to be selected by * hdmi_channel_allocation(). */ -static struct cea_channel_speaker_allocation channel_allocations[] = { +static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { /* channel: 7 6 5 4 3 2 1 0 */ { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, /* 2.1 */ @@ -683,7 +683,7 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) static void init_channel_allocations(void) { int i, j; - struct cea_channel_speaker_allocation *p; + struct hdac_cea_channel_speaker_allocation *p; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { p = channel_allocations + i; @@ -791,7 +791,7 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int ca) { struct hdmi_spec *spec = codec->spec; - struct cea_channel_speaker_allocation *ch_alloc; + struct hdac_cea_channel_speaker_allocation *ch_alloc; int i; int err; int order; @@ -2358,7 +2358,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, } static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdac_cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2368,8 +2368,9 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; } -static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void hdmi_cea_alloc_to_tlv_chmap( + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { int count = 0; int c; @@ -2439,7 +2440,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, dst = tlv + 2; for (chs = 2; chs <= chmap->channels_max; chs++) { int i; - struct cea_channel_speaker_allocation *cap; + struct hdac_cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; @@ -3524,7 +3525,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0040 */ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdac_cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; @@ -3805,7 +3806,7 @@ static int atihdmi_paired_swap_fc_lfe(int pos) static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) { - struct cea_channel_speaker_allocation *cap; + struct hdac_cea_channel_speaker_allocation *cap; int i, j; /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ @@ -3916,7 +3917,7 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n static int atihdmi_paired_chmap_cea_alloc_validate_get_type( struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) { int c; @@ -3944,8 +3945,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; } -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void atihdmi_paired_cea_alloc_to_tlv_chmap( + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { /* produce paired maps for pre-rev3 ATI/AMD codecs */ int count = 0; -- cgit v1.2.3 From 739ffee97ed550a2899a925ed3f260fa1e8fa955 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:49 +0530 Subject: ALSA: hda - Add hdmi chmap verb programming ops to chmap object Add slot and channel count programming to hdmi_chmap object and move the chmap_ops to core. Use register_chmap_ops API to register for default ops. Override specific chmap ops in the driver. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 10 ++++++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 49 ++++++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 87 +++++++++++++++------------------------------- 4 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 sound/hda/hdmi_chmap.c (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index d3f80e728593..7afffb90921f 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -36,6 +36,14 @@ struct hdac_chmap_ops { void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap, int prepared); bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx); + + /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ + int (*pin_get_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot); + int (*pin_set_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel); + void (*set_channel_count)(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs); }; struct hdac_chmap { @@ -44,4 +52,6 @@ struct hdac_chmap { struct hdac_device *hdac; }; +void snd_hdac_register_chmap_ops(struct hdac_device *hdac, + struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 7e999c995cdc..3b9bedee2fa4 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c new file mode 100644 index 000000000000..6b9879013c86 --- /dev/null +++ b/sound/hda/hdmi_chmap.c @@ -0,0 +1,49 @@ +/* + * HDMI Channel map support helpers + */ + +#include + +static int hdmi_pin_set_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel) +{ + return snd_hdac_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot) +{ + return (snd_hdac_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; +} + +static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) +{ + return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hdac_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + +static const struct hdac_chmap_ops chmap_ops = { + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .set_channel_count = hdmi_set_channel_count, +}; + +void snd_hdac_register_chmap_ops(struct hdac_device *hdac, + struct hdac_chmap *chmap) +{ + chmap->ops = chmap_ops; + chmap->hdac = hdac; +} +EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 12b0287784c8..ac6c702f89db 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -105,12 +105,6 @@ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char *buf, int *eld_size); - /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ - int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot); - int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel); - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, int ca, int active_channels, int conn_type); @@ -596,20 +590,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); } -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t cvt_nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, cvt_nid)) - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - /* * ELD proc files */ @@ -778,7 +758,8 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, int channel; for (i = 0; i < 8; i++) { - channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); + channel = spec->chmap.ops.pin_get_slot_channel( + &codec->core, pin_nid, i); codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", channel, i); } @@ -826,7 +807,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); + err = spec->chmap.ops.pin_set_slot_channel( + &codec->core, pin_nid, hdmi_slot, channel); if (err) { codec_dbg(codec, "HDMI: channel mapping failed\n"); break; @@ -953,8 +935,8 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, - assignments[hdmi_slot]); + err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; } @@ -990,22 +972,6 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); } -static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel) -{ - return snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (channel << 4) | asp_slot); -} - -static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) -{ - return (snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, - asp_slot) & 0xf0) >> 4; -} - /* * Audio InfoFrame routines */ @@ -1181,6 +1147,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, bool non_pcm) { struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels; @@ -1207,7 +1174,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, ordered_ca = get_channel_allocation_order(ca); active_channels = channel_allocations[ordered_ca].channels; - hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, + active_channels); /* * always configure channel mapping, it may have been changed by the @@ -2815,21 +2783,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = snd_hdmi_get_eld, - .pin_get_slot_channel = hdmi_pin_get_slot_channel, - .pin_set_slot_channel = hdmi_pin_set_slot_channel, .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, }; -static const struct hdac_chmap_ops chmap_ops = { - .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, - .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, - .get_chmap = hdmi_get_chmap, - .set_chmap = hdmi_set_chmap, - .is_pcm_attached = is_hdmi_pcm_attached, -}; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2931,8 +2889,15 @@ static int patch_generic_hdmi(struct hda_codec *codec) spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); - spec->chmap.ops = chmap_ops; - spec->chmap.hdac = &codec->core; + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + hdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + codec->spec = spec; hdmi_array_init(spec, 4); @@ -3851,9 +3816,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) return 0; } -static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int hdmi_slot, int stream_channel) +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); int verb; int ati_channel_setup = 0; @@ -3886,9 +3852,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); } -static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; @@ -4058,8 +4025,6 @@ static int patch_atihdmi(struct hda_codec *codec) spec = codec->spec; spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; spec->ops.setup_stream = atihdmi_setup_stream; @@ -4071,6 +4036,10 @@ static int patch_atihdmi(struct hda_codec *codec) spec->chmap.ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.pin_get_slot_channel = + atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = + atihdmi_pin_set_slot_channel; } /* ATI/AMD converters do not advertise all of their capabilities */ -- cgit v1.2.3 From 828cb4edd8074d35f814e03bee3e2ad8b7d8f64b Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:50 +0530 Subject: ALSA: hda - chmap helper args modified to use generic hdac objs. Chmap helper arguments are modified to use either hdac_device object or hdac_chmap object instead of codec specific object. With this moving these APIs to core will be easier. Helper added to access a specific channel_allocation object instead of directly accessing. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 10 +++-- sound/pci/hda/patch_hdmi.c | 108 ++++++++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 7afffb90921f..f7fd752fc817 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -7,6 +7,9 @@ #include + +#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + struct hdac_cea_channel_speaker_allocation { int ca_index; int speakers[8]; @@ -24,12 +27,13 @@ struct hdac_chmap_ops { */ int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, struct hdac_cea_channel_speaker_allocation *cap, int channels); - void (*cea_alloc_to_tlv_chmap) - (struct hdac_cea_channel_speaker_allocation *cap, + void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels); /* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + int (*chmap_validate)(struct hdac_chmap *hchmap, int ca, + int channels, unsigned char *chmap); void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ac6c702f89db..3718b41f4b60 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -696,8 +696,8 @@ static int get_channel_allocation_order(int ca) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_channel_allocation(struct hda_codec *codec, - struct hdmi_eld *eld, int channels) +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) { int i; int ca = 0; @@ -717,7 +717,7 @@ static int hdmi_channel_allocation(struct hda_codec *codec, * expand ELD's notions to match the ones used by Audio InfoFrame. */ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->info.spk_alloc & (1 << i)) + if (spk_alloc & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -742,36 +742,34 @@ static int hdmi_channel_allocation(struct hda_codec *codec, } } - snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); return ca; } -static void hdmi_debug_channel_mapping(struct hda_codec *codec, +static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE - struct hdmi_spec *spec = codec->spec; int i; int channel; for (i = 0; i < 8; i++) { - channel = spec->chmap.ops.pin_get_slot_channel( - &codec->core, pin_nid, i); - codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", channel, i); } #endif } -static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca) { - struct hdmi_spec *spec = codec->spec; struct hdac_cea_channel_speaker_allocation *ch_alloc; int i; int err; @@ -807,10 +805,10 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->chmap.ops.pin_set_slot_channel( - &codec->core, pin_nid, hdmi_slot, channel); + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); if (err) { - codec_dbg(codec, "HDMI: channel mapping failed\n"); + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); break; } } @@ -912,12 +910,11 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) } /* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, +static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, int chs, unsigned char *map, int ca) { - struct hdmi_spec *spec = codec->spec; int ordered_ca = get_channel_allocation_order(ca); int alsa_pos, hdmi_slot; int assignments[8] = {[0 ... 7] = 0xf}; @@ -935,7 +932,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err; - err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + err = chmap->ops.pin_set_slot_channel(chmap->hdac, pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; @@ -956,20 +953,20 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) } } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set) { if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(codec, pin_nid, + hdmi_manual_setup_channel_mapping(chmap, pin_nid, channels, map, ca); } else { - hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); } - hdmi_debug_channel_mapping(codec, pin_nid); + hdmi_debug_channel_mapping(chmap, pin_nid); } /* @@ -1142,6 +1139,35 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } } +static int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} + +static struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} + +static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -1152,7 +1178,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; - int ca, ordered_ca; + int ca; if (!channels) return; @@ -1164,15 +1190,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, eld = &per_pin->sink_eld; - if (!non_pcm && per_pin->chmap_set) - ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); - else - ca = hdmi_channel_allocation(codec, eld, channels); - if (ca < 0) - ca = 0; + ca = hdmi_channel_allocation(&codec->core, + eld->info.spk_alloc, channels, + per_pin->chmap_set, non_pcm, per_pin->chmap); - ordered_ca = get_channel_allocation_order(ca); - active_channels = channel_allocations[ordered_ca].channels; + active_channels = hdmi_get_active_channels(ca); chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, active_channels); @@ -1181,9 +1203,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap, - per_pin->chmap_set); + hdmi_setup_channel_mapping(&spec->chmap, + pin_nid, non_pcm, ca, channels, + per_pin->chmap, per_pin->chmap_set); spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, eld->info.conn_type); @@ -2336,7 +2358,7 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; } -static void hdmi_cea_alloc_to_tlv_chmap( +static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { @@ -2430,7 +2452,8 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, + tlv_chmap, chs); if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; @@ -2501,7 +2524,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (ca < 0) return -EINVAL; if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -3499,7 +3523,8 @@ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, chmap, cap, channels); } -static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) return -EINVAL; @@ -3769,14 +3794,15 @@ static int atihdmi_paired_swap_fc_lfe(int pos) return pos; } -static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { struct hdac_cea_channel_speaker_allocation *cap; int i, j; /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - cap = &channel_allocations[get_channel_allocation_order(ca)]; + cap = hdmi_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { int mask = to_spk_mask(map[i]); bool ok = false; @@ -3912,7 +3938,7 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; } -static void atihdmi_paired_cea_alloc_to_tlv_chmap( +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { -- cgit v1.2.3 From 2f6e8a8518f33b6fac1fb9c79d245e23b6f9b765 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:51 +0530 Subject: ALSA: hda - Move chmap support helpers/ops to core Chmap helpers, ops, controls are moved to core. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 15 + sound/hda/hdmi_chmap.c | 742 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_eld.c | 27 +- sound/pci/hda/patch_hdmi.c | 689 +---------------------------------------- 4 files changed, 759 insertions(+), 714 deletions(-) (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index f7fd752fc817..5a85b319439a 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -5,6 +5,7 @@ #ifndef __SOUND_HDA_CHMAP_H #define __SOUND_HDA_CHMAP_H +#include #include @@ -58,4 +59,18 @@ struct hdac_chmap { void snd_hdac_register_chmap_ops(struct hdac_device *hdac, struct hdac_chmap *chmap); +int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, + bool non_pcm, unsigned char *map); +int hdmi_get_active_channels(int ca); +void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set); +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); +struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca); +int to_spk_mask(unsigned char c); +int spk_to_chmap(int spk); +int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 6b9879013c86..880666aa01c0 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -2,8 +2,182 @@ * HDMI Channel map support helpers */ +#include +#include +#include #include +/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +static const char * const cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + static int hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid, int asp_slot, int channel) { @@ -34,7 +208,548 @@ static void hdmi_set_channel_count(struct hdac_device *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } +/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct hdac_cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +static int get_channel_allocation_order(int ca) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == ca) + break; + } + return i; +} + +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += snprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_GPL(snd_print_channel_allocation); + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + if (!ca) { + /* + * if there was no match, select the regular ALSA channel + * allocation with the matching number of channels + */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels) { + ca = channel_allocations[i].ca_index; + break; + } + } + } + + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ca, channels, buf); + + return ca; +} + +static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int channel; + + for (i = 0; i < 8; i++) { + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", + channel, i); + } +#endif +} + +static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + bool non_pcm, + int ca) +{ + struct hdac_cea_channel_speaker_allocation *ch_alloc; + int i; + int err; + int order; + int non_pcm_mapping[8]; + + order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; + + if (hdmi_channel_mapping[ca][1] == 0) { + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; + } + + if (non_pcm) { + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; + for (; i < 8; i++) + non_pcm_mapping[i] = (0xf << 4) | i; + } + + for (i = 0; i < 8; i++) { + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); + if (err) { + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); + break; + } + } +} + +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_TFL, FLH }, + { SNDRV_CHMAP_TFR, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_TFC, FCH }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +int to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} +EXPORT_SYMBOL_GPL(to_spk_mask); + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(int ordered_ca, unsigned char pos) +{ + int mask = to_spk_mask(pos); + int i; + + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } + } + + return -1; +} + +/* from speaker bit mask to ALSA API channel position */ +int spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} +EXPORT_SYMBOL_GPL(spk_to_chmap); + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return spk_to_chmap(mask); +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = to_spk_mask(map[i]); + + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + int chs, unsigned char *map, + int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { + int err; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, assignments[hdmi_slot]); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + int ordered_ca = get_channel_allocation_order(ca); + + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); + else + map[i] = 0; + } +} + +void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set) +{ + if (!non_pcm && chmap_set) { + hdmi_manual_setup_channel_mapping(chmap, pin_nid, + channels, map, ca); + } else { + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } + + hdmi_debug_channel_mapping(chmap, pin_nid); +} +EXPORT_SYMBOL_GPL(hdmi_setup_channel_mapping); + +int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} +EXPORT_SYMBOL_GPL(hdmi_get_active_channels); + +struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} +EXPORT_SYMBOL_GPL(hdmi_get_ch_alloc_from_ca); + +int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} +EXPORT_SYMBOL_GPL(hdmi_channel_allocation); + +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chmap->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + + if (!spk) + continue; + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= chmap->channels_max; chs++) { + int i; + struct hdac_cea_channel_speaker_allocation *cap; + + cap = channel_allocations; + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { + int chs_bytes = chs * 4; + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); + unsigned int tlv_chmap[8]; + + if (type < 0) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(type, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, + tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) + return -EFAULT; + dst += chs; + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned char pcm_chmap[8]; + int i; + + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; + + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *hchmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8], per_pin_chmap[8]; + int i, err, ca, prepared = 0; + + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) + return 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return 0; /* just for avoiding error from alsactl restore */ + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); + + return 0; +} + static const struct hdac_chmap_ops chmap_ops = { + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, .pin_get_slot_channel = hdmi_pin_get_slot_channel, .pin_set_slot_channel = hdmi_pin_set_slot_channel, .set_channel_count = hdmi_set_channel_count, @@ -45,5 +760,32 @@ void snd_hdac_register_chmap_ops(struct hdac_device *hdac, { chmap->ops = chmap_ops; chmap->hdac = hdac; + init_channel_allocations(); } EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); + +int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdac_chmap *hchmap) +{ + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int err, i; + + err = snd_pcm_add_chmap_ctls(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pcm_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = hchmap; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index bc2e08257c2e..7c6a9737ad8b 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" @@ -42,20 +43,6 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, }; -static const char * const cea_speaker_allocation_names[] = { - /* 0 */ "FL/FR", - /* 1 */ "LFE", - /* 2 */ "FC", - /* 3 */ "RL/RR", - /* 4 */ "RC", - /* 5 */ "FLC/FRC", - /* 6 */ "RLC/RRC", - /* 7 */ "FLW/FRW", - /* 8 */ "FLH/FRH", - /* 9 */ "TC", - /* 10 */ "FCH", -}; - static const char * const eld_connection_type_names[4] = { "HDMI", "DisplayPort", @@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec, a->channels, buf, buf2); } -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { - if (spk_alloc & (1 << i)) - j += snprintf(buf + j, buflen - j, " %s", - cea_speaker_allocation_names[i]); - } - buf[j] = '\0'; /* necessary when j == 0 */ -} - void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) { int i; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3718b41f4b60..bdfa045f28af 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -197,164 +197,6 @@ union audio_infoframe { u8 bytes[0]; }; -/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_channel_allocation(). - */ -static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - - /* * HDMI routines */ @@ -653,322 +495,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) } #endif -/* - * Channel mapping routines - */ - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct hdac_cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -static int get_channel_allocation_order(int ca) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channel_allocations[i].ca_index == ca) - break; - } - return i; -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, - int spk_alloc, int channels) -{ - int i; - int ca = 0; - int spk_mask = 0; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ca = channel_allocations[i].ca_index; - break; - } - } - - if (!ca) { - /* if there was no match, select the regular ALSA channel - * allocation with the matching number of channels */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels) { - ca = channel_allocations[i].ca_index; - break; - } - } - } - - snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); - dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ca, channels, buf); - - return ca; -} - -static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int channel; - - for (i = 0; i < 8; i++) { - channel = chmap->ops.pin_get_slot_channel( - chmap->hdac, pin_nid, i); - dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", - channel, i); - } -#endif -} - -static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, - bool non_pcm, - int ca) -{ - struct hdac_cea_channel_speaker_allocation *ch_alloc; - int i; - int err; - int order; - int non_pcm_mapping[8]; - - order = get_channel_allocation_order(ca); - ch_alloc = &channel_allocations[order]; - - if (hdmi_channel_mapping[ca][1] == 0) { - int hdmi_slot = 0; - /* fill actual channel mappings in ALSA channel (i) order */ - for (i = 0; i < ch_alloc->channels; i++) { - while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) - hdmi_slot++; /* skip zero slots */ - - hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; - } - /* fill the rest of the slots with ALSA channel 0xf */ - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) - if (!ch_alloc->speakers[7 - hdmi_slot]) - hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; - } - - if (non_pcm) { - for (i = 0; i < ch_alloc->channels; i++) - non_pcm_mapping[i] = (i << 4) | i; - for (; i < 8; i++) - non_pcm_mapping[i] = (0xf << 4) | i; - } - - for (i = 0; i < 8; i++) { - int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; - int hdmi_slot = slotsetup & 0x0f; - int channel = (slotsetup & 0xf0) >> 4; - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, channel); - if (err) { - dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); - break; - } - } -} - -struct channel_map_table { - unsigned char map; /* ALSA API channel map position */ - int spk_mask; /* speaker position bit mask */ -}; - -static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, FL }, - { SNDRV_CHMAP_FR, FR }, - { SNDRV_CHMAP_RL, RL }, - { SNDRV_CHMAP_RR, RR }, - { SNDRV_CHMAP_LFE, LFE }, - { SNDRV_CHMAP_FC, FC }, - { SNDRV_CHMAP_RLC, RLC }, - { SNDRV_CHMAP_RRC, RRC }, - { SNDRV_CHMAP_RC, RC }, - { SNDRV_CHMAP_FLC, FLC }, - { SNDRV_CHMAP_FRC, FRC }, - { SNDRV_CHMAP_TFL, FLH }, - { SNDRV_CHMAP_TFR, FRH }, - { SNDRV_CHMAP_FLW, FLW }, - { SNDRV_CHMAP_FRW, FRW }, - { SNDRV_CHMAP_TC, TC }, - { SNDRV_CHMAP_TFC, FCH }, - {} /* terminator */ -}; - -/* from ALSA API channel position to speaker bit mask */ -static int to_spk_mask(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->spk_mask; - } - return 0; -} - -/* from ALSA API channel position to CEA slot */ -static int to_cea_slot(int ordered_ca, unsigned char pos) -{ - int mask = to_spk_mask(pos); - int i; - - if (mask) { - for (i = 0; i < 8; i++) { - if (channel_allocations[ordered_ca].speakers[7 - i] == mask) - return i; - } - } - - return -1; -} - -/* from speaker bit mask to ALSA API channel position */ -static int spk_to_chmap(int spk) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->spk_mask == spk) - return t->map; - } - return 0; -} - -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(int ordered_ca, unsigned char slot) -{ - int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - - return spk_to_chmap(mask); -} - -/* get the CA index corresponding to the given ALSA API channel map */ -static int hdmi_manual_channel_allocation(int chs, unsigned char *map) -{ - int i, spks = 0, spk_mask = 0; - - for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); - if (mask) { - spk_mask |= mask; - spks++; - } - } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if ((chs == channel_allocations[i].channels || - spks == channel_allocations[i].channels) && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) - return channel_allocations[i].ca_index; - } - return -1; -} - -/* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, - int chs, unsigned char *map, - int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - int alsa_pos, hdmi_slot; - int assignments[8] = {[0 ... 7] = 0xf}; - - for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { - - hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); - - if (hdmi_slot < 0) - continue; /* unassigned channel */ - - assignments[hdmi_slot] = alsa_pos; - } - - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { - int err; - - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, assignments[hdmi_slot]); - if (err) - return -EINVAL; - } - return 0; -} - -/* store ALSA API channel map from the current default map */ -static void hdmi_setup_fake_chmap(unsigned char *map, int ca) -{ - int i; - int ordered_ca = get_channel_allocation_order(ca); - for (i = 0; i < 8; i++) { - if (i < channel_allocations[ordered_ca].channels) - map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); - else - map[i] = 0; - } -} - -static void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map, - bool chmap_set) -{ - if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(chmap, pin_nid, - channels, map, ca); - } else { - hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); - hdmi_setup_fake_chmap(map, ca); - } - - hdmi_debug_channel_mapping(chmap, pin_nid); -} - /* * Audio InfoFrame routines */ @@ -1139,35 +665,6 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } } -static int hdmi_get_active_channels(int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - - return channel_allocations[ordered_ca].channels; -} - -static struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) -{ - return &channel_allocations[get_channel_allocation_order(ca)]; -} - -static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, - int channels, bool chmap_set, bool non_pcm, unsigned char *map) -{ - int ca; - - if (!non_pcm && chmap_set) - ca = hdmi_manual_channel_allocation(channels, map); - else - ca = hdmi_channel_allocation_spk_alloc_blk(hdac, - spk_alloc, channels); - - if (ca < 0) - ca = 0; - - return ca; -} - static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -2331,51 +1828,6 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; -/* - * ALSA API channel-map control callbacks - */ -static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chmap->channels_max; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = SNDRV_CHMAP_LAST; - return 0; -} - -static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, int channels) -{ - /* If the speaker allocation matches the channel count, it is OK.*/ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, - struct hdac_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - - chmap[count++] = spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { @@ -2414,127 +1866,6 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) return per_pin ? true:false; } -static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - unsigned int __user *dst; - int chs, count = 0; - - if (size < 8) - return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) - return -EFAULT; - size -= 8; - dst = tlv + 2; - for (chs = 2; chs <= chmap->channels_max; chs++) { - int i; - struct hdac_cea_channel_speaker_allocation *cap; - cap = channel_allocations; - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { - int chs_bytes = chs * 4; - int type = chmap->ops.chmap_cea_alloc_validate_get_type( - chmap, cap, chs); - unsigned int tlv_chmap[8]; - - if (type < 0) - continue; - if (size < 8) - return -ENOMEM; - if (put_user(type, dst) || - put_user(chs_bytes, dst + 1)) - return -EFAULT; - dst += 2; - size -= 8; - count += 8; - if (size < chs_bytes) - return -ENOMEM; - size -= chs_bytes; - count += chs_bytes; - chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, - tlv_chmap, chs); - if (copy_to_user(dst, tlv_chmap, chs_bytes)) - return -EFAULT; - dst += chs; - } - } - if (put_user(count, tlv + 1)) - return -EFAULT; - return 0; -} - -static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned char pcm_chmap[8]; - int i; - - memset(pcm_chmap, 0, sizeof(pcm_chmap)); - chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); - - for (i = 0; i < sizeof(chmap); i++) - ucontrol->value.integer.value[i] = pcm_chmap[i]; - - return 0; -} - -static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *hchmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned int ctl_idx; - struct snd_pcm_substream *substream; - unsigned char chmap[8], per_pin_chmap[8]; - int i, err, ca, prepared = 0; - - /* No monitor is connected in dyn_pcm_assign. - * It's invalid to setup the chmap - */ - if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) - return 0; - - ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - substream = snd_pcm_chmap_substream(info, ctl_idx); - if (!substream || !substream->runtime) - return 0; /* just for avoiding error from alsactl restore */ - switch (substream->runtime->status->state) { - case SNDRV_PCM_STATE_OPEN: - case SNDRV_PCM_STATE_SETUP: - break; - case SNDRV_PCM_STATE_PREPARED: - prepared = 1; - break; - default: - return -EBUSY; - } - memset(chmap, 0, sizeof(chmap)); - for (i = 0; i < ARRAY_SIZE(chmap); i++) - chmap[i] = ucontrol->value.integer.value[i]; - - hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); - if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) - return 0; - ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); - if (ca < 0) - return -EINVAL; - if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(hchmap, ca, - ARRAY_SIZE(chmap), chmap); - if (err) - return err; - } - - hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); - - return 0; -} - static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2675,27 +2006,13 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add channel maps */ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { struct hda_pcm *pcm; - struct snd_pcm_chmap *chmap; - struct snd_kcontrol *kctl; - int i; pcm = get_pcm_rec(spec, pcm_idx); if (!pcm || !pcm->pcm) break; - err = snd_pcm_add_chmap_ctls(pcm->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - NULL, 0, pcm_idx, &chmap); + err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); if (err < 0) return err; - /* override handlers */ - chmap->private_data = &spec->chmap; - kctl = chmap->kctl; - for (i = 0; i < kctl->count; i++) - kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - kctl->info = hdmi_chmap_ctl_info; - kctl->get = hdmi_chmap_ctl_get; - kctl->put = hdmi_chmap_ctl_put; - kctl->tlv.c = hdmi_chmap_ctl_tlv; } return 0; @@ -2915,9 +2232,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) mutex_init(&spec->pcm_lock); snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - hdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; @@ -2965,7 +2279,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) generic_hdmi_init_per_pins(codec); - init_channel_allocations(); if (codec_has_acomp(codec)) { codec->depop_delay = 0; -- cgit v1.2.3 From bb63f726f98bec032c7322a9c36eb4167307d856 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:52 +0530 Subject: ALSA: hda - Use snd_hdac namespace prefix for chmap exported APIs Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 14 +++++++------- sound/hda/hdmi_chmap.c | 38 +++++++++++++++++++------------------- sound/pci/hda/hda_eld.c | 4 ++-- sound/pci/hda/patch_hdmi.c | 14 +++++++------- 4 files changed, 35 insertions(+), 35 deletions(-) (limited to 'sound') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 5a85b319439a..e20d219a0304 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -59,18 +59,18 @@ struct hdac_chmap { void snd_hdac_register_chmap_ops(struct hdac_device *hdac, struct hdac_chmap *chmap); -int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, +int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, int channels, bool chmap_set, bool non_pcm, unsigned char *map); -int hdmi_get_active_channels(int ca); -void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, +int snd_hdac_get_active_channels(int ca); +void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set); -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); -struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca); -int to_spk_mask(unsigned char c); -int spk_to_chmap(int spk); +void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen); +struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca); +int snd_hdac_chmap_to_spk_mask(unsigned char c); +int snd_hdac_spk_to_chmap(int spk); int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 880666aa01c0..d7ec86263828 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -243,7 +243,7 @@ static int get_channel_allocation_order(int ca) return i; } -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) { int i, j; @@ -254,7 +254,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) } buf[j] = '\0'; /* necessary when j == 0 */ } -EXPORT_SYMBOL_GPL(snd_print_channel_allocation); +EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation); /* * The transformation takes two steps: @@ -312,7 +312,7 @@ static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, } } - snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf)); dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); @@ -412,7 +412,7 @@ static struct channel_map_table map_tables[] = { }; /* from ALSA API channel position to speaker bit mask */ -int to_spk_mask(unsigned char c) +int snd_hdac_chmap_to_spk_mask(unsigned char c) { struct channel_map_table *t = map_tables; @@ -422,12 +422,12 @@ int to_spk_mask(unsigned char c) } return 0; } -EXPORT_SYMBOL_GPL(to_spk_mask); +EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask); /* from ALSA API channel position to CEA slot */ static int to_cea_slot(int ordered_ca, unsigned char pos) { - int mask = to_spk_mask(pos); + int mask = snd_hdac_chmap_to_spk_mask(pos); int i; if (mask) { @@ -441,7 +441,7 @@ static int to_cea_slot(int ordered_ca, unsigned char pos) } /* from speaker bit mask to ALSA API channel position */ -int spk_to_chmap(int spk) +int snd_hdac_spk_to_chmap(int spk) { struct channel_map_table *t = map_tables; @@ -451,14 +451,14 @@ int spk_to_chmap(int spk) } return 0; } -EXPORT_SYMBOL_GPL(spk_to_chmap); +EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap); /* from CEA slot to ALSA API channel position */ static int from_cea_slot(int ordered_ca, unsigned char slot) { int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - return spk_to_chmap(mask); + return snd_hdac_spk_to_chmap(mask); } /* get the CA index corresponding to the given ALSA API channel map */ @@ -467,7 +467,7 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) int i, spks = 0, spk_mask = 0; for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdac_chmap_to_spk_mask(map[i]); if (mask) { spk_mask |= mask; @@ -530,7 +530,7 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) } } -void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, +void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set) @@ -545,23 +545,23 @@ void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, hdmi_debug_channel_mapping(chmap, pin_nid); } -EXPORT_SYMBOL_GPL(hdmi_setup_channel_mapping); +EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping); -int hdmi_get_active_channels(int ca) +int snd_hdac_get_active_channels(int ca) { int ordered_ca = get_channel_allocation_order(ca); return channel_allocations[ordered_ca].channels; } -EXPORT_SYMBOL_GPL(hdmi_get_active_channels); +EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels); -struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca) { return &channel_allocations[get_channel_allocation_order(ca)]; } -EXPORT_SYMBOL_GPL(hdmi_get_ch_alloc_from_ca); +EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca); -int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, +int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, int channels, bool chmap_set, bool non_pcm, unsigned char *map) { int ca; @@ -577,7 +577,7 @@ int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, return ca; } -EXPORT_SYMBOL_GPL(hdmi_channel_allocation); +EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation); /* * ALSA API channel-map control callbacks @@ -619,7 +619,7 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, if (!spk) continue; - chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdac_spk_to_chmap(spk); } WARN_ON(count != channels); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 7c6a9737ad8b..ba7fe9b6655c 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -416,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) if (e->spk_alloc) { char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); codec_dbg(codec, "HDMI: available speakers:%s\n", buf); } @@ -491,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld, snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bdfa045f28af..eb7da9992aad 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -687,11 +687,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, eld = &per_pin->sink_eld; - ca = hdmi_channel_allocation(&codec->core, + ca = snd_hdac_channel_allocation(&codec->core, eld->info.spk_alloc, channels, per_pin->chmap_set, non_pcm, per_pin->chmap); - active_channels = hdmi_get_active_channels(ca); + active_channels = snd_hdac_get_active_channels(ca); chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, active_channels); @@ -700,7 +700,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(&spec->chmap, + snd_hdac_setup_channel_mapping(&spec->chmap, pin_nid, non_pcm, ca, channels, per_pin->chmap, per_pin->chmap_set); @@ -3115,9 +3115,9 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - cap = hdmi_get_ch_alloc_from_ca(ca); + cap = snd_hdac_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdac_chmap_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false; @@ -3133,7 +3133,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, if (i % 2 == 0 && i + 1 < chs) { /* even channel, check the odd companion */ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx]; if (comp_mask_req == comp_mask_act) @@ -3270,7 +3270,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, continue; } - chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdac_spk_to_chmap(spk); } WARN_ON(count != channels); -- cgit v1.2.3 From c77dd678a7659d58625bd3b3a36d1329d9e7d44c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 8 Mar 2016 13:35:31 +0530 Subject: ASoC: rt5640: remove unused variable We are getting build warning about: sound/soc/codecs/rt5640.c:1892:11: warning: unused variable 'dai_sel' The use of the variable was removed but the variable itself was not removed. Fixes: c467fc0e010b ("ASoC: rt5640: Set PLL src according to source") Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index af9b5f18f0e0..e8b5ba04417a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1889,7 +1889,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, struct snd_soc_codec *codec = dai->codec; struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); struct rl6231_pll_code pll_code; - int ret, dai_sel; + int ret; if (source == rt5640->pll_src && freq_in == rt5640->pll_in && freq_out == rt5640->pll_out) -- cgit v1.2.3 From fc4f000bf8c0cbf38f44de6bd5e225574e572ed4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Mar 2016 11:34:18 +0100 Subject: ALSA: hda - Fix unexpected resume through regmap code path HD-audio driver has a mechanism to trigger the runtime resume automatically at accessing the verbs. This auto-resume, however, causes the mutex deadlock when invoked from the regmap handler since the regmap keeps the mutex while auto-resuming. For avoiding that, there is some tricky check in the HDA regmap handler to return -EAGAIN error to back-off when the codec is powered down. Then the caller of regmap r/w will retry after properly turning on the codec power. This works in most cases, but there seems a slight race between the codec power check and the actual on-demand auto-resume trigger. This resulted in the lockdep splat, eventually leading to a real deadlock. This patch tries to address the race window by getting the runtime PM refcount at the check time using pm_runtime_get_if_in_use(). With this call, we can keep the power on only when the codec has been already turned on, and back off if not. For keeping the code consistency, the code touching the runtime PM is stored in hdac_device.c although it's used only locally in hdac_regmap.c. Reported-by: Jiri Slaby Cc: Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_device.c | 16 ++++++++++++ sound/hda/hdac_regmap.c | 69 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 64 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index c21c38ce7450..93e63c56f48f 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -168,11 +168,13 @@ int snd_hdac_power_up(struct hdac_device *codec); int snd_hdac_power_down(struct hdac_device *codec); int snd_hdac_power_up_pm(struct hdac_device *codec); int snd_hdac_power_down_pm(struct hdac_device *codec); +int snd_hdac_keep_power_up(struct hdac_device *codec); #else static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; } +static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; } #endif /* diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index e361024eabb6..d1a4d6973330 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); +/* like snd_hdac_power_up_pm(), but only increment the pm count when + * already powered up. Returns -1 if not powered up, 1 if incremented + * or 0 if unchanged. Only used in hdac_regmap.c + */ +int snd_hdac_keep_power_up(struct hdac_device *codec) +{ + if (!atomic_inc_not_zero(&codec->in_pm)) { + int ret = pm_runtime_get_if_in_use(&codec->dev); + if (!ret) + return -1; + if (ret < 0) + return 0; + } + return 1; +} + /** * snd_hdac_power_down_pm - power down the codec * @codec: the codec object diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index eb8f7c30cb09..bdbcd6b75ff6 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -21,13 +21,16 @@ #include #include -#ifdef CONFIG_PM -#define codec_is_running(codec) \ - (atomic_read(&(codec)->in_pm) || \ - !pm_runtime_suspended(&(codec)->dev)) -#else -#define codec_is_running(codec) true -#endif +static int codec_pm_lock(struct hdac_device *codec) +{ + return snd_hdac_keep_power_up(codec); +} + +static void codec_pm_unlock(struct hdac_device *codec, int lock) +{ + if (lock == 1) + snd_hdac_power_down_pm(codec); +} #define get_verb(reg) (((reg) >> 8) & 0xfff) @@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) struct hdac_device *codec = context; int verb = get_verb(reg); int err; + int pm_lock = 0; - if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) - return -EAGAIN; + if (verb != AC_VERB_GET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return -EAGAIN; + } reg |= (codec->addr << 28); - if (is_stereo_amp_verb(reg)) - return hda_reg_read_stereo_amp(codec, reg, val); - if (verb == AC_VERB_GET_PROC_COEF) - return hda_reg_read_coef(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_read_stereo_amp(codec, reg, val); + goto out; + } + if (verb == AC_VERB_GET_PROC_COEF) { + err = hda_reg_read_coef(codec, reg, val); + goto out; + } if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE) reg &= ~AC_AMP_FAKE_MUTE; err = snd_hdac_exec_verb(codec, reg, 0, val); if (err < 0) - return err; + goto out; /* special handling for asymmetric reads */ if (verb == AC_VERB_GET_POWER_STATE) { if (*val & AC_PWRST_ERROR) @@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) else /* take only the actual state */ *val = (*val >> 4) & 0x0f; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static int hda_reg_write(void *context, unsigned int reg, unsigned int val) @@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) struct hdac_device *codec = context; unsigned int verb; int i, bytes, err; + int pm_lock = 0; if (codec->caps_overwriting) return 0; @@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (codec->addr << 28); verb = get_verb(reg); - if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) - return codec->lazy_cache ? 0 : -EAGAIN; + if (verb != AC_VERB_SET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return codec->lazy_cache ? 0 : -EAGAIN; + } - if (is_stereo_amp_verb(reg)) - return hda_reg_write_stereo_amp(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_write_stereo_amp(codec, reg, val); + goto out; + } - if (verb == AC_VERB_SET_PROC_COEF) - return hda_reg_write_coef(codec, reg, val); + if (verb == AC_VERB_SET_PROC_COEF) { + err = hda_reg_write_coef(codec, reg, val); + goto out; + } switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: @@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); err = snd_hdac_exec_verb(codec, reg, 0, NULL); if (err < 0) - return err; + goto out; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static const struct regmap_config hda_regmap_cfg = { -- cgit v1.2.3 From a1ce94d03bfdfaa1ef31c1a84383105888ac5f3b Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Wed, 2 Mar 2016 19:26:28 +0100 Subject: ALSA: seq: Provide card number / PID via sequencer client info rawmidi devices expose the card number via IOCTLs, which allows to find the corresponding device in sysfs. The sequencer provides no identifing data. Chromium works around this issue by scanning rawmidi as well as sequencer devices and matching them by using assumtions, how the kernel register sequencer devices. This changes adds support for exposing the card number for kernel clients as well as the PID for user client. The minor of the API version is changed to distinguish between the zero initialised reserved field and card number 0. [minor coding style fixes by tiwai] Signed-off-by: Martin Koegler Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- include/uapi/sound/asequencer.h | 6 ++++-- sound/core/seq/seq_clientmgr.c | 14 ++++++++++++++ sound/core/seq/seq_clientmgr.h | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index af96f2044f91..7b7659a79ac4 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -25,7 +25,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2) /** * definition of sequencer event types @@ -357,7 +357,9 @@ struct snd_seq_client_info { unsigned char event_filter[32]; /* event filter bitmap */ int num_ports; /* RO: number of ports */ int event_lost; /* number of lost events */ - char reserved[64]; /* for future use */ + int card; /* RO: card number[kernel] */ + int pid; /* RO: pid[user] */ + char reserved[56]; /* for future use */ }; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 58e79e02f217..d6d9419d8bac 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file) /* fill client data */ user->file = file; sprintf(client->name, "Client-%d", c); + client->data.user.owner = get_pid(task_pid(current)); /* make others aware this new client */ snd_seq_system_client_ev_client_start(c); @@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file) seq_free_client(client); if (client->data.user.fifo) snd_seq_fifo_delete(&client->data.user.fifo); + put_pid(client->data.user.owner); kfree(client); } @@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr, info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); info->num_ports = cptr->num_ports; + + if (cptr->type == USER_CLIENT) + info->pid = pid_vnr(cptr->data.user.owner); + else + info->pid = -1; + + if (cptr->type == KERNEL_CLIENT) + info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1; + else + info->card = -1; + memset(info->reserved, 0, sizeof(info->reserved)); } @@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, client->accept_input = 1; client->accept_output = 1; + client->data.kernel.card = card; va_start(args, name_fmt); vsnprintf(client->name, sizeof(client->name), name_fmt, args); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index 20f0a725ec7d..c6614254ef8a 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -33,6 +33,7 @@ struct snd_seq_user_client { struct file *file; /* file struct of client */ /* ... */ + struct pid *owner; /* fifo */ struct snd_seq_fifo *fifo; /* queue for incoming events */ @@ -41,6 +42,7 @@ struct snd_seq_user_client { struct snd_seq_kernel_client { /* ... */ + struct snd_card *card; }; -- cgit v1.2.3 From 8ae25b760ade9856e5a217ca4f7c3d531b312ca4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 7 Mar 2016 22:35:42 +0900 Subject: ALSA: dice: have two sets of isochronous resources/streams Currently ALSA dice driver handles a pair of isochronous resources for IEC 61883-1/6 packet streaming. While, according to some documents about ASICs named as 'Dice', several isochronous streams are available. Here, I start to describe ASICs produced under 'Dice' name. * Dice II (designed by wavefront semiconductor, including TCAT's IP) * STD (with limited functionality of DTCP) * CP (with full functionality of DTCP) * TCD2210/2210-E (so-called 'Dice Mini') * TCD2220/2220-E (so-called 'Dice Jr.') * TCD3070-CH (so-called 'Dice III') Some documents are public and we can see hardware design of them. We can find some articles about hardware internal register definitions (not registers exported to IEEE 1394 bus). * DICE II User Guide * http://www.tctechnologies.tc/archive/downloads/dice_ii_user_guide.pdf * 6.1 AVS Audio Receivers * Table 6.1: AVS Audio Receiver Memory Map * ARX1-ARX4 * 6.2 AVS Audio Transmitters * Table 6.2: AVS Audio Transmitter Memory Map * ATX1, ATX2 * TCD22xx User Guide * http://www.tctechnologies.tc/downloads/tcd22xx_user_guide.pdf * 6.1 AVS Audio Receivers * Table 66: AVS Audio Receiver Memory Map * ARX1, ARX2 * 6/2 AVS Audio Transmitters * Table 67: AVS Audio Transmitter Memory Map * ATX1, ATX2 * DICE III * http://www.tctechnologies.tc/downloads/TCD3070-CH.pdf * Dual stream 63 channel transmitter/receiver For Dice II and TCD22xx series, maximum 16 data channels are transferred in an AMDTP packet, while for Dice III, maximum 32 data channels are transferred. According to the design of the series of these ASICs, this commit allows this driver to handle additional set of isochronous resources. For practical reason, two pair of isochronous resources are added. As of this commit, this driver still use a pair of the first isochronous resources. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-midi.c | 8 +++--- sound/firewire/dice/dice-pcm.c | 34 ++++++++++++++---------- sound/firewire/dice/dice-stream.c | 54 +++++++++++++++++++-------------------- sound/firewire/dice/dice.h | 31 +++++++++++++++++++--- 4 files changed, 79 insertions(+), 48 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 2461311e695a..a040617505a7 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); @@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index a5c9b58655ef..e25294910736 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -22,7 +22,7 @@ static int limit_channels_and_rates(struct snd_dice *dice, * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ - if (stream == &dice->tx_stream) { + if (stream == &dice->tx_stream[0]) { err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { @@ -74,10 +74,10 @@ static int init_hw_info(struct snd_dice *dice, if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; - stream = &dice->tx_stream; + stream = &dice->tx_stream[0]; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; - stream = &dice->rx_stream; + stream = &dice->rx_stream[0]; } err = limit_channels_and_rates(dice, runtime, stream); @@ -122,6 +122,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -135,7 +136,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params)); return 0; } @@ -143,6 +144,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -156,7 +158,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params)); return 0; } @@ -196,26 +198,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->tx_stream); + amdtp_stream_pcm_prepare(stream); return 0; } static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->rx_stream); + amdtp_stream_pcm_prepare(stream); return err; } @@ -223,13 +227,14 @@ static int playback_prepare(struct snd_pcm_substream *substream) static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -240,13 +245,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -258,14 +264,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; - return amdtp_stream_pcm_pointer(&dice->tx_stream); + return amdtp_stream_pcm_pointer(stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; - return amdtp_stream_pcm_pointer(&dice->rx_stream); + return amdtp_stream_pcm_pointer(stream); } int snd_dice_create_pcm(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index df035b1dd44a..15d581de5cae 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -72,7 +72,7 @@ static void release_resources(struct snd_dice *dice, /* Reset channel number */ channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources) + if (resources == &dice->tx_resources[0]) snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, &channel, sizeof(channel)); else @@ -96,7 +96,7 @@ static int keep_resources(struct snd_dice *dice, /* Set channel number */ channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources) + if (resources == &dice->tx_resources[0]) err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, &channel, sizeof(channel)); else @@ -113,10 +113,10 @@ static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) amdtp_stream_pcm_abort(stream); amdtp_stream_stop(stream); - if (stream == &dice->tx_stream) - release_resources(dice, &dice->tx_resources); + if (stream == &dice->tx_stream[0]) + release_resources(dice, &dice->tx_resources[0]); else - release_resources(dice, &dice->rx_resources); + release_resources(dice, &dice->rx_resources[0]); } static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, @@ -128,12 +128,12 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, bool double_pcm_frames; int err; - if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) { + resources = &dice->tx_resources[0]; err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { - resources = &dice->rx_resources; + resources = &dice->rx_resources[0]; err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, reg, sizeof(reg)); } @@ -200,8 +200,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (dice->substreams_counter == 0) goto end; - master = &dice->rx_stream; - slave = &dice->tx_stream; + master = &dice->rx_stream[0]; + slave = &dice->tx_stream[0]; /* Some packet queueing errors. */ if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) @@ -275,8 +275,8 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice) snd_dice_transaction_clear_enable(dice); - stop_stream(dice, &dice->tx_stream); - stop_stream(dice, &dice->rx_stream); + stop_stream(dice, &dice->tx_stream[0]); + stop_stream(dice, &dice->rx_stream[0]); } static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) @@ -285,11 +285,11 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) struct fw_iso_resources *resources; enum amdtp_stream_direction dir; - if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) { + resources = &dice->tx_resources[0]; dir = AMDTP_IN_STREAM; } else { - resources = &dice->rx_resources; + resources = &dice->rx_resources[0]; dir = AMDTP_OUT_STREAM; } @@ -315,10 +315,10 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) { struct fw_iso_resources *resources; - if (stream == &dice->tx_stream) - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) + resources = &dice->tx_resources[0]; else - resources = &dice->rx_resources; + resources = &dice->rx_resources[0]; amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); @@ -330,13 +330,13 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice) dice->substreams_counter = 0; - err = init_stream(dice, &dice->tx_stream); + err = init_stream(dice, &dice->tx_stream[0]); if (err < 0) goto end; - err = init_stream(dice, &dice->rx_stream); + err = init_stream(dice, &dice->rx_stream[0]); if (err < 0) - destroy_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream[0]); end: return err; } @@ -345,8 +345,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { snd_dice_transaction_clear_enable(dice); - destroy_stream(dice, &dice->tx_stream); - destroy_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->tx_stream[0]); + destroy_stream(dice, &dice->rx_stream[0]); dice->substreams_counter = 0; } @@ -363,11 +363,11 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false; - stop_stream(dice, &dice->rx_stream); - stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream[0]); + stop_stream(dice, &dice->tx_stream[0]); - fw_iso_resources_update(&dice->rx_resources); - fw_iso_resources_update(&dice->tx_resources); + fw_iso_resources_update(&dice->rx_resources[0]); + fw_iso_resources_update(&dice->tx_resources[0]); } static void dice_lock_changed(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 423cdba99726..8fba87d83810 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -39,6 +39,29 @@ #include "../lib.h" #include "dice-interface.h" +/* + * This module support maximum 2 pairs of tx/rx isochronous streams for + * our convinience. + * + * In documents for ASICs called with a name of 'DICE': + * - ASIC for DICE II: + * - Maximum 2 tx and 4 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2210/2210-E (so-called 'Dice Mini'): + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2220/2220-E (so-called 'Dice Jr.') + * - 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD3070-CH (so-called 'Dice III') + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 32 data channels. + * + * For the above, MIDI conformant data channel is just on the first isochronous + * stream. + */ +#define MAX_STREAMS 2 + struct snd_dice { struct snd_card *card; struct fw_unit *unit; @@ -67,10 +90,10 @@ struct snd_dice { wait_queue_head_t hwdep_wait; /* For streaming */ - struct fw_iso_resources tx_resources; - struct fw_iso_resources rx_resources; - struct amdtp_stream tx_stream; - struct amdtp_stream rx_stream; + struct fw_iso_resources tx_resources[MAX_STREAMS]; + struct fw_iso_resources rx_resources[MAX_STREAMS]; + struct amdtp_stream tx_stream[MAX_STREAMS]; + struct amdtp_stream rx_stream[MAX_STREAMS]; bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; -- cgit v1.2.3 From 436b5abe2224d6788eda7c5eb627b593bd96c438 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 7 Mar 2016 22:35:43 +0900 Subject: ALSA: dice: handle whole available isochronous streams This commit enables ALSA dice driver to handle whole available streams. In Dice, certain registers represent the number of available streams at current sampling transfer frequency for both directions. The parameters of each stream are represented in a block of register. This block is aligned sequentially. These streams start simultaneously by writing enable bit to a register. This commit operates these registers when starting/stopping streams. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- 1 file changed, 255 insertions(+), 143 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 15d581de5cae..2077f182726a 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -65,85 +65,84 @@ static int ensure_phase_lock(struct snd_dice *dice) return 0; } -static void release_resources(struct snd_dice *dice, - struct fw_iso_resources *resources) +static int get_register_params(struct snd_dice *dice, unsigned int params[4]) { - __be32 channel; - - /* Reset channel number */ - channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources[0]) - snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); - - fw_iso_resources_free(resources); -} - -static int keep_resources(struct snd_dice *dice, - struct fw_iso_resources *resources, - unsigned int max_payload_bytes) -{ - __be32 channel; + __be32 reg[2]; int err; - err = fw_iso_resources_allocate(resources, max_payload_bytes, - fw_parent_device(dice->unit)->max_speed); + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); if (err < 0) - goto end; + return err; + params[0] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + params[1] = be32_to_cpu(reg[1]) * 4; - /* Set channel number */ - channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources[0]) - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); if (err < 0) - release_resources(dice, resources); -end: - return err; + return err; + params[2] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + params[3] = be32_to_cpu(reg[1]) * 4; + + return 0; } -static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void release_resources(struct snd_dice *dice) { - amdtp_stream_pcm_abort(stream); - amdtp_stream_stop(stream); + unsigned int i; + + for (i = 0; i < MAX_STREAMS; i++) { + if (amdtp_stream_running(&dice->tx_stream[i])) { + amdtp_stream_pcm_abort(&dice->tx_stream[i]); + amdtp_stream_stop(&dice->tx_stream[i]); + } + if (amdtp_stream_running(&dice->rx_stream[i])) { + amdtp_stream_pcm_abort(&dice->rx_stream[i]); + amdtp_stream_stop(&dice->rx_stream[i]); + } - if (stream == &dice->tx_stream[0]) - release_resources(dice, &dice->tx_resources[0]); - else - release_resources(dice, &dice->rx_resources[0]); + fw_iso_resources_free(&dice->tx_resources[i]); + fw_iso_resources_free(&dice->rx_resources[i]); + } } -static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, - unsigned int rate) +static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int count, unsigned int size) { + __be32 reg; + unsigned int i; + + for (i = 0; i < count; i++) { + reg = cpu_to_be32((u32)-1); + if (dir == AMDTP_IN_STREAM) { + snd_dice_transaction_write_tx(dice, + size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); + } else { + snd_dice_transaction_write_rx(dice, + size * i + RX_ISOCHRONOUS, + ®, sizeof(reg)); + } + } +} + +static int keep_resources(struct snd_dice *dice, + enum amdtp_stream_direction dir, unsigned int index, + unsigned int rate, unsigned int pcm_chs, + unsigned int midi_ports) +{ + struct amdtp_stream *stream; struct fw_iso_resources *resources; - __be32 reg[2]; - unsigned int i, pcm_chs, midi_ports; bool double_pcm_frames; + unsigned int i; int err; - if (stream == &dice->tx_stream[0]) { - resources = &dice->tx_resources[0]; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - reg, sizeof(reg)); + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources[0]; - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - reg, sizeof(reg)); + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } - if (err < 0) - goto end; - - pcm_chs = be32_to_cpu(reg[0]); - midi_ports = be32_to_cpu(reg[1]); - /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -163,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, double_pcm_frames); if (err < 0) - goto end; + return err; if (double_pcm_frames) { pcm_chs /= 2; @@ -175,122 +174,208 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, } } - err = keep_resources(dice, resources, - amdtp_stream_get_max_payload(stream)); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to keep isochronous resources\n"); - goto end; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(dice->unit)->max_speed); +} + +static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int rate, unsigned int count, + unsigned int size) +{ + __be32 reg[2]; + unsigned int i, pcm_chs, midi_ports; + struct amdtp_stream *streams; + struct fw_iso_resources *resources; + int err = 0; + + if (dir == AMDTP_IN_STREAM) { + streams = dice->tx_stream; + resources = dice->tx_resources; + } else { + streams = dice->rx_stream; + resources = dice->rx_resources; + } + + for (i = 0; i < count; i++) { + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_read_tx(dice, + size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, + size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); + } + if (err < 0) + return err; + pcm_chs = be32_to_cpu(reg[0]); + midi_ports = be32_to_cpu(reg[1]); + + err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); + if (err < 0) + return err; + + reg[0] = cpu_to_be32(resources[i].channel); + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_write_tx(dice, + size * i + TX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } else { + err = snd_dice_transaction_write_rx(dice, + size * i + RX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } + if (err < 0) + return err; + + err = amdtp_stream_start(&streams[i], resources[i].channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; } - err = amdtp_stream_start(stream, resources->channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - release_resources(dice, resources); -end: return err; } +/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { - struct amdtp_stream *master, *slave; unsigned int curr_rate; - int err = 0; + unsigned int i; + unsigned int reg_params[4]; + bool need_to_start; + int err; if (dice->substreams_counter == 0) - goto end; - - master = &dice->rx_stream[0]; - slave = &dice->tx_stream[0]; + return -EIO; - /* Some packet queueing errors. */ - if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) - stop_stream(dice, master); + err = get_register_params(dice, reg_params); + if (err < 0) + return err; - /* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) { dev_err(&dice->unit->device, "fail to get sampling rate\n"); - goto end; + return err; } if (rate == 0) rate = curr_rate; - if (rate != curr_rate) { - err = -EINVAL; - goto end; + if (rate != curr_rate) + return -EINVAL; + + /* Judge to need to restart streams. */ + for (i = 0; i < MAX_STREAMS; i++) { + if (i < reg_params[0]) { + if (amdtp_streaming_error(&dice->tx_stream[i]) || + !amdtp_stream_running(&dice->tx_stream[i])) + break; + } + if (i < reg_params[2]) { + if (amdtp_streaming_error(&dice->rx_stream[i]) || + !amdtp_stream_running(&dice->rx_stream[i])) + break; + } } + need_to_start = (i < MAX_STREAMS); - if (!amdtp_stream_running(master)) { - stop_stream(dice, slave); + if (need_to_start) { + /* Stop transmission. */ snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + release_resources(dice); err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to ensure phase lock\n"); - goto end; + return err; } /* Start both streams. */ - err = start_stream(dice, master, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP master stream\n"); - goto end; - } - err = start_stream(dice, slave, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP slave stream\n"); - stop_stream(dice, master); - goto end; - } + err = start_streams(dice, AMDTP_IN_STREAM, rate, reg_params[0], + reg_params[1]); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, reg_params[2], + reg_params[3]); + if (err < 0) + goto error; + err = snd_dice_transaction_set_enable(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to enable interface\n"); - stop_stream(dice, master); - stop_stream(dice, slave); - goto end; + goto error; } - /* Wait first callbacks */ - if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { - snd_dice_transaction_clear_enable(dice); - stop_stream(dice, master); - stop_stream(dice, slave); - err = -ETIMEDOUT; + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < reg_params[0] && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < reg_params[2] && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } } } -end: + + return err; +error: + snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], reg_params[3]); + release_resources(dice); return err; } +/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + if (dice->substreams_counter > 0) return; snd_dice_transaction_clear_enable(dice); - stop_stream(dice, &dice->tx_stream[0]); - stop_stream(dice, &dice->rx_stream[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } + + release_resources(dice); } -static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int index) { - int err; + struct amdtp_stream *stream; struct fw_iso_resources *resources; - enum amdtp_stream_direction dir; + int err; - if (stream == &dice->tx_stream[0]) { - resources = &dice->tx_resources[0]; - dir = AMDTP_IN_STREAM; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources[0]; - dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } err = fw_iso_resources_init(resources, dice->unit); @@ -311,14 +396,20 @@ end: * This function should be called before starting streams or after stopping * streams. */ -static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void destroy_stream(struct snd_dice *dice, + enum amdtp_stream_direction dir, + unsigned int index) { + struct amdtp_stream *stream; struct fw_iso_resources *resources; - if (stream == &dice->tx_stream[0]) - resources = &dice->tx_resources[0]; - else - resources = &dice->rx_resources[0]; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; + } else { + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; + } amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); @@ -326,33 +417,53 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) int snd_dice_stream_init_duplex(struct snd_dice *dice) { - int err; + int i, err; - dice->substreams_counter = 0; - - err = init_stream(dice, &dice->tx_stream[0]); - if (err < 0) - goto end; + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_IN_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + goto end; + } + } - err = init_stream(dice, &dice->rx_stream[0]); - if (err < 0) - destroy_stream(dice, &dice->tx_stream[0]); + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_OUT_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + for (i = 0; i < MAX_STREAMS; i++) + destroy_stream(dice, AMDTP_IN_STREAM, i); + break; + } + } end: return err; } void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + snd_dice_transaction_clear_enable(dice); - destroy_stream(dice, &dice->tx_stream[0]); - destroy_stream(dice, &dice->rx_stream[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } + + release_resources(dice); dice->substreams_counter = 0; } void snd_dice_stream_update_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -363,11 +474,12 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false; - stop_stream(dice, &dice->rx_stream[0]); - stop_stream(dice, &dice->tx_stream[0]); - - fw_iso_resources_update(&dice->rx_resources[0]); - fw_iso_resources_update(&dice->tx_resources[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } } static void dice_lock_changed(struct snd_dice *dice) -- cgit v1.2.3 From 4bdc495c87b323721eace38c581d7bd2652aa613 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 7 Mar 2016 22:35:44 +0900 Subject: ALSA: dice: handle several PCM substreams when any isochronous streams are available In former commits, ALSA dice driver can handle available isochronous streams. This commit adds support for several PCM substreams on the streams. The additional PCM substreams are available via another ALSA PCM character devices so that one ALSA PCM application can handle them without cumbersome operations. For example, two PCM substreams are available on each stream, two ALSA character devices are added for them. In configuration space of alsa-lib, it's represented with 'hw:0,0' and 'hw:0,1'. The PCM substreams are constraint to parameters of the corresponding streams. If the PCM substreams are unavailable for some reasons, open(2) to ALSA PCM character device returns error and reports ENXIO. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-pcm.c | 121 +++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 46 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index e25294910736..05e2e18b4c30 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -11,28 +11,34 @@ static int limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, - struct amdtp_stream *stream) + enum amdtp_stream_direction dir, + unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; + struct amdtp_stream *stream; unsigned int rate; - __be32 reg[2]; + __be32 reg; int err; /* * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ - if (stream == &dice->tx_stream[0]) { - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - reg, sizeof(reg)); + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + err = snd_dice_transaction_read_tx(dice, + size * index + TX_NUMBER_AUDIO, + ®, sizeof(reg)); } else { - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - reg, sizeof(reg)); + stream = &dice->rx_stream[index]; + err = snd_dice_transaction_read_rx(dice, + size * index + RX_NUMBER_AUDIO, + ®, sizeof(reg)); } if (err < 0) return err; - hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]); + hw->channels_min = hw->channels_max = be32_to_cpu(reg); /* Retrieve current sampling transfer frequency and limit to it. */ err = snd_dice_transaction_get_rate(dice, &rate); @@ -62,7 +68,10 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + enum amdtp_stream_direction dir; struct amdtp_stream *stream; + __be32 reg[2]; + unsigned int count, size; int err; hw->info = SNDRV_PCM_INFO_MMAP | @@ -74,13 +83,28 @@ static int init_hw_info(struct snd_dice *dice, if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; - stream = &dice->tx_stream[0]; + dir = AMDTP_IN_STREAM; + stream = &dice->tx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, + sizeof(reg)); } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; - stream = &dice->rx_stream[0]; + dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, + sizeof(reg)); } - err = limit_channels_and_rates(dice, runtime, stream); + if (err < 0) + return err; + + count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + if (substream->pcm->device >= count) + return -ENXIO; + + size = be32_to_cpu(reg[1]) * 4; + err = limit_channels_and_rates(dice, substream->runtime, dir, + substream->pcm->device, size); if (err < 0) return err; limit_period_and_buffer(hw); @@ -122,7 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -144,7 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -198,7 +222,7 @@ static int playback_hw_free(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err; mutex_lock(&dice->mutex); @@ -212,7 +236,7 @@ static int capture_prepare(struct snd_pcm_substream *substream) static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err; mutex_lock(&dice->mutex); @@ -227,7 +251,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -245,7 +269,7 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -264,14 +288,14 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; return amdtp_stream_pcm_pointer(stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; return amdtp_stream_pcm_pointer(stream); } @@ -304,40 +328,45 @@ int snd_dice_create_pcm(struct snd_dice *dice) }; __be32 reg; struct snd_pcm *pcm; - unsigned int capture, playback; + unsigned int i, max_capture, max_playback, capture, playback; int err; - /* - * Check whether PCM substreams are required. - * - * TODO: in the case that any PCM substreams are not avail at a certain - * sampling transfer frequency? - */ - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - ®, sizeof(reg)); - if (err < 0) - return err; - if (be32_to_cpu(reg) > 0) - capture = 1; - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - ®, sizeof(reg)); + /* Check whether PCM substreams are required. */ + max_capture = max_playback = 0; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; - if (be32_to_cpu(reg) > 0) - playback = 1; + max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - - if (capture > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); - - if (playback > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + + for (i = 0; i < MAX_STREAMS; i++) { + capture = playback = 0; + if (i < max_capture) + capture = 1; + if (i < max_playback) + playback = 1; + if (capture == 0 && playback == 0) + break; + + err = snd_pcm_new(dice->card, "DICE", i, playback, capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + + if (capture > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &capture_ops); + + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &playback_ops); + } return 0; } -- cgit v1.2.3 From 7cafc65b3aa1b6970f92211c55712f115d876cfc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 7 Mar 2016 22:35:45 +0900 Subject: ALSA: dice: force to add two pcm devices for listed models Some models reduce the number of available isochronous streams for higher sampling transfer frequency. Such models bring an issue about how to add PCM substreams. When at lower sampling transfer frequency, the models reports whole available streams, thus this driver can add enough number of PCM substreams at probing time. On the other hand, at higher sampling transfer frequency, this driver can just add reduced number of PCM substreams. After probed, even if the sampling transfer frequency is changed to lower rate, fewer PCM substreams are actually available. This is inconvenience. For the reason, this commit adds a list so that this driver assume models on the list to have two pairs of PCM substreams. This list keeps the name of model in which the number of available streams differs depending on sampling transfer frequency. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-pcm.c | 24 +++++++++++++++--------- sound/firewire/dice/dice.c | 41 +++++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice.h | 2 ++ 3 files changed, 58 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 05e2e18b4c30..4aa0249826fd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -332,16 +332,22 @@ int snd_dice_create_pcm(struct snd_dice *dice) int err; /* Check whether PCM substreams are required. */ - max_capture = max_playback = 0; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); - if (err < 0) - return err; - max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + if (dice->force_two_pcms) { + max_capture = max_playback = 2; + } else { + max_capture = max_playback = 0; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); - if (err < 0) - return err; - max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + } for (i = 0; i < MAX_STREAMS; i++) { capture = playback = 0; diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index f7303a650ac2..8b64aef31a86 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2"); #define OUI_WEISS 0x001c6a #define OUI_LOUD 0x000ff2 +#define OUI_FOCUSRITE 0x00130e +#define OUI_TCELECTRONIC 0x001486 #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2"); #define PROBE_DELAY_MS (2 * MSEC_PER_SEC) +/* + * Some models support several isochronous channels, while these streams are not + * always available. In this case, add the model name to this list. + */ +static bool force_two_pcm_support(struct fw_unit *unit) +{ + const char *const models[] = { + /* TC Electronic models. */ + "StudioKonnekt48", + /* Focusrite models. */ + "SAFFIRE_PRO_40", + "LIQUID_SAFFIRE_56", + "SAFFIRE_PRO_40_1", + }; + char model[32]; + unsigned int i; + int err; + + err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model)); + if (err < 0) + return false; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + if (strcmp(models[i], model) == 0) + break; + } + + return i < ARRAY_SIZE(models); +} + static int check_dice_category(struct fw_unit *unit) { struct fw_device *device = fw_parent_device(unit); @@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit) break; } } + + if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) { + if (force_two_pcm_support(unit)) + return 0; + } + if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; else if (vendor == OUI_LOUD) @@ -150,6 +188,9 @@ static void do_registration(struct work_struct *work) if (err < 0) return; + if (force_two_pcm_support(dice->unit)) + dice->force_two_pcms = true; + err = snd_dice_transaction_init(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 8fba87d83810..e6c07857f475 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -97,6 +97,8 @@ struct snd_dice { bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; + + bool force_two_pcms; }; enum snd_dice_addr_type { -- cgit v1.2.3 From 391005e8e3cd5bb641dff5abb1ad37254602cd7e Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 10 Mar 2016 09:04:07 +0530 Subject: ASoC: hdac_hdmi: Fix infoframe programming Audio infoframe used incorrect buffer, so fix it. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 7851dd204a49..4f7bd1fae0db 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -303,7 +303,6 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, if (ret < 0) return ret; - dip = (u8 *)&frame; break; case DRM_ELD_CONN_TYPE_DP: @@ -332,9 +331,9 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, /* Fill infoframe. Index auto-incremented */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { - for (i = 0; i < sizeof(frame); i++) + for (i = 0; i < sizeof(buffer); i++) snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, - AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); } else { for (i = 0; i < sizeof(dp_ai); i++) snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, -- cgit v1.2.3 From 56dc66ff1c6d71f9a38c4a7c000b72b921fe4c89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2016 11:33:43 +0100 Subject: ALSA: hda - Apply reboot D3 fix for CX20724 codec, too Just like CX20722, CX7024 codec also requires the power down at reboot in order to reduce the noise at reboot/shutdown. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=113511 Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6122b8ca872f..56fefbd85782 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - if (codec->core.vendor_id != 0x14f150f2) + switch (codec->core.vendor_id) { + case 0x14f150f2: /* CX20722 */ + case 0x14f150f4: /* CX20724 */ + break; + default: return; + } /* Turn the CX20722 codec into D3 to avoid spurious noises from the internal speaker during (and after) reboot */ -- cgit v1.2.3 From 4f8e4f3537cafc4de128e6bfdf83baa78bc60eb1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2016 12:02:49 +0100 Subject: ALSA: hda - Don't handle ELD notify from invalid port The current Intel HDMI codec driver supports only three fixed ports from port B to port D. However, i915 driver may assign a DP on other ports, e.g. port A, when no eDP is used. This incompatibility is caught later at pin_nid_to_pin_index() and results in a warning message like "HDMI: pin nid 4 not registered" at each time. This patch filters out such invalid events beforehand, so that the kernel won't be too grumbling. Reported-by: Stefan Assmann Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bcbc4ee10130..b90e4f3967de 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2432,6 +2432,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port) struct hda_codec *codec = audio_ptr; int pin_nid = port + 0x04; + /* we assume only from port-B to port-D */ + if (port < 1 || port > 3) + return; + /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ -- cgit v1.2.3 From 8cc1a8ab477e974a3516e73276ef4b6e546c4c65 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 10 Mar 2016 21:44:28 +0900 Subject: ALSA: dice: use structure to represent register parameters instead of array with basic type element In dice interface, two blocks of register are accessible via IEEE 1394 asynchronous transaction to represent the number of supported isochronous streams and the number of quadlets for stream information. Current ALSA dice driver uses array with 'unsigned int' element for temporary cache of these information. But using structure is preferable for begin easily comprehensible. This commit applies a local structure for this aim. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 110 +++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 56 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 2077f182726a..845d5e5884a4 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -12,6 +12,11 @@ #define CALLBACK_TIMEOUT 200 #define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) +struct reg_params { + unsigned int count; + unsigned int size; +}; + const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ [0] = 32000, @@ -65,7 +70,9 @@ static int ensure_phase_lock(struct snd_dice *dice) return 0; } -static int get_register_params(struct snd_dice *dice, unsigned int params[4]) +static int get_register_params(struct snd_dice *dice, + struct reg_params *tx_params, + struct reg_params *rx_params) { __be32 reg[2]; int err; @@ -73,14 +80,16 @@ static int get_register_params(struct snd_dice *dice, unsigned int params[4]) err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); if (err < 0) return err; - params[0] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - params[1] = be32_to_cpu(reg[1]) * 4; + tx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + tx_params->size = be32_to_cpu(reg[1]) * 4; err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); if (err < 0) return err; - params[2] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - params[3] = be32_to_cpu(reg[1]) * 4; + rx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + rx_params->size = be32_to_cpu(reg[1]) * 4; return 0; } @@ -105,21 +114,21 @@ static void release_resources(struct snd_dice *dice) } static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, - unsigned int count, unsigned int size) + struct reg_params *params) { __be32 reg; unsigned int i; - for (i = 0; i < count; i++) { + for (i = 0; i < params->count; i++) { reg = cpu_to_be32((u32)-1); if (dir == AMDTP_IN_STREAM) { snd_dice_transaction_write_tx(dice, - size * i + TX_ISOCHRONOUS, - ®, sizeof(reg)); + params->size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); } else { snd_dice_transaction_write_rx(dice, - size * i + RX_ISOCHRONOUS, - ®, sizeof(reg)); + params->size * i + RX_ISOCHRONOUS, + ®, sizeof(reg)); } } } @@ -180,8 +189,7 @@ static int keep_resources(struct snd_dice *dice, } static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, - unsigned int rate, unsigned int count, - unsigned int size) + unsigned int rate, struct reg_params *params) { __be32 reg[2]; unsigned int i, pcm_chs, midi_ports; @@ -197,15 +205,15 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, resources = dice->rx_resources; } - for (i = 0; i < count; i++) { + for (i = 0; i < params->count; i++) { if (dir == AMDTP_IN_STREAM) { err = snd_dice_transaction_read_tx(dice, - size * i + TX_NUMBER_AUDIO, - reg, sizeof(reg)); + params->size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); } else { err = snd_dice_transaction_read_rx(dice, - size * i + RX_NUMBER_AUDIO, - reg, sizeof(reg)); + params->size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); } if (err < 0) return err; @@ -219,12 +227,12 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, reg[0] = cpu_to_be32(resources[i].channel); if (dir == AMDTP_IN_STREAM) { err = snd_dice_transaction_write_tx(dice, - size * i + TX_ISOCHRONOUS, - reg, sizeof(reg[0])); + params->size * i + TX_ISOCHRONOUS, + reg, sizeof(reg[0])); } else { err = snd_dice_transaction_write_rx(dice, - size * i + RX_ISOCHRONOUS, - reg, sizeof(reg[0])); + params->size * i + RX_ISOCHRONOUS, + reg, sizeof(reg[0])); } if (err < 0) return err; @@ -247,14 +255,14 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { unsigned int curr_rate; unsigned int i; - unsigned int reg_params[4]; + struct reg_params tx_params, rx_params; bool need_to_start; int err; if (dice->substreams_counter == 0) return -EIO; - err = get_register_params(dice, reg_params); + err = get_register_params(dice, &tx_params, &rx_params); if (err < 0) return err; @@ -271,12 +279,12 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) /* Judge to need to restart streams. */ for (i = 0; i < MAX_STREAMS; i++) { - if (i < reg_params[0]) { + if (i < tx_params.count) { if (amdtp_streaming_error(&dice->tx_stream[i]) || !amdtp_stream_running(&dice->tx_stream[i])) break; } - if (i < reg_params[2]) { + if (i < rx_params.count) { if (amdtp_streaming_error(&dice->rx_stream[i]) || !amdtp_stream_running(&dice->rx_stream[i])) break; @@ -287,10 +295,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (need_to_start) { /* Stop transmission. */ snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], - reg_params[1]); - stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], - reg_params[3]); + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); release_resources(dice); err = ensure_phase_lock(dice); @@ -301,12 +307,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) } /* Start both streams. */ - err = start_streams(dice, AMDTP_IN_STREAM, rate, reg_params[0], - reg_params[1]); + err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); if (err < 0) goto error; - err = start_streams(dice, AMDTP_OUT_STREAM, rate, reg_params[2], - reg_params[3]); + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); if (err < 0) goto error; @@ -318,10 +322,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) } for (i = 0; i < MAX_STREAMS; i++) { - if ((i < reg_params[0] && + if ((i < tx_params.count && !amdtp_stream_wait_callback(&dice->tx_stream[i], CALLBACK_TIMEOUT)) || - (i < reg_params[2] && + (i < rx_params.count && !amdtp_stream_wait_callback(&dice->rx_stream[i], CALLBACK_TIMEOUT))) { err = -ETIMEDOUT; @@ -333,8 +337,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) return err; error: snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], reg_params[1]); - stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], reg_params[3]); + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); release_resources(dice); return err; } @@ -346,18 +350,16 @@ error: */ void snd_dice_stream_stop_duplex(struct snd_dice *dice) { - unsigned int reg_params[4]; + struct reg_params tx_params, rx_params; if (dice->substreams_counter > 0) return; snd_dice_transaction_clear_enable(dice); - if (get_register_params(dice, reg_params) == 0) { - stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], - reg_params[1]); - stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], - reg_params[3]); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); } release_resources(dice); @@ -444,15 +446,13 @@ end: void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { - unsigned int reg_params[4]; + struct reg_params tx_params, rx_params; snd_dice_transaction_clear_enable(dice); - if (get_register_params(dice, reg_params) == 0) { - stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], - reg_params[1]); - stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], - reg_params[3]); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); } release_resources(dice); @@ -462,7 +462,7 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice) void snd_dice_stream_update_duplex(struct snd_dice *dice) { - unsigned int reg_params[4]; + struct reg_params tx_params, rx_params; /* * On a bus reset, the DICE firmware disables streaming and then goes @@ -474,11 +474,9 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false; - if (get_register_params(dice, reg_params) == 0) { - stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], - reg_params[1]); - stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], - reg_params[3]); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); } } -- cgit v1.2.3 From 1a414f48d7ccb197b61c18888b72327c98171844 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2016 15:49:35 +0100 Subject: ALSA: hda - Add a sanity check of pin / port mapping on i915 HDMI/DP There is an implicit rule to map between pin NID and port number on Intel HDMI/DP codec: the mapping is fixed only for NID 0x05, 0x06 and 0x07. For avoiding the possible memory corruption, add a sanity check for the NID value and splat WARN_ON() for invalid accesses. Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index f6854dbd7d8d..fb96aead8257 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk); */ static int pin2port(hda_nid_t pin_nid) { + if (WARN_ON(pin_nid < 5 || pin_nid > 7)) + return -1; return pin_nid - 4; } @@ -144,10 +146,14 @@ static int pin2port(hda_nid_t pin_nid) int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate) { struct i915_audio_component *acomp = bus->audio_component; + int port; if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) return -ENODEV; - return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate); + port = pin2port(nid); + if (port < 0) + return -EINVAL; + return acomp->ops->sync_audio_rate(acomp->dev, port, rate); } EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); @@ -175,11 +181,15 @@ int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, bool *audio_enabled, char *buffer, int max_bytes) { struct i915_audio_component *acomp = bus->audio_component; + int port; if (!acomp || !acomp->ops || !acomp->ops->get_eld) return -ENODEV; - return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled, + port = pin2port(nid); + if (port < 0) + return -EINVAL; + return acomp->ops->get_eld(acomp->dev, port, audio_enabled, buffer, max_bytes); } EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); -- cgit v1.2.3 From 0ab1ace856205d10cbc1924b2d931c01ffd216a6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2016 20:56:20 +0100 Subject: ALSA: pcm: Avoid "BUG:" string for warnings again The commit [d507941beb1e: ALSA: pcm: Correct PCM BUG error message] made the warning prefix back to "BUG:" due to its previous wrong prefix. But a kernel message containing "BUG:" seems taken as an Oops message wrongly by some brain-dead daemons, and it annoys users in the end. Instead of teaching daemons, change the string again to a more reasonable one. Fixes: 507941beb1e ('ALSA: pcm: Correct PCM BUG error message') Cc: # v3.19+ Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6b5a811e01a5..3a9b66c6e09c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); pcm_err(substream->pcm, - "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, runtime->period_size); } -- cgit v1.2.3 From 5602a64318a4a6aad028141bb68e9bbd6bb5749a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 10 Mar 2016 10:46:07 +0000 Subject: ASoC: wm_adsp: Use correct local length in error message Fixes: 44029e9e1290 ("ASoC: wm_adsp: wm_coeff_{read|write}_control should use passed length") Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 36687db5da61..ba9213c0ad53 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -787,7 +787,7 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, ret = regmap_raw_read(dsp->regmap, reg, scratch, len); if (ret) { adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", - ctl->len, reg, ret); + len, reg, ret); kfree(scratch); return ret; } -- cgit v1.2.3 From 6ef2f68fa38bf415830f67903d87180d933e0f47 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Fri, 11 Mar 2016 12:04:02 +0800 Subject: ALSA: hda - fix the mic mute button and led problem for a Lenovo AIO This Lenovo ThinkCentre AIO also uses Line2 as mic mute button and uses GPIO2 to control the mic mute led, so applying this quirk can make both the button and led work. Cc: stable@vger.kernel.org BugLink: https://bugs.launchpad.net/bugs/1555912 Signed-off-by: Hui Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 93d2156b6241..4f5ca0b9ce27 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5556,6 +5556,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), -- cgit v1.2.3 From 4a11ff260053b1097a5afa6c51e8d5e064aa88a7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Mar 2016 13:18:51 +0200 Subject: ASoC: davinci-mcasp: dai format runtime reconfiguration In case when the dai format is set via the dai_link the format configuration happens once when the links are probed. If the McASP lose context after this, the information will be lost and McASP will not going to work correctly. To overcome this issue, we save the fmt and set it within hw_params as well. Reported-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 3cf46afb353f..e1324989bd6b 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -77,6 +77,7 @@ struct davinci_mcasp { u32 fifo_base; struct device *dev; struct snd_pcm_substream *substreams[2]; + unsigned int dai_fmt; /* McASP specific data */ int tdm_slots; @@ -398,6 +399,9 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, bool fs_pol_rising; bool inv_fs = false; + if (!fmt) + return 0; + pm_runtime_get_sync(mcasp->dev); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: @@ -529,6 +533,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); } + + mcasp->dai_fmt = fmt; out: pm_runtime_put(mcasp->dev); return ret; @@ -1026,6 +1032,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int period_size = params_period_size(params); int ret; + ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt); + if (ret) + return ret; + /* * If mcasp is BCLK master, and a BCLK divider was not provided by * the machine driver, we need to calculate the ratio. -- cgit v1.2.3 From dc6cdb420359103e1f20752d01cb1e2bbe8ba8b8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Mar 2016 16:04:20 +0200 Subject: ASoC: davinci: Kconfig: Update the edma-pcm section's dependency and help Instead of depending on individual SoCs make the edma-pcm depend on the eDMA dmaengine driver (TI_EDMA). Update the help text and add DRA7xx family since they have eDMA integrated as well along with sDMA. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 3736d9aabc56..50ca291cc225 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -5,7 +5,7 @@ config SND_DAVINCI_SOC config SND_EDMA_SOC tristate "SoC Audio for Texas Instruments chips using eDMA" - depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI + depends on TI_EDMA select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want audio support for TI SoC which uses eDMA. @@ -13,6 +13,7 @@ config SND_EDMA_SOC - daVinci devices - AM335x - AM437x/AM438x + - DRA7xx family config SND_DAVINCI_SOC_I2S tristate -- cgit v1.2.3 From 568cecf42f35bf40bdf94107fb638997f82827f7 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 11 Mar 2016 23:42:52 +0530 Subject: ASoC: qcom: fix build error While building m32r allmodconfig the build failed with: ERROR: "bad_dma_ops" [sound/soc/qcom/snd-soc-lpass-platform.ko] undefined! ERROR: "dma_common_mmap" [sound/soc/qcom/snd-soc-lpass-platform.ko] undefined! To satisfy the dependency CONFIG_SND_SOC_LPASS_PLATFORM should depend on HAS_DMA. Some other configs also needs the dependency on HAS_DMA as they are directly or indirectly selecting SND_SOC_LPASS_PLATFORM. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 3cc252e55468..8ec9a074b38b 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU config SND_SOC_LPASS_PLATFORM tristate + depends on HAS_DMA select REGMAP_MMIO config SND_SOC_LPASS_IPQ806X tristate + depends on HAS_DMA select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_APQ8016 tristate + depends on HAS_DMA select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM + depends on SND_SOC_QCOM && HAS_DMA select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -34,7 +37,7 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM + depends on SND_SOC_QCOM && HAS_DMA select SND_SOC_LPASS_APQ8016 help Support for Qualcomm Technologies LPASS audio block in -- cgit v1.2.3 From 45d5eb3a342f2ef3d6dae16b074fdd9a01992fb5 Mon Sep 17 00:00:00 2001 From: John Hsu Date: Fri, 11 Mar 2016 17:33:58 -0800 Subject: ASoC: nau8825: fix issue that pop noise when start playback Reduce pop noise in power up and down sequence when playback. The DAPM widgets graph is reconstructed to ensure the register write sequence at playback matches exactly to the v5 clickless sequence provided by Nuvoton. Signed-off-by: John Hsu Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 169 +++++++++++++++++++++++++++++++++------------ sound/soc/codecs/nau8825.h | 16 ++++- 2 files changed, 141 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c1b87c5800b1..1c8729984c2b 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = { static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_IIC_ADDR_SET, 0x0 }, { NAU8825_REG_CLK_DIVIDER, 0x0050 }, { NAU8825_REG_FLL1, 0x0 }, { NAU8825_REG_FLL2, 0x3126 }, @@ -158,8 +159,7 @@ static const struct reg_default nau8825_reg_defaults[] = { static bool nau8825_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case NAU8825_REG_ENA_CTRL: - case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: @@ -184,8 +184,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg) static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: - case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_INTERRUPT_MASK: case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: @@ -227,10 +226,42 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) static int nau8825_pump_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + switch (event) { case SND_SOC_DAPM_POST_PMU: /* Prevent startup click by letting charge pump to ramp up */ msleep(10); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disables the TESTDAC to let DAC signal pass through. */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); break; default: return -EINVAL; @@ -316,10 +347,10 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, NAU8825_SAR_ADC_EN_SFT, 0), - SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), - SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), - SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0), SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_DACR_SFT, 0), @@ -330,29 +361,48 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), - SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, - 0), + SND_SOC_DAPM_PGA_S("HP amp L", 0, + NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP amp R", 0, + NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, - nau8825_pump_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_PGA("Output Driver R Stage 1", + SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4, NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver L Stage 1", + SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4, NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver R Stage 2", + SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5, NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver L Stage 2", + SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5, NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6, NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6, NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), - SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACL", 7, + NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 7, + NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ + SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, + NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8, + NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0), + + /* High current HPOL/R boost driver */ + SND_SOC_DAPM_PGA_S("HP Boost Driver", 9, + NAU8825_REG_BOOST, 9, 1, NULL, 0), + + /* Class G operation control*/ + SND_SOC_DAPM_PGA_S("Class G", 10, + NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOR"), @@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { {"DACR Mux", "DACR", "DDACR"}, {"HP amp L", NULL, "DACL Mux"}, {"HP amp R", NULL, "DACR Mux"}, - {"HP amp L", NULL, "HP amp power"}, - {"HP amp R", NULL, "HP amp power"}, - {"ADACL", NULL, "HP amp L"}, - {"ADACR", NULL, "HP amp R"}, - {"ADACL", NULL, "ADACL Clock"}, - {"ADACR", NULL, "ADACR Clock"}, - {"Output Driver L Stage 1", NULL, "ADACL"}, - {"Output Driver R Stage 1", NULL, "ADACR"}, + {"Charge Pump", NULL, "HP amp L"}, + {"Charge Pump", NULL, "HP amp R"}, + {"ADACL", NULL, "Charge Pump"}, + {"ADACR", NULL, "Charge Pump"}, + {"ADACL Clock", NULL, "ADACL"}, + {"ADACR Clock", NULL, "ADACR"}, + {"Output Driver L Stage 1", NULL, "ADACL Clock"}, + {"Output Driver R Stage 1", NULL, "ADACR Clock"}, {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, {"Output DACL", NULL, "Output Driver L Stage 3"}, {"Output DACR", NULL, "Output Driver R Stage 3"}, - {"HPOL", NULL, "Output DACL"}, - {"HPOR", NULL, "Output DACR"}, - {"HPOL", NULL, "Charge Pump"}, - {"HPOR", NULL, "Charge Pump"}, + {"HPOL Pulldown", NULL, "Output DACL"}, + {"HPOR Pulldown", NULL, "Output DACR"}, + {"HP Boost Driver", NULL, "HPOL Pulldown"}, + {"HP Boost Driver", NULL, "HPOR Pulldown"}, + {"Class G", NULL, "HP Boost Driver"}, + {"HPOL", NULL, "Class G"}, + {"HPOR", NULL, "Class G"}, }; static int nau8825_hw_params(struct snd_pcm_substream *substream, @@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) break; } - if (type & SND_JACK_HEADPHONE) { - /* Unground HPL/R */ - regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); - } - + /* Leaving HPOL/R grounded after jack insert by default. They will be + * ungrounded as part of the widget power up sequence at the beginning + * of playback to reduce pop. + */ return type; } @@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825) { struct regmap *regmap = nau8825->regmap; + /* Latch IIC LSB value */ + regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001); /* Enable Bias/Vmid */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); @@ -780,10 +834,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); /* Disable Boost Driver, Automatic Short circuit protection enable */ regmap_update_bits(regmap, NAU8825_REG_BOOST, - NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | - NAU8825_SHORT_SHUTDOWN_EN, - NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | - NAU8825_SHORT_SHUTDOWN_EN); + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN); regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, NAU8825_JKDET_OUTPUT_EN, @@ -822,6 +876,35 @@ static void nau8825_init_regs(struct nau8825 *nau8825) NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); + /* Disable DACR/L power */ + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input + * signal to avoid any glitches due to power up transients in both + * the analog and digital DAC circuit. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + /* CICCLP off */ + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF); + + /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */ + regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL, + NAU8825_CLASSG_TIMER_MASK, + 0x20 << NAU8825_CLASSG_TIMER_SFT); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8825_REG_RDAC, + NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK, + (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) | + (0x3 << NAU8825_RDAC_VREF_SFT)); } static const struct regmap_config nau8825_regmap_config = { diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index dff8edb83bfd..8ceb5f385478 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -14,6 +14,7 @@ #define NAU8825_REG_RESET 0x00 #define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_IIC_ADDR_SET 0x02 #define NAU8825_REG_CLK_DIVIDER 0x03 #define NAU8825_REG_FLL1 0x04 #define NAU8825_REG_FLL2 0x05 @@ -129,7 +130,7 @@ /* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) -/* 0 - short to GND, 1 - open */ +/* 0 - open, 1 - short to GND */ #define NAU8825_SPKR_DWN1R (1 << 1) #define NAU8825_SPKR_DWN1L (1 << 0) @@ -251,12 +252,18 @@ /* DACR_CTRL (0x34) */ #define NAU8825_DACR_CH_SEL_SFT 9 +/* CLASSG_CTRL (0x50) */ +#define NAU8825_CLASSG_TIMER_SFT 8 +#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_EN (1 << 0) + /* I2C_DEVICE_ID (0x58) */ #define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_NAU8825 0x0 /* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8) #define NAU8825_BIAS_VMID (1 << 6) #define NAU8825_BIAS_VMID_SEL_SFT 4 #define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) @@ -274,6 +281,12 @@ #define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) #define NAU8825_POWERUP_ADCL (1 << 6) +/* RDAC (0x73) */ +#define NAU8825_RDAC_CLK_DELAY_SFT 4 +#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT) +#define NAU8825_RDAC_VREF_SFT 2 +#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT) + /* MIC_BIAS (0x74) */ #define NAU8825_MICBIAS_JKSLV (1 << 14) #define NAU8825_MICBIAS_JKR2 (1 << 12) @@ -284,6 +297,7 @@ /* BOOST (0x76) */ #define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_HP_BOOST_DIS (1 << 9) #define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) -- cgit v1.2.3 From e04fadaa169b09609fb55779236d35fb3197c932 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Mar 2016 16:04:54 +0200 Subject: ASoC: omap-hdmi-audio: Support for DRA7xx family DRA7xx family is compatible with the OMAP5 HDMI. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/omap/omap-hdmi-audio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f83cc2bc0fc4..64425d352962 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -345,6 +345,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) dai_drv = &omap4_hdmi_dai; break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: dai_drv = &omap5_hdmi_dai; break; default: -- cgit v1.2.3 From 39fa37d51ae904968c49ef49165df7fec66fd9ea Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 11 Mar 2016 10:12:49 +0530 Subject: ASoC: Intel: Skylake: Move module id defines The module ID defines are common to other platforms so can be reused if moved to a common driver header so move it Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.h | 4 ++++ sound/soc/intel/skylake/skl-sst.c | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index cbb40751c37e..a82c3de1a228 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -53,6 +53,10 @@ struct sst_dsp_device; /* HIPCT */ #define SKL_ADSP_REG_HIPCT_BUSY BIT(31) +/* FW base IDs */ +#define SKL_INSTANCE_ID 0 +#define SKL_BASE_FW_MODULE_ID 0 + /* Intel HD Audio SRAM Window 1 */ #define SKL_ADSP_SRAM1_BASE 0xA000 diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index e26f4746afb7..348a734f8e24 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -35,9 +35,6 @@ #define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE #define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) -#define SKL_INSTANCE_ID 0 -#define SKL_BASE_FW_MODULE_ID 0 - #define SKL_NUM_MODULES 1 static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) -- cgit v1.2.3 From a360b623fc5593d896a8762153bacdabfe29dfb9 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 11 Mar 2016 10:12:50 +0530 Subject: ASoC: Intel: Skylake: Fix whitepsace issues Some double whitespaces issues existed in driver, so fix them up. Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 14 +++++++------- sound/soc/intel/skylake/skl-sst-dsp.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 1bfb7f63b572..646d10a0d176 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -34,7 +34,7 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) mutex_unlock(&ctx->mutex); } -static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) +static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) { int ret; @@ -60,7 +60,7 @@ static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) +static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) { int ret; @@ -87,7 +87,7 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) return ret; } -static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) +static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) { int val; bool is_enable; @@ -140,7 +140,7 @@ static int skl_dsp_start_core(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_power_up(struct sst_dsp *ctx) +static int skl_dsp_core_power_up(struct sst_dsp *ctx) { int ret; @@ -166,7 +166,7 @@ static int skl_dsp_core_power_up(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_power_down(struct sst_dsp *ctx) +static int skl_dsp_core_power_down(struct sst_dsp *ctx) { /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, @@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) "Power down"); } -static int skl_dsp_enable_core(struct sst_dsp *ctx) +static int skl_dsp_enable_core(struct sst_dsp *ctx) { int ret; @@ -195,7 +195,7 @@ static int skl_dsp_enable_core(struct sst_dsp *ctx) return skl_dsp_start_core(ctx); } -int skl_dsp_disable_core(struct sst_dsp *ctx) +int skl_dsp_disable_core(struct sst_dsp *ctx) { int ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index a82c3de1a228..676e78be0169 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -148,7 +148,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx); void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); -int skl_dsp_disable_core(struct sst_dsp *ctx); +int skl_dsp_disable_core(struct sst_dsp *ctx); bool is_skl_dsp_running(struct sst_dsp *ctx); irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); int skl_dsp_wake(struct sst_dsp *ctx); -- cgit v1.2.3 From 130e69a4773fb65af284d3fd1f3de762324d06e6 Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Fri, 11 Mar 2016 10:12:51 +0530 Subject: ASoC: Intel: Skylake: Unstatify skl_dsp_enable_core The function skl_dsp_enable_core will be called by other parts of driver so this can no longer be a static function. Signed-off-by: Jayachandran B Signed-off-by: GuruprasadX Pawse Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 2 +- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 646d10a0d176..a5267e8a96e0 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) "Power down"); } -static int skl_dsp_enable_core(struct sst_dsp *ctx) +int skl_dsp_enable_core(struct sst_dsp *ctx) { int ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 676e78be0169..b6e310d49dd6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -148,6 +148,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx); void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); +int skl_dsp_enable_core(struct sst_dsp *ctx); int skl_dsp_disable_core(struct sst_dsp *ctx); bool is_skl_dsp_running(struct sst_dsp *ctx); irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); -- cgit v1.2.3 From b822ee63586c7150c10b445ae6b4bb29486f01e4 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 11 Mar 2016 10:12:52 +0530 Subject: ASoC: Intel: add dmabuffer to common sst_dsp Broxton uses HDA DMA so needs to keep dmab in the driver context so add it Signed-off-by: GuruprasadX Pawse Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 81aa1ed64201..97dc1ae05e69 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -317,6 +317,7 @@ struct sst_dsp { struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; + struct snd_dma_buffer dmab; }; /* Size optimised DRAM/IRAM memcpy */ -- cgit v1.2.3 From bc23ca35cfb18b0a98afa42de1df1efc7e2e9f8f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 11 Mar 2016 10:12:53 +0530 Subject: ASoC: Intel: Skylake: Move Skylake dsp ops & loader ops The code loading for Skylake and other platforms is different, so add a dsp_ops and a loader_ops which can be defined for each platform. Move the dsp init, cleanup and loader ops (alloc and free dma) to these ops Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 62 +++++++++++++++++++++++++++++----- sound/soc/intel/skylake/skl.h | 12 ++++++- 2 files changed, 64 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 737934cc620d..79c5089b85d6 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -72,17 +72,47 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); } +static struct skl_dsp_loader_ops skl_get_loader_ops(void) +{ + struct skl_dsp_loader_ops loader_ops; + + memset(&loader_ops, 0, sizeof(struct skl_dsp_loader_ops)); + + loader_ops.alloc_dma_buf = skl_alloc_dma_buf; + loader_ops.free_dma_buf = skl_free_dma_buf; + + return loader_ops; +}; + +static const struct skl_dsp_ops dsp_ops[] = { + { + .id = 0x9d70, + .loader_ops = skl_get_loader_ops, + .init = skl_sst_dsp_init, + .cleanup = skl_sst_dsp_cleanup + }, +}; + +static int skl_get_dsp_ops(int pci_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) { + if (dsp_ops[i].id == pci_id) + return i; + } + + return -EINVAL; +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); - int irq = bus->irq; struct skl_dsp_loader_ops loader_ops; - int ret; - - loader_ops.alloc_dma_buf = skl_alloc_dma_buf; - loader_ops.free_dma_buf = skl_free_dma_buf; + int irq = bus->irq; + int ret, index; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); @@ -95,8 +125,14 @@ int skl_init_dsp(struct skl *skl) return -ENXIO; } - ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, + index = skl_get_dsp_ops(skl->pci->device); + if (index < 0) + return -EINVAL; + + loader_ops = dsp_ops[index].loader_ops(); + ret = dsp_ops[index].init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, &skl->skl_sst); + if (ret < 0) return ret; @@ -106,18 +142,26 @@ int skl_init_dsp(struct skl *skl) return ret; } -void skl_free_dsp(struct skl *skl) +int skl_free_dsp(struct skl *skl) { struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl_sst *ctx = skl->skl_sst; + struct skl_sst *ctx = skl->skl_sst; + int index; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); - skl_sst_dsp_cleanup(bus->dev, ctx); + index = skl_get_dsp_ops(skl->pci->device); + if (index < 0) + return -EIO; + + dsp_ops[index].cleanup(bus->dev, ctx); + if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); + + return 0; } int skl_suspend_dsp(struct skl *skl) diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 1ca1911f1587..39e16fa7a92b 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -90,6 +90,16 @@ struct skl_dma_params { u8 stream_tag; }; +struct skl_dsp_ops { + int id; + struct skl_dsp_loader_ops (*loader_ops)(void); + int (*init)(struct device *dev, void __iomem *mmio_base, + int irq, const char *fw_name, + struct skl_dsp_loader_ops loader_ops, + struct skl_sst **skl_sst); + void (*cleanup)(struct device *dev, struct skl_sst *ctx); +}; + int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); @@ -100,7 +110,7 @@ struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); -void skl_free_dsp(struct skl *skl); +int skl_free_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); #endif /* __SOUND_SOC_SKL_H */ -- cgit v1.2.3 From b379b1fad6e80274309f650f3a82c6f3c4fe9d6b Mon Sep 17 00:00:00 2001 From: Senthilnathan Veppur Date: Fri, 11 Mar 2016 10:12:54 +0530 Subject: ASoC: Intel: Bxtn: Add Broxton PCI ID Broxton is an Intel SoC which sports a DSP and system is quite like Skylake. So add this ID in Skylake driver Signed-off-by: Senthilnathan Veppur Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 0d38bdb8321b..ab5e25aaeee3 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -742,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = { {} }; +static struct sst_acpi_mach sst_bxtp_devdata[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&sst_skl_devdata}, + /* BXT-P */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&sst_bxtp_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); -- cgit v1.2.3 From e2304803fd05eac509c8e37bb626c192510e4e77 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 11 Mar 2016 10:12:55 +0530 Subject: ASoC: hdac_hdmi: Add broxton device ID Broxton HDMI codec is similar to Skylake so add the device ID Signed-off-by: Jeeja KP Signed-off-by: GuruprasadX Pawse Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 7851dd204a49..cb0b036a1139 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1634,6 +1634,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = { static const struct hda_device_id hdmi_list[] = { HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), {} }; -- cgit v1.2.3 From f1511a14a4a94d063cf8f8f12f36cf2ebb0cc8a8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Mar 2016 05:29:21 +0000 Subject: ASoC: rsnd: add simplified module explanation Renesas sound driver user needs to read its datasheet when create DT. But it is difficult to understand, because it has many modules (SRC/CTU/MIX/DVC/SSIU/SSI/AudioDMAC/AudioDMACperiperi), and many features (Asynchronous/Synchronous mode on SRC, CTU matrix, DVC volume settings feature, Multi-SSI/TDM-SSI, etc). This patch adds simplified explanation to help setting/understanding. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/renesas,rsnd.txt | 340 +++++++++++++++++++++ sound/soc/sh/rcar/dvc.c | 23 ++ 2 files changed, 363 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 8ee0fa91e4a0..c7b29df4a963 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -1,6 +1,337 @@ Renesas R-Car sound +============================================= +* Modules +============================================= + +Renesas R-Car sound is constructed from below modules +(for Gen2 or later) + + SCU : Sampling Rate Converter Unit + - SRC : Sampling Rate Converter + - CMD + - CTU : Channel Transfer Unit + - MIX : Mixer + - DVC : Digital Volume and Mute Function + SSIU : Serial Sound Interface Unit + SSI : Serial Sound Interface + +See detail of each module's channels, connection, limitation on datasheet + +============================================= +* Multi channel +============================================= + +Multi channel is supported by Multi-SSI, or TDM-SSI. + + Multi-SSI : 6ch case, you can use stereo x 3 SSI + TDM-SSI : 6ch case, you can use TDM + +============================================= +* Enable/Disable each modules +============================================= + +See datasheet to check SRC/CTU/MIX/DVC connect-limitation. +DT controls enabling/disabling module. +${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example. +This is example of + +Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec] +Capture: [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec] + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&ssi0 &src2 &dvc0>; + capture = <&ssi1 &src3 &dvc1>; + }; + }; + }; + +You can use below. +${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example. + + &src0 &ctu00 &mix0 &dvc0 &ssi0 + &src1 &ctu01 &mix1 &dvc1 &ssi1 + &src2 &ctu02 &ssi2 + &src3 &ctu03 &ssi3 + &src4 &ssi4 + &src5 &ctu10 &ssi5 + &src6 &ctu11 &ssi6 + &src7 &ctu12 &ssi7 + &src8 &ctu13 &ssi8 + &src9 &ssi9 + +============================================= +* SRC (Sampling Rate Converter) +============================================= + + [xx]Hz [yy]Hz + ------> [SRC] ------> + +SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes + + Asynchronous mode: input data / output data are based on different clocks. + you can use this mode on Playback / Capture + Synchronous mode: input data / output data are based on same clocks. + This mode will be used if system doesn't have its input clock, + for example digital TV case. + you can use this mode on Playback + +------------------ +** Asynchronous mode +------------------ + +You need to use "renesas,rsrc-card" sound card for it. +example) + + sound { + compatible = "renesas,rsrc-card"; + ... + /* + * SRC Asynchronous mode setting + * Playback: + * All input data will be converted to 48kHz + * Capture: + * Inputed 48kHz data will be converted to + * system specified Hz + */ + convert-rate = <48000>; + ... + cpu { + sound-dai = <&rcar_sound>; + }; + codec { + ... + }; + }; + +------------------ +** Synchronous mode +------------------ + + > amixer set "SRC Out Rate" on + > aplay xxxx.wav + > amixer set "SRC Out Rate" 48000 + > amixer set "SRC Out Rate" 44100 + +============================================= +* CTU (Channel Transfer Unit) +============================================= + + [xx]ch [yy]ch + ------> [CTU] --------> + +CTU can convert [xx]ch to [yy]ch, or exchange outputed channel. +CTU conversion needs matrix settings. +For more detail information, see below + + Renesas R-Car datasheet + - Sampling Rate Converter Unit (SCU) + - SCU Operation + - CMD Block + - Functional Blocks in CMD + + Renesas R-Car datasheet + - Sampling Rate Converter Unit (SCU) + - Register Description + - CTUn Scale Value exx Register (CTUn_SVxxR) + + ${LINUX}/sound/soc/sh/rcar/ctu.c + - comment of header + +You need to use "renesas,rsrc-card" sound card for it. +example) + + sound { + compatible = "renesas,rsrc-card"; + ... + /* + * CTU setting + * All input data will be converted to 2ch + * as output data + */ + convert-channels = <2>; + ... + cpu { + sound-dai = <&rcar_sound>; + }; + codec { + ... + }; + }; + +Ex) Exchange output channel + Input -> Output + 1ch -> 0ch + 0ch -> 1ch + + example of using matrix + output 0ch = (input 0ch x 0) + (input 1ch x 1) + output 1ch = (input 0ch x 1) + (input 1ch x 0) + + amixer set "CTU Reset" on + amixer set "CTU Pass" 9,10 + amixer set "CTU SV0" 0,4194304 + amixer set "CTU SV1" 4194304,0 + + example of changing connection + amixer set "CTU Reset" on + amixer set "CTU Pass" 2,1 + +============================================= +* MIX (Mixer) +============================================= + +MIX merges 2 sounds path. You can see 2 sound interface on system, +and these sounds will be merged by MIX. + + aplay -D plughw:0,0 xxxx.wav & + aplay -D plughw:0,1 yyyy.wav + +You need to use "renesas,rsrc-card" sound card for it. +Ex) + [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] + | + [MEM] -> [SRC2] -> [CTU03] -+ + + sound { + compatible = "renesas,rsrc-card"; + ... + cpu@0 { + sound-dai = <&rcar_sound 0>; + }; + cpu@1 { + sound-dai = <&rcar_sound 1>; + }; + codec { + ... + }; + }; + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>; + }; + dai1 { + playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; + }; + }; + }; + +============================================= +* DVC (Digital Volume and Mute Function) +============================================= + +DVC controls Playback/Capture volume. + +Playback Volume + amixer set "DVC Out" 100% + +Capture Volume + amixer set "DVC In" 100% + +Playback Mute + amixer set "DVC Out Mute" on + +Capture Mute + amixer set "DVC In Mute" on + +Volume Ramp + amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + amixer set "DVC Out Ramp" on + aplay xxx.wav & + amixer set "DVC Out" 80% // Volume Down + amixer set "DVC Out" 100% // Volume Up + +============================================= +* SSIU (Serial Sound Interface Unit) +============================================= + +There is no DT settings for SSIU, because SSIU will be automatically +selected via SSI. +SSIU can avoid some under/over run error, because it has some buffer. +But you can't use it if SSI was PIO mode. +In DMA mode, you can select not to use SSIU by using "no-busif" on DT. + + &ssi0 { + no-busif; + }; + +============================================= +* SSI (Serial Sound Interface) +============================================= + +** PIO mode + +You can use PIO mode which is for connection check by using. +Note: The system will drop non-SSI modules in PIO mode +even though if DT is selecting other modules. + + &ssi0 { + pio-transfer + }; + +** DMA mode without SSIU + +You can use DMA without SSIU. +Note: under/over run, or noise are likely to occur + + &ssi0 { + no-busif; + }; + +** PIN sharing + +Each SSI can share WS pin. It is based on platform. +This is example if SSI1 want to share WS pin with SSI0 + + &ssi1 { + shared-pin; + }; + +** Multi-SSI + +You can use Multi-SSI. +This is example of SSI0/SSI1/SSI2 (= for 6ch) + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>; + }; + }; + }; + +** TDM-SSI + +You can use TDM with SSI. +This is example of TDM 6ch. +Driver can automatically switches TDM <-> stereo mode in this case. + + rsnd_tdm: sound { + compatible = "simple-audio-card"; + ... + simple-audio-card,cpu { + /* system can use TDM 6ch */ + dai-tdm-slot-num = <6>; + sound-dai = <&rcar_sound>; + }; + simple-audio-card,codec { + ... + }; + }; + + +============================================= Required properties: +============================================= + - compatible : "renesas,rcar_sound-", fallbacks "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 @@ -64,7 +395,10 @@ DAI subnode properties: - playback : list of playback modules - capture : list of capture modules + +============================================= Example: +============================================= rcar_sound: sound@ec500000 { #sound-dai-cells = <1>; @@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 { }; }; +============================================= Example: simple sound card +============================================= rsnd_ak4643: sound { compatible = "simple-audio-card"; @@ -290,7 +626,9 @@ Example: simple sound card shared-pin; }; +============================================= Example: simple sound card for TDM +============================================= rsnd_tdm: sound { compatible = "simple-audio-card"; @@ -309,7 +647,9 @@ Example: simple sound card for TDM }; }; +============================================= Example: simple sound card for Multi channel +============================================= &rcar_sound { pinctrl-0 = <&sound_pins &sound_clk_pins>; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 93b11e1c5d7f..02d971f69eff 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -8,6 +8,29 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * Playback Volume + * amixer set "DVC Out" 100% + * + * Capture Volume + * amixer set "DVC In" 100% + * + * Playback Mute + * amixer set "DVC Out Mute" on + * + * Capture Mute + * amixer set "DVC In Mute" on + * + * Volume Ramp + * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + * amixer set "DVC Out Ramp" on + * aplay xxx.wav & + * amixer set "DVC Out" 80% // Volume Down + * amixer set "DVC Out" 100% // Volume Up + */ + #include "rsnd.h" #define RSND_DVC_NAME_SIZE 16 -- cgit v1.2.3 From 2d369c748c2ecc2a012ee85412a04007e67913ec Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Sun, 13 Mar 2016 13:58:57 -0700 Subject: ALSA: hda - Add new GPU codec ID 0x10de0082 to snd-hda Vendor ID 0x10de0082 is used by a yet-to-be-named GPU chip. This chip also has the 2-ch audio swapping bug, so patch_nvhdmi is appropriate here. Signed-off-by: Aaron Plattner Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 4447aa57c42c..5fc9ff31b1c5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3457,6 +3457,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -- cgit v1.2.3 From 4061db03dd71d195b9973ee466f6ed32f6a3fc16 Mon Sep 17 00:00:00 2001 From: "Vittorio Gambaletta (VittGam)" Date: Sun, 13 Mar 2016 22:19:34 +0100 Subject: ALSA: intel8x0: Add clock quirk entry for AD1981B on IBM ThinkPad X41. The clock measurement on the AC'97 audio card found in the IBM ThinkPad X41 will often fail, so add a quirk entry to fix it. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=441087 Cc: Signed-off-by: Vittorio Gambaletta Signed-off-by: Takashi Iwai --- sound/pci/intel8x0.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 42bcbac801a3..ccdab29a8b66 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip) static struct snd_pci_quirk intel8x0_clock_list[] = { SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000), + SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000), SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100), SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000), SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000), -- cgit v1.2.3 From 028cb68ee3d01f5323493cb3c07ba92b0acb2f03 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 14 Mar 2016 10:35:06 +0530 Subject: ALSA: hda - Fixes double fault in nvhdmi_chmap_cea_alloc_validate_get_type nvhdmi_chmap_cea_alloc_validate_get_type calls itself recursively using chmap ops causing the double fault. Fixed by adding the default validate_get_type handling inside nvdia validate_get_type handler. Link: https://bugzilla.kernel.org/show_bug.cgi?id=114311 Fixes: 67b90cb ("ALSA: hda - Create common chmap object") Reported-by: Andreas Reis Signed-off-by: Subhransu S. Prusty Tested-by: Andreas Reis Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5fc9ff31b1c5..3fc259154c0b 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2836,8 +2836,12 @@ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; - return chmap->ops.chmap_cea_alloc_validate_get_type( - chmap, cap, channels); + /* If the speaker allocation matches the channel count, it is OK. */ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; } static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, -- cgit v1.2.3 From 294783e092217bb973b38dbefa11ec4c86878a0e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2016 10:09:17 +0300 Subject: ALSA: mixart: silence unitialized variable warnings We print can print the uninitialized memory on error. Which is an info leak, I suppose but it's basically harmless. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart_mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 24a1955b8c29..58fd79ebac20 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -726,7 +726,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int int volume[2]; struct mixart_msg request; struct mixart_set_out_stream_level_req set_level; - u32 status; + u32 status = 0; struct mixart_pipe *pipe; memset(&set_level, 0, sizeof(set_level)); @@ -778,7 +778,7 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes) struct mixart_pipe *pipe; struct mixart_msg request; struct mixart_set_in_audio_level_req set_level; - u32 status; + u32 status = 0; if(is_aes) { idx = 1; -- cgit v1.2.3 From 1f7c6658962fa1260c1658d681bd6bb0c746b99a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 16:44:55 +0100 Subject: ALSA: hda - Fix unconditional GPIO toggle via automute Cirrus HD-audio driver may adjust GPIO pins for EAPD dynamically depending on the jack plug state. This works fine for the auto-mute mode where the speaker gets muted upon the HP jack plug. OTOH, when the auto-mute mode is off, this turns off the EAPD unexpectedly depending on the jack state, which results in the silent speaker output. This patch fixes the silent speaker output issue by setting GPIO bits constantly when the auto-mute mode is off. Reported-and-tested-by: moosotc@gmail.com Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c1c855a6c0af..a47e8ae0eb30 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec) snd_hda_gen_update_outputs(codec); if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { - spec->gpio_data = spec->gen.hp_jack_present ? - spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + if (spec->gen.automute_speaker) + spec->gpio_data = spec->gen.hp_jack_present ? + spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + else + spec->gpio_data = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); } -- cgit v1.2.3 From b62232d429fa8b1dcf5d8503aa5a5397a03e646a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 18:15:26 +0100 Subject: ALSA: hda - Limit i915 HDMI binding only for HSW and later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out that the pre-HSW Intel chips are incompatible with the naive assumption we had -- the fixed mapping between the port and the HD-audio widget. This may result in the bad access, as captured by the recent patch to add a WARN_ON() for the port mapping check. As a quick workaround, disable the i915 audio component binding for all pre-Haswell models. Reported-by: Ville Syrjälä Cc: # v4.5 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3fc259154c0b..cde9746cda8e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2243,9 +2243,10 @@ static int patch_generic_hdmi(struct hda_codec *codec) codec->spec = spec; hdmi_array_init(spec, 4); - /* Try to bind with i915 for any Intel codecs (if not done yet) */ + /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */ if (!codec_has_acomp(codec) && - (codec->core.vendor_id >> 16) == 0x8086) + (codec->core.vendor_id >> 16) == 0x8086 && + is_haswell_plus(codec)) if (!snd_hdac_i915_init(&codec->bus->core)) spec->i915_bound = true; -- cgit v1.2.3 From 0f886ca12765d20124bd06291c82951fd49a33be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:09:10 +0100 Subject: ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk() create_fixed_stream_quirk() may cause a NULL-pointer dereference by accessing the non-existing endpoint when a USB device with a malformed USB descriptor is used. This patch avoids it simply by adding a sanity check of bNumEndpoints before the accesses. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=971125 Cc: Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 5b03296a5bd9..529c35cceaa6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -180,6 +180,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, } alts = &iface->altsetting[fp->altset_idx]; altsd = get_iface_desc(alts); + if (altsd->bNumEndpoints < 1) { + kfree(fp); + kfree(rate_table); + return -EINVAL; + } + fp->protocol = altsd->bInterfaceProtocol; if (fp->datainterval == 0) -- cgit v1.2.3 From 902eb7fd1e4af3ac69b9b30f8373f118c92b9729 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:14:49 +0100 Subject: ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk() Just a minor code cleanup: unify the error paths. Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 529c35cceaa6..a889d433cbdf 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -167,23 +167,18 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, stream = (fp->endpoint & USB_DIR_IN) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); - if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; - } + if (err < 0) + goto error; if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } alts = &iface->altsetting[fp->altset_idx]; altsd = get_iface_desc(alts); if (altsd->bNumEndpoints < 1) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } fp->protocol = altsd->bInterfaceProtocol; @@ -196,6 +191,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp->iface, alts, fp); snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; + + error: + kfree(fp); + kfree(rate_table); + return err; } static int create_auto_pcm_quirk(struct snd_usb_audio *chip, -- cgit v1.2.3 From 447d6275f0c21f6cc97a88b3a0c601436a4cdf2a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 15:20:58 +0100 Subject: ALSA: usb-audio: Add sanity checks for endpoint accesses Add some sanity check codes before actually accessing the endpoint via get_endpoint() in order to avoid the invalid access through a malformed USB descriptor. Mostly just checking bNumEndpoints, but in one place (snd_microii_spdif_default_get()), the validity of iface and altsetting index is checked as well. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=971125 Cc: Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 2 ++ sound/usb/endpoint.c | 3 +++ sound/usb/mixer_quirks.c | 4 ++++ sound/usb/pcm.c | 2 ++ 4 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2ed260b10f6d..7ccbcaf6a147 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, unsigned char data[3]; int err, crate; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint doesn't have sampling rate control, bail out */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7b1cb365ffab..c07a7eda42a2 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -438,6 +438,9 @@ exit_clear: * * New endpoints will be added to chip->ep_list and must be freed by * calling snd_usb_endpoint_free(). + * + * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that + * bNumEndpoints > 1 beforehand. */ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct usb_host_interface *alts, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 279025650568..f6c3bf79af9a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, /* use known values for that card: interface#1 altsetting#1 */ iface = usb_ifnum_to_if(chip->dev, 1); + if (!iface || iface->num_altsetting < 2) + return -EINVAL; alts = &iface->altsetting[1]; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9245f52d43bd..44d178ee9177 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -159,6 +159,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, unsigned char data[1]; int err; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; data[0] = 1; -- cgit v1.2.3 From 8cf3968ccb4e6768425ed3e26124e690046cc023 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Mar 2016 10:42:13 +0300 Subject: ALSA: mixart: silence an uninitialized variable warning We could print the uninitialized value of "stat" in the error message. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index bc81b9f75ed0..25c0ddd3a53b 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -132,7 +132,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr, } if(start) { - u32 stat; + u32 stat = 0; group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ -- cgit v1.2.3 From 6f0217441839c258b82dd83ee15082666d6ab9c2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 17 Mar 2016 21:14:36 +0900 Subject: ALSA: ctl: change return value in compatibility layer so that it's the same value in core implementation In control compatibility layer, when no elements are found by ELEM_READ/ELEM_WRITE ioctl commands, ENXIO is returned. On the other hand, in core implementation, ENOENT is returned. This is not good for ALSA ctl applications. This commit changes the return value from the compatibility layer so that the same value is returned. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control_compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 0608f216f359..1fa70766ffab 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -196,7 +196,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, kctl = snd_ctl_find_id(card, id); if (! kctl) { up_read(&card->controls_rwsem); - return -ENXIO; + return -ENOENT; } info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { -- cgit v1.2.3 From 222bde03881c470de8aa4ca8e58f5950c2b84d12 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Mar 2016 14:48:13 +0100 Subject: ALSA: hda - Fix mutex deadlock at HDMI/DP hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent change in HD-audio HDMI/DP codec driver for allowing the dynamic PCM binding introduced a new spec->pcm_mutex. One of the protected area by this mutex is hdmi_present_sense(). As reported by Intel CI tests, unfortunately, the new mutex causes a deadlock when the hotplug/unplug is triggered during the codec is in runtime suspend. The buggy code path is like the following: hdmi_unsol_event() -> ... -> hdmi_present_sense() ==> ** here taking pcm_mutex -> hdmi_present_sense_via_verbs() -> snd_hda_power_up_pm() -> ... (runtime resume calls) -> generic_hdmi_resume() -> hdmi_present_sense() ==> ** here taking pcm_mutex again! As we can see here, the problem is that the mutex is taken before snd_hda_power_up_pm() call that triggers the runtime resume. That is, the obvious solution is to move the power up/down call outside the mutex; it is exactly what this patch provides. The patch also clarifies why this bug wasn't caught beforehand. We used to have the i915 audio component for hotplug for all Intel chips, and in that code path, there is no power up required but the information is taken directly from the graphics side. However, we recently switched back to the old method for some old Intel chips due to regressions, and now the deadlock issue is surfaced. Fixes: a76056f2e57e ('ALSA: hda - hdmi dynamically bind PCM to pin when monitor hotplug') Reported-by: Ville Syrjälä Tested-by: Ville Syrjälä Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index cde9746cda8e..49ee4e55dd16 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1405,7 +1405,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, bool ret; bool do_repoll = false; - snd_hda_power_up_pm(codec); present = snd_hda_pin_sense(codec, pin_nid); mutex_lock(&per_pin->lock); @@ -1444,7 +1443,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, jack->block_report = !ret; mutex_unlock(&per_pin->lock); - snd_hda_power_down_pm(codec); return ret; } @@ -1522,6 +1520,10 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) struct hdmi_spec *spec = codec->spec; int ret; + /* no temporary power up/down needed for component notifier */ + if (!codec_has_acomp(codec)) + snd_hda_power_up_pm(codec); + mutex_lock(&spec->pcm_lock); if (codec_has_acomp(codec)) { sync_eld_via_acomp(codec, per_pin); @@ -1531,6 +1533,9 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) } mutex_unlock(&spec->pcm_lock); + if (!codec_has_acomp(codec)) + snd_hda_power_down_pm(codec); + return ret; } -- cgit v1.2.3