soundwire updates for 5.9-rc1
This contains few core changes and bunch of Intel driver updates: - Adds definitions for 1.2 spec - Sanyog left as a MAINTAINER and Bard took his place while Sanyog is a reviewer now. - Intel: Lots of updates to stream/dai handling, wake support and link synchronization. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAl8ZNqYACgkQfBQHDyUj g0dFOA//aqGKs+mqCCCPmWNU8H6RgF7Ml2v9z5oRsamV/lR+sSq+0YzMpa1mpWmh XQswZ0j4x6CRCKDqmLKYM7pOhxGFJ6RLbtYNL+EvWyJx2/O/vcaTlYAnguAtQzim Sz1V3Rm07Xua9iOFOK3YpHaFrhG1oZZZmYeefgdx21BYOJm20ohp3CFlGDxvPhJO pgwkGGZOxbNt8C9JVbBB6S8Vu9zOU2o2re0CL7ZRA4+Rf+jkYhtZKkYP7e5BfcCU 6MKwFyB0GvnNkEUCe1/6l3pJ98E1gbBFvLAKRHPSu3U9ib0/jtn7Z7XEl7lOwNcC aeNBSvewK74B6Ow8VcHerFnd1q/6AfYEJ2ev7w2WAciVK4YlBI05CxdkJUkkmce0 1lLp6GyrnP73+QAvtj5ybdHGnVViEwFHDhfmc+bx5UnTiE6YZdqhz+gsxCFYD7ob ah0D5ZZl8cVB4qXjsqQhCLUcVZDfj05LUecd7ZdIWTbOGuQmeHdMkdXbGVECE2RY wdwQNt1uXeeWf0Olitp7e1O0iAWP2xY4xJW8Ec44w7qmY+Hdpi76ZST2KR8995dS lbqodCKOzNrE7jKf8xa3xRj/i1zysdAIMbWHDylef5Iu4puWlBshdjhS3Uc5IX9C drdmBuHjuYYFBQeK7cYgAdNZscTxtKtQXRkYXnL+WfHMQUZRjL8= =De+P -----END PGP SIGNATURE----- Merge tag 'soundwire-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next Vinod writes: soundwire updates for 5.9-rc1 This contains few core changes and bunch of Intel driver updates: - Adds definitions for 1.2 spec - Sanyog left as a MAINTAINER and Bard took his place while Sanyog is a reviewer now. - Intel: Lots of updates to stream/dai handling, wake support and link synchronization. * tag 'soundwire-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (31 commits) Soundwire: intel_init: save Slave(s) _ADR info in sdw_intel_ctx soundwire: intel: add wake interrupt support soundwire: intel/cadence: merge Soundwire interrupt handlers/threads soundwire: intel_init: use EXPORT_SYMBOL_NS soundwire: intel_init: add implementation of sdw_intel_enable_irq() soundwire: intel: introduce helper for link synchronization soundwire: intel: introduce a helper to arm link synchronization soundwire: intel: revisit SHIM programming sequences. soundwire: intel: reuse code for wait loops to set/clear bits soundwire: fix the kernel-doc comment soundwire: sdw.h: fix indentation soundwire: sdw.h: fix PRBS/Static_1 swapped definitions soundwire: intel: don't free dma_data in DAI shutdown soundwire: cadence: allocate/free dma_data in set_sdw_stream soundwire: intel: remove stream allocation/free soundwire: stream: add helper to startup/shutdown streams soundwire: intel: implement get_sdw_stream() operations MAINTAINERS: change SoundWire maintainer soundwire: bus: initialize bus clock base and scale registers soundwire: extend SDW_SLAVE_ENTRY ...
This commit is contained in:
commit
575ec5e5e9
|
@ -293,6 +293,10 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to
|
||||||
|
|
||||||
int sdw_alloc_stream(char * stream_name);
|
int sdw_alloc_stream(char * stream_name);
|
||||||
|
|
||||||
|
The SoundWire core provides a sdw_startup_stream() helper function,
|
||||||
|
typically called during a dailink .startup() callback, which performs
|
||||||
|
stream allocation and sets the stream pointer for all DAIs
|
||||||
|
connected to a stream.
|
||||||
|
|
||||||
SDW_STREAM_CONFIGURED
|
SDW_STREAM_CONFIGURED
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -509,6 +513,11 @@ In .shutdown() the data structure maintaining stream state are freed up.
|
||||||
|
|
||||||
void sdw_release_stream(struct sdw_stream_runtime * stream);
|
void sdw_release_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
The SoundWire core provides a sdw_shutdown_stream() helper function,
|
||||||
|
typically called during a dailink .shutdown() callback, which clears
|
||||||
|
the stream pointer for all DAIS connected to a stream and releases the
|
||||||
|
memory allocated for the stream.
|
||||||
|
|
||||||
Not Supported
|
Not Supported
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -16014,8 +16014,9 @@ F: sound/soc/sof/
|
||||||
|
|
||||||
SOUNDWIRE SUBSYSTEM
|
SOUNDWIRE SUBSYSTEM
|
||||||
M: Vinod Koul <vkoul@kernel.org>
|
M: Vinod Koul <vkoul@kernel.org>
|
||||||
M: Sanyog Kale <sanyog.r.kale@intel.com>
|
M: Bard Liao <yung-chuan.liao@linux.intel.com>
|
||||||
R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
|
R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
|
||||||
|
R: Sanyog Kale <sanyog.r.kale@intel.com>
|
||||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||||
S: Supported
|
S: Supported
|
||||||
F: Documentation/driver-api/soundwire/
|
F: Documentation/driver-api/soundwire/
|
||||||
|
|
|
@ -4,22 +4,22 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
#Bus Objs
|
#Bus Objs
|
||||||
soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \
|
soundwire-bus-y := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \
|
||||||
sysfs_slave.o sysfs_slave_dpn.o
|
sysfs_slave.o sysfs_slave_dpn.o
|
||||||
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
|
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
|
||||||
|
|
||||||
ifdef CONFIG_DEBUG_FS
|
ifdef CONFIG_DEBUG_FS
|
||||||
soundwire-bus-objs += debugfs.o
|
soundwire-bus-y += debugfs.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#Cadence Objs
|
#Cadence Objs
|
||||||
soundwire-cadence-objs := cadence_master.o
|
soundwire-cadence-y := cadence_master.o
|
||||||
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o
|
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o
|
||||||
|
|
||||||
#Intel driver
|
#Intel driver
|
||||||
soundwire-intel-objs := intel.o intel_init.o
|
soundwire-intel-y := intel.o intel_init.o
|
||||||
obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
|
obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
|
||||||
|
|
||||||
#Qualcomm driver
|
#Qualcomm driver
|
||||||
soundwire-qcom-objs := qcom.o
|
soundwire-qcom-y := qcom.o
|
||||||
obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o
|
obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o
|
||||||
|
|
|
@ -863,13 +863,13 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
|
||||||
if (!slave->dev_num)
|
if (!slave->dev_num)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Identify if Slave(s) are available on Bus */
|
|
||||||
is_slave = true;
|
|
||||||
|
|
||||||
if (slave->status != SDW_SLAVE_ATTACHED &&
|
if (slave->status != SDW_SLAVE_ATTACHED &&
|
||||||
slave->status != SDW_SLAVE_ALERT)
|
slave->status != SDW_SLAVE_ALERT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Identify if Slave(s) are available on Bus */
|
||||||
|
is_slave = true;
|
||||||
|
|
||||||
slave_mode = sdw_get_clk_stop_mode(slave);
|
slave_mode = sdw_get_clk_stop_mode(slave);
|
||||||
slave->curr_clk_stop_mode = slave_mode;
|
slave->curr_clk_stop_mode = slave_mode;
|
||||||
|
|
||||||
|
@ -900,6 +900,10 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't need to inform slaves if there is no slave attached */
|
||||||
|
if (!is_slave)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* Inform slaves that prep is done */
|
/* Inform slaves that prep is done */
|
||||||
list_for_each_entry(slave, &bus->slaves, node) {
|
list_for_each_entry(slave, &bus->slaves, node) {
|
||||||
if (!slave->dev_num)
|
if (!slave->dev_num)
|
||||||
|
@ -985,13 +989,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
|
||||||
if (!slave->dev_num)
|
if (!slave->dev_num)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Identify if Slave(s) are available on Bus */
|
|
||||||
is_slave = true;
|
|
||||||
|
|
||||||
if (slave->status != SDW_SLAVE_ATTACHED &&
|
if (slave->status != SDW_SLAVE_ATTACHED &&
|
||||||
slave->status != SDW_SLAVE_ALERT)
|
slave->status != SDW_SLAVE_ALERT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Identify if Slave(s) are available on Bus */
|
||||||
|
is_slave = true;
|
||||||
|
|
||||||
mode = slave->curr_clk_stop_mode;
|
mode = slave->curr_clk_stop_mode;
|
||||||
|
|
||||||
if (mode == SDW_CLK_STOP_MODE1) {
|
if (mode == SDW_CLK_STOP_MODE1) {
|
||||||
|
@ -1016,6 +1020,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
|
||||||
if (is_slave && !simple_clk_stop)
|
if (is_slave && !simple_clk_stop)
|
||||||
sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM);
|
sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't need to call slave callback function if there is no slave
|
||||||
|
* attached
|
||||||
|
*/
|
||||||
|
if (!is_slave)
|
||||||
|
return 0;
|
||||||
|
|
||||||
list_for_each_entry(slave, &bus->slaves, node) {
|
list_for_each_entry(slave, &bus->slaves, node) {
|
||||||
if (!slave->dev_num)
|
if (!slave->dev_num)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1059,12 +1070,119 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave,
|
||||||
return ret;
|
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)
|
static int sdw_initialize_slave(struct sdw_slave *slave)
|
||||||
{
|
{
|
||||||
struct sdw_slave_prop *prop = &slave->prop;
|
struct sdw_slave_prop *prop = &slave->prop;
|
||||||
int ret;
|
int ret;
|
||||||
u8 val;
|
u8 val;
|
||||||
|
|
||||||
|
ret = sdw_slave_set_frequency(slave);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set bus clash, parity and SCP implementation
|
* Set bus clash, parity and SCP implementation
|
||||||
* defined interrupt mask
|
* defined interrupt mask
|
||||||
|
|
|
@ -20,14 +20,16 @@
|
||||||
static const struct sdw_device_id *
|
static const struct sdw_device_id *
|
||||||
sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv)
|
sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv)
|
||||||
{
|
{
|
||||||
const struct sdw_device_id *id = drv->id_table;
|
const struct sdw_device_id *id;
|
||||||
|
|
||||||
while (id && id->mfg_id) {
|
for (id = drv->id_table; id && id->mfg_id; id++)
|
||||||
if (slave->id.mfg_id == id->mfg_id &&
|
if (slave->id.mfg_id == id->mfg_id &&
|
||||||
slave->id.part_id == id->part_id)
|
slave->id.part_id == id->part_id &&
|
||||||
|
(!id->sdw_version ||
|
||||||
|
slave->id.sdw_version == id->sdw_version) &&
|
||||||
|
(!id->class_id ||
|
||||||
|
slave->id.class_id == id->class_id))
|
||||||
return id;
|
return id;
|
||||||
id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -49,10 +51,11 @@ static int sdw_bus_match(struct device *dev, struct device_driver *ddrv)
|
||||||
|
|
||||||
int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size)
|
int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size)
|
||||||
{
|
{
|
||||||
/* modalias is sdw:m<mfg_id>p<part_id> */
|
/* modalias is sdw:m<mfg_id>p<part_id>v<version>c<class_id> */
|
||||||
|
|
||||||
return snprintf(buf, size, "sdw:m%04Xp%04X\n",
|
return snprintf(buf, size, "sdw:m%04Xp%04Xv%02Xc%02X\n",
|
||||||
slave->id.mfg_id, slave->id.part_id);
|
slave->id.mfg_id, slave->id.part_id,
|
||||||
|
slave->id.sdw_version, slave->id.class_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdw_slave_uevent(struct device *dev, struct kobj_uevent_env *env)
|
int sdw_slave_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/soundwire/sdw.h>
|
#include <linux/soundwire/sdw.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "cadence_master.h"
|
#include "cadence_master.h"
|
||||||
|
|
||||||
|
@ -790,7 +791,7 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
|
||||||
CDNS_MCP_INT_SLAVE_MASK, 0);
|
CDNS_MCP_INT_SLAVE_MASK, 0);
|
||||||
|
|
||||||
int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
|
int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
|
||||||
ret = IRQ_WAKE_THREAD;
|
schedule_work(&cdns->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
|
cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
|
||||||
|
@ -799,13 +800,15 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
|
||||||
EXPORT_SYMBOL(sdw_cdns_irq);
|
EXPORT_SYMBOL(sdw_cdns_irq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sdw_cdns_thread() - Cadence irq thread handler
|
* To update slave status in a work since we will need to handle
|
||||||
* @irq: irq number
|
* other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave
|
||||||
* @dev_id: irq context
|
* process.
|
||||||
|
* @work: cdns worker thread
|
||||||
*/
|
*/
|
||||||
irqreturn_t sdw_cdns_thread(int irq, void *dev_id)
|
static void cdns_update_slave_status_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct sdw_cdns *cdns = dev_id;
|
struct sdw_cdns *cdns =
|
||||||
|
container_of(work, struct sdw_cdns, work);
|
||||||
u32 slave0, slave1;
|
u32 slave0, slave1;
|
||||||
|
|
||||||
dev_dbg_ratelimited(cdns->dev, "Slave status change\n");
|
dev_dbg_ratelimited(cdns->dev, "Slave status change\n");
|
||||||
|
@ -822,9 +825,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id)
|
||||||
cdns_updatel(cdns, CDNS_MCP_INTMASK,
|
cdns_updatel(cdns, CDNS_MCP_INTMASK,
|
||||||
CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
|
CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sdw_cdns_thread);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* init routines
|
* init routines
|
||||||
|
@ -1427,6 +1428,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns)
|
||||||
init_completion(&cdns->tx_complete);
|
init_completion(&cdns->tx_complete);
|
||||||
cdns->bus.port_ops = &cdns_port_ops;
|
cdns->bus.port_ops = &cdns_port_ops;
|
||||||
|
|
||||||
|
INIT_WORK(&cdns->work, cdns_update_slave_status_work);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sdw_cdns_probe);
|
EXPORT_SYMBOL(sdw_cdns_probe);
|
||||||
|
@ -1437,6 +1439,21 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai,
|
||||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||||
struct sdw_cdns_dma_data *dma;
|
struct sdw_cdns_dma_data *dma;
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
/* first paranoia check */
|
||||||
|
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
dma = dai->playback_dma_data;
|
||||||
|
else
|
||||||
|
dma = dai->capture_dma_data;
|
||||||
|
|
||||||
|
if (dma) {
|
||||||
|
dev_err(dai->dev,
|
||||||
|
"dma_data already allocated for dai %s\n",
|
||||||
|
dai->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate and set dma info */
|
||||||
dma = kzalloc(sizeof(*dma), GFP_KERNEL);
|
dma = kzalloc(sizeof(*dma), GFP_KERNEL);
|
||||||
if (!dma)
|
if (!dma)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1455,7 +1472,16 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai,
|
||||||
dai->playback_dma_data = dma;
|
dai->playback_dma_data = dma;
|
||||||
else
|
else
|
||||||
dai->capture_dma_data = dma;
|
dai->capture_dma_data = dma;
|
||||||
|
} else {
|
||||||
|
/* for NULL stream we release allocated dma_data */
|
||||||
|
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
|
kfree(dai->playback_dma_data);
|
||||||
|
dai->playback_dma_data = NULL;
|
||||||
|
} else {
|
||||||
|
kfree(dai->capture_dma_data);
|
||||||
|
dai->capture_dma_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(cdns_set_sdw_stream);
|
EXPORT_SYMBOL(cdns_set_sdw_stream);
|
||||||
|
|
|
@ -129,6 +129,10 @@ struct sdw_cdns {
|
||||||
|
|
||||||
bool link_up;
|
bool link_up;
|
||||||
unsigned int msg_count;
|
unsigned int msg_count;
|
||||||
|
|
||||||
|
struct work_struct work;
|
||||||
|
|
||||||
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus)
|
#define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <linux/soundwire/sdw_registers.h>
|
#include <linux/soundwire/sdw_registers.h>
|
||||||
#include <linux/soundwire/sdw.h>
|
#include <linux/soundwire/sdw.h>
|
||||||
|
@ -46,7 +47,8 @@
|
||||||
#define SDW_SHIM_LCTL_SPA BIT(0)
|
#define SDW_SHIM_LCTL_SPA BIT(0)
|
||||||
#define SDW_SHIM_LCTL_CPA BIT(8)
|
#define SDW_SHIM_LCTL_CPA BIT(8)
|
||||||
|
|
||||||
#define SDW_SHIM_SYNC_SYNCPRD_VAL 0x176F
|
#define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1)
|
||||||
|
#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1)
|
||||||
#define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0)
|
#define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0)
|
||||||
#define SDW_SHIM_SYNC_SYNCCPU BIT(15)
|
#define SDW_SHIM_SYNC_SYNCCPU BIT(15)
|
||||||
#define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16)
|
#define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16)
|
||||||
|
@ -92,23 +94,12 @@
|
||||||
#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
|
#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
|
||||||
#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
|
#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
|
||||||
|
|
||||||
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
|
|
||||||
|
|
||||||
enum intel_pdi_type {
|
enum intel_pdi_type {
|
||||||
INTEL_PDI_IN = 0,
|
INTEL_PDI_IN = 0,
|
||||||
INTEL_PDI_OUT = 1,
|
INTEL_PDI_OUT = 1,
|
||||||
INTEL_PDI_BD = 2,
|
INTEL_PDI_BD = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdw_intel {
|
|
||||||
struct sdw_cdns cdns;
|
|
||||||
int instance;
|
|
||||||
struct sdw_intel_link_res *link_res;
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
|
||||||
struct dentry *debugfs;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
|
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -134,40 +125,33 @@ static inline void intel_writew(void __iomem *base, int offset, u16 value)
|
||||||
writew(value, base + offset);
|
writew(value, base + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
|
static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
|
||||||
{
|
{
|
||||||
int timeout = 10;
|
int timeout = 10;
|
||||||
u32 reg_read;
|
u32 reg_read;
|
||||||
|
|
||||||
writel(value, base + offset);
|
|
||||||
do {
|
do {
|
||||||
reg_read = readl(base + offset);
|
reg_read = readl(base + offset);
|
||||||
if (!(reg_read & mask))
|
if ((reg_read & mask) == target)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
timeout--;
|
timeout--;
|
||||||
udelay(50);
|
usleep_range(50, 100);
|
||||||
} while (timeout != 0);
|
} while (timeout != 0);
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
|
||||||
|
{
|
||||||
|
writel(value, base + offset);
|
||||||
|
return intel_wait_bit(base, offset, mask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
|
static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
|
||||||
{
|
{
|
||||||
int timeout = 10;
|
|
||||||
u32 reg_read;
|
|
||||||
|
|
||||||
writel(value, base + offset);
|
writel(value, base + offset);
|
||||||
do {
|
return intel_wait_bit(base, offset, mask, mask);
|
||||||
reg_read = readl(base + offset);
|
|
||||||
if (reg_read & mask)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
timeout--;
|
|
||||||
udelay(50);
|
|
||||||
} while (timeout != 0);
|
|
||||||
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -290,8 +274,46 @@ static int intel_link_power_up(struct sdw_intel *sdw)
|
||||||
{
|
{
|
||||||
unsigned int link_id = sdw->instance;
|
unsigned int link_id = sdw->instance;
|
||||||
void __iomem *shim = sdw->link_res->shim;
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
u32 *shim_mask = sdw->link_res->shim_mask;
|
||||||
|
struct sdw_bus *bus = &sdw->cdns.bus;
|
||||||
|
struct sdw_master_prop *prop = &bus->prop;
|
||||||
int spa_mask, cpa_mask;
|
int spa_mask, cpa_mask;
|
||||||
int link_control, ret;
|
int link_control;
|
||||||
|
int ret = 0;
|
||||||
|
u32 syncprd;
|
||||||
|
u32 sync_reg;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The hardware relies on an internal counter, typically 4kHz,
|
||||||
|
* to generate the SoundWire SSP - which defines a 'safe'
|
||||||
|
* synchronization point between commands and audio transport
|
||||||
|
* and allows for multi link synchronization. The SYNCPRD value
|
||||||
|
* is only dependent on the oscillator clock provided to
|
||||||
|
* the IP, so adjust based on _DSD properties reported in DSDT
|
||||||
|
* tables. The values reported are based on either 24MHz
|
||||||
|
* (CNL/CML) or 38.4 MHz (ICL/TGL+).
|
||||||
|
*/
|
||||||
|
if (prop->mclk_freq % 6000000)
|
||||||
|
syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
|
||||||
|
else
|
||||||
|
syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
|
||||||
|
|
||||||
|
if (!*shim_mask) {
|
||||||
|
/* we first need to program the SyncPRD/CPU registers */
|
||||||
|
dev_dbg(sdw->cdns.dev,
|
||||||
|
"%s: first link up, programming SYNCPRD\n", __func__);
|
||||||
|
|
||||||
|
/* set SyncPRD period */
|
||||||
|
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||||
|
sync_reg |= (syncprd <<
|
||||||
|
SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD));
|
||||||
|
|
||||||
|
/* Set SyncCPU bit */
|
||||||
|
sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
|
||||||
|
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Link power up sequence */
|
/* Link power up sequence */
|
||||||
link_control = intel_readl(shim, SDW_SHIM_LCTL);
|
link_control = intel_readl(shim, SDW_SHIM_LCTL);
|
||||||
|
@ -300,66 +322,218 @@ static int intel_link_power_up(struct sdw_intel *sdw)
|
||||||
link_control |= spa_mask;
|
link_control |= spa_mask;
|
||||||
|
|
||||||
ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
|
ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
return ret;
|
dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
|
||||||
|
goto out;
|
||||||
sdw->cdns.link_up = true;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_shim_init(struct sdw_intel *sdw)
|
if (!*shim_mask) {
|
||||||
|
/* SyncCPU will change once link is active */
|
||||||
|
ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
|
||||||
|
SDW_SHIM_SYNC_SYNCCPU, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(sdw->cdns.dev,
|
||||||
|
"Failed to set SHIM_SYNC: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*shim_mask |= BIT(link_id);
|
||||||
|
|
||||||
|
sdw->cdns.link_up = true;
|
||||||
|
out:
|
||||||
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this needs to be called with shim_lock */
|
||||||
|
static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
|
||||||
{
|
{
|
||||||
void __iomem *shim = sdw->link_res->shim;
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
unsigned int link_id = sdw->instance;
|
unsigned int link_id = sdw->instance;
|
||||||
int sync_reg, ret;
|
u16 ioctl;
|
||||||
u16 ioctl = 0, act = 0;
|
|
||||||
|
|
||||||
/* Initialize Shim */
|
|
||||||
ioctl |= SDW_SHIM_IOCTL_BKE;
|
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
|
||||||
|
|
||||||
ioctl |= SDW_SHIM_IOCTL_WPDD;
|
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
|
||||||
|
|
||||||
ioctl |= SDW_SHIM_IOCTL_DO;
|
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
|
||||||
|
|
||||||
ioctl |= SDW_SHIM_IOCTL_DOE;
|
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
|
||||||
|
|
||||||
/* Switch to MIP from Glue logic */
|
/* Switch to MIP from Glue logic */
|
||||||
ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
|
ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
|
||||||
|
|
||||||
ioctl &= ~(SDW_SHIM_IOCTL_DOE);
|
ioctl &= ~(SDW_SHIM_IOCTL_DOE);
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
ioctl &= ~(SDW_SHIM_IOCTL_DO);
|
ioctl &= ~(SDW_SHIM_IOCTL_DO);
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
ioctl |= (SDW_SHIM_IOCTL_MIF);
|
ioctl |= (SDW_SHIM_IOCTL_MIF);
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
ioctl &= ~(SDW_SHIM_IOCTL_BKE);
|
ioctl &= ~(SDW_SHIM_IOCTL_BKE);
|
||||||
ioctl &= ~(SDW_SHIM_IOCTL_COE);
|
ioctl &= ~(SDW_SHIM_IOCTL_COE);
|
||||||
|
|
||||||
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
/* at this point Master IP has full control of the I/Os */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this needs to be called with shim_lock */
|
||||||
|
static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
|
||||||
|
{
|
||||||
|
unsigned int link_id = sdw->instance;
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
u16 ioctl;
|
||||||
|
|
||||||
|
/* Glue logic */
|
||||||
|
ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_BKE;
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_COE;
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
ioctl &= ~(SDW_SHIM_IOCTL_MIF);
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
/* at this point Integration Glue has full control of the I/Os */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
|
||||||
|
{
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
unsigned int link_id = sdw->instance;
|
||||||
|
int ret = 0;
|
||||||
|
u16 ioctl = 0, act = 0;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
|
/* Initialize Shim */
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_BKE;
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_WPDD;
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_DO;
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
ioctl |= SDW_SHIM_IOCTL_DOE;
|
||||||
|
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
|
intel_shim_glue_to_master_ip(sdw);
|
||||||
|
|
||||||
act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS);
|
act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS);
|
||||||
act |= SDW_SHIM_CTMCTL_DACTQE;
|
act |= SDW_SHIM_CTMCTL_DACTQE;
|
||||||
act |= SDW_SHIM_CTMCTL_DODS;
|
act |= SDW_SHIM_CTMCTL_DODS;
|
||||||
intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
|
intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
|
||||||
|
usleep_range(10, 15);
|
||||||
|
|
||||||
/* Now set SyncPRD period */
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
|
||||||
sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL <<
|
return ret;
|
||||||
SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD));
|
}
|
||||||
|
|
||||||
|
static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
|
||||||
|
{
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
unsigned int link_id = sdw->instance;
|
||||||
|
u16 wake_en, wake_sts;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
|
||||||
|
|
||||||
|
if (wake_enable) {
|
||||||
|
/* Enable the wakeup */
|
||||||
|
wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
|
||||||
|
intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
|
||||||
|
} else {
|
||||||
|
/* Disable the wake up interrupt */
|
||||||
|
wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
|
||||||
|
intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
|
||||||
|
|
||||||
|
/* Clear wake status */
|
||||||
|
wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
|
||||||
|
wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
|
||||||
|
intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
|
||||||
|
}
|
||||||
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused intel_link_power_down(struct sdw_intel *sdw)
|
||||||
|
{
|
||||||
|
int link_control, spa_mask, cpa_mask;
|
||||||
|
unsigned int link_id = sdw->instance;
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
u32 *shim_mask = sdw->link_res->shim_mask;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
|
intel_shim_master_ip_to_glue(sdw);
|
||||||
|
|
||||||
|
/* Link power down sequence */
|
||||||
|
link_control = intel_readl(shim, SDW_SHIM_LCTL);
|
||||||
|
spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id);
|
||||||
|
cpa_mask = (SDW_SHIM_LCTL_CPA << link_id);
|
||||||
|
link_control &= spa_mask;
|
||||||
|
|
||||||
|
ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
|
||||||
|
|
||||||
|
if (!(*shim_mask & BIT(link_id)))
|
||||||
|
dev_err(sdw->cdns.dev,
|
||||||
|
"%s: Unbalanced power-up/down calls\n", __func__);
|
||||||
|
|
||||||
|
*shim_mask &= ~BIT(link_id);
|
||||||
|
|
||||||
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
/* Set SyncCPU bit */
|
|
||||||
sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
|
|
||||||
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
|
|
||||||
SDW_SHIM_SYNC_SYNCCPU);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret);
|
return ret;
|
||||||
|
|
||||||
|
sdw->cdns.link_up = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intel_shim_sync_arm(struct sdw_intel *sdw)
|
||||||
|
{
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
u32 sync_reg;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
|
/* update SYNC register */
|
||||||
|
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||||
|
sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
|
||||||
|
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
|
||||||
|
|
||||||
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
|
||||||
|
{
|
||||||
|
void __iomem *shim = sdw->link_res->shim;
|
||||||
|
u32 sync_reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Read SYNC register */
|
||||||
|
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set SyncGO bit to synchronously trigger a bank switch for
|
||||||
|
* all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
|
||||||
|
* the Masters.
|
||||||
|
*/
|
||||||
|
sync_reg |= SDW_SHIM_SYNC_SYNCGO;
|
||||||
|
|
||||||
|
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
|
||||||
|
SDW_SHIM_SYNC_SYNCGO);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -577,17 +751,12 @@ static int intel_pre_bank_switch(struct sdw_bus *bus)
|
||||||
{
|
{
|
||||||
struct sdw_cdns *cdns = bus_to_cdns(bus);
|
struct sdw_cdns *cdns = bus_to_cdns(bus);
|
||||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||||
void __iomem *shim = sdw->link_res->shim;
|
|
||||||
int sync_reg;
|
|
||||||
|
|
||||||
/* Write to register only for multi-link */
|
/* Write to register only for multi-link */
|
||||||
if (!bus->multi_link)
|
if (!bus->multi_link)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Read SYNC register */
|
intel_shim_sync_arm(sdw);
|
||||||
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
|
||||||
sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance;
|
|
||||||
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -603,6 +772,8 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
|
||||||
if (!bus->multi_link)
|
if (!bus->multi_link)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(sdw->link_res->shim_lock);
|
||||||
|
|
||||||
/* Read SYNC register */
|
/* Read SYNC register */
|
||||||
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||||
|
|
||||||
|
@ -614,18 +785,15 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
|
||||||
*
|
*
|
||||||
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
|
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
|
||||||
*/
|
*/
|
||||||
if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK))
|
if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
ret = intel_shim_sync_go_unlocked(sdw);
|
||||||
* Set SyncGO bit to synchronously trigger a bank switch for
|
unlock:
|
||||||
* all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
|
mutex_unlock(sdw->link_res->shim_lock);
|
||||||
* the Masters.
|
|
||||||
*/
|
|
||||||
sync_reg |= SDW_SHIM_SYNC_SYNCGO;
|
|
||||||
|
|
||||||
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
|
|
||||||
SDW_SHIM_SYNC_SYNCGO);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
|
dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
|
||||||
|
|
||||||
|
@ -636,57 +804,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
|
||||||
* DAI routines
|
* DAI routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int sdw_stream_setup(struct snd_pcm_substream *substream,
|
|
||||||
struct snd_soc_dai *dai)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct sdw_stream_runtime *sdw_stream = NULL;
|
|
||||||
char *name;
|
|
||||||
int i, ret;
|
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
||||||
name = kasprintf(GFP_KERNEL, "%s-Playback", dai->name);
|
|
||||||
else
|
|
||||||
name = kasprintf(GFP_KERNEL, "%s-Capture", dai->name);
|
|
||||||
|
|
||||||
if (!name)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
sdw_stream = sdw_alloc_stream(name);
|
|
||||||
if (!sdw_stream) {
|
|
||||||
dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name);
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set stream pointer on CPU DAI */
|
|
||||||
ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dai->dev, "failed to set stream pointer on cpu dai %s",
|
|
||||||
dai->name);
|
|
||||||
goto release_stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set stream pointer on all CODEC DAIs */
|
|
||||||
for (i = 0; i < rtd->num_codecs; i++) {
|
|
||||||
ret = snd_soc_dai_set_sdw_stream(asoc_rtd_to_codec(rtd, i), sdw_stream,
|
|
||||||
substream->stream);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dai->dev, "failed to set stream pointer on codec dai %s",
|
|
||||||
asoc_rtd_to_codec(rtd, i)->name);
|
|
||||||
goto release_stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
release_stream:
|
|
||||||
sdw_release_stream(sdw_stream);
|
|
||||||
error:
|
|
||||||
kfree(name);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int intel_startup(struct snd_pcm_substream *substream,
|
static int intel_startup(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
|
@ -694,8 +811,7 @@ static int intel_startup(struct snd_pcm_substream *substream,
|
||||||
* TODO: add pm_runtime support here, the startup callback
|
* TODO: add pm_runtime support here, the startup callback
|
||||||
* will make sure the IP is 'active'
|
* will make sure the IP is 'active'
|
||||||
*/
|
*/
|
||||||
|
return 0;
|
||||||
return sdw_stream_setup(substream, dai);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_hw_params(struct snd_pcm_substream *substream,
|
static int intel_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
@ -863,23 +979,13 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(dma->stream->name);
|
|
||||||
sdw_release_stream(dma->stream);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_shutdown(struct snd_pcm_substream *substream,
|
static void intel_shutdown(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct sdw_cdns_dma_data *dma;
|
|
||||||
|
|
||||||
dma = snd_soc_dai_get_dma_data(dai, substream);
|
|
||||||
if (!dma)
|
|
||||||
return;
|
|
||||||
|
|
||||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
|
||||||
kfree(dma);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
|
static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
|
||||||
|
@ -894,6 +1000,22 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
|
||||||
return cdns_set_sdw_stream(dai, stream, false, direction);
|
return cdns_set_sdw_stream(dai, stream, false, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
|
||||||
|
int direction)
|
||||||
|
{
|
||||||
|
struct sdw_cdns_dma_data *dma;
|
||||||
|
|
||||||
|
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
dma = dai->playback_dma_data;
|
||||||
|
else
|
||||||
|
dma = dai->capture_dma_data;
|
||||||
|
|
||||||
|
if (!dma)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return dma->stream;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
|
static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
|
||||||
.startup = intel_startup,
|
.startup = intel_startup,
|
||||||
.hw_params = intel_hw_params,
|
.hw_params = intel_hw_params,
|
||||||
|
@ -902,6 +1024,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
|
||||||
.hw_free = intel_hw_free,
|
.hw_free = intel_hw_free,
|
||||||
.shutdown = intel_shutdown,
|
.shutdown = intel_shutdown,
|
||||||
.set_sdw_stream = intel_pcm_set_sdw_stream,
|
.set_sdw_stream = intel_pcm_set_sdw_stream,
|
||||||
|
.get_sdw_stream = intel_get_sdw_stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
|
static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
|
||||||
|
@ -912,6 +1035,7 @@ static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
|
||||||
.hw_free = intel_hw_free,
|
.hw_free = intel_hw_free,
|
||||||
.shutdown = intel_shutdown,
|
.shutdown = intel_shutdown,
|
||||||
.set_sdw_stream = intel_pdm_set_sdw_stream,
|
.set_sdw_stream = intel_pdm_set_sdw_stream,
|
||||||
|
.get_sdw_stream = intel_get_sdw_stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_component_driver dai_component = {
|
static const struct snd_soc_component_driver dai_component = {
|
||||||
|
@ -1074,9 +1198,17 @@ static struct sdw_master_ops sdw_intel_ops = {
|
||||||
|
|
||||||
static int intel_init(struct sdw_intel *sdw)
|
static int intel_init(struct sdw_intel *sdw)
|
||||||
{
|
{
|
||||||
|
bool clock_stop;
|
||||||
|
|
||||||
/* Initialize shim and controller */
|
/* Initialize shim and controller */
|
||||||
intel_link_power_up(sdw);
|
intel_link_power_up(sdw);
|
||||||
intel_shim_init(sdw);
|
|
||||||
|
clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
|
||||||
|
|
||||||
|
intel_shim_init(sdw, clock_stop);
|
||||||
|
|
||||||
|
if (clock_stop)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return sdw_cdns_init(&sdw->cdns);
|
return sdw_cdns_init(&sdw->cdns);
|
||||||
}
|
}
|
||||||
|
@ -1084,41 +1216,66 @@ static int intel_init(struct sdw_intel *sdw)
|
||||||
/*
|
/*
|
||||||
* probe and init
|
* probe and init
|
||||||
*/
|
*/
|
||||||
static int intel_probe(struct platform_device *pdev)
|
static int intel_master_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sdw_cdns_stream_config config;
|
struct device *dev = &pdev->dev;
|
||||||
struct sdw_intel *sdw;
|
struct sdw_intel *sdw;
|
||||||
|
struct sdw_cdns *cdns;
|
||||||
|
struct sdw_bus *bus;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL);
|
sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
|
||||||
if (!sdw)
|
if (!sdw)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
sdw->instance = pdev->id;
|
cdns = &sdw->cdns;
|
||||||
sdw->link_res = dev_get_platdata(&pdev->dev);
|
bus = &cdns->bus;
|
||||||
sdw->cdns.dev = &pdev->dev;
|
|
||||||
sdw->cdns.registers = sdw->link_res->registers;
|
|
||||||
sdw->cdns.instance = sdw->instance;
|
|
||||||
sdw->cdns.msg_count = 0;
|
|
||||||
sdw->cdns.bus.link_id = pdev->id;
|
|
||||||
|
|
||||||
sdw_cdns_probe(&sdw->cdns);
|
sdw->instance = pdev->id;
|
||||||
|
sdw->link_res = dev_get_platdata(dev);
|
||||||
|
cdns->dev = dev;
|
||||||
|
cdns->registers = sdw->link_res->registers;
|
||||||
|
cdns->instance = sdw->instance;
|
||||||
|
cdns->msg_count = 0;
|
||||||
|
|
||||||
|
bus->link_id = pdev->id;
|
||||||
|
|
||||||
|
sdw_cdns_probe(cdns);
|
||||||
|
|
||||||
/* Set property read ops */
|
/* Set property read ops */
|
||||||
sdw_intel_ops.read_prop = intel_prop_read;
|
sdw_intel_ops.read_prop = intel_prop_read;
|
||||||
sdw->cdns.bus.ops = &sdw_intel_ops;
|
bus->ops = &sdw_intel_ops;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, sdw);
|
/* set driver data, accessed by snd_soc_dai_get_drvdata() */
|
||||||
|
dev_set_drvdata(dev, cdns);
|
||||||
|
|
||||||
ret = sdw_bus_master_add(&sdw->cdns.bus, &pdev->dev, pdev->dev.fwnode);
|
ret = sdw_bus_master_add(bus, dev, dev->fwnode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "sdw_bus_master_add fail: %d\n", ret);
|
dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdw->cdns.bus.prop.hw_disabled) {
|
if (bus->prop.hw_disabled)
|
||||||
dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n",
|
dev_info(dev,
|
||||||
sdw->cdns.bus.link_id);
|
"SoundWire master %d is disabled, will be ignored\n",
|
||||||
|
bus->link_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int intel_master_startup(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct sdw_cdns_stream_config config;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct sdw_cdns *cdns = dev_get_drvdata(dev);
|
||||||
|
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||||
|
struct sdw_bus *bus = &cdns->bus;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (bus->prop.hw_disabled) {
|
||||||
|
dev_info(dev,
|
||||||
|
"SoundWire master %d is disabled, ignoring\n",
|
||||||
|
sdw->instance);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1129,39 +1286,29 @@ static int intel_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* Read the PDI config and initialize cadence PDI */
|
/* Read the PDI config and initialize cadence PDI */
|
||||||
intel_pdi_init(sdw, &config);
|
intel_pdi_init(sdw, &config);
|
||||||
ret = sdw_cdns_pdi_init(&sdw->cdns, config);
|
ret = sdw_cdns_pdi_init(cdns, config);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_init;
|
goto err_init;
|
||||||
|
|
||||||
intel_pdi_ch_update(sdw);
|
intel_pdi_ch_update(sdw);
|
||||||
|
|
||||||
/* Acquire IRQ */
|
ret = sdw_cdns_enable_interrupt(cdns, true);
|
||||||
ret = request_threaded_irq(sdw->link_res->irq,
|
|
||||||
sdw_cdns_irq, sdw_cdns_thread,
|
|
||||||
IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n",
|
dev_err(dev, "cannot enable interrupts\n");
|
||||||
sdw->link_res->irq);
|
|
||||||
goto err_init;
|
goto err_init;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdw_cdns_enable_interrupt(&sdw->cdns, true);
|
ret = sdw_cdns_exit_reset(cdns);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(sdw->cdns.dev, "cannot enable interrupts\n");
|
dev_err(dev, "unable to exit bus reset sequence\n");
|
||||||
goto err_init;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = sdw_cdns_exit_reset(&sdw->cdns);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(sdw->cdns.dev, "unable to exit bus reset sequence\n");
|
|
||||||
goto err_interrupt;
|
goto err_interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register DAIs */
|
/* Register DAIs */
|
||||||
ret = intel_register_dai(sdw);
|
ret = intel_register_dai(sdw);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(sdw->cdns.dev, "DAI registration failed: %d\n", ret);
|
dev_err(dev, "DAI registration failed: %d\n", ret);
|
||||||
snd_soc_unregister_component(sdw->cdns.dev);
|
snd_soc_unregister_component(dev);
|
||||||
goto err_interrupt;
|
goto err_interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,41 +1317,75 @@ static int intel_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_interrupt:
|
err_interrupt:
|
||||||
sdw_cdns_enable_interrupt(&sdw->cdns, false);
|
sdw_cdns_enable_interrupt(cdns, false);
|
||||||
free_irq(sdw->link_res->irq, sdw);
|
|
||||||
err_init:
|
err_init:
|
||||||
sdw_bus_master_delete(&sdw->cdns.bus);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_remove(struct platform_device *pdev)
|
static int intel_master_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct sdw_cdns *cdns = dev_get_drvdata(dev);
|
||||||
|
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||||
|
struct sdw_bus *bus = &cdns->bus;
|
||||||
|
|
||||||
|
if (!bus->prop.hw_disabled) {
|
||||||
|
intel_debugfs_exit(sdw);
|
||||||
|
sdw_cdns_enable_interrupt(cdns, false);
|
||||||
|
snd_soc_unregister_component(dev);
|
||||||
|
}
|
||||||
|
sdw_bus_master_delete(bus);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int intel_master_process_wakeen_event(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct sdw_intel *sdw;
|
struct sdw_intel *sdw;
|
||||||
|
struct sdw_bus *bus;
|
||||||
|
void __iomem *shim;
|
||||||
|
u16 wake_sts;
|
||||||
|
|
||||||
sdw = platform_get_drvdata(pdev);
|
sdw = platform_get_drvdata(pdev);
|
||||||
|
bus = &sdw->cdns.bus;
|
||||||
|
|
||||||
if (!sdw->cdns.bus.prop.hw_disabled) {
|
if (bus->prop.hw_disabled) {
|
||||||
intel_debugfs_exit(sdw);
|
dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id);
|
||||||
sdw_cdns_enable_interrupt(&sdw->cdns, false);
|
return 0;
|
||||||
free_irq(sdw->link_res->irq, sdw);
|
|
||||||
snd_soc_unregister_component(sdw->cdns.dev);
|
|
||||||
}
|
}
|
||||||
sdw_bus_master_delete(&sdw->cdns.bus);
|
|
||||||
|
shim = sdw->link_res->shim;
|
||||||
|
wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
|
||||||
|
|
||||||
|
if (!(wake_sts & BIT(sdw->instance)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
|
||||||
|
intel_shim_wake(sdw, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* resume the Master, which will generate a bus reset and result in
|
||||||
|
* Slaves re-attaching and be re-enumerated. The SoundWire physical
|
||||||
|
* device which generated the wake will trigger an interrupt, which
|
||||||
|
* will in turn cause the corresponding Linux Slave device to be
|
||||||
|
* resumed and the Slave codec driver to check the status.
|
||||||
|
*/
|
||||||
|
pm_request_resume(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver sdw_intel_drv = {
|
static struct platform_driver sdw_intel_drv = {
|
||||||
.probe = intel_probe,
|
.probe = intel_master_probe,
|
||||||
.remove = intel_remove,
|
.remove = intel_master_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "int-sdw",
|
.name = "intel-sdw",
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(sdw_intel_drv);
|
module_platform_driver(sdw_intel_drv);
|
||||||
|
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_LICENSE("Dual BSD/GPL");
|
||||||
MODULE_ALIAS("platform:int-sdw");
|
MODULE_ALIAS("platform:intel-sdw");
|
||||||
MODULE_DESCRIPTION("Intel Soundwire Master Driver");
|
MODULE_DESCRIPTION("Intel Soundwire Master Driver");
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
* @irq: Interrupt line
|
* @irq: Interrupt line
|
||||||
* @ops: Shim callback ops
|
* @ops: Shim callback ops
|
||||||
* @dev: device implementing hw_params and free callbacks
|
* @dev: device implementing hw_params and free callbacks
|
||||||
|
* @shim_lock: mutex to handle access to shared SHIM registers
|
||||||
|
* @shim_mask: global pointer to check SHIM register initialization
|
||||||
|
* @cdns: Cadence master descriptor
|
||||||
|
* @list: used to walk-through all masters exposed by the same controller
|
||||||
*/
|
*/
|
||||||
struct sdw_intel_link_res {
|
struct sdw_intel_link_res {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
@ -25,6 +29,24 @@ struct sdw_intel_link_res {
|
||||||
int irq;
|
int irq;
|
||||||
const struct sdw_intel_ops *ops;
|
const struct sdw_intel_ops *ops;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct mutex *shim_lock; /* protect shared registers */
|
||||||
|
u32 *shim_mask;
|
||||||
|
struct sdw_cdns *cdns;
|
||||||
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sdw_intel {
|
||||||
|
struct sdw_cdns cdns;
|
||||||
|
int instance;
|
||||||
|
struct sdw_intel_link_res *link_res;
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
struct dentry *debugfs;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
|
||||||
|
|
||||||
|
int intel_master_startup(struct platform_device *pdev);
|
||||||
|
int intel_master_process_wakeen_event(struct platform_device *pdev);
|
||||||
|
|
||||||
#endif /* __SDW_INTEL_LOCAL_H */
|
#endif /* __SDW_INTEL_LOCAL_H */
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/soundwire/sdw_intel.h>
|
#include <linux/soundwire/sdw_intel.h>
|
||||||
|
#include "cadence_master.h"
|
||||||
#include "intel.h"
|
#include "intel.h"
|
||||||
|
|
||||||
#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
|
#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
|
||||||
|
@ -23,98 +25,219 @@
|
||||||
#define SDW_LINK_BASE 0x30000
|
#define SDW_LINK_BASE 0x30000
|
||||||
#define SDW_LINK_SIZE 0x10000
|
#define SDW_LINK_SIZE 0x10000
|
||||||
|
|
||||||
static int link_mask;
|
static int ctrl_link_mask;
|
||||||
module_param_named(sdw_link_mask, link_mask, int, 0444);
|
module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
|
||||||
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
|
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
|
||||||
|
|
||||||
static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
|
static bool is_link_enabled(struct fwnode_handle *fw_node, int i)
|
||||||
|
{
|
||||||
|
struct fwnode_handle *link;
|
||||||
|
char name[32];
|
||||||
|
u32 quirk_mask = 0;
|
||||||
|
|
||||||
|
/* Find master handle */
|
||||||
|
snprintf(name, sizeof(name),
|
||||||
|
"mipi-sdw-link-%d-subproperties", i);
|
||||||
|
|
||||||
|
link = fwnode_get_named_child_node(fw_node, name);
|
||||||
|
if (!link)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fwnode_property_read_u32(link,
|
||||||
|
"intel-quirk-mask",
|
||||||
|
&quirk_mask);
|
||||||
|
|
||||||
|
if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct sdw_intel_link_res *link = ctx->links;
|
struct sdw_intel_link_res *link = ctx->links;
|
||||||
|
u32 link_mask;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!link)
|
if (!link)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < ctx->count; i++) {
|
link_mask = ctx->link_mask;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->count; i++, link++) {
|
||||||
|
if (!(link_mask & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (link->pdev)
|
if (link->pdev)
|
||||||
platform_device_unregister(link->pdev);
|
platform_device_unregister(link->pdev);
|
||||||
link++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(ctx->links);
|
|
||||||
ctx->links = NULL;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sdw_intel_ctx
|
static int
|
||||||
*sdw_intel_add_controller(struct sdw_intel_res *res)
|
sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
|
||||||
{
|
{
|
||||||
struct platform_device_info pdevinfo;
|
|
||||||
struct platform_device *pdev;
|
|
||||||
struct sdw_intel_link_res *link;
|
|
||||||
struct sdw_intel_ctx *ctx;
|
|
||||||
struct acpi_device *adev;
|
struct acpi_device *adev;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
u8 count;
|
u8 count;
|
||||||
u32 caps;
|
|
||||||
|
|
||||||
if (acpi_bus_get_device(res->handle, &adev))
|
if (acpi_bus_get_device(info->handle, &adev))
|
||||||
return NULL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Found controller, find links supported */
|
/* Found controller, find links supported */
|
||||||
count = 0;
|
count = 0;
|
||||||
ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
|
ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
|
||||||
"mipi-sdw-master-count", &count, 1);
|
"mipi-sdw-master-count", &count, 1);
|
||||||
|
|
||||||
/* Don't fail on error, continue and use hw value */
|
/*
|
||||||
|
* In theory we could check the number of links supported in
|
||||||
|
* hardware, but in that step we cannot assume SoundWire IP is
|
||||||
|
* powered.
|
||||||
|
*
|
||||||
|
* In addition, if the BIOS doesn't even provide this
|
||||||
|
* 'master-count' property then all the inits based on link
|
||||||
|
* masks will fail as well.
|
||||||
|
*
|
||||||
|
* We will check the hardware capabilities in the startup() step
|
||||||
|
*/
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&adev->dev,
|
dev_err(&adev->dev,
|
||||||
"Failed to read mipi-sdw-master-count: %d\n", ret);
|
"Failed to read mipi-sdw-master-count: %d\n", ret);
|
||||||
count = SDW_MAX_LINKS;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check SNDWLCAP.LCOUNT */
|
|
||||||
caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP);
|
|
||||||
caps &= GENMASK(2, 0);
|
|
||||||
|
|
||||||
/* Check HW supported vs property value and use min of two */
|
|
||||||
count = min_t(u8, caps, count);
|
|
||||||
|
|
||||||
/* Check count is within bounds */
|
/* Check count is within bounds */
|
||||||
if (count > SDW_MAX_LINKS) {
|
if (count > SDW_MAX_LINKS) {
|
||||||
dev_err(&adev->dev, "Link count %d exceeds max %d\n",
|
dev_err(&adev->dev, "Link count %d exceeds max %d\n",
|
||||||
count, SDW_MAX_LINKS);
|
count, SDW_MAX_LINKS);
|
||||||
return NULL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!count) {
|
if (!count) {
|
||||||
dev_warn(&adev->dev, "No SoundWire links detected\n");
|
dev_warn(&adev->dev, "No SoundWire links detected\n");
|
||||||
return NULL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count);
|
||||||
|
|
||||||
|
info->count = count;
|
||||||
|
info->link_mask = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) {
|
||||||
|
dev_dbg(&adev->dev,
|
||||||
|
"Link %d masked, will not be enabled\n", i);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_link_enabled(acpi_fwnode_handle(adev), i)) {
|
||||||
|
dev_dbg(&adev->dev,
|
||||||
|
"Link %d not selected in firmware\n", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->link_mask |= BIT(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HDA_DSP_REG_ADSPIC2 (0x10)
|
||||||
|
#define HDA_DSP_REG_ADSPIS2 (0x14)
|
||||||
|
#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ
|
||||||
|
* @mmio_base: The mmio base of the control register
|
||||||
|
* @enable: true if enable
|
||||||
|
*/
|
||||||
|
void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(mmio_base + HDA_DSP_REG_ADSPIC2);
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
val |= HDA_DSP_REG_ADSPIC2_SNDW;
|
||||||
|
else
|
||||||
|
val &= ~HDA_DSP_REG_ADSPIC2_SNDW;
|
||||||
|
|
||||||
|
writel(val, mmio_base + HDA_DSP_REG_ADSPIC2);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
|
irqreturn_t sdw_intel_thread(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sdw_intel_ctx *ctx = dev_id;
|
||||||
|
struct sdw_intel_link_res *link;
|
||||||
|
|
||||||
|
list_for_each_entry(link, &ctx->link_list, list)
|
||||||
|
sdw_cdns_irq(irq, link->cdns);
|
||||||
|
|
||||||
|
sdw_intel_enable_irq(ctx->mmio_base, true);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
|
static struct sdw_intel_ctx
|
||||||
|
*sdw_intel_probe_controller(struct sdw_intel_res *res)
|
||||||
|
{
|
||||||
|
struct platform_device_info pdevinfo;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct sdw_intel_link_res *link;
|
||||||
|
struct sdw_intel_ctx *ctx;
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct sdw_slave *slave;
|
||||||
|
struct list_head *node;
|
||||||
|
struct sdw_bus *bus;
|
||||||
|
u32 link_mask;
|
||||||
|
int num_slaves = 0;
|
||||||
|
int count;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (acpi_bus_get_device(res->handle, &adev))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!res->count)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
count = res->count;
|
||||||
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
|
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
|
||||||
|
|
||||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ctx->count = count;
|
ctx->count = count;
|
||||||
ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL);
|
ctx->links = devm_kcalloc(&adev->dev, ctx->count,
|
||||||
|
sizeof(*ctx->links), GFP_KERNEL);
|
||||||
if (!ctx->links)
|
if (!ctx->links)
|
||||||
goto link_err;
|
return NULL;
|
||||||
|
|
||||||
|
ctx->count = count;
|
||||||
|
ctx->mmio_base = res->mmio_base;
|
||||||
|
ctx->link_mask = res->link_mask;
|
||||||
|
ctx->handle = res->handle;
|
||||||
|
mutex_init(&ctx->shim_lock);
|
||||||
|
|
||||||
link = ctx->links;
|
link = ctx->links;
|
||||||
|
link_mask = ctx->link_mask;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&ctx->link_list);
|
||||||
|
|
||||||
/* Create SDW Master devices */
|
/* Create SDW Master devices */
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++, link++) {
|
||||||
if (link_mask && !(link_mask & BIT(i))) {
|
if (!(link_mask & BIT(i))) {
|
||||||
dev_dbg(&adev->dev,
|
dev_dbg(&adev->dev,
|
||||||
"Link %d masked, will not be enabled\n", i);
|
"Link %d masked, will not be enabled\n", i);
|
||||||
link++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
link->mmio_base = res->mmio_base;
|
||||||
link->registers = res->mmio_base + SDW_LINK_BASE
|
link->registers = res->mmio_base + SDW_LINK_BASE
|
||||||
+ (SDW_LINK_SIZE * i);
|
+ (SDW_LINK_SIZE * i);
|
||||||
link->shim = res->mmio_base + SDW_SHIM_BASE;
|
link->shim = res->mmio_base + SDW_SHIM_BASE;
|
||||||
|
@ -123,38 +246,103 @@ static struct sdw_intel_ctx
|
||||||
link->ops = res->ops;
|
link->ops = res->ops;
|
||||||
link->dev = res->dev;
|
link->dev = res->dev;
|
||||||
|
|
||||||
|
link->shim_lock = &ctx->shim_lock;
|
||||||
|
link->shim_mask = &ctx->shim_mask;
|
||||||
|
|
||||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||||
|
|
||||||
pdevinfo.parent = res->parent;
|
pdevinfo.parent = res->parent;
|
||||||
pdevinfo.name = "int-sdw";
|
pdevinfo.name = "intel-sdw";
|
||||||
pdevinfo.id = i;
|
pdevinfo.id = i;
|
||||||
pdevinfo.fwnode = acpi_fwnode_handle(adev);
|
pdevinfo.fwnode = acpi_fwnode_handle(adev);
|
||||||
|
pdevinfo.data = link;
|
||||||
|
pdevinfo.size_data = sizeof(*link);
|
||||||
|
|
||||||
pdev = platform_device_register_full(&pdevinfo);
|
pdev = platform_device_register_full(&pdevinfo);
|
||||||
if (IS_ERR(pdev)) {
|
if (IS_ERR(pdev)) {
|
||||||
dev_err(&adev->dev,
|
dev_err(&adev->dev,
|
||||||
"platform device creation failed: %ld\n",
|
"platform device creation failed: %ld\n",
|
||||||
PTR_ERR(pdev));
|
PTR_ERR(pdev));
|
||||||
goto pdev_err;
|
goto err;
|
||||||
|
}
|
||||||
|
link->pdev = pdev;
|
||||||
|
link->cdns = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
list_add_tail(&link->list, &ctx->link_list);
|
||||||
|
bus = &link->cdns->bus;
|
||||||
|
/* Calculate number of slaves */
|
||||||
|
list_for_each(node, &bus->slaves)
|
||||||
|
num_slaves++;
|
||||||
}
|
}
|
||||||
|
|
||||||
link->pdev = pdev;
|
ctx->ids = devm_kcalloc(&adev->dev, num_slaves,
|
||||||
link++;
|
sizeof(*ctx->ids), GFP_KERNEL);
|
||||||
|
if (!ctx->ids)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ctx->num_slaves = num_slaves;
|
||||||
|
i = 0;
|
||||||
|
list_for_each_entry(link, &ctx->link_list, list) {
|
||||||
|
bus = &link->cdns->bus;
|
||||||
|
list_for_each_entry(slave, &bus->slaves, node) {
|
||||||
|
ctx->ids[i].id = slave->id;
|
||||||
|
ctx->ids[i].link_id = bus->link_id;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
|
|
||||||
pdev_err:
|
err:
|
||||||
sdw_intel_cleanup_pdev(ctx);
|
ctx->count = i;
|
||||||
link_err:
|
sdw_intel_cleanup(ctx);
|
||||||
kfree(ctx);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct sdw_intel_link_res *link;
|
||||||
|
u32 caps;
|
||||||
|
u32 link_mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (acpi_bus_get_device(ctx->handle, &adev))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check SNDWLCAP.LCOUNT */
|
||||||
|
caps = ioread32(ctx->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP);
|
||||||
|
caps &= GENMASK(2, 0);
|
||||||
|
|
||||||
|
/* Check HW supported vs property value */
|
||||||
|
if (caps < ctx->count) {
|
||||||
|
dev_err(&adev->dev,
|
||||||
|
"BIOS master count is larger than hardware capabilities\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->links)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
link = ctx->links;
|
||||||
|
link_mask = ctx->link_mask;
|
||||||
|
|
||||||
|
/* Startup SDW Master devices */
|
||||||
|
for (i = 0; i < ctx->count; i++, link++) {
|
||||||
|
if (!(link_mask & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
intel_master_startup(link->pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
|
static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
|
||||||
void *cdata, void **return_value)
|
void *cdata, void **return_value)
|
||||||
{
|
{
|
||||||
struct sdw_intel_res *res = cdata;
|
struct sdw_intel_acpi_info *info = cdata;
|
||||||
struct acpi_device *adev;
|
struct acpi_device *adev;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
u64 adr;
|
u64 adr;
|
||||||
|
@ -168,7 +356,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
|
||||||
return AE_NOT_FOUND;
|
return AE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
res->handle = handle;
|
info->handle = handle;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On some Intel platforms, multiple children of the HDAS
|
* On some Intel platforms, multiple children of the HDAS
|
||||||
|
@ -185,39 +373,93 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sdw_intel_init() - SoundWire Intel init routine
|
* sdw_intel_acpi_scan() - SoundWire Intel init routine
|
||||||
* @parent_handle: ACPI parent handle
|
* @parent_handle: ACPI parent handle
|
||||||
* @res: resource data
|
* @info: description of what firmware/DSDT tables expose
|
||||||
*
|
*
|
||||||
* This scans the namespace and creates SoundWire link controller devices
|
* This scans the namespace and queries firmware to figure out which
|
||||||
* based on the info queried.
|
* links to enable. A follow-up use of sdw_intel_probe() and
|
||||||
|
* sdw_intel_startup() is required for creation of devices and bus
|
||||||
|
* startup
|
||||||
*/
|
*/
|
||||||
void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
|
int sdw_intel_acpi_scan(acpi_handle *parent_handle,
|
||||||
|
struct sdw_intel_acpi_info *info)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
|
||||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
|
status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
|
||||||
parent_handle, 1,
|
parent_handle, 1,
|
||||||
sdw_intel_acpi_cb,
|
sdw_intel_acpi_cb,
|
||||||
NULL, res, NULL);
|
NULL, info, NULL);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return NULL;
|
return -ENODEV;
|
||||||
|
|
||||||
return sdw_intel_add_controller(res);
|
return sdw_intel_scan_controller(info);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdw_intel_probe() - SoundWire Intel probe routine
|
||||||
|
* @res: resource data
|
||||||
|
*
|
||||||
|
* This registers a platform device for each Master handled by the controller,
|
||||||
|
* and SoundWire Master and Slave devices will be created by the platform
|
||||||
|
* device probe. All the information necessary is stored in the context, and
|
||||||
|
* the res argument pointer can be freed after this step.
|
||||||
|
* This function will be called after sdw_intel_acpi_scan() by SOF probe.
|
||||||
|
*/
|
||||||
|
struct sdw_intel_ctx
|
||||||
|
*sdw_intel_probe(struct sdw_intel_res *res)
|
||||||
|
{
|
||||||
|
return sdw_intel_probe_controller(res);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_probe, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdw_intel_startup() - SoundWire Intel startup
|
||||||
|
* @ctx: SoundWire context allocated in the probe
|
||||||
|
*
|
||||||
|
* Startup Intel SoundWire controller. This function will be called after
|
||||||
|
* Intel Audio DSP is powered up.
|
||||||
|
*/
|
||||||
|
int sdw_intel_startup(struct sdw_intel_ctx *ctx)
|
||||||
|
{
|
||||||
|
return sdw_intel_startup_controller(ctx);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT);
|
||||||
/**
|
/**
|
||||||
* sdw_intel_exit() - SoundWire Intel exit
|
* sdw_intel_exit() - SoundWire Intel exit
|
||||||
* @arg: callback context
|
* @ctx: SoundWire context allocated in the probe
|
||||||
*
|
*
|
||||||
* Delete the controller instances created and cleanup
|
* Delete the controller instances created and cleanup
|
||||||
*/
|
*/
|
||||||
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
|
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
|
||||||
{
|
{
|
||||||
sdw_intel_cleanup_pdev(ctx);
|
sdw_intel_cleanup(ctx);
|
||||||
kfree(ctx);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sdw_intel_exit);
|
EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
|
void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct sdw_intel_link_res *link;
|
||||||
|
u32 link_mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ctx->links)
|
||||||
|
return;
|
||||||
|
|
||||||
|
link = ctx->links;
|
||||||
|
link_mask = ctx->link_mask;
|
||||||
|
|
||||||
|
/* Startup SDW Master devices */
|
||||||
|
for (i = 0; i < ctx->count; i++, link++) {
|
||||||
|
if (!(link_mask & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
intel_master_process_wakeen_event(link->pdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT);
|
||||||
|
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_LICENSE("Dual BSD/GPL");
|
||||||
MODULE_DESCRIPTION("Intel Soundwire Init Library");
|
MODULE_DESCRIPTION("Intel Soundwire Init Library");
|
||||||
|
|
|
@ -406,13 +406,13 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus,
|
||||||
return ctrl->reg_write(ctrl, reg, val);
|
return ctrl->reg_write(ctrl, reg, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sdw_master_port_ops qcom_swrm_port_ops = {
|
static const struct sdw_master_port_ops qcom_swrm_port_ops = {
|
||||||
.dpn_set_port_params = qcom_swrm_port_params,
|
.dpn_set_port_params = qcom_swrm_port_params,
|
||||||
.dpn_set_port_transport_params = qcom_swrm_transport_params,
|
.dpn_set_port_transport_params = qcom_swrm_transport_params,
|
||||||
.dpn_port_enable_ch = qcom_swrm_port_enable,
|
.dpn_port_enable_ch = qcom_swrm_port_enable,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdw_master_ops qcom_swrm_ops = {
|
static const struct sdw_master_ops qcom_swrm_ops = {
|
||||||
.xfer_msg = qcom_swrm_xfer_msg,
|
.xfer_msg = qcom_swrm_xfer_msg,
|
||||||
.pre_bank_switch = qcom_swrm_pre_bank_switch,
|
.pre_bank_switch = qcom_swrm_pre_bank_switch,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/soundwire/sdw_registers.h>
|
#include <linux/soundwire/sdw_registers.h>
|
||||||
#include <linux/soundwire/sdw.h>
|
#include <linux/soundwire/sdw.h>
|
||||||
|
#include <sound/soc.h>
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1826,3 +1827,100 @@ state_err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sdw_deprepare_stream);
|
EXPORT_SYMBOL(sdw_deprepare_stream);
|
||||||
|
|
||||||
|
static int set_stream(struct snd_pcm_substream *substream,
|
||||||
|
struct sdw_stream_runtime *sdw_stream)
|
||||||
|
{
|
||||||
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
|
struct snd_soc_dai *dai;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Set stream pointer on all DAIs */
|
||||||
|
for_each_rtd_dais(rtd, i, dai) {
|
||||||
|
ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(rtd->dev, "failed to set stream pointer on dai %s", dai->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdw_startup_stream() - Startup SoundWire stream
|
||||||
|
*
|
||||||
|
* @sdw_substream: Soundwire stream
|
||||||
|
*
|
||||||
|
* Documentation/driver-api/soundwire/stream.rst explains this API in detail
|
||||||
|
*/
|
||||||
|
int sdw_startup_stream(void *sdw_substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_substream *substream = sdw_substream;
|
||||||
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
|
struct sdw_stream_runtime *sdw_stream;
|
||||||
|
char *name;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
name = kasprintf(GFP_KERNEL, "%s-Playback", substream->name);
|
||||||
|
else
|
||||||
|
name = kasprintf(GFP_KERNEL, "%s-Capture", substream->name);
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sdw_stream = sdw_alloc_stream(name);
|
||||||
|
if (!sdw_stream) {
|
||||||
|
dev_err(rtd->dev, "alloc stream failed for substream DAI %s", substream->name);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = set_stream(substream, sdw_stream);
|
||||||
|
if (ret < 0)
|
||||||
|
goto release_stream;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
release_stream:
|
||||||
|
sdw_release_stream(sdw_stream);
|
||||||
|
set_stream(substream, NULL);
|
||||||
|
error:
|
||||||
|
kfree(name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sdw_startup_stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdw_shutdown_stream() - Shutdown SoundWire stream
|
||||||
|
*
|
||||||
|
* @sdw_substream: Soundwire stream
|
||||||
|
*
|
||||||
|
* Documentation/driver-api/soundwire/stream.rst explains this API in detail
|
||||||
|
*/
|
||||||
|
void sdw_shutdown_stream(void *sdw_substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_substream *substream = sdw_substream;
|
||||||
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
|
struct sdw_stream_runtime *sdw_stream;
|
||||||
|
struct snd_soc_dai *dai;
|
||||||
|
|
||||||
|
/* Find stream from first CPU DAI */
|
||||||
|
dai = asoc_rtd_to_cpu(rtd, 0);
|
||||||
|
|
||||||
|
sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
|
||||||
|
|
||||||
|
if (!sdw_stream) {
|
||||||
|
dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release memory */
|
||||||
|
kfree(sdw_stream->name);
|
||||||
|
sdw_release_stream(sdw_stream);
|
||||||
|
|
||||||
|
/* clear DAI data */
|
||||||
|
set_stream(substream, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sdw_shutdown_stream);
|
||||||
|
|
|
@ -251,6 +251,8 @@ struct hda_device_id {
|
||||||
struct sdw_device_id {
|
struct sdw_device_id {
|
||||||
__u16 mfg_id;
|
__u16 mfg_id;
|
||||||
__u16 part_id;
|
__u16 part_id;
|
||||||
|
__u8 sdw_version;
|
||||||
|
__u8 class_id;
|
||||||
kernel_ulong_t driver_data;
|
kernel_ulong_t driver_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -152,19 +152,19 @@ enum sdw_data_direction {
|
||||||
*
|
*
|
||||||
* @SDW_PORT_DATA_MODE_NORMAL: Normal data mode where audio data is received
|
* @SDW_PORT_DATA_MODE_NORMAL: Normal data mode where audio data is received
|
||||||
* and transmitted.
|
* and transmitted.
|
||||||
|
* @SDW_PORT_DATA_MODE_PRBS: Test mode which uses a PRBS generator to produce
|
||||||
|
* a pseudo random data pattern that is transferred
|
||||||
|
* @SDW_PORT_DATA_MODE_STATIC_0: Simple test mode which uses static value of
|
||||||
|
* logic 0. The encoding will result in no signal transitions
|
||||||
* @SDW_PORT_DATA_MODE_STATIC_1: Simple test mode which uses static value of
|
* @SDW_PORT_DATA_MODE_STATIC_1: Simple test mode which uses static value of
|
||||||
* logic 1. The encoding will result in signal transitions at every bitslot
|
* logic 1. The encoding will result in signal transitions at every bitslot
|
||||||
* owned by this Port
|
* owned by this Port
|
||||||
* @SDW_PORT_DATA_MODE_STATIC_0: Simple test mode which uses static value of
|
|
||||||
* logic 0. The encoding will result in no signal transitions
|
|
||||||
* @SDW_PORT_DATA_MODE_PRBS: Test mode which uses a PRBS generator to produce
|
|
||||||
* a pseudo random data pattern that is transferred
|
|
||||||
*/
|
*/
|
||||||
enum sdw_port_data_mode {
|
enum sdw_port_data_mode {
|
||||||
SDW_PORT_DATA_MODE_NORMAL = 0,
|
SDW_PORT_DATA_MODE_NORMAL = 0,
|
||||||
SDW_PORT_DATA_MODE_STATIC_1 = 1,
|
SDW_PORT_DATA_MODE_PRBS = 1,
|
||||||
SDW_PORT_DATA_MODE_STATIC_0 = 2,
|
SDW_PORT_DATA_MODE_STATIC_0 = 2,
|
||||||
SDW_PORT_DATA_MODE_PRBS = 3,
|
SDW_PORT_DATA_MODE_STATIC_1 = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -426,8 +426,7 @@ int sdw_slave_read_prop(struct sdw_slave *slave);
|
||||||
* struct sdw_slave_id - Slave ID
|
* struct sdw_slave_id - Slave ID
|
||||||
* @mfg_id: MIPI Manufacturer ID
|
* @mfg_id: MIPI Manufacturer ID
|
||||||
* @part_id: Device Part ID
|
* @part_id: Device Part ID
|
||||||
* @class_id: MIPI Class ID, unused now.
|
* @class_id: MIPI Class ID (defined starting with SoundWire 1.2 spec)
|
||||||
* Currently a placeholder in MIPI SoundWire Spec
|
|
||||||
* @unique_id: Device unique ID
|
* @unique_id: Device unique ID
|
||||||
* @sdw_version: SDW version implemented
|
* @sdw_version: SDW version implemented
|
||||||
*
|
*
|
||||||
|
@ -659,10 +658,14 @@ struct sdw_driver {
|
||||||
struct device_driver driver;
|
struct device_driver driver;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \
|
#define SDW_SLAVE_ENTRY_EXT(_mfg_id, _part_id, _version, _c_id, _drv_data) \
|
||||||
{ .mfg_id = (_mfg_id), .part_id = (_part_id), \
|
{ .mfg_id = (_mfg_id), .part_id = (_part_id), \
|
||||||
|
.sdw_version = (_version), .class_id = (_c_id), \
|
||||||
.driver_data = (unsigned long)(_drv_data) }
|
.driver_data = (unsigned long)(_drv_data) }
|
||||||
|
|
||||||
|
#define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \
|
||||||
|
SDW_SLAVE_ENTRY_EXT((_mfg_id), (_part_id), 0, 0, (_drv_data))
|
||||||
|
|
||||||
int sdw_handle_slave_status(struct sdw_bus *bus,
|
int sdw_handle_slave_status(struct sdw_bus *bus,
|
||||||
enum sdw_slave_status status[]);
|
enum sdw_slave_status status[]);
|
||||||
|
|
||||||
|
@ -952,10 +955,12 @@ int sdw_stream_remove_master(struct sdw_bus *bus,
|
||||||
struct sdw_stream_runtime *stream);
|
struct sdw_stream_runtime *stream);
|
||||||
int sdw_stream_remove_slave(struct sdw_slave *slave,
|
int sdw_stream_remove_slave(struct sdw_slave *slave,
|
||||||
struct sdw_stream_runtime *stream);
|
struct sdw_stream_runtime *stream);
|
||||||
|
int sdw_startup_stream(void *sdw_substream);
|
||||||
int sdw_prepare_stream(struct sdw_stream_runtime *stream);
|
int sdw_prepare_stream(struct sdw_stream_runtime *stream);
|
||||||
int sdw_enable_stream(struct sdw_stream_runtime *stream);
|
int sdw_enable_stream(struct sdw_stream_runtime *stream);
|
||||||
int sdw_disable_stream(struct sdw_stream_runtime *stream);
|
int sdw_disable_stream(struct sdw_stream_runtime *stream);
|
||||||
int sdw_deprepare_stream(struct sdw_stream_runtime *stream);
|
int sdw_deprepare_stream(struct sdw_stream_runtime *stream);
|
||||||
|
void sdw_shutdown_stream(void *sdw_substream);
|
||||||
int sdw_bus_prep_clk_stop(struct sdw_bus *bus);
|
int sdw_bus_prep_clk_stop(struct sdw_bus *bus);
|
||||||
int sdw_bus_clk_stop(struct sdw_bus *bus);
|
int sdw_bus_clk_stop(struct sdw_bus *bus);
|
||||||
int sdw_bus_exit_clk_stop(struct sdw_bus *bus);
|
int sdw_bus_exit_clk_stop(struct sdw_bus *bus);
|
||||||
|
|
|
@ -115,6 +115,7 @@ struct sdw_intel_slave_id {
|
||||||
* links
|
* links
|
||||||
* @link_list: list to handle interrupts across all links
|
* @link_list: list to handle interrupts across all links
|
||||||
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
|
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
|
||||||
|
* @shim_mask: flags to track initialization of SHIM shared registers
|
||||||
*/
|
*/
|
||||||
struct sdw_intel_ctx {
|
struct sdw_intel_ctx {
|
||||||
int count;
|
int count;
|
||||||
|
@ -126,6 +127,7 @@ struct sdw_intel_ctx {
|
||||||
struct sdw_intel_slave_id *ids;
|
struct sdw_intel_slave_id *ids;
|
||||||
struct list_head link_list;
|
struct list_head link_list;
|
||||||
struct mutex shim_lock; /* lock for access to shared SHIM registers */
|
struct mutex shim_lock; /* lock for access to shared SHIM registers */
|
||||||
|
u32 shim_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#define SDW_REG_SHIFT(n) (ffs(n) - 1)
|
#define SDW_REG_SHIFT(n) (ffs(n) - 1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SDW registers as defined by MIPI 1.1 Spec
|
* SDW registers as defined by MIPI 1.2 Spec
|
||||||
*/
|
*/
|
||||||
#define SDW_REGADDR GENMASK(14, 0)
|
#define SDW_REGADDR GENMASK(14, 0)
|
||||||
#define SDW_SCP_ADDRPAGE2_MASK GENMASK(22, 15)
|
#define SDW_SCP_ADDRPAGE2_MASK GENMASK(22, 15)
|
||||||
|
@ -43,6 +43,8 @@
|
||||||
#define SDW_DP0_INT_TEST_FAIL BIT(0)
|
#define SDW_DP0_INT_TEST_FAIL BIT(0)
|
||||||
#define SDW_DP0_INT_PORT_READY BIT(1)
|
#define SDW_DP0_INT_PORT_READY BIT(1)
|
||||||
#define SDW_DP0_INT_BRA_FAILURE BIT(2)
|
#define SDW_DP0_INT_BRA_FAILURE BIT(2)
|
||||||
|
#define SDW_DP0_SDCA_CASCADE BIT(3)
|
||||||
|
/* BIT(4) not allocated in SoundWire specification 1.2 */
|
||||||
#define SDW_DP0_INT_IMPDEF1 BIT(5)
|
#define SDW_DP0_INT_IMPDEF1 BIT(5)
|
||||||
#define SDW_DP0_INT_IMPDEF2 BIT(6)
|
#define SDW_DP0_INT_IMPDEF2 BIT(6)
|
||||||
#define SDW_DP0_INT_IMPDEF3 BIT(7)
|
#define SDW_DP0_INT_IMPDEF3 BIT(7)
|
||||||
|
@ -106,6 +108,20 @@
|
||||||
#define SDW_SCP_ADDRPAGE2 0x49
|
#define SDW_SCP_ADDRPAGE2 0x49
|
||||||
#define SDW_SCP_KEEPEREN 0x4A
|
#define SDW_SCP_KEEPEREN 0x4A
|
||||||
#define SDW_SCP_BANKDELAY 0x4B
|
#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_TESTMODE 0x4F
|
||||||
#define SDW_SCP_DEVID_0 0x50
|
#define SDW_SCP_DEVID_0 0x50
|
||||||
#define SDW_SCP_DEVID_1 0x51
|
#define SDW_SCP_DEVID_1 0x51
|
||||||
|
@ -114,12 +130,111 @@
|
||||||
#define SDW_SCP_DEVID_4 0x54
|
#define SDW_SCP_DEVID_4 0x54
|
||||||
#define SDW_SCP_DEVID_5 0x55
|
#define SDW_SCP_DEVID_5 0x55
|
||||||
|
|
||||||
|
/* Both INT and STATUS register are same */
|
||||||
|
#define SDW_SCP_SDCA_INT1 0x58
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_0 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_1 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_2 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_3 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_4 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_5 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_6 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_7 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INT2 0x59
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_8 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_9 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_10 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_11 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_12 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_13 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_14 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_15 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INT3 0x5A
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_16 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_17 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_18 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_19 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_20 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_21 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_22 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_23 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INT4 0x5B
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_24 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_25 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_26 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_27 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_28 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_29 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INT_SDCA_30 BIT(6)
|
||||||
|
/* BIT(7) not allocated in SoundWire 1.2 specification */
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INTMASK1 0x5C
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_0 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_1 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_2 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_3 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_4 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_5 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_6 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_7 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INTMASK2 0x5D
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_8 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_9 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_10 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_11 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_12 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_13 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_14 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_15 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INTMASK3 0x5E
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_16 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_17 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_18 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_19 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_20 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_21 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_22 BIT(6)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_23 BIT(7)
|
||||||
|
|
||||||
|
#define SDW_SCP_SDCA_INTMASK4 0x5F
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_24 BIT(0)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_25 BIT(1)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_26 BIT(2)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_27 BIT(3)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_28 BIT(4)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_29 BIT(5)
|
||||||
|
#define SDW_SCP_SDCA_INTMASK_SDCA_30 BIT(6)
|
||||||
|
/* BIT(7) not allocated in SoundWire 1.2 specification */
|
||||||
|
|
||||||
/* Banked Registers */
|
/* Banked Registers */
|
||||||
#define SDW_SCP_FRAMECTRL_B0 0x60
|
#define SDW_SCP_FRAMECTRL_B0 0x60
|
||||||
#define SDW_SCP_FRAMECTRL_B1 (0x60 + SDW_BANK1_OFFSET)
|
#define SDW_SCP_FRAMECTRL_B1 (0x60 + SDW_BANK1_OFFSET)
|
||||||
#define SDW_SCP_NEXTFRAME_B0 0x61
|
#define SDW_SCP_NEXTFRAME_B0 0x61
|
||||||
#define SDW_SCP_NEXTFRAME_B1 (0x61 + SDW_BANK1_OFFSET)
|
#define SDW_SCP_NEXTFRAME_B1 (0x61 + SDW_BANK1_OFFSET)
|
||||||
|
|
||||||
|
#define SDW_SCP_BUSCLOCK_SCALE_B0 0x62
|
||||||
|
#define SDW_SCP_BUSCLOCK_SCALE_B1 (0x62 + SDW_BANK1_OFFSET)
|
||||||
|
#define SDW_SCP_CLOCK_SCALE GENMASK(3, 0)
|
||||||
|
|
||||||
|
/* PHY registers - CTRL and STAT are the same address */
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_0 0x80
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_1 0x81
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_2 0x82
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_3 0x83
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_4 0x84
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_5 0x85
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_6 0x86
|
||||||
|
#define SDW_SCP_PHY_OUT_CTRL_7 0x87
|
||||||
|
|
||||||
|
#define SDW_SCP_CAP_LOAD_CTRL GENMASK(2, 0)
|
||||||
|
#define SDW_SCP_DRIVE_STRENGTH_CTRL GENMASK(5, 3)
|
||||||
|
#define SDW_SCP_SLEW_TIME_CTRL GENMASK(7, 6)
|
||||||
|
|
||||||
/* Both INT and STATUS register is same */
|
/* Both INT and STATUS register is same */
|
||||||
#define SDW_DPN_INT(n) (0x0 + SDW_DPN_SIZE * (n))
|
#define SDW_DPN_INT(n) (0x0 + SDW_DPN_SIZE * (n))
|
||||||
#define SDW_DPN_INTMASK(n) (0x1 + SDW_DPN_SIZE * (n))
|
#define SDW_DPN_INTMASK(n) (0x1 + SDW_DPN_SIZE * (n))
|
||||||
|
|
|
@ -216,6 +216,8 @@ int main(void)
|
||||||
DEVID(sdw_device_id);
|
DEVID(sdw_device_id);
|
||||||
DEVID_FIELD(sdw_device_id, mfg_id);
|
DEVID_FIELD(sdw_device_id, mfg_id);
|
||||||
DEVID_FIELD(sdw_device_id, part_id);
|
DEVID_FIELD(sdw_device_id, part_id);
|
||||||
|
DEVID_FIELD(sdw_device_id, sdw_version);
|
||||||
|
DEVID_FIELD(sdw_device_id, class_id);
|
||||||
|
|
||||||
DEVID(fsl_mc_device_id);
|
DEVID(fsl_mc_device_id);
|
||||||
DEVID_FIELD(fsl_mc_device_id, vendor);
|
DEVID_FIELD(fsl_mc_device_id, vendor);
|
||||||
|
|
|
@ -1258,15 +1258,19 @@ static int do_hda_entry(const char *filename, void *symval, char *alias)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Looks like: sdw:mNpN */
|
/* Looks like: sdw:mNpNvNcN */
|
||||||
static int do_sdw_entry(const char *filename, void *symval, char *alias)
|
static int do_sdw_entry(const char *filename, void *symval, char *alias)
|
||||||
{
|
{
|
||||||
DEF_FIELD(symval, sdw_device_id, mfg_id);
|
DEF_FIELD(symval, sdw_device_id, mfg_id);
|
||||||
DEF_FIELD(symval, sdw_device_id, part_id);
|
DEF_FIELD(symval, sdw_device_id, part_id);
|
||||||
|
DEF_FIELD(symval, sdw_device_id, sdw_version);
|
||||||
|
DEF_FIELD(symval, sdw_device_id, class_id);
|
||||||
|
|
||||||
strcpy(alias, "sdw:");
|
strcpy(alias, "sdw:");
|
||||||
ADD(alias, "m", mfg_id != 0, mfg_id);
|
ADD(alias, "m", mfg_id != 0, mfg_id);
|
||||||
ADD(alias, "p", part_id != 0, part_id);
|
ADD(alias, "p", part_id != 0, part_id);
|
||||||
|
ADD(alias, "v", sdw_version != 0, sdw_version);
|
||||||
|
ADD(alias, "c", class_id != 0, class_id);
|
||||||
|
|
||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Reference in New Issue