Merge remote-tracking branches 'asoc/topic/atmel', 'asoc/topic/bcm2835' and 'asoc...
authorMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:16:53 +0000 (15:16 +0700)
committerMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:16:53 +0000 (15:16 +0700)
drivers/misc/atmel-ssc.c
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/bcm/bcm2835-i2s.c
sound/soc/codecs/cs42xx8.c

index e11a0bd6c66e3f6068fbe032e1c67412ab09b6d3..0516ecda54d35fc6a590750a3f27e76083e80003 100644 (file)
@@ -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;
                        }
index ba8def5665c458842d2f333d35e34c2974b2136e..276897033639b0d719d0d4408e1ffb9ec9efe90d 100644 (file)
@@ -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);
index 3303d5f58082f68b68b535b2bdf7ce8d14cafcc4..1c1f2210387b26e9551959378f6792f18648652e 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
 #include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
-/* 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 =
index d562e1b9a5d163bb22e990280b8f2e396e584846..1179101b2b055a639e711049dc389d34e999a011 100644 (file)
@@ -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;
 }