Skip to content

Commit 4c1ce9f

Browse files
plbossartvinodkoul
authored andcommitted
soundwire: intel_ace2x: add BPT send_async/wait callbacks
Add support for BTP API using Cadence and hda-sdw-bpt helpers. Signed-off-by: Pierre-Louis Bossart <[email protected]> Signed-off-by: Bard Liao <[email protected]> Reviewed-by: Péter Ujfalusi <[email protected]> Reviewed-by: Liam Girdwood <[email protected]> Reviewed-by: Ranjani Sridharan <[email protected]> Tested-by: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent 5cdc237 commit 4c1ce9f

File tree

1 file changed

+312
-0
lines changed

1 file changed

+312
-0
lines changed

drivers/soundwire/intel_ace2x.c

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,320 @@
1313
#include <linux/soundwire/sdw_intel.h>
1414
#include <sound/hdaudio.h>
1515
#include <sound/hda-mlink.h>
16+
#include <sound/hda-sdw-bpt.h>
1617
#include <sound/hda_register.h>
1718
#include <sound/pcm_params.h>
1819
#include "cadence_master.h"
1920
#include "bus.h"
2021
#include "intel.h"
2122

23+
static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_runtime *stream)
24+
{
25+
struct sdw_stream_config sconfig = {0};
26+
struct sdw_port_config pconfig = {0};
27+
int ret;
28+
29+
/* arbitrary configuration */
30+
sconfig.frame_rate = 16000;
31+
sconfig.ch_count = 1;
32+
sconfig.bps = 32; /* this is required for BPT/BRA */
33+
sconfig.direction = SDW_DATA_DIR_RX;
34+
sconfig.type = SDW_STREAM_BPT;
35+
36+
pconfig.num = 0;
37+
pconfig.ch_mask = BIT(0);
38+
39+
ret = sdw_stream_add_slave(slave, &sconfig, &pconfig, 1, stream);
40+
if (ret)
41+
dev_err(&slave->dev, "%s: failed: %d\n", __func__, ret);
42+
43+
return ret;
44+
}
45+
46+
static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave,
47+
struct sdw_bpt_msg *msg)
48+
{
49+
struct sdw_cdns *cdns = &sdw->cdns;
50+
struct sdw_bus *bus = &cdns->bus;
51+
struct sdw_master_prop *prop = &bus->prop;
52+
struct sdw_stream_runtime *stream;
53+
struct sdw_stream_config sconfig;
54+
struct sdw_port_config *pconfig;
55+
unsigned int pdi0_buffer_size;
56+
unsigned int tx_dma_bandwidth;
57+
unsigned int pdi1_buffer_size;
58+
unsigned int rx_dma_bandwidth;
59+
unsigned int data_per_frame;
60+
unsigned int tx_total_bytes;
61+
struct sdw_cdns_pdi *pdi0;
62+
struct sdw_cdns_pdi *pdi1;
63+
unsigned int num_frames;
64+
int command;
65+
int ret1;
66+
int ret;
67+
int dir;
68+
int i;
69+
70+
stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT);
71+
if (!stream)
72+
return -ENOMEM;
73+
74+
cdns->bus.bpt_stream = stream;
75+
76+
ret = sdw_slave_bpt_stream_add(slave, stream);
77+
if (ret < 0)
78+
goto release_stream;
79+
80+
/* handle PDI0 first */
81+
dir = SDW_DATA_DIR_TX;
82+
83+
pdi0 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 0);
84+
if (!pdi0) {
85+
dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi0 failed\n", __func__);
86+
ret = -EINVAL;
87+
goto remove_slave;
88+
}
89+
90+
sdw_cdns_config_stream(cdns, 1, dir, pdi0);
91+
92+
/* handle PDI1 */
93+
dir = SDW_DATA_DIR_RX;
94+
95+
pdi1 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 1);
96+
if (!pdi1) {
97+
dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi1 failed\n", __func__);
98+
ret = -EINVAL;
99+
goto remove_slave;
100+
}
101+
102+
sdw_cdns_config_stream(cdns, 1, dir, pdi1);
103+
104+
/*
105+
* the port config direction, number of channels and frame
106+
* rate is totally arbitrary
107+
*/
108+
sconfig.direction = dir;
109+
sconfig.ch_count = 1;
110+
sconfig.frame_rate = 16000;
111+
sconfig.type = SDW_STREAM_BPT;
112+
sconfig.bps = 32; /* this is required for BPT/BRA */
113+
114+
/* Port configuration */
115+
pconfig = kcalloc(2, sizeof(*pconfig), GFP_KERNEL);
116+
if (!pconfig) {
117+
ret = -ENOMEM;
118+
goto remove_slave;
119+
}
120+
121+
for (i = 0; i < 2 /* num_pdi */; i++) {
122+
pconfig[i].num = i;
123+
pconfig[i].ch_mask = 1;
124+
}
125+
126+
ret = sdw_stream_add_master(&cdns->bus, &sconfig, pconfig, 2, stream);
127+
kfree(pconfig);
128+
129+
if (ret < 0) {
130+
dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
131+
goto remove_slave;
132+
}
133+
134+
ret = sdw_prepare_stream(cdns->bus.bpt_stream);
135+
if (ret < 0)
136+
goto remove_master;
137+
138+
command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1;
139+
140+
ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col,
141+
msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame,
142+
&pdi0_buffer_size, &pdi1_buffer_size, &num_frames);
143+
if (ret < 0)
144+
goto deprepare_stream;
145+
146+
sdw->bpt_ctx.pdi0_buffer_size = pdi0_buffer_size;
147+
sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size;
148+
sdw->bpt_ctx.num_frames = num_frames;
149+
sdw->bpt_ctx.data_per_frame = data_per_frame;
150+
tx_dma_bandwidth = div_u64((u64)pdi0_buffer_size * 8 * (u64)prop->default_frame_rate,
151+
num_frames);
152+
rx_dma_bandwidth = div_u64((u64)pdi1_buffer_size * 8 * (u64)prop->default_frame_rate,
153+
num_frames);
154+
155+
dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n",
156+
msg->len, num_frames, data_per_frame);
157+
dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n",
158+
pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth);
159+
160+
ret = hda_sdw_bpt_open(cdns->dev->parent, /* PCI device */
161+
sdw->instance, &sdw->bpt_ctx.bpt_tx_stream,
162+
&sdw->bpt_ctx.dmab_tx_bdl, pdi0_buffer_size, tx_dma_bandwidth,
163+
&sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl,
164+
pdi1_buffer_size, rx_dma_bandwidth);
165+
if (ret < 0) {
166+
dev_err(cdns->dev, "%s: hda_sdw_bpt_open failed %d\n", __func__, ret);
167+
goto deprepare_stream;
168+
}
169+
170+
if (!command) {
171+
ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->addr, msg->buf,
172+
msg->len, data_per_frame,
173+
sdw->bpt_ctx.dmab_tx_bdl.area,
174+
pdi0_buffer_size, &tx_total_bytes);
175+
} else {
176+
ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len,
177+
data_per_frame,
178+
sdw->bpt_ctx.dmab_tx_bdl.area,
179+
pdi0_buffer_size, &tx_total_bytes);
180+
}
181+
182+
if (!ret)
183+
return 0;
184+
185+
dev_err(cdns->dev, "%s: sdw_prepare_%s_dma_buffer failed %d\n",
186+
__func__, command ? "read" : "write", ret);
187+
188+
ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */
189+
sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl,
190+
sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl);
191+
if (ret1 < 0)
192+
dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n",
193+
__func__, ret1);
194+
195+
deprepare_stream:
196+
sdw_deprepare_stream(cdns->bus.bpt_stream);
197+
198+
remove_master:
199+
ret1 = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream);
200+
if (ret1 < 0)
201+
dev_err(cdns->dev, "%s: remove master failed: %d\n",
202+
__func__, ret1);
203+
204+
remove_slave:
205+
ret1 = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream);
206+
if (ret1 < 0)
207+
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
208+
__func__, ret1);
209+
210+
release_stream:
211+
sdw_release_stream(cdns->bus.bpt_stream);
212+
cdns->bus.bpt_stream = NULL;
213+
214+
return ret;
215+
}
216+
217+
static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave *slave,
218+
struct sdw_bpt_msg *msg)
219+
{
220+
struct sdw_cdns *cdns = &sdw->cdns;
221+
int ret;
222+
223+
ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream,
224+
&sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream,
225+
&sdw->bpt_ctx.dmab_rx_bdl);
226+
if (ret < 0)
227+
dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n",
228+
__func__, ret);
229+
230+
ret = sdw_deprepare_stream(cdns->bus.bpt_stream);
231+
if (ret < 0)
232+
dev_err(cdns->dev, "%s: sdw_deprepare_stream failed: ret %d\n",
233+
__func__, ret);
234+
235+
ret = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream);
236+
if (ret < 0)
237+
dev_err(cdns->dev, "%s: remove master failed: %d\n",
238+
__func__, ret);
239+
240+
ret = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream);
241+
if (ret < 0)
242+
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
243+
__func__, ret);
244+
245+
cdns->bus.bpt_stream = NULL;
246+
}
247+
248+
#define INTEL_BPT_MSG_BYTE_ALIGNMENT 32
249+
250+
static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *slave,
251+
struct sdw_bpt_msg *msg)
252+
{
253+
struct sdw_cdns *cdns = &sdw->cdns;
254+
int ret;
255+
256+
if (msg->len % INTEL_BPT_MSG_BYTE_ALIGNMENT) {
257+
dev_err(cdns->dev, "BPT message length %d is not a multiple of %d bytes\n",
258+
msg->len, INTEL_BPT_MSG_BYTE_ALIGNMENT);
259+
return -EINVAL;
260+
}
261+
262+
dev_dbg(cdns->dev, "BPT Transfer start\n");
263+
264+
ret = intel_ace2x_bpt_open_stream(sdw, slave, msg);
265+
if (ret < 0)
266+
return ret;
267+
268+
ret = hda_sdw_bpt_send_async(cdns->dev->parent, /* PCI device */
269+
sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream);
270+
if (ret < 0) {
271+
dev_err(cdns->dev, "%s: hda_sdw_bpt_send_async failed: %d\n",
272+
__func__, ret);
273+
274+
intel_ace2x_bpt_close_stream(sdw, slave, msg);
275+
276+
return ret;
277+
}
278+
279+
ret = sdw_enable_stream(cdns->bus.bpt_stream);
280+
if (ret < 0) {
281+
dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n",
282+
__func__, ret);
283+
intel_ace2x_bpt_close_stream(sdw, slave, msg);
284+
}
285+
286+
return ret;
287+
}
288+
289+
static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave,
290+
struct sdw_bpt_msg *msg)
291+
{
292+
struct sdw_cdns *cdns = &sdw->cdns;
293+
int ret;
294+
295+
dev_dbg(cdns->dev, "BPT Transfer wait\n");
296+
297+
ret = hda_sdw_bpt_wait(cdns->dev->parent, /* PCI device */
298+
sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream);
299+
if (ret < 0)
300+
dev_err(cdns->dev, "%s: hda_sdw_bpt_wait failed: %d\n", __func__, ret);
301+
302+
ret = sdw_disable_stream(cdns->bus.bpt_stream);
303+
if (ret < 0) {
304+
dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n",
305+
__func__, ret);
306+
goto err;
307+
}
308+
309+
if (msg->flags & SDW_MSG_FLAG_WRITE) {
310+
ret = sdw_cdns_check_write_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area,
311+
sdw->bpt_ctx.pdi1_buffer_size,
312+
sdw->bpt_ctx.num_frames);
313+
if (ret < 0)
314+
dev_err(cdns->dev, "%s: BPT Write failed %d\n", __func__, ret);
315+
} else {
316+
ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area,
317+
sdw->bpt_ctx.pdi1_buffer_size,
318+
msg->buf, msg->len, sdw->bpt_ctx.num_frames,
319+
sdw->bpt_ctx.data_per_frame);
320+
if (ret < 0)
321+
dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret);
322+
}
323+
324+
err:
325+
intel_ace2x_bpt_close_stream(sdw, slave, msg);
326+
327+
return ret;
328+
}
329+
22330
/*
23331
* shim vendor-specific (vs) ops
24332
*/
@@ -753,7 +1061,11 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
7531061
.sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked,
7541062

7551063
.program_sdi = intel_program_sdi,
1064+
1065+
.bpt_send_async = intel_ace2x_bpt_send_async,
1066+
.bpt_wait = intel_ace2x_bpt_wait,
7561067
};
7571068
EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL");
7581069

7591070
MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
1071+
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_SDW_BPT");

0 commit comments

Comments
 (0)