Skip to content

Commit

Permalink
soundwire: bus: initialize bus clock base and scale registers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
plbossart authored and vinodkoul committed Jun 30, 2020
1 parent b592426 commit 29d158f
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
107 changes: 107 additions & 0 deletions drivers/soundwire/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions include/linux/soundwire/sdw_registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,18 @@
#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
Expand Down

0 comments on commit 29d158f

Please sign in to comment.