ASoC: qcom: q6adm: the the copp device only during last instance

A matching Common object post processing instance is normally resused
across multiple streams. However currently we close this on DSP
even though there is a refcount on this copp object, this can result in
below error.

q6routing ab00000.remoteproc:glink-edge:apr:service@8:routing: Found Matching Copp 0x0
qcom-q6adm aprsvc:service:4:8: cmd = 0x10325 return error = 0x2
q6routing ab00000.remoteproc:glink-edge:apr:service@8:routing: DSP returned error[2]
q6routing ab00000.remoteproc:glink-edge:apr:service@8:routing: Found Matching Copp 0x0
qcom-q6adm aprsvc:service:4:8: cmd = 0x10325 return error = 0x2
q6routing ab00000.remoteproc:glink-edge:apr:service@8:routing: DSP returned error[2]
qcom-q6adm aprsvc:service:4:8: cmd = 0x10327 return error = 0x2
qcom-q6adm aprsvc:service:4:8: DSP returned error[2]
qcom-q6adm aprsvc:service:4:8: Failed to close copp -22
qcom-q6adm aprsvc:service:4:8: cmd = 0x10327 return error = 0x2
qcom-q6adm aprsvc:service:4:8: DSP returned error[2]
qcom-q6adm aprsvc:service:4:8: Failed to close copp -22

Fix this by addressing moving the adm_close to copp_kref destructor
callback.

Fixes: 7b20b2be51 ("ASoC: qdsp6: q6adm: Add q6adm driver")
Cc: Stable@vger.kernel.org
Reported-by: Martino Facchin <m.facchin@arduino.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Tested-by: Alexey Klimov <alexey.klimov@linaro.org> # RB5, RB3
Link: https://patch.msgid.link/20251023102444.88158-3-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Srinivas Kandagatla
2025-10-23 11:24:26 +01:00
committed by Mark Brown
parent 950a4e5788
commit 74cc4f3ea4

View File

@@ -109,11 +109,75 @@ static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx,
}
static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
struct apr_pkt *pkt, uint32_t rsp_opcode)
{
struct device *dev = adm->dev;
uint32_t opcode = pkt->hdr.opcode;
int ret;
mutex_lock(&adm->lock);
copp->result.opcode = 0;
copp->result.status = 0;
ret = apr_send_pkt(adm->apr, pkt);
if (ret < 0) {
dev_err(dev, "Failed to send APR packet\n");
ret = -EINVAL;
goto err;
}
/* Wait for the callback with copp id */
if (rsp_opcode)
ret = wait_event_timeout(copp->wait,
(copp->result.opcode == opcode) ||
(copp->result.opcode == rsp_opcode),
msecs_to_jiffies(TIMEOUT_MS));
else
ret = wait_event_timeout(copp->wait,
(copp->result.opcode == opcode),
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
dev_err(dev, "ADM copp cmd timedout\n");
ret = -ETIMEDOUT;
} else if (copp->result.status > 0) {
dev_err(dev, "DSP returned error[%d]\n",
copp->result.status);
ret = -EINVAL;
}
err:
mutex_unlock(&adm->lock);
return ret;
}
static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
int port_id, int copp_idx)
{
struct apr_pkt close;
close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
close.hdr.pkt_size = sizeof(close);
close.hdr.src_port = port_id;
close.hdr.dest_port = copp->id;
close.hdr.token = port_id << 16 | copp_idx;
close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
}
static void q6adm_free_copp(struct kref *ref)
{
struct q6copp *c = container_of(ref, struct q6copp, refcount);
struct q6adm *adm = c->adm;
unsigned long flags;
int ret;
ret = q6adm_device_close(adm, c, c->afe_port, c->copp_idx);
if (ret < 0)
dev_err(adm->dev, "Failed to close copp %d\n", ret);
spin_lock_irqsave(&adm->copps_list_lock, flags);
clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]);
@@ -155,13 +219,13 @@ static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
switch (result->opcode) {
case ADM_CMD_DEVICE_OPEN_V5:
case ADM_CMD_DEVICE_CLOSE_V5:
copp = q6adm_find_copp(adm, port_idx, copp_idx);
if (!copp)
return 0;
copp->result = *result;
wake_up(&copp->wait);
kref_put(&copp->refcount, q6adm_free_copp);
list_for_each_entry(copp, &adm->copps_list, node) {
if ((port_idx == copp->afe_port) && (copp_idx == copp->copp_idx)) {
copp->result = *result;
wake_up(&copp->wait);
break;
}
}
break;
case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
adm->result = *result;
@@ -234,65 +298,6 @@ static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx)
return c;
}
static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
struct apr_pkt *pkt, uint32_t rsp_opcode)
{
struct device *dev = adm->dev;
uint32_t opcode = pkt->hdr.opcode;
int ret;
mutex_lock(&adm->lock);
copp->result.opcode = 0;
copp->result.status = 0;
ret = apr_send_pkt(adm->apr, pkt);
if (ret < 0) {
dev_err(dev, "Failed to send APR packet\n");
ret = -EINVAL;
goto err;
}
/* Wait for the callback with copp id */
if (rsp_opcode)
ret = wait_event_timeout(copp->wait,
(copp->result.opcode == opcode) ||
(copp->result.opcode == rsp_opcode),
msecs_to_jiffies(TIMEOUT_MS));
else
ret = wait_event_timeout(copp->wait,
(copp->result.opcode == opcode),
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
dev_err(dev, "ADM copp cmd timedout\n");
ret = -ETIMEDOUT;
} else if (copp->result.status > 0) {
dev_err(dev, "DSP returned error[%d]\n",
copp->result.status);
ret = -EINVAL;
}
err:
mutex_unlock(&adm->lock);
return ret;
}
static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
int port_id, int copp_idx)
{
struct apr_pkt close;
close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
close.hdr.pkt_size = sizeof(close);
close.hdr.src_port = port_id;
close.hdr.dest_port = copp->id;
close.hdr.token = port_id << 16 | copp_idx;
close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
}
static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm,
int port_id, int topology,
int mode, int rate,
@@ -567,15 +572,6 @@ EXPORT_SYMBOL_GPL(q6adm_matrix_map);
*/
int q6adm_close(struct device *dev, struct q6copp *copp)
{
struct q6adm *adm = dev_get_drvdata(dev->parent);
int ret = 0;
ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx);
if (ret < 0) {
dev_err(adm->dev, "Failed to close copp %d\n", ret);
return ret;
}
kref_put(&copp->refcount, q6adm_free_copp);
return 0;