Commit 575ec5e5 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'soundwire-5.9-rc1' of...

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
  ...
parents 7a4462a9 a8184403
...@@ -293,6 +293,10 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to ...@@ -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,7 +513,12 @@ In .shutdown() the data structure maintaining stream state are freed up. ...@@ -509,7 +513,12 @@ 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);
Not Supported 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
============= =============
1. A single port with multiple channels supported cannot be used between two 1. A single port with multiple channels supported cannot be used between two
......
...@@ -16014,8 +16014,9 @@ F: sound/soc/sof/ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 @@ ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,25 +1439,49 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, ...@@ -1437,25 +1439,49 @@ 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;
dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (stream) {
if (!dma) /* first paranoia check */
return -ENOMEM; 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;
}
if (pcm) /* allocate and set dma info */
dma->stream_type = SDW_STREAM_PCM; dma = kzalloc(sizeof(*dma), GFP_KERNEL);
else if (!dma)
dma->stream_type = SDW_STREAM_PDM; return -ENOMEM;
dma->bus = &cdns->bus; if (pcm)
dma->link_id = cdns->instance; dma->stream_type = SDW_STREAM_PCM;
else
dma->stream_type = SDW_STREAM_PDM;
dma->stream = stream; dma->bus = &cdns->bus;
dma->link_id = cdns->instance;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) dma->stream = stream;
dai->playback_dma_data = dma;
else
dai->capture_dma_data = dma;
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
dai->playback_dma_data = dma;
else
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 { ...@@ -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)
......
This diff is collapsed.
...@@ -15,6 +15,10 @@ ...@@ -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 { ...@@ -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 */
This diff is collapsed.
...@@ -406,13 +406,13 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, ...@@ -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 @@ ...@@ -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 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) ...@@ -1826,3 +1827,100 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
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 { ...@@ -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;
}; };
......
...@@ -88,10 +88,10 @@ enum sdw_slave_status { ...@@ -88,10 +88,10 @@ enum sdw_slave_status {
* @SDW_CLK_POST_DEPREPARE: post clock stop de-prepare * @SDW_CLK_POST_DEPREPARE: post clock stop de-prepare
*/ */
enum sdw_clk_stop_type { enum sdw_clk_stop_type {
SDW_CLK_PRE_PREPARE = 0, SDW_CLK_PRE_PREPARE = 0,
SDW_CLK_POST_PREPARE, SDW_CLK_POST_PREPARE,
SDW_CLK_PRE_DEPREPARE, SDW_CLK_PRE_DEPREPARE,
SDW_CLK_POST_DEPREPARE, SDW_CLK_POST_DEPREPARE,
}; };
/** /**
...@@ -152,19 +152,19 @@ enum sdw_data_direction { ...@@ -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); ...@@ -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 { ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment