Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions drivers/soundwire/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -2040,8 +2040,14 @@ EXPORT_SYMBOL(sdw_clear_slave_status);

int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
{
if (msg->len > SDW_BPT_MSG_MAX_BYTES) {
dev_err(bus->dev, "Invalid BPT message length %d\n", msg->len);
int len = 0;
int i;

for (i = 0; i < msg->sections; i++)
len += msg->sec[i].len;

if (len > SDW_BPT_MSG_MAX_BYTES) {
dev_err(bus->dev, "Invalid BPT message length %d\n", len);
return -EINVAL;
}

Expand Down
22 changes: 16 additions & 6 deletions drivers/soundwire/bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,31 @@ struct sdw_msg {
};

/**
* struct sdw_btp_msg - Message structure
* struct sdw_btp_section - Message section structure
* @addr: Start Register address accessed in the Slave
* @len: number of bytes to transfer. More than 64Kb can be transferred
* but a practical limit of SDW_BPT_MSG_MAX_BYTES is enforced.
* @dev_num: Slave device number
* @flags: transfer flags, indicate if xfer is read or write
* @buf: message data buffer (filled by host for write, filled
* @buf: section data buffer (filled by host for write, filled
* by Peripheral hardware for reads)
*/
struct sdw_bpt_msg {
struct sdw_bpt_section {
u32 addr;
u32 len;
u8 *buf;
};

/**
* struct sdw_btp_msg - Message structure
* @sec: Pointer to array of sections
* @sections: Number of sections in the array
* @dev_num: Slave device number
* @flags: transfer flags, indicate if xfer is read or write
*/
struct sdw_bpt_msg {
struct sdw_bpt_section *sec;
int sections;
u8 dev_num;
u8 flags;
u8 *buf;
};

#define SDW_DOUBLE_RATE_FACTOR 2
Expand Down
186 changes: 112 additions & 74 deletions drivers/soundwire/cadence_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -2328,65 +2328,78 @@ static int sdw_cdns_prepare_read_pd0_buffer(u8 *header, unsigned int header_size

#define CDNS_BPT_ROLLING_COUNTER_START 1

int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size,
int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
int *dma_buffer_total_bytes)
int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec,
int data_per_frame, u8 *dma_buffer,
int dma_buffer_size, int *dma_buffer_total_bytes)
{
int total_dma_data_written = 0;
u8 *p_dma_buffer = dma_buffer;
u8 header[SDW_CDNS_BRA_HDR];
unsigned int start_register;
unsigned int section_size;
int dma_data_written;
u8 *p_data = data;
u8 *p_data;
u8 counter;
int ret;
int i;

counter = CDNS_BPT_ROLLING_COUNTER_START;

header[0] = BIT(1); /* write command: BIT(1) set */
header[0] |= GENMASK(7, 6); /* header is active */
header[0] |= (dev_num << 2);

while (data_size >= data_per_frame) {
header[1] = data_per_frame;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;

ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_data, data_per_frame,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;
for (i = 0; i < num_sec; i++) {
start_register = sec[i].addr;
section_size = sec[i].len;
p_data = sec[i].buf;

counter++;
while (section_size >= data_per_frame) {
header[1] = data_per_frame;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;

p_data += data_per_frame;
data_size -= data_per_frame;
ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_data, data_per_frame,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;

p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;
counter++;

start_register += data_per_frame;
}
p_data += data_per_frame;
section_size -= data_per_frame;

if (data_size) {
header[1] = data_size;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;
p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;

ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_data, data_size,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;
start_register += data_per_frame;
}

total_dma_data_written += dma_data_written;
if (section_size) {
header[1] = section_size;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;

ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_data, section_size,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;

counter++;

p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;
}
}

*dma_buffer_total_bytes = total_dma_data_written;
Expand All @@ -2395,65 +2408,72 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data,
}
EXPORT_SYMBOL(sdw_cdns_prepare_write_dma_buffer);

int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size,
int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec,
int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
int *dma_buffer_total_bytes, unsigned int fake_size)
{
int total_dma_data_written = 0;
u8 *p_dma_buffer = dma_buffer;
u8 header[SDW_CDNS_BRA_HDR];
unsigned int start_register;
unsigned int data_size;
int dma_data_written;
u8 counter;
int ret;
int i;

counter = CDNS_BPT_ROLLING_COUNTER_START;

header[0] = 0; /* read command: BIT(1) cleared */
header[0] |= GENMASK(7, 6); /* header is active */
header[0] |= (dev_num << 2);

while (data_size >= data_per_frame) {
header[1] = data_per_frame;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;
for (i = 0; i < num_sec; i++) {
start_register = sec[i].addr;
data_size = sec[i].len;
while (data_size >= data_per_frame) {
header[1] = data_per_frame;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;

ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
dma_buffer_size, &dma_data_written,
counter);
if (ret < 0)
return ret;
ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;

counter++;
counter++;

data_size -= data_per_frame;
data_size -= data_per_frame;

p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;
p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;

start_register += data_per_frame;
}
start_register += data_per_frame;
}

if (data_size) {
header[1] = data_size;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;
if (data_size) {
header[1] = data_size;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
header[5] = start_register >> 0 & 0xFF;

ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
dma_buffer_size, &dma_data_written,
counter);
if (ret < 0)
return ret;
ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR,
p_dma_buffer, dma_buffer_size,
&dma_data_written, counter);
if (ret < 0)
return ret;

counter++;
counter++;

p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;
p_dma_buffer += dma_data_written;
dma_buffer_size -= dma_data_written;
total_dma_data_written += dma_data_written;
}
}

/* Add fake frame */
Expand Down Expand Up @@ -2620,9 +2640,12 @@ static u8 extract_read_data(u32 *data, int num_bytes, u8 *buffer)
}

int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size,
u8 *buffer, int buffer_size, int num_frames, int data_per_frame)
struct sdw_bpt_section *sec, int num_sec, int num_frames,
int data_per_frame)
{
int total_num_bytes = 0;
int buffer_size = 0;
int sec_index;
u32 *p_data;
u8 *p_buf;
int counter;
Expand All @@ -2636,7 +2659,10 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf

counter = CDNS_BPT_ROLLING_COUNTER_START;
p_data = (u32 *)dma_buffer;
p_buf = buffer;

sec_index = 0;
p_buf = sec[sec_index].buf;
buffer_size = sec[sec_index].len;

for (i = 0; i < num_frames; i++) {
header = *p_data++;
Expand Down Expand Up @@ -2676,6 +2702,18 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf

counter++;
counter &= GENMASK(3, 0);

if (buffer_size == total_num_bytes && (i + 1) < num_frames) {
sec_index++;
if (sec_index >= num_sec) {
dev_err(dev, "%s: incorrect section index %d i %d\n",
__func__, sec_index, i);
return -EINVAL;
}
p_buf = sec[sec_index].buf;
buffer_size = sec[sec_index].len;
total_num_bytes = 0;
}
Comment on lines +2706 to +2716
Copy link

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

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

The condition buffer_size == total_num_bytes may incorrectly trigger section advancement. This logic assumes sections are processed sequentially with exact byte counts, but the comparison should account for accumulated data across frames. Consider using a running total per section instead.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

buffer_size is the buffer size of each section and total_num_bytes is the buffer size that has been written. They should be exactly the same when a section is sent. Then reset buffer_size and total_num_bytes for the next section.

}
return 0;
}
Expand Down
12 changes: 7 additions & 5 deletions drivers/soundwire/cadence_master.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/* Copyright(c) 2015-17 Intel Corporation. */
#include <sound/soc.h>
#include "bus.h"

#ifndef __SDW_CADENCE_H
#define __SDW_CADENCE_H
Expand Down Expand Up @@ -220,17 +221,18 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */
unsigned int *data_per_frame, unsigned int *pdi0_buffer_size,
unsigned int *pdi1_buffer_size, unsigned int *num_frames);

int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size,
int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
int *dma_buffer_total_bytes);
int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec,
int data_per_frame, u8 *dma_buffer,
int dma_buffer_size, int *dma_buffer_total_bytes);

int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size,
int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, int num_sec,
int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
int *dma_buffer_total_bytes, unsigned int fake_size);

int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer,
int dma_buffer_size, int num_frames);

int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size,
u8 *buffer, int buffer_size, int num_frames, int data_per_frame);
struct sdw_bpt_section *sec, int num_sec, int num_frames,
int data_per_frame);
#endif /* __SDW_CADENCE_H */
14 changes: 11 additions & 3 deletions drivers/soundwire/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,23 @@ DEFINE_DEBUGFS_ATTRIBUTE(set_num_bytes_fops, NULL,
static int do_bpt_sequence(struct sdw_slave *slave, bool write, u8 *buffer)
{
struct sdw_bpt_msg msg = {0};
struct sdw_bpt_section *sec;

msg.addr = start_addr;
msg.len = num_bytes;
sec = kcalloc(1, sizeof(*sec), GFP_KERNEL);
if (!sec)
return -ENOMEM;
msg.sections = 1;

sec[0].addr = start_addr;
sec[0].len = num_bytes;

msg.sec = sec;
msg.dev_num = slave->dev_num;
if (write)
msg.flags = SDW_MSG_FLAG_WRITE;
else
msg.flags = SDW_MSG_FLAG_READ;
msg.buf = buffer;
sec[0].buf = buffer;

return sdw_bpt_send_sync(slave->bus, slave, &msg);
}
Expand Down
Loading
Loading