diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index af177f0144b8fe..b42c1868378ba3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -2098,6 +2098,36 @@ static unsigned int sdw_cdns_read_pdi1_buffer_size(unsigned int actual_data_size return total * 2; } +int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */ + int row, int col, int frame_rate, + unsigned int *tx_dma_bandwidth, + unsigned int *rx_dma_bandwidth) +{ + unsigned int bpt_bits = row * (col - 1); + unsigned int bpt_bytes = bpt_bits >> 3; + unsigned int pdi0_buffer_size; + unsigned int pdi1_buffer_size; + unsigned int data_per_frame; + + data_per_frame = sdw_cdns_bra_actual_data_size(bpt_bytes); + if (!data_per_frame) + return -EINVAL; + + if (command == 0) { + pdi0_buffer_size = sdw_cdns_write_pdi0_buffer_size(data_per_frame); + pdi1_buffer_size = SDW_CDNS_WRITE_PDI1_BUFFER_SIZE; + } else { + pdi0_buffer_size = SDW_CDNS_READ_PDI0_BUFFER_SIZE; + pdi1_buffer_size = sdw_cdns_read_pdi1_buffer_size(data_per_frame); + } + + *tx_dma_bandwidth = pdi0_buffer_size * 8 * frame_rate; + *rx_dma_bandwidth = pdi1_buffer_size * 8 * frame_rate; + + return 0; +} +EXPORT_SYMBOL(sdw_cdns_bpt_find_bandwidth); + int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, @@ -2118,9 +2148,6 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ if (!actual_bpt_bytes) return -EINVAL; - if (data_bytes < actual_bpt_bytes) - actual_bpt_bytes = data_bytes; - /* * the caller may want to set the number of bytes per frame, * allow when possible @@ -2130,6 +2157,9 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ *data_per_frame = actual_bpt_bytes; + if (data_bytes < actual_bpt_bytes) + actual_bpt_bytes = data_bytes; + if (command == 0) { /* * for writes we need to send all the data_bytes per frame, @@ -2367,7 +2397,7 @@ 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 data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes) + int *dma_buffer_total_bytes, unsigned int fake_size) { int total_dma_data_written = 0; u8 *p_dma_buffer = dma_buffer; @@ -2419,6 +2449,43 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_si 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; + } + + /* Add fake frame */ + header[0] &= ~GENMASK(7, 6); /* Set inactive flag in BPT/BRA frame heade */ + while (fake_size >= data_per_frame) { + header[1] = data_per_frame; + 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++; + + fake_size -= data_per_frame; + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; + total_dma_data_written += dma_data_written; + } + + if (fake_size) { + header[1] = fake_size; + 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++; + + p_dma_buffer += dma_data_written; + dma_buffer_size -= dma_data_written; total_dma_data_written += dma_data_written; } @@ -2499,14 +2566,14 @@ int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer, ret = check_frame_start(header, counter); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d start header %x\n", - __func__, i, num_frames, header); + __func__, i + 1, num_frames, header); return ret; } ret = check_frame_end(footer); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d end footer %x\n", - __func__, i, num_frames, footer); + __func__, i + 1, num_frames, footer); return ret; } @@ -2577,7 +2644,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf ret = check_frame_start(header, counter); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d start header %x\n", - __func__, i, num_frames, header); + __func__, i + 1, num_frames, header); return ret; } @@ -2592,7 +2659,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf if (crc != expected_crc) { dev_err(dev, "%s: bad frame %d/%d crc %#x expected %#x\n", - __func__, i, num_frames, crc, expected_crc); + __func__, i + 1, num_frames, crc, expected_crc); return -EIO; } @@ -2603,7 +2670,7 @@ int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buf ret = check_frame_end(footer); if (ret < 0) { dev_err(dev, "%s: bad frame %d/%d end footer %x\n", - __func__, i, num_frames, footer); + __func__, i + 1, num_frames, footer); return ret; } diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 9373426c7f630b..a269a87486fc6d 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -209,6 +209,11 @@ void sdw_cdns_config_update(struct sdw_cdns *cdns); int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns); /* SoundWire BPT/BRA helpers to format data */ +int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */ + int row, int col, int frame_rate, + unsigned int *tx_dma_bandwidth, + unsigned int *rx_dma_bandwidth); + int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */ int row, int col, unsigned int data_bytes, unsigned int requested_bytes_per_frame, @@ -221,7 +226,7 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size, int data_per_frame, u8 *dma_buffer, int dma_buffer_size, - int *dma_buffer_total_bytes); + 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); diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index aad8bf8eb211a5..ee607dd6802862 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -125,6 +125,9 @@ static void sdw_compute_dp0_port_params(struct sdw_bus *bus) struct sdw_master_runtime *m_rt; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + /* DP0 is for BPT only */ + if (m_rt->stream->type != SDW_STREAM_BPT) + continue; sdw_compute_dp0_master_ports(m_rt); sdw_compute_dp0_slave_ports(m_rt); } diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 5d08364ad6d141..57524cc7a05dd0 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -44,6 +44,8 @@ static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_r return ret; } +#define READ_PDI1_MIN_SIZE 12 + static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { @@ -53,15 +55,23 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_stream_runtime *stream; struct sdw_stream_config sconfig; struct sdw_port_config *pconfig; + unsigned int pdi0_buf_size_pre_frame; + unsigned int pdi1_buf_size_pre_frame; unsigned int pdi0_buffer_size; unsigned int tx_dma_bandwidth; unsigned int pdi1_buffer_size; unsigned int rx_dma_bandwidth; + unsigned int fake_num_frames; unsigned int data_per_frame; unsigned int tx_total_bytes; struct sdw_cdns_pdi *pdi0; struct sdw_cdns_pdi *pdi1; + unsigned int rx_alignment; + unsigned int tx_alignment; unsigned int num_frames; + unsigned int fake_size; + unsigned int tx_pad; + unsigned int rx_pad; int command; int ret1; int ret; @@ -138,6 +148,13 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1; + ret = sdw_cdns_bpt_find_bandwidth(command, cdns->bus.params.row, + cdns->bus.params.col, + prop->default_frame_rate, + &tx_dma_bandwidth, &rx_dma_bandwidth); + if (ret < 0) + goto deprepare_stream; + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame, &pdi0_buffer_size, &pdi1_buffer_size, &num_frames); @@ -148,10 +165,44 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size; sdw->bpt_ctx.num_frames = num_frames; sdw->bpt_ctx.data_per_frame = data_per_frame; - tx_dma_bandwidth = div_u64((u64)pdi0_buffer_size * 8 * (u64)prop->default_frame_rate, - num_frames); - rx_dma_bandwidth = div_u64((u64)pdi1_buffer_size * 8 * (u64)prop->default_frame_rate, - num_frames); + + rx_alignment = hda_sdw_bpt_get_buf_size_alignment(rx_dma_bandwidth); + tx_alignment = hda_sdw_bpt_get_buf_size_alignment(tx_dma_bandwidth); + + if (command) { /* read */ + /* Get buffer size of a full frame */ + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, + cdns->bus.params.col, + data_per_frame, SDW_BPT_MSG_MAX_BYTES, + &data_per_frame, &pdi0_buf_size_pre_frame, + &pdi1_buf_size_pre_frame, &fake_num_frames); + if (ret < 0) + goto deprepare_stream; + + /* find fake pdi1 buffer size */ + rx_pad = rx_alignment - (pdi1_buffer_size % rx_alignment); + while (rx_pad <= READ_PDI1_MIN_SIZE) { + rx_pad += rx_alignment; + } + pdi1_buffer_size += rx_pad; + /* It is fine if we request more than enough byte to read */ + fake_num_frames = DIV_ROUND_UP(rx_pad, pdi1_buf_size_pre_frame); + fake_size = fake_num_frames * data_per_frame; + + /* find fake pdi0 buffer size */ + pdi0_buffer_size += (fake_num_frames * pdi0_buf_size_pre_frame); + tx_pad = tx_alignment - (pdi0_buffer_size % tx_alignment); + pdi0_buffer_size += tx_pad; + } else { /* write */ + /* + * For the write command, the rx data block is 4, and the rx buffer size of a frame + * is 8. So the rx buffer size (pdi0_buffer_size) is always a mutiple of rx + * alignment. + */ + tx_pad = tx_alignment - (pdi0_buffer_size % tx_alignment); + pdi0_buffer_size += tx_pad; + + } dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", msg->len, num_frames, data_per_frame); @@ -177,7 +228,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len, data_per_frame, sdw->bpt_ctx.dmab_tx_bdl.area, - pdi0_buffer_size, &tx_total_bytes); + pdi0_buffer_size, &tx_total_bytes, fake_size); } if (!ret) diff --git a/include/sound/hda-sdw-bpt.h b/include/sound/hda-sdw-bpt.h index f649549b75d52d..9b654c31829ad6 100644 --- a/include/sound/hda-sdw-bpt.h +++ b/include/sound/hda-sdw-bpt.h @@ -30,6 +30,8 @@ int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, struct snd_dma_buffer *dmab_rx_bdl); + +unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth); #else static inline int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, @@ -64,6 +66,11 @@ static inline int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream * WARN_ONCE(1, "SoundWire BPT is disabled"); return -EOPNOTSUPP; } + +static inline unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth) +{ + return 0; +} #endif #endif /* __HDA_SDW_BPT_H */ diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index 421ca71c5ddbad..f2f0d13ef1a0d2 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -10,6 +10,7 @@ * Hardware interface for SoundWire BPT support with HDA DMA */ +#include #include #include #include @@ -247,6 +248,18 @@ static int hda_sdw_bpt_dma_disable(struct device *dev, struct hdac_ext_stream *s return ret; } +#define FIFO_ALIGNMENT 64 + +unsigned int hda_sdw_bpt_get_buf_size_alignment(unsigned int dma_bandwidth) +{ + unsigned int num_channels = DIV_ROUND_UP(dma_bandwidth, BPT_FREQUENCY * 32); + unsigned int data_block = num_channels * 4; + unsigned int alignment = lcm(data_block, FIFO_ALIGNMENT); + + return alignment; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_get_buf_size_alignment, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); + int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes, u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream,