Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASoC/SoundWire: add BTP/BRA prerequisites #4735

Merged
30 changes: 9 additions & 21 deletions drivers/soundwire/cadence_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,7 @@ EXPORT_SYMBOL(sdw_cdns_enable_interrupt);

static int cdns_allocate_pdi(struct sdw_cdns *cdns,
struct sdw_cdns_pdi **stream,
u32 num, u32 pdi_offset)
u32 num)
{
struct sdw_cdns_pdi *pdi;
int i;
Expand All @@ -1249,7 +1249,7 @@ static int cdns_allocate_pdi(struct sdw_cdns *cdns,
return -ENOMEM;

for (i = 0; i < num; i++) {
pdi[i].num = i + pdi_offset;
pdi[i].num = i;
}

*stream = pdi;
Expand All @@ -1266,7 +1266,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config)
{
struct sdw_cdns_streams *stream;
int offset;
int ret;

cdns->pcm.num_bd = config.pcm_bd;
Expand All @@ -1277,24 +1276,15 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
stream = &cdns->pcm;

/* we allocate PDI0 and PDI1 which are used for Bulk */
offset = 0;

ret = cdns_allocate_pdi(cdns, &stream->bd,
stream->num_bd, offset);
ret = cdns_allocate_pdi(cdns, &stream->bd, stream->num_bd);
if (ret)
return ret;

offset += stream->num_bd;

ret = cdns_allocate_pdi(cdns, &stream->in,
stream->num_in, offset);
ret = cdns_allocate_pdi(cdns, &stream->in, stream->num_in);
if (ret)
return ret;

offset += stream->num_in;

ret = cdns_allocate_pdi(cdns, &stream->out,
stream->num_out, offset);
ret = cdns_allocate_pdi(cdns, &stream->out, stream->num_out);
if (ret)
return ret;

Expand Down Expand Up @@ -1802,7 +1792,6 @@ EXPORT_SYMBOL(cdns_set_sdw_stream);
* cdns_find_pdi() - Find a free PDI
*
* @cdns: Cadence instance
* @offset: Starting offset
* @num: Number of PDIs
* @pdi: PDI instances
* @dai_id: DAI id
Expand All @@ -1811,14 +1800,13 @@ EXPORT_SYMBOL(cdns_set_sdw_stream);
* expected to match, return NULL otherwise.
*/
static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
unsigned int offset,
unsigned int num,
struct sdw_cdns_pdi *pdi,
int dai_id)
{
int i;

for (i = offset; i < offset + num; i++)
for (i = 0; i < num; i++)
if (pdi[i].num == dai_id)
return &pdi[i];

Expand Down Expand Up @@ -1872,15 +1860,15 @@ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
struct sdw_cdns_pdi *pdi = NULL;

if (dir == SDW_DATA_DIR_RX)
pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in,
pdi = cdns_find_pdi(cdns, stream->num_in, stream->in,
dai_id);
else
pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out,
pdi = cdns_find_pdi(cdns, stream->num_out, stream->out,
dai_id);

/* check if we found a PDI, else find in bi-directional */
if (!pdi)
pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd,
pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd,
dai_id);

if (pdi) {
Expand Down
150 changes: 150 additions & 0 deletions drivers/soundwire/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/firmware.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -137,6 +138,145 @@ static int sdw_slave_reg_show(struct seq_file *s_file, void *data)
}
DEFINE_SHOW_ATTRIBUTE(sdw_slave_reg);

#define MAX_CMD_BYTES 256

static int cmd;
static u32 start_addr;
static size_t num_bytes;
static u8 read_buffer[MAX_CMD_BYTES];
static char *firmware_file;

static int set_command(void *data, u64 value)
{
struct sdw_slave *slave = data;

if (value > 1)
return -EINVAL;

/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);

dev_dbg(&slave->dev, "command: %s\n", value ? "read" : "write");
cmd = value;

return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(set_command_fops, NULL,
set_command, "%llu\n");

static int set_start_address(void *data, u64 value)
{
struct sdw_slave *slave = data;

/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);

dev_dbg(&slave->dev, "start address %#llx\n", value);

start_addr = value;

return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(set_start_address_fops, NULL,
set_start_address, "%llu\n");

static int set_num_bytes(void *data, u64 value)
{
struct sdw_slave *slave = data;

if (value == 0 || value > MAX_CMD_BYTES)
return -EINVAL;

/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);

dev_dbg(&slave->dev, "number of bytes %lld\n", value);

num_bytes = value;

return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(set_num_bytes_fops, NULL,
set_num_bytes, "%llu\n");

static int cmd_go(void *data, u64 value)
{
struct sdw_slave *slave = data;
int ret;

if (value != 1)
return -EINVAL;

/* one last check */
if (start_addr > SDW_REG_MAX ||
num_bytes == 0 || num_bytes > MAX_CMD_BYTES)
return -EINVAL;

ret = pm_runtime_get_sync(&slave->dev);
if (ret < 0 && ret != -EACCES) {
pm_runtime_put_noidle(&slave->dev);
return ret;
}

/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);

dev_dbg(&slave->dev, "starting command\n");

if (cmd == 0) {
const struct firmware *fw;

ret = request_firmware(&fw, firmware_file, &slave->dev);
if (ret < 0) {
dev_err(&slave->dev, "firmware %s not found\n", firmware_file);
goto out;
}

if (fw->size != num_bytes) {
dev_err(&slave->dev,
"firmware %s: unexpected size %zd, desired %zd\n",
firmware_file, fw->size, num_bytes);
release_firmware(fw);
goto out;
}

ret = sdw_nwrite_no_pm(slave, start_addr, num_bytes, fw->data);
release_firmware(fw);
} else {
ret = sdw_nread_no_pm(slave, start_addr, num_bytes, read_buffer);
}

dev_dbg(&slave->dev, "command completed %d\n", ret);

out:
pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put(&slave->dev);

return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(cmd_go_fops, NULL,
cmd_go, "%llu\n");

#define MAX_LINE_LEN 128

static int read_buffer_show(struct seq_file *s_file, void *data)
{
char buf[MAX_LINE_LEN];
int i;

if (num_bytes == 0 || num_bytes > MAX_CMD_BYTES)
return -EINVAL;

for (i = 0; i < num_bytes; i++) {
scnprintf(buf, MAX_LINE_LEN, "address %#x val 0x%02x\n",
start_addr + i, read_buffer[i]);
seq_printf(s_file, "%s", buf);
}

return 0;
}
DEFINE_SHOW_ATTRIBUTE(read_buffer);

void sdw_slave_debugfs_init(struct sdw_slave *slave)
{
struct dentry *master;
Expand All @@ -151,6 +291,16 @@ void sdw_slave_debugfs_init(struct sdw_slave *slave)

debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);

/* interface to send arbitrary commands */
debugfs_create_file("command", 0200, d, slave, &set_command_fops);
debugfs_create_file("start_address", 0200, d, slave, &set_start_address_fops);
debugfs_create_file("num_bytes", 0200, d, slave, &set_num_bytes_fops);
debugfs_create_file("go", 0200, d, slave, &cmd_go_fops);

debugfs_create_file("read_buffer", 0400, d, slave, &read_buffer_fops);
firmware_file = NULL;
debugfs_create_str("firmware_file", 0200, d, &firmware_file);

slave->debugfs = d;
}

Expand Down
5 changes: 5 additions & 0 deletions drivers/soundwire/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,8 @@ static struct sdw_master_runtime
m_rt->bus = bus;
m_rt->stream = stream;

bus->stream_refcount++;

return m_rt;
}

Expand Down Expand Up @@ -1216,6 +1218,7 @@ static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
struct sdw_stream_runtime *stream)
{
struct sdw_slave_runtime *s_rt, *_s_rt;
struct sdw_bus *bus = m_rt->bus;

list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) {
sdw_slave_port_free(s_rt->slave, stream);
Expand All @@ -1225,6 +1228,8 @@ static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
list_del(&m_rt->stream_node);
list_del(&m_rt->bus_node);
kfree(m_rt);

bus->stream_refcount--;
}

/**
Expand Down
21 changes: 5 additions & 16 deletions include/linux/soundwire/sdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ enum sdw_clk_stop_mode {
* @BRA_flow_controlled: Slave implementation results in an OK_NotReady
* response
* @simple_ch_prep_sm: If channel prepare sequence is required
* @imp_def_interrupts: If set, each bit corresponds to support for
* @ch_prep_timeout: Port-specific timeout value, in milliseconds
* @imp_def_interrupts: If set, each bit corresponds to support for
* implementation-defined interrupts
*
* The wordlengths are specified by Spec as max, min AND number of
Expand All @@ -249,6 +250,7 @@ struct sdw_dp0_prop {
u32 *words;
bool BRA_flow_controlled;
bool simple_ch_prep_sm;
u32 ch_prep_timeout;
bool imp_def_interrupts;
};

Expand Down Expand Up @@ -542,21 +544,6 @@ enum sdw_reg_bank {
SDW_BANK1,
};

/**
* struct sdw_bus_conf: Bus configuration
*
* @clk_freq: Clock frequency, in Hz
* @num_rows: Number of rows in frame
* @num_cols: Number of columns in frame
* @bank: Next register bank
*/
struct sdw_bus_conf {
unsigned int clk_freq;
unsigned int num_rows;
unsigned int num_cols;
unsigned int bank;
};

/**
* struct sdw_prepare_ch: Prepare/De-prepare Data Port channel
*
Expand Down Expand Up @@ -915,6 +902,7 @@ struct sdw_master_ops {
* meaningful if multi_link is set. If set to 1, hardware-based
* synchronization will be used even if a stream only uses a single
* SoundWire segment.
* @stream_refcount: number of streams currently using this bus
*/
struct sdw_bus {
struct device *dev;
Expand Down Expand Up @@ -944,6 +932,7 @@ struct sdw_bus {
u32 bank_switch_timeout;
bool multi_link;
int hw_sync_min_links;
int stream_refcount;
Copy link

@RanderWang RanderWang Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refcount is native supported by Linux kernel. How about to use it ? It can help to decrease multi-thread risk

refcount_inc, refcount_set, refcount_dec_and_test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good point @RanderWang, I initially planned to use the refcount helpers, but there's a protection:

sdw_master_rt_alloc() and sdw_master_rt_free() need to be called with bus_lock held, it's clearly written in the documentation and it's used this way.

I didn't see the need for additional protection.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a comment in the commit message to make this clear.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, get it

};

int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
Expand Down
2 changes: 1 addition & 1 deletion include/linux/soundwire/sdw_registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#define SDW_REG_NO_PAGE 0x00008000
#define SDW_REG_OPTIONAL_PAGE 0x00010000
#define SDW_REG_MAX 0x80000000
#define SDW_REG_MAX 0x48000000

#define SDW_DPN_SIZE 0x100
#define SDW_BANK1_OFFSET 0x10
Expand Down
2 changes: 1 addition & 1 deletion include/sound/hda-mlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,4 @@ hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enabl
{
return 0;
}
#endif /* CONFIG_SND_SOC_SOF_HDA */
#endif /* CONFIG_SND_SOC_SOF_HDA_MLINK */
1 change: 1 addition & 0 deletions sound/soc/sof/intel/hda-stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1080,3 +1080,4 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,

return pos;
}
EXPORT_SYMBOL_NS(hda_dsp_stream_get_position, SND_SOC_SOF_INTEL_HDA_COMMON);