From patchwork Wed Feb 5 08:45:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Bard Liao X-Patchwork-Id: 13960676 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4DB6922C331; Wed, 5 Feb 2025 08:45:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738745161; cv=none; b=TFYviEQnTwEZZaQfyxI+aWx8roDPmo2Xw21l0yprSMMOZOoK/j7vC42UAv1RXzBC9MWQXC/F67K7jevR8dX+vH/zUE98ifMczDkJsWORPOyWocDgPJuidITaX7XQAwAgSRvOQZ9uieBHBbxIK9J71XBIPtnm4S6oWu0GYQGDPoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738745161; c=relaxed/simple; bh=ysR+QHqvp71AWsOi8hjkD6DmFGaPhP+g6HFaUTZRV80=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=px+HlHIYF0RIySu0NpNCztRBPMa/VjoTfsWtGzc1i1IXsiGd6yO7SIMJT2AAuq5hXZsF9zCnRZRsLU1ncqtre1fgpK7m1adj7QRi/xltxb0KM6I2CzS0Sy5WFMkeFrfMzDMzKCDOzotU71CKRMTLtvndMNTBk8O0m3p44hoQrTk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=A8Le/Lhv; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="A8Le/Lhv" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1738745159; x=1770281159; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ysR+QHqvp71AWsOi8hjkD6DmFGaPhP+g6HFaUTZRV80=; b=A8Le/Lhv/feiRHltd0lVDCY9c0HvPK0d3qg4En+chz4bJMOiaaSjTw6L iHGA7qxob4crsb+u8gEcQWBRqnxLc1F7WoZTdTG1CK3JlEV3zcYGTHi3F xhEhYmWGqDpplXhrCv5rHCH42GImQVav3M7Q5NHoNskfdosOpu2cNql4Q Sp5nAVBsUXhnkq1qbutgkGG+KcgEvExHCE8yAbxAQ5jQr/GOP4IZyYQbM 14jd0dry9LM0C1zPSP8R+KWrAEZEwUFxE1TSmaYRj7eo1DLWtwqYd5pij /n+hIZlbQtk4ZvLlyowf9uIopq0REH/U3R2sYaXxVLbFTKTcWlj2dEepR w==; X-CSE-ConnectionGUID: ArPb7nYcRi2WH3ceolc3VA== X-CSE-MsgGUID: c8mI9p1uScG3lS4cp9+BoA== X-IronPort-AV: E=McAfee;i="6700,10204,11336"; a="38532552" X-IronPort-AV: E=Sophos;i="6.13,261,1732608000"; d="scan'208";a="38532552" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Feb 2025 00:45:59 -0800 X-CSE-ConnectionGUID: qOhvvqHcTn+/0gIZXraAXQ== X-CSE-MsgGUID: Skl840KzRD6ISNQjLLUqxQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.13,261,1732608000"; d="scan'208";a="115887918" Received: from mdroper-mobl2.amr.corp.intel.com (HELO yungchua-desk.intel.com) ([10.124.223.241]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Feb 2025 00:45:58 -0800 From: Bard Liao To: linux-sound@vger.kernel.org, vkoul@kernel.org Cc: vinod.koul@linaro.org, linux-kernel@vger.kernel.org, pierre-louis.bossart@linux.dev, bard.liao@intel.com Subject: [PATCH 13/16] soundwire: intel_ace2x: add BPT send_async/wait callbacks Date: Wed, 5 Feb 2025 16:45:27 +0800 Message-ID: <20250205084531.158763-14-yung-chuan.liao@linux.intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250205084531.158763-1-yung-chuan.liao@linux.intel.com> References: <20250205084531.158763-1-yung-chuan.liao@linux.intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Pierre-Louis Bossart Add support for BTP API using Cadence and hda-sdw-bpt helpers. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan --- drivers/soundwire/intel_ace2x.c | 312 ++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index e305c6258ca9..5b31e1f69591 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -13,12 +13,320 @@ #include #include #include +#include #include #include #include "cadence_master.h" #include "bus.h" #include "intel.h" +static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_runtime *stream) +{ + struct sdw_stream_config sconfig = {0}; + struct sdw_port_config pconfig = {0}; + int ret; + + /* arbitrary configuration */ + sconfig.frame_rate = 16000; + sconfig.ch_count = 1; + sconfig.bps = 32; /* this is required for BPT/BRA */ + sconfig.direction = SDW_DATA_DIR_RX; + sconfig.type = SDW_STREAM_BPT; + + pconfig.num = 0; + pconfig.ch_mask = BIT(0); + + ret = sdw_stream_add_slave(slave, &sconfig, &pconfig, 1, stream); + if (ret) + dev_err(&slave->dev, "%s: failed: %d\n", __func__, ret); + + return ret; +} + +static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave, + struct sdw_bpt_msg *msg) +{ + struct sdw_cdns *cdns = &sdw->cdns; + struct sdw_bus *bus = &cdns->bus; + struct sdw_master_prop *prop = &bus->prop; + struct sdw_stream_runtime *stream; + struct sdw_stream_config sconfig; + struct sdw_port_config *pconfig; + unsigned int pdi0_buffer_size; + unsigned int tx_dma_bandwidth; + unsigned int pdi1_buffer_size; + unsigned int rx_dma_bandwidth; + unsigned int data_per_frame; + unsigned int tx_total_bytes; + struct sdw_cdns_pdi *pdi0; + struct sdw_cdns_pdi *pdi1; + unsigned int num_frames; + int command; + int ret1; + int ret; + int dir; + int i; + + stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT); + if (!stream) + return -ENOMEM; + + cdns->bus.bpt_stream = stream; + + ret = sdw_slave_bpt_stream_add(slave, stream); + if (ret < 0) + goto release_stream; + + /* handle PDI0 first */ + dir = SDW_DATA_DIR_TX; + + pdi0 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 0); + if (!pdi0) { + dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi0 failed\n", __func__); + ret = -EINVAL; + goto remove_slave; + } + + sdw_cdns_config_stream(cdns, 1, dir, pdi0); + + /* handle PDI1 */ + dir = SDW_DATA_DIR_RX; + + pdi1 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 1); + if (!pdi1) { + dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi1 failed\n", __func__); + ret = -EINVAL; + goto remove_slave; + } + + sdw_cdns_config_stream(cdns, 1, dir, pdi1); + + /* + * the port config direction, number of channels and frame + * rate is totally arbitrary + */ + sconfig.direction = dir; + sconfig.ch_count = 1; + sconfig.frame_rate = 16000; + sconfig.type = SDW_STREAM_BPT; + sconfig.bps = 32; /* this is required for BPT/BRA */ + + /* Port configuration */ + pconfig = kcalloc(2, sizeof(*pconfig), GFP_KERNEL); + if (!pconfig) { + ret = -ENOMEM; + goto remove_slave; + } + + for (i = 0; i < 2 /* num_pdi */; i++) { + pconfig[i].num = i; + pconfig[i].ch_mask = 1; + } + + ret = sdw_stream_add_master(&cdns->bus, &sconfig, pconfig, 2, stream); + kfree(pconfig); + + if (ret < 0) { + dev_err(cdns->dev, "add master to stream failed:%d\n", ret); + goto remove_slave; + } + + ret = sdw_prepare_stream(cdns->bus.bpt_stream); + if (ret < 0) + goto remove_master; + + command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1; + + 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); + if (ret < 0) + goto deprepare_stream; + + sdw->bpt_ctx.pdi0_buffer_size = pdi0_buffer_size; + 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); + + dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", + msg->len, num_frames, data_per_frame); + dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n", + pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth); + + ret = hda_sdw_bpt_open(cdns->dev->parent, /* PCI device */ + sdw->instance, &sdw->bpt_ctx.bpt_tx_stream, + &sdw->bpt_ctx.dmab_tx_bdl, pdi0_buffer_size, tx_dma_bandwidth, + &sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl, + pdi1_buffer_size, rx_dma_bandwidth); + if (ret < 0) { + dev_err(cdns->dev, "%s: hda_sdw_bpt_open failed %d\n", __func__, ret); + goto deprepare_stream; + } + + if (!command) { + ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->addr, msg->buf, + msg->len, data_per_frame, + sdw->bpt_ctx.dmab_tx_bdl.area, + pdi0_buffer_size, &tx_total_bytes); + } else { + 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); + } + + if (!ret) + return 0; + + dev_err(cdns->dev, "%s: sdw_prepare_%s_dma_buffer failed %d\n", + __func__, command ? "read" : "write", ret); + + ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */ + sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl, + sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl); + if (ret1 < 0) + dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n", + __func__, ret1); + +deprepare_stream: + sdw_deprepare_stream(cdns->bus.bpt_stream); + +remove_master: + ret1 = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream); + if (ret1 < 0) + dev_err(cdns->dev, "%s: remove master failed: %d\n", + __func__, ret1); + +remove_slave: + ret1 = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream); + if (ret1 < 0) + dev_err(cdns->dev, "%s: remove slave failed: %d\n", + __func__, ret1); + +release_stream: + sdw_release_stream(cdns->bus.bpt_stream); + cdns->bus.bpt_stream = NULL; + + return ret; +} + +static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave *slave, + struct sdw_bpt_msg *msg) +{ + struct sdw_cdns *cdns = &sdw->cdns; + int ret; + + ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream, + &sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream, + &sdw->bpt_ctx.dmab_rx_bdl); + if (ret < 0) + dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n", + __func__, ret); + + ret = sdw_deprepare_stream(cdns->bus.bpt_stream); + if (ret < 0) + dev_err(cdns->dev, "%s: sdw_deprepare_stream failed: ret %d\n", + __func__, ret); + + ret = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream); + if (ret < 0) + dev_err(cdns->dev, "%s: remove master failed: %d\n", + __func__, ret); + + ret = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream); + if (ret < 0) + dev_err(cdns->dev, "%s: remove slave failed: %d\n", + __func__, ret); + + cdns->bus.bpt_stream = NULL; +} + +#define INTEL_BPT_MSG_BYTE_ALIGNMENT 32 + +static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *slave, + struct sdw_bpt_msg *msg) +{ + struct sdw_cdns *cdns = &sdw->cdns; + int ret; + + if (msg->len % INTEL_BPT_MSG_BYTE_ALIGNMENT) { + dev_err(cdns->dev, "BPT message length %d is not a multiple of %d bytes\n", + msg->len, INTEL_BPT_MSG_BYTE_ALIGNMENT); + return -EINVAL; + } + + dev_dbg(cdns->dev, "BPT Transfer start\n"); + + ret = intel_ace2x_bpt_open_stream(sdw, slave, msg); + if (ret < 0) + return ret; + + ret = hda_sdw_bpt_send_async(cdns->dev->parent, /* PCI device */ + sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream); + if (ret < 0) { + dev_err(cdns->dev, "%s: hda_sdw_bpt_send_async failed: %d\n", + __func__, ret); + + intel_ace2x_bpt_close_stream(sdw, slave, msg); + + return ret; + } + + ret = sdw_enable_stream(cdns->bus.bpt_stream); + if (ret < 0) { + dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n", + __func__, ret); + intel_ace2x_bpt_close_stream(sdw, slave, msg); + } + + return ret; +} + +static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave, + struct sdw_bpt_msg *msg) +{ + struct sdw_cdns *cdns = &sdw->cdns; + int ret; + + dev_dbg(cdns->dev, "BPT Transfer wait\n"); + + ret = hda_sdw_bpt_wait(cdns->dev->parent, /* PCI device */ + sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream); + if (ret < 0) + dev_err(cdns->dev, "%s: hda_sdw_bpt_wait failed: %d\n", __func__, ret); + + ret = sdw_disable_stream(cdns->bus.bpt_stream); + if (ret < 0) { + dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n", + __func__, ret); + goto err; + } + + if (msg->flags & SDW_MSG_FLAG_WRITE) { + ret = sdw_cdns_check_write_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, + sdw->bpt_ctx.pdi1_buffer_size, + sdw->bpt_ctx.num_frames); + if (ret < 0) + dev_err(cdns->dev, "%s: BPT Write failed %d\n", __func__, ret); + } else { + ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, + sdw->bpt_ctx.pdi1_buffer_size, + msg->buf, msg->len, sdw->bpt_ctx.num_frames, + sdw->bpt_ctx.data_per_frame); + if (ret < 0) + dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret); + } + +err: + intel_ace2x_bpt_close_stream(sdw, slave, msg); + + return ret; +} + /* * shim vendor-specific (vs) ops */ @@ -753,7 +1061,11 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = { .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, .program_sdi = intel_program_sdi, + + .bpt_send_async = intel_ace2x_bpt_send_async, + .bpt_wait = intel_ace2x_bpt_wait, }; EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL"); MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_SDW_BPT");