soundwire: bus: initialize bus clock base and scale registers
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Mon, 8 Jun 2020 20:54:36 +0000 (04:54 +0800)
committerVinod Koul <vkoul@kernel.org>
Tue, 30 Jun 2020 15:56:17 +0000 (21:26 +0530)
The SoundWire 1.2 specification adds new registers to allow for
seamless clock changes while audio transfers are on-going. Program
them following the specification.

Note that dynamic clock changes are not supported for now, this only
adds the register initialization.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Link: https://lore.kernel.org/r/20200608205436.2402-5-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/soundwire/bus.c
include/linux/soundwire/sdw_registers.h

index fcfba1c..e6e0fb9 100644 (file)
@@ -1070,12 +1070,119 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave,
        return ret;
 }
 
+static int sdw_slave_set_frequency(struct sdw_slave *slave)
+{
+       u32 mclk_freq = slave->bus->prop.mclk_freq;
+       u32 curr_freq = slave->bus->params.curr_dr_freq >> 1;
+       unsigned int scale;
+       u8 scale_index;
+       u8 base;
+       int ret;
+
+       /*
+        * frequency base and scale registers are required for SDCA
+        * devices. They may also be used for 1.2+/non-SDCA devices,
+        * but we will need a DisCo property to cover this case
+        */
+       if (!slave->id.class_id)
+               return 0;
+
+       if (!mclk_freq) {
+               dev_err(&slave->dev,
+                       "no bus MCLK, cannot set SDW_SCP_BUS_CLOCK_BASE\n");
+               return -EINVAL;
+       }
+
+       /*
+        * map base frequency using Table 89 of SoundWire 1.2 spec.
+        * The order of the tests just follows the specification, this
+        * is not a selection between possible values or a search for
+        * the best value but just a mapping.  Only one case per platform
+        * is relevant.
+        * Some BIOS have inconsistent values for mclk_freq but a
+        * correct root so we force the mclk_freq to avoid variations.
+        */
+       if (!(19200000 % mclk_freq)) {
+               mclk_freq = 19200000;
+               base = SDW_SCP_BASE_CLOCK_19200000_HZ;
+       } else if (!(24000000 % mclk_freq)) {
+               mclk_freq = 24000000;
+               base = SDW_SCP_BASE_CLOCK_24000000_HZ;
+       } else if (!(24576000 % mclk_freq)) {
+               mclk_freq = 24576000;
+               base = SDW_SCP_BASE_CLOCK_24576000_HZ;
+       } else if (!(22579200 % mclk_freq)) {
+               mclk_freq = 22579200;
+               base = SDW_SCP_BASE_CLOCK_22579200_HZ;
+       } else if (!(32000000 % mclk_freq)) {
+               mclk_freq = 32000000;
+               base = SDW_SCP_BASE_CLOCK_32000000_HZ;
+       } else {
+               dev_err(&slave->dev,
+                       "Unsupported clock base, mclk %d\n",
+                       mclk_freq);
+               return -EINVAL;
+       }
+
+       if (mclk_freq % curr_freq) {
+               dev_err(&slave->dev,
+                       "mclk %d is not multiple of bus curr_freq %d\n",
+                       mclk_freq, curr_freq);
+               return -EINVAL;
+       }
+
+       scale = mclk_freq / curr_freq;
+
+       /*
+        * map scale to Table 90 of SoundWire 1.2 spec - and check
+        * that the scale is a power of two and maximum 64
+        */
+       scale_index = ilog2(scale);
+
+       if (BIT(scale_index) != scale || scale_index > 6) {
+               dev_err(&slave->dev,
+                       "No match found for scale %d, bus mclk %d curr_freq %d\n",
+                       scale, mclk_freq, curr_freq);
+               return -EINVAL;
+       }
+       scale_index++;
+
+       ret = sdw_write(slave, SDW_SCP_BUS_CLOCK_BASE, base);
+       if (ret < 0) {
+               dev_err(&slave->dev,
+                       "SDW_SCP_BUS_CLOCK_BASE write failed:%d\n", ret);
+               return ret;
+       }
+
+       /* initialize scale for both banks */
+       ret = sdw_write(slave, SDW_SCP_BUSCLOCK_SCALE_B0, scale_index);
+       if (ret < 0) {
+               dev_err(&slave->dev,
+                       "SDW_SCP_BUSCLOCK_SCALE_B0 write failed:%d\n", ret);
+               return ret;
+       }
+       ret = sdw_write(slave, SDW_SCP_BUSCLOCK_SCALE_B1, scale_index);
+       if (ret < 0)
+               dev_err(&slave->dev,
+                       "SDW_SCP_BUSCLOCK_SCALE_B1 write failed:%d\n", ret);
+
+       dev_dbg(&slave->dev,
+               "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n",
+               base, scale_index, mclk_freq, curr_freq);
+
+       return ret;
+}
+
 static int sdw_initialize_slave(struct sdw_slave *slave)
 {
        struct sdw_slave_prop *prop = &slave->prop;
        int ret;
        u8 val;
 
+       ret = sdw_slave_set_frequency(slave);
+       if (ret < 0)
+               return ret;
+
        /*
         * Set bus clash, parity and SCP implementation
         * defined interrupt mask
index 12f9ffc..5d3c271 100644 (file)
 #define SDW_SCP_KEEPEREN                       0x4A
 #define SDW_SCP_BANKDELAY                      0x4B
 #define SDW_SCP_COMMIT                         0x4C
+
 #define SDW_SCP_BUS_CLOCK_BASE                 0x4D
 #define SDW_SCP_BASE_CLOCK_FREQ                        GENMASK(2, 0)
+#define SDW_SCP_BASE_CLOCK_UNKNOWN             0x0
+#define SDW_SCP_BASE_CLOCK_19200000_HZ         0x1
+#define SDW_SCP_BASE_CLOCK_24000000_HZ         0x2
+#define SDW_SCP_BASE_CLOCK_24576000_HZ         0x3
+#define SDW_SCP_BASE_CLOCK_22579200_HZ         0x4
+#define SDW_SCP_BASE_CLOCK_32000000_HZ         0x5
+#define SDW_SCP_BASE_CLOCK_RESERVED            0x6
+#define SDW_SCP_BASE_CLOCK_IMP_DEF             0x7
+
 /* 0x4E is not allocated in SoundWire specification 1.2 */
 #define SDW_SCP_TESTMODE                       0x4F
 #define SDW_SCP_DEVID_0                                0x50