soundwire updates for 5.14-rc1

Updates for v5.14-rc1 are:
 
 - Core has odd updates including improving clock stop codes, write api,
   handling ENODATA etc
 
  - Drivers has Big move of Intel driver to be aux dev and minor updates
    to Intel/cadence driver
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmDRcwkACgkQfBQHDyUj
 g0eDQg//RfLrqVuEiLRk0D3shOvxJszEtZkBsEkgXOcB6FlQyh/WG63GE8bwdW1W
 hanPmKrJqwH3zqPwKZlocVnbJ6GaAg8lY31rOiMfoj71cRBG5odGA4DZDx1KYe0l
 JdJxFmgaOgny1hZgeWXdjoODYXg8+a6R/Cl0HoZexA7lk2bK7tehfBTftuejDIzx
 W5NmvjJ4MueHfANE3dHfY+4PGMOtEuuSg50VFaE+xfoMeBXzikvWshtDsuC9R7ym
 dLYXfnKlWp55jhk1e0KWTKDyfzsWptQSOThYiHVgU5WkaBI8lHLKctscIxfianT0
 9RIcGCwuQDz2ARn0KLbsIEVyJdNsGmUAGg/tnLvsgHsg+AyvATuBVS5Q3t0QFdal
 NyDaGMhrGa1U+thx9n99zurNrjU+rOUS8BxjHKioCpJ/+/DQ12FEO8wF5T8LwLlB
 0zQyXbFB0KFYRyWvfWLFC1Hua8NlBTgrfwPhtjPyCRpUtIx/rrW1hwiNFVTw5SC/
 qaGnOFXlZ3vSManHCFZHk9ZC5SAahBtfLb7gorVyJT5Sl2amwV+o0NWcoZg+cgoj
 EElZThYhKY5FjEhvhtfXE4/vmQs1KVaNQ5FYIq2mCPzTI9MEuc8uzYkomXTmHoAY
 LGD3MwbLRQcwaXEKwSCvg07bPDM891GiHOUEjP6RBMsBBvVb6Mg=
 =JGCd
 -----END PGP SIGNATURE-----

Merge tag 'soundwire-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next

Vinod writes:

soundwire updates for 5.14-rc1

Updates for v5.14-rc1 are:

- Core has odd updates including improving clock stop codes, write api,
  handling ENODATA etc

 - Drivers has Big move of Intel driver to be aux dev and minor updates
   to Intel/cadence driver

* tag 'soundwire-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: stream: Fix test for DP prepare complete
  soundwire: bus: Make sdw_nwrite() data pointer argument const
  soundwire: intel: move to auxiliary bus
  soundwire: cadence: remove the repeated declaration
  soundwire: dmi-quirks: remove duplicate initialization
  soundwire: cadence_master: always set CMD_ACCEPT
  soundwire: bus: add missing \n in dynamic debug
  soundwire: bus: handle -ENODATA errors in clock stop/start sequences
  soundwire: add missing kernel-doc description
  soundwire: bus: only use CLOCK_STOP_MODE0 and fix confusions
  soundwire: bandwidth allocation: improve error messages
  soundwire/ASoC: add leading zeroes in peripheral device name
This commit is contained in:
Greg Kroah-Hartman 2021-06-22 12:36:29 +02:00
commit 1730a594ac
14 changed files with 301 additions and 241 deletions

View File

@ -25,6 +25,7 @@ config SOUNDWIRE_INTEL
tristate "Intel SoundWire Master driver"
select SOUNDWIRE_CADENCE
select SOUNDWIRE_GENERIC_ALLOCATION
select AUXILIARY_BUS
depends on ACPI && SND_SOC
help
SoundWire Intel Master driver.

View File

@ -394,13 +394,13 @@ sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
}
static int
sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{
struct sdw_msg msg;
int ret;
ret = sdw_fill_msg(&msg, slave, addr, count,
slave->dev_num, SDW_MSG_FLAG_WRITE, val);
slave->dev_num, SDW_MSG_FLAG_WRITE, (u8 *)val);
if (ret < 0)
return ret;
@ -535,9 +535,9 @@ EXPORT_SYMBOL(sdw_nread);
* @slave: SDW Slave
* @addr: Register address
* @count: length
* @val: Buffer for values to be read
* @val: Buffer for values to be written
*/
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{
int ret;
@ -821,26 +821,6 @@ static void sdw_modify_slave_status(struct sdw_slave *slave,
mutex_unlock(&bus->bus_lock);
}
static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave)
{
enum sdw_clk_stop_mode mode;
/*
* Query for clock stop mode if Slave implements
* ops->get_clk_stop_mode, else read from property.
*/
if (slave->ops && slave->ops->get_clk_stop_mode) {
mode = slave->ops->get_clk_stop_mode(slave);
} else {
if (slave->prop.clk_stop_mode1)
mode = SDW_CLK_STOP_MODE1;
else
mode = SDW_CLK_STOP_MODE0;
}
return mode;
}
static int sdw_slave_clk_stop_callback(struct sdw_slave *slave,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type)
@ -849,11 +829,8 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave,
if (slave->ops && slave->ops->clk_stop) {
ret = slave->ops->clk_stop(slave, mode, type);
if (ret < 0) {
dev_err(&slave->dev,
"Clk Stop type =%d failed: %d\n", type, ret);
if (ret < 0)
return ret;
}
}
return 0;
@ -880,7 +857,8 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave,
} else {
ret = sdw_read_no_pm(slave, SDW_SCP_SYSTEMCTRL);
if (ret < 0) {
dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL read failed:%d\n", ret);
if (ret != -ENODATA)
dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL read failed:%d\n", ret);
return ret;
}
val = ret;
@ -889,9 +867,8 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave,
ret = sdw_write_no_pm(slave, SDW_SCP_SYSTEMCTRL, val);
if (ret < 0)
dev_err(&slave->dev,
"Clock Stop prepare failed for slave: %d", ret);
if (ret < 0 && ret != -ENODATA)
dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL write failed:%d\n", ret);
return ret;
}
@ -909,7 +886,7 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num)
}
val &= SDW_SCP_STAT_CLK_STP_NF;
if (!val) {
dev_dbg(bus->dev, "clock stop prep/de-prep done slave:%d",
dev_dbg(bus->dev, "clock stop prep/de-prep done slave:%d\n",
dev_num);
return 0;
}
@ -918,7 +895,7 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num)
retry--;
} while (retry);
dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d",
dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d\n",
dev_num);
return -ETIMEDOUT;
@ -933,7 +910,6 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num)
*/
int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
{
enum sdw_clk_stop_mode slave_mode;
bool simple_clk_stop = true;
struct sdw_slave *slave;
bool is_slave = false;
@ -943,6 +919,9 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
* In order to save on transition time, prepare
* each Slave and then wait for all Slave(s) to be
* prepared for clock stop.
* If one of the Slave devices has lost sync and
* replies with Command Ignored/-ENODATA, we continue
* the loop
*/
list_for_each_entry(slave, &bus->slaves, node) {
if (!slave->dev_num)
@ -955,36 +934,45 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
/* Identify if Slave(s) are available on Bus */
is_slave = true;
slave_mode = sdw_get_clk_stop_mode(slave);
slave->curr_clk_stop_mode = slave_mode;
ret = sdw_slave_clk_stop_callback(slave, slave_mode,
ret = sdw_slave_clk_stop_callback(slave,
SDW_CLK_STOP_MODE0,
SDW_CLK_PRE_PREPARE);
if (ret < 0) {
dev_err(&slave->dev,
"pre-prepare failed:%d", ret);
if (ret < 0 && ret != -ENODATA) {
dev_err(&slave->dev, "clock stop pre-prepare cb failed:%d\n", ret);
return ret;
}
ret = sdw_slave_clk_stop_prepare(slave,
slave_mode, true);
if (ret < 0) {
dev_err(&slave->dev,
"pre-prepare failed:%d", ret);
return ret;
}
if (slave_mode == SDW_CLK_STOP_MODE1)
/* Only prepare a Slave device if needed */
if (!slave->prop.simple_clk_stop_capable) {
simple_clk_stop = false;
ret = sdw_slave_clk_stop_prepare(slave,
SDW_CLK_STOP_MODE0,
true);
if (ret < 0 && ret != -ENODATA) {
dev_err(&slave->dev, "clock stop prepare failed:%d\n", ret);
return ret;
}
}
}
/* Skip remaining clock stop preparation if no Slave is attached */
if (!is_slave)
return ret;
return 0;
/*
* Don't wait for all Slaves to be ready if they follow the simple
* state machine
*/
if (!simple_clk_stop) {
ret = sdw_bus_wait_for_clk_prep_deprep(bus,
SDW_BROADCAST_DEV_NUM);
/*
* if there are no Slave devices present and the reply is
* Command_Ignored/-ENODATA, we don't need to continue with the
* flow and can just return here. The error code is not modified
* and its handling left as an exercise for the caller.
*/
if (ret < 0)
return ret;
}
@ -998,21 +986,17 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
slave->status != SDW_SLAVE_ALERT)
continue;
slave_mode = slave->curr_clk_stop_mode;
ret = sdw_slave_clk_stop_callback(slave,
SDW_CLK_STOP_MODE0,
SDW_CLK_POST_PREPARE);
if (slave_mode == SDW_CLK_STOP_MODE1) {
ret = sdw_slave_clk_stop_callback(slave,
slave_mode,
SDW_CLK_POST_PREPARE);
if (ret < 0) {
dev_err(&slave->dev,
"post-prepare failed:%d", ret);
}
if (ret < 0 && ret != -ENODATA) {
dev_err(&slave->dev, "clock stop post-prepare cb failed:%d\n", ret);
return ret;
}
}
return ret;
return 0;
}
EXPORT_SYMBOL(sdw_bus_prep_clk_stop);
@ -1035,12 +1019,8 @@ int sdw_bus_clk_stop(struct sdw_bus *bus)
ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM,
SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW);
if (ret < 0) {
if (ret == -ENODATA)
dev_dbg(bus->dev,
"ClockStopNow Broadcast msg ignored %d", ret);
else
dev_err(bus->dev,
"ClockStopNow Broadcast msg failed %d", ret);
if (ret != -ENODATA)
dev_err(bus->dev, "ClockStopNow Broadcast msg failed %d\n", ret);
return ret;
}
@ -1059,7 +1039,6 @@ EXPORT_SYMBOL(sdw_bus_clk_stop);
*/
int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
{
enum sdw_clk_stop_mode mode;
bool simple_clk_stop = true;
struct sdw_slave *slave;
bool is_slave = false;
@ -1081,33 +1060,36 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
/* Identify if Slave(s) are available on Bus */
is_slave = true;
mode = slave->curr_clk_stop_mode;
if (mode == SDW_CLK_STOP_MODE1) {
simple_clk_stop = false;
continue;
}
ret = sdw_slave_clk_stop_callback(slave, mode,
ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0,
SDW_CLK_PRE_DEPREPARE);
if (ret < 0)
dev_warn(&slave->dev,
"clk stop deprep failed:%d", ret);
dev_warn(&slave->dev, "clock stop pre-deprepare cb failed:%d\n", ret);
ret = sdw_slave_clk_stop_prepare(slave, mode,
false);
/* Only de-prepare a Slave device if needed */
if (!slave->prop.simple_clk_stop_capable) {
simple_clk_stop = false;
if (ret < 0)
dev_warn(&slave->dev,
"clk stop deprep failed:%d", ret);
ret = sdw_slave_clk_stop_prepare(slave, SDW_CLK_STOP_MODE0,
false);
if (ret < 0)
dev_warn(&slave->dev, "clock stop deprepare failed:%d\n", ret);
}
}
/* Skip remaining clock stop de-preparation if no Slave is attached */
if (!is_slave)
return 0;
if (!simple_clk_stop)
sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM);
/*
* Don't wait for all Slaves to be ready if they follow the simple
* state machine
*/
if (!simple_clk_stop) {
ret = sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM);
if (ret < 0)
dev_warn(&slave->dev, "clock stop deprepare wait failed:%d\n", ret);
}
list_for_each_entry(slave, &bus->slaves, node) {
if (!slave->dev_num)
@ -1117,9 +1099,10 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
slave->status != SDW_SLAVE_ALERT)
continue;
mode = slave->curr_clk_stop_mode;
sdw_slave_clk_stop_callback(slave, mode,
SDW_CLK_POST_DEPREPARE);
ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0,
SDW_CLK_POST_DEPREPARE);
if (ret < 0)
dev_warn(&slave->dev, "clock stop post-deprepare cb failed:%d\n", ret);
}
return 0;

View File

@ -1428,20 +1428,6 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake)
}
}
/*
* This CMD_ACCEPT should be used when there are no devices
* attached on the link when entering clock stop mode. If this is
* not set and there is a broadcast write then the command ignored
* will be treated as a failure
*/
if (!slave_present)
cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_CMD_ACCEPT,
CDNS_MCP_CONTROL_CMD_ACCEPT);
else
cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_CMD_ACCEPT, 0);
/* commit changes */
ret = cdns_config_update(cdns);
if (ret < 0) {
@ -1508,11 +1494,8 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset)
cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0);
/*
* clear CMD_ACCEPT so that the command ignored
* will be treated as a failure during a broadcast write
*/
cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0);
cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT,
CDNS_MCP_CONTROL_CMD_ACCEPT);
if (!bus_reset) {

View File

@ -180,9 +180,6 @@ enum sdw_command_response
cdns_xfer_msg_defer(struct sdw_bus *bus,
struct sdw_msg *msg, struct sdw_defer *defer);
enum sdw_command_response
cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
int cdns_set_sdw_stream(struct snd_soc_dai *dai,

View File

@ -80,7 +80,7 @@ u64 sdw_dmi_override_adr(struct sdw_bus *bus, u64 addr)
/* check if any address remap quirk applies */
dmi_id = dmi_first_match(adr_remap_quirk_table);
if (dmi_id) {
struct adr_remap *map = dmi_id->driver_data;
struct adr_remap *map;
for (map = dmi_id->driver_data; map->adr; map++) {
if (map->adr == addr) {

View File

@ -382,12 +382,18 @@ static int sdw_compute_bus_params(struct sdw_bus *bus)
*/
}
if (i == clk_values)
if (i == clk_values) {
dev_err(bus->dev, "%s: could not find clock value for bandwidth %d\n",
__func__, bus->params.bandwidth);
return -EINVAL;
}
ret = sdw_select_row_col(bus, curr_dr_freq);
if (ret < 0)
if (ret < 0) {
dev_err(bus->dev, "%s: could not find frame configuration for bus dr_freq %d\n",
__func__, curr_dr_freq);
return -EINVAL;
}
bus->params.curr_dr_freq = curr_dr_freq;
return 0;
@ -404,10 +410,8 @@ int sdw_compute_params(struct sdw_bus *bus)
/* Computes clock frequency, frame shape and frame frequency */
ret = sdw_compute_bus_params(bus);
if (ret < 0) {
dev_err(bus->dev, "Compute bus params failed: %d\n", ret);
if (ret < 0)
return ret;
}
/* Compute transport and port params */
ret = sdw_compute_port_params(bus);

View File

@ -11,7 +11,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/auxiliary_bus.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
@ -1327,11 +1327,14 @@ static int intel_init(struct sdw_intel *sdw)
}
/*
* probe and init
* probe and init (aux_dev_id argument is required by function prototype but not used)
*/
static int intel_master_probe(struct platform_device *pdev)
static int intel_link_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *aux_dev_id)
{
struct device *dev = &pdev->dev;
struct device *dev = &auxdev->dev;
struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
struct sdw_intel *sdw;
struct sdw_cdns *cdns;
struct sdw_bus *bus;
@ -1344,14 +1347,14 @@ static int intel_master_probe(struct platform_device *pdev)
cdns = &sdw->cdns;
bus = &cdns->bus;
sdw->instance = pdev->id;
sdw->link_res = dev_get_platdata(dev);
sdw->instance = auxdev->id;
sdw->link_res = &ldev->link_res;
cdns->dev = dev;
cdns->registers = sdw->link_res->registers;
cdns->instance = sdw->instance;
cdns->msg_count = 0;
bus->link_id = pdev->id;
bus->link_id = auxdev->id;
sdw_cdns_probe(cdns);
@ -1384,10 +1387,10 @@ static int intel_master_probe(struct platform_device *pdev)
return 0;
}
int intel_master_startup(struct platform_device *pdev)
int intel_link_startup(struct auxiliary_device *auxdev)
{
struct sdw_cdns_stream_config config;
struct device *dev = &pdev->dev;
struct device *dev = &auxdev->dev;
struct sdw_cdns *cdns = dev_get_drvdata(dev);
struct sdw_intel *sdw = cdns_to_intel(cdns);
struct sdw_bus *bus = &cdns->bus;
@ -1524,9 +1527,9 @@ err_init:
return ret;
}
static int intel_master_remove(struct platform_device *pdev)
static void intel_link_remove(struct auxiliary_device *auxdev)
{
struct device *dev = &pdev->dev;
struct device *dev = &auxdev->dev;
struct sdw_cdns *cdns = dev_get_drvdata(dev);
struct sdw_intel *sdw = cdns_to_intel(cdns);
struct sdw_bus *bus = &cdns->bus;
@ -1542,19 +1545,17 @@ static int intel_master_remove(struct platform_device *pdev)
snd_soc_unregister_component(dev);
}
sdw_bus_master_delete(bus);
return 0;
}
int intel_master_process_wakeen_event(struct platform_device *pdev)
int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
{
struct device *dev = &pdev->dev;
struct device *dev = &auxdev->dev;
struct sdw_intel *sdw;
struct sdw_bus *bus;
void __iomem *shim;
u16 wake_sts;
sdw = platform_get_drvdata(pdev);
sdw = dev_get_drvdata(dev);
bus = &sdw->cdns.bus;
if (bus->prop.hw_disabled) {
@ -1976,17 +1977,22 @@ static const struct dev_pm_ops intel_pm = {
SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
};
static struct platform_driver sdw_intel_drv = {
.probe = intel_master_probe,
.remove = intel_master_remove,
.driver = {
.name = "intel-sdw",
.pm = &intel_pm,
}
static const struct auxiliary_device_id intel_link_id_table[] = {
{ .name = "soundwire_intel.link" },
{},
};
MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table);
module_platform_driver(sdw_intel_drv);
static struct auxiliary_driver sdw_intel_drv = {
.probe = intel_link_probe,
.remove = intel_link_remove,
.driver = {
/* auxiliary_driver_register() sets .name to be the modname */
.pm = &intel_pm,
},
.id_table = intel_link_id_table
};
module_auxiliary_driver(sdw_intel_drv);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:intel-sdw");
MODULE_DESCRIPTION("Intel Soundwire Master Driver");
MODULE_DESCRIPTION("Intel Soundwire Link Driver");

View File

@ -7,7 +7,6 @@
/**
* struct sdw_intel_link_res - Soundwire Intel link resource structure,
* typically populated by the controller driver.
* @pdev: platform_device
* @mmio_base: mmio base of SoundWire registers
* @registers: Link IO registers base
* @shim: Audio shim pointer
@ -23,7 +22,6 @@
* @list: used to walk-through all masters exposed by the same controller
*/
struct sdw_intel_link_res {
struct platform_device *pdev;
void __iomem *mmio_base; /* not strictly needed, useful for debug */
void __iomem *registers;
void __iomem *shim;
@ -48,7 +46,15 @@ struct sdw_intel {
#endif
};
int intel_master_startup(struct platform_device *pdev);
int intel_master_process_wakeen_event(struct platform_device *pdev);
int intel_link_startup(struct auxiliary_device *auxdev);
int intel_link_process_wakeen_event(struct auxiliary_device *auxdev);
struct sdw_intel_link_dev {
struct auxiliary_device auxdev;
struct sdw_intel_link_res link_res;
};
#define auxiliary_dev_to_sdw_intel_link_dev(auxiliary_dev) \
container_of(auxiliary_dev, struct sdw_intel_link_dev, auxdev)
#endif /* __SDW_INTEL_LOCAL_H */

View File

@ -12,7 +12,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/auxiliary_bus.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
@ -24,28 +24,108 @@
#define SDW_LINK_BASE 0x30000
#define SDW_LINK_SIZE 0x10000
static void intel_link_dev_release(struct device *dev)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
kfree(ldev);
}
/* alloc, init and add link devices */
static struct sdw_intel_link_dev *intel_link_dev_register(struct sdw_intel_res *res,
struct sdw_intel_ctx *ctx,
struct fwnode_handle *fwnode,
const char *name,
int link_id)
{
struct sdw_intel_link_dev *ldev;
struct sdw_intel_link_res *link;
struct auxiliary_device *auxdev;
int ret;
ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
if (!ldev)
return ERR_PTR(-ENOMEM);
auxdev = &ldev->auxdev;
auxdev->name = name;
auxdev->dev.parent = res->parent;
auxdev->dev.fwnode = fwnode;
auxdev->dev.release = intel_link_dev_release;
/* we don't use an IDA since we already have a link ID */
auxdev->id = link_id;
/*
* keep a handle on the allocated memory, to be used in all other functions.
* Since the same pattern is used to skip links that are not enabled, there is
* no need to check if ctx->ldev[i] is NULL later on.
*/
ctx->ldev[link_id] = ldev;
/* Add link information used in the driver probe */
link = &ldev->link_res;
link->mmio_base = res->mmio_base;
link->registers = res->mmio_base + SDW_LINK_BASE
+ (SDW_LINK_SIZE * link_id);
link->shim = res->mmio_base + SDW_SHIM_BASE;
link->alh = res->mmio_base + SDW_ALH_BASE;
link->ops = res->ops;
link->dev = res->dev;
link->clock_stop_quirks = res->clock_stop_quirks;
link->shim_lock = &ctx->shim_lock;
link->shim_mask = &ctx->shim_mask;
link->link_mask = ctx->link_mask;
/* now follow the two-step init/add sequence */
ret = auxiliary_device_init(auxdev);
if (ret < 0) {
dev_err(res->parent, "failed to initialize link dev %s link_id %d\n",
name, link_id);
kfree(ldev);
return ERR_PTR(ret);
}
ret = auxiliary_device_add(&ldev->auxdev);
if (ret < 0) {
dev_err(res->parent, "failed to add link dev %s link_id %d\n",
ldev->auxdev.name, link_id);
/* ldev will be freed with the put_device() and .release sequence */
auxiliary_device_uninit(&ldev->auxdev);
return ERR_PTR(ret);
}
return ldev;
}
static void intel_link_dev_unregister(struct sdw_intel_link_dev *ldev)
{
auxiliary_device_delete(&ldev->auxdev);
auxiliary_device_uninit(&ldev->auxdev);
}
static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx)
{
struct sdw_intel_link_res *link = ctx->links;
struct sdw_intel_link_dev *ldev;
u32 link_mask;
int i;
if (!link)
return 0;
link_mask = ctx->link_mask;
for (i = 0; i < ctx->count; i++, link++) {
for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
if (link->pdev) {
pm_runtime_disable(&link->pdev->dev);
platform_device_unregister(link->pdev);
}
ldev = ctx->ldev[i];
if (!link->clock_stop_quirks)
pm_runtime_put_noidle(link->dev);
pm_runtime_disable(&ldev->auxdev.dev);
if (!ldev->link_res.clock_stop_quirks)
pm_runtime_put_noidle(ldev->link_res.dev);
intel_link_dev_unregister(ldev);
}
return 0;
@ -91,9 +171,8 @@ 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_link_dev *ldev;
struct sdw_intel_ctx *ctx;
struct acpi_device *adev;
struct sdw_slave *slave;
@ -116,67 +195,60 @@ static struct sdw_intel_ctx
count = res->count;
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL);
/*
* we need to alloc/free memory manually and can't use devm:
* this routine may be called from a workqueue, and not from
* the parent .probe.
* If devm_ was used, the memory might never be freed on errors.
*/
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
ctx->count = count;
ctx->links = devm_kcalloc(&adev->dev, ctx->count,
sizeof(*ctx->links), GFP_KERNEL);
if (!ctx->links)
return NULL;
ctx->count = count;
/*
* allocate the array of pointers. The link-specific data is allocated
* as part of the first loop below and released with the auxiliary_device_uninit().
* If some links are disabled, the link pointer will remain NULL. Given that the
* number of links is small, this is simpler than using a list to keep track of links.
*/
ctx->ldev = kcalloc(ctx->count, sizeof(*ctx->ldev), GFP_KERNEL);
if (!ctx->ldev) {
kfree(ctx);
return NULL;
}
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_mask = ctx->link_mask;
INIT_LIST_HEAD(&ctx->link_list);
/* Create SDW Master devices */
for (i = 0; i < count; i++, link++) {
if (!(link_mask & BIT(i))) {
dev_dbg(&adev->dev,
"Link %d masked, will not be enabled\n", i);
for (i = 0; i < count; i++) {
if (!(link_mask & BIT(i)))
continue;
}
link->mmio_base = res->mmio_base;
link->registers = res->mmio_base + SDW_LINK_BASE
+ (SDW_LINK_SIZE * i);
link->shim = res->mmio_base + SDW_SHIM_BASE;
link->alh = res->mmio_base + SDW_ALH_BASE;
link->ops = res->ops;
link->dev = res->dev;
link->clock_stop_quirks = res->clock_stop_quirks;
link->shim_lock = &ctx->shim_lock;
link->shim_mask = &ctx->shim_mask;
link->link_mask = link_mask;
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = res->parent;
pdevinfo.name = "intel-sdw";
pdevinfo.id = i;
pdevinfo.fwnode = acpi_fwnode_handle(adev);
pdevinfo.data = link;
pdevinfo.size_data = sizeof(*link);
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) {
dev_err(&adev->dev,
"platform device creation failed: %ld\n",
PTR_ERR(pdev));
/*
* init and add a device for each link
*
* The name of the device will be soundwire_intel.link.[i],
* with the "soundwire_intel" module prefix automatically added
* by the auxiliary bus core.
*/
ldev = intel_link_dev_register(res,
ctx,
acpi_fwnode_handle(adev),
"link",
i);
if (IS_ERR(ldev))
goto err;
}
link->pdev = pdev;
link->cdns = platform_get_drvdata(pdev);
link = &ldev->link_res;
link->cdns = dev_get_drvdata(&ldev->auxdev.dev);
if (!link->cdns) {
dev_err(&adev->dev, "failed to get link->cdns\n");
@ -194,8 +266,7 @@ static struct sdw_intel_ctx
num_slaves++;
}
ctx->ids = devm_kcalloc(&adev->dev, num_slaves,
sizeof(*ctx->ids), GFP_KERNEL);
ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
if (!ctx->ids)
goto err;
@ -213,8 +284,14 @@ static struct sdw_intel_ctx
return ctx;
err:
ctx->count = i;
sdw_intel_cleanup(ctx);
while (i--) {
if (!(link_mask & BIT(i)))
continue;
ldev = ctx->ldev[i];
intel_link_dev_unregister(ldev);
}
kfree(ctx->ldev);
kfree(ctx);
return NULL;
}
@ -222,7 +299,7 @@ static int
sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
{
struct acpi_device *adev;
struct sdw_intel_link_res *link;
struct sdw_intel_link_dev *ldev;
u32 caps;
u32 link_mask;
int i;
@ -241,27 +318,28 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
return -EINVAL;
}
if (!ctx->links)
if (!ctx->ldev)
return -EINVAL;
link = ctx->links;
link_mask = ctx->link_mask;
/* Startup SDW Master devices */
for (i = 0; i < ctx->count; i++, link++) {
for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
intel_master_startup(link->pdev);
ldev = ctx->ldev[i];
if (!link->clock_stop_quirks) {
intel_link_startup(&ldev->auxdev);
if (!ldev->link_res.clock_stop_quirks) {
/*
* we need to prevent the parent PCI device
* from entering pm_runtime suspend, so that
* power rails to the SoundWire IP are not
* turned off.
*/
pm_runtime_get_noresume(link->dev);
pm_runtime_get_noresume(ldev->link_res.dev);
}
}
@ -272,8 +350,8 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
* 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
* This registers an auxiliary device for each Master handled by the controller,
* and SoundWire Master and Slave devices will be created by the auxiliary
* 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.
@ -306,27 +384,31 @@ EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT);
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
{
sdw_intel_cleanup(ctx);
kfree(ctx->ids);
kfree(ctx->ldev);
kfree(ctx);
}
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;
struct sdw_intel_link_dev *ldev;
u32 link_mask;
int i;
if (!ctx->links)
if (!ctx->ldev)
return;
link = ctx->links;
link_mask = ctx->link_mask;
/* Startup SDW Master devices */
for (i = 0; i < ctx->count; i++, link++) {
for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
intel_master_process_wakeen_event(link->pdev);
ldev = ctx->ldev[i];
intel_link_process_wakeen_event(&ldev->auxdev);
}
}
EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT);

View File

@ -39,12 +39,12 @@ int sdw_slave_add(struct sdw_bus *bus,
if (id->unique_id == SDW_IGNORED_UNIQUE_ID) {
/* name shall be sdw:link:mfg:part:class */
dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x",
dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x",
bus->link_id, id->mfg_id, id->part_id,
id->class_id);
} else {
/* name shall be sdw:link:mfg:part:class:unique */
dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x",
dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x:%01x",
bus->link_id, id->mfg_id, id->part_id,
id->class_id, id->unique_id);
}

View File

@ -422,7 +422,6 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
struct completion *port_ready;
struct sdw_dpn_prop *dpn_prop;
struct sdw_prepare_ch prep_ch;
unsigned int time_left;
bool intr = false;
int ret = 0, val;
u32 addr;
@ -479,15 +478,15 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
/* Wait for completion on port ready */
port_ready = &s_rt->slave->port_ready[prep_ch.num];
time_left = wait_for_completion_timeout(port_ready,
msecs_to_jiffies(dpn_prop->ch_prep_timeout));
wait_for_completion_timeout(port_ready,
msecs_to_jiffies(dpn_prop->ch_prep_timeout));
val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
val &= p_rt->ch_mask;
if (!time_left || val) {
if ((val < 0) || (val & p_rt->ch_mask)) {
ret = (val < 0) ? val : -ETIMEDOUT;
dev_err(&s_rt->slave->dev,
"Chn prep failed for port:%d\n", prep_ch.num);
return -ETIMEDOUT;
"Chn prep failed for port %d: %d\n", prep_ch.num, ret);
return ret;
}
}

View File

@ -612,6 +612,7 @@ struct sdw_bus_params {
* @update_status: Update Slave status
* @bus_config: Update the bus config for Slave
* @port_prep: Prepare the port with parameters
* @clk_stop: handle imp-def sequences before and after prepare and de-prepare
*/
struct sdw_slave_ops {
int (*read_prop)(struct sdw_slave *sdw);
@ -624,7 +625,6 @@ struct sdw_slave_ops {
int (*port_prep)(struct sdw_slave *slave,
struct sdw_prepare_ch *prepare_ch,
enum sdw_port_prep_ops pre_ops);
int (*get_clk_stop_mode)(struct sdw_slave *slave);
int (*clk_stop)(struct sdw_slave *slave,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type);
@ -675,7 +675,6 @@ struct sdw_slave {
struct list_head node;
struct completion port_ready[SDW_MAX_PORTS];
unsigned int m_port_map[SDW_MAX_PORTS];
enum sdw_clk_stop_mode curr_clk_stop_mode;
u16 dev_num;
u16 dev_num_sticky;
bool probed;
@ -1040,7 +1039,7 @@ int sdw_write(struct sdw_slave *slave, u32 addr, u8 value);
int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value);
int sdw_read_no_pm(struct sdw_slave *slave, u32 addr);
int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val);
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val);
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val);
int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id);
void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);

View File

@ -58,7 +58,7 @@ struct sdw_intel_acpi_info {
u32 link_mask;
};
struct sdw_intel_link_res;
struct sdw_intel_link_dev;
/* Intel clock-stop/pm_runtime quirk definitions */
@ -109,7 +109,7 @@ struct sdw_intel_slave_id {
* Controller
* @num_slaves: total number of devices exposed across all enabled links
* @handle: ACPI parent handle
* @links: information for each link (controller-specific and kept
* @ldev: information for each link (controller-specific and kept
* opaque here)
* @ids: array of slave_id, representing Slaves exposed across all enabled
* links
@ -123,7 +123,7 @@ struct sdw_intel_ctx {
u32 link_mask;
int num_slaves;
acpi_handle handle;
struct sdw_intel_link_res *links;
struct sdw_intel_link_dev **ldev;
struct sdw_intel_slave_id *ids;
struct list_head link_list;
struct mutex shim_lock; /* lock for access to shared SHIM registers */

View File

@ -581,13 +581,13 @@ static int create_codec_dai_name(struct device *dev,
comp_index = i + offset;
if (is_unique_device(link, sdw_version, mfg_id, part_id,
class_id, i)) {
codec_str = "sdw:%x:%x:%x:%x";
codec_str = "sdw:%01x:%04x:%04x:%02x";
codec[comp_index].name =
devm_kasprintf(dev, GFP_KERNEL, codec_str,
link_id, mfg_id, part_id,
class_id);
} else {
codec_str = "sdw:%x:%x:%x:%x:%x";
codec_str = "sdw:%01x:%04x:%04x:%02x:%01x";
codec[comp_index].name =
devm_kasprintf(dev, GFP_KERNEL, codec_str,
link_id, mfg_id, part_id,