[PATCH] fpga: altera-cvp: Handle truncated bitstream with padding buffer

[email protected] Thu, 18 Jun 2026 21:08:11 -0700
Newsgroups org.kernel.vger.linux-fpga,org.kernel.vger.linux-kernel
Message-ID <20260619040811.20038-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
From: Nazim Amirul <[email protected]>

The AIB CvP firmware requires a minimum transfer size of 4096 bytes.
When a bitstream smaller than this is sent, the firmware DMA engine
stalls waiting for the remaining data, causing a hang.

Allocate a 4096-byte send_buf at probe time and copy host data into
it before each block transfer, ensuring the full block size is always
sent regardless of the actual data length. Also improve error
detection in write_complete() by checking VSE_CVP_STATUS_CFG_ERR
instead of VSE_UNCOR_ERR_CVP_CFG_ERR to catch truncated or
mismatched bitstream errors.

Signed-off-by: Nazim Amirul <[email protected]>
---
 drivers/fpga/altera-cvp.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..36557c5a0c3f 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -77,6 +77,7 @@ struct altera_cvp_conf {
 	u8			numclks;
 	u32			sent_packets;
 	u32			vsec_offset;
+	u8			*send_buf;
 	const struct cvp_priv	*priv;
 };
 
@@ -449,7 +450,9 @@ static int altera_cvp_write(struct fpga_manager *mgr, const char *buf,
 		}
 
 		len = min(conf->priv->block_size, remaining);
-		altera_cvp_send_block(conf, data, len);
+		memcpy(conf->send_buf, data, len);
+		altera_cvp_send_block(conf, (const u32 *)conf->send_buf,
+		conf->priv->block_size);
 		data += len / sizeof(u32);
 		done += len;
 		remaining -= len;
@@ -488,10 +491,10 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
 	if (ret)
 		return ret;
 
-	/* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */
-	altera_read_config_dword(conf, VSE_UNCOR_ERR_STATUS, &val);
-	if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) {
-		dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n");
+	/* STEP 16 - check CVP_CONFIG_ERROR bit */
+	ret = altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+	if (ret || (val & VSE_CVP_STATUS_CFG_ERR)) {
+		dev_err(&mgr->dev, "CVP_CONFIG_ERROR!\n");
 		return -EPROTO;
 	}
 
@@ -650,6 +653,11 @@ static int altera_cvp_probe(struct pci_dev *pdev,
 
 	pci_set_drvdata(pdev, mgr);
 
+	conf->send_buf = devm_kzalloc(&pdev->dev, conf->priv->block_size, GFP_KERNEL);
+	if (!conf->send_buf) {
+		ret = -ENOMEM;
+		goto err_unmap;
+	}
 	return 0;
 
 err_unmap:
-- 
2.43.7