Re: [RESEND PATCH v2 06/15] ASoC: qcom: qdsp6: Add support to Q6ASM
From: Bjorn Andersson
Date: Mon Jan 01 2018 - 23:44:03 EST
On Thu 14 Dec 09:33 PST 2017, srinivas.kandagatla@xxxxxxxxxx wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxx>
>
> This patch adds basic support to Q6 ASM (Audio Stream Manager) module on
> Q6DSP. ASM supports up to 8 concurrent streams. each stream can be setup
> as playback/capture.
"...streams, each one setup as either playback or capture".
or "each" need to be capitalized.
> ASM provides top control functions like
> Pause/flush/resume for playback and record. ASM can Create/destroy encoder,
lower case p and c
> decoder and also provides POPP dynamic services.
Please describe what POPP is.
[..]
> +struct audio_client {
> + int session;
> + app_cb cb;
> + int cmd_state;
> + void *priv;
> + uint32_t io_mode;
> + uint64_t time_stamp;
Unused.
> + struct apr_device *adev;
> + struct mutex cmd_lock;
> + wait_queue_head_t cmd_wait;
> + int perf_mode;
> + int stream_id;
> + struct device *dev;
> +};
> +
> +struct q6asm {
> + struct apr_device *adev;
> + int mem_state;
> + struct device *dev;
> + wait_queue_head_t mem_wait;
> + struct mutex session_lock;
> + struct platform_device *pcmdev;
> + struct audio_client *session[MAX_SESSIONS + 1];
> +};
> +
> +static int q6asm_session_alloc(struct audio_client *ac, struct q6asm *a)
Move the allocation of ac into this function, and return the newly
allocated ac - that way the name of this function makes more sense.
> +{
> + int n = -EINVAL;
You're returning MAX_SESSIONS if no free sessions are found, but are
checking for <= 0 in the caller.
> +
> + mutex_lock(&a->session_lock);
> + for (n = 1; n <= MAX_SESSIONS; n++) {
Is there an external reason for session 0 not being considered?
> + if (!a->session[n]) {
> + a->session[n] = ac;
> + break;
> + }
> + }
If you make session an idr this function would become idr_alloc(1,
MAX_SESSIONS + 1).
> + mutex_unlock(&a->session_lock);
> +
> + return n;
> +}
> +
> +static bool q6asm_is_valid_audio_client(struct audio_client *ac)
> +{
> + struct q6asm *a = dev_get_drvdata(ac->dev->parent);
> + int n;
> +
> + for (n = 1; n <= MAX_SESSIONS; n++) {
> + if (a->session[n] == ac)
> + return 1;
"true"
> + }
> +
> + return 0;
"false"
> +}
> +
> +static void q6asm_session_free(struct audio_client *ac)
> +{
> + struct q6asm *a = dev_get_drvdata(ac->dev->parent);
> +
> + if (!a)
> + return;
> +
> + mutex_lock(&a->session_lock);
> + a->session[ac->session] = 0;
> + ac->session = 0;
> + ac->perf_mode = LEGACY_PCM_MODE;
No need to update ac->*, as you kfree ac as soon as you return from
here.
> + mutex_unlock(&a->session_lock);
> +}
> +
> +/**
> + * q6asm_audio_client_free() - Freee allocated audio client
> + *
> + * @ac: audio client to free
> + */
> +void q6asm_audio_client_free(struct audio_client *ac)
> +{
> + q6asm_session_free(ac);
Inline q6asm_session_free() here.
> + kfree(ac);
> +}
> +EXPORT_SYMBOL_GPL(q6asm_audio_client_free);
> +
> +static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
> + int session_id)
> +{
> + if ((session_id <= 0) || (session_id > MAX_SESSIONS)) {
> + dev_err(a->dev, "invalid session: %d\n", session_id);
> + goto err;
Just return NULL here instead.
> + }
> +
> + if (!a->session[session_id]) {
> + dev_err(a->dev, "session not active: %d\n", session_id);
> + goto err;
Dito
> + }
But this is another place where an idr would be preferable, as both
these cases would be covered with a call to idr_find()
> + return a->session[session_id];
> +err:
> + return NULL;
> +}
> +
> +static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data)
> +{
> + struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
> + struct audio_client *ac = NULL;
> + uint32_t sid = 0;
This is 4 bits, so just use int.
> + uint32_t *payload;
payload is unused.
> +
> + if (!data) {
> + dev_err(&adev->dev, "%s: Invalid CB\n", __func__);
> + return 0;
> + }
Again, define the apr to never invoke the callback with data = NULL
> +
> + payload = data->payload;
> + sid = (data->token >> 8) & 0x0F;
> + ac = q6asm_get_audio_client(q6asm, sid);
> + if (!ac) {
> + dev_err(&adev->dev, "Audio Client not active\n");
> + return 0;
> + }
> +
> + if (ac->cb)
> + ac->cb(data->opcode, data->token, data->payload, ac->priv);
> + return 0;
> +}
> +
> +/**
> + * q6asm_get_session_id() - get session id for audio client
> + *
> + * @ac: audio client pointer
> + *
> + * Return: Will be an session id of the audio client.
> + */
> +int q6asm_get_session_id(struct audio_client *c)
> +{
> + return c->session;
> +}
> +EXPORT_SYMBOL_GPL(q6asm_get_session_id);
> +
> +/**
> + * q6asm_audio_client_alloc() - Allocate a new audio client
> + *
> + * @dev: Pointer to asm child device.
> + * @cb: event callback.
> + * @priv: private data associated with this client.
> + *
> + * Return: Will be an error pointer on error or a valid audio client
> + * on success.
> + */
> +struct audio_client *q6asm_audio_client_alloc(struct device *dev,
> + app_cb cb, void *priv)
> +{
> + struct q6asm *a = dev_get_drvdata(dev->parent);
> + struct audio_client *ac;
> + int n;
> +
> + ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
sizeof(*ac)
> + if (!ac)
> + return NULL;
> +
> + n = q6asm_session_alloc(ac, a);
As stated above, moving the kzalloc into q6asm_session_alloc() would
clean the code up here, as you only need to deal with one possible
error case here.
> + if (n <= 0) {
> + dev_err(dev, "ASM Session alloc fail n=%d\n", n);
> + kfree(ac);
> + return NULL;
Per the kerneldoc I expect an ERR_PTR(n) here.
> + }
> +
> + ac->session = n;
> + ac->cb = cb;
> + ac->dev = dev;
> + ac->priv = priv;
> + ac->io_mode = SYNC_IO_MODE;
> + ac->perf_mode = LEGACY_PCM_MODE;
> + /* DSP expects stream id from 1 */
> + ac->stream_id = 1;
> + ac->adev = a->adev;
> +
> + init_waitqueue_head(&ac->cmd_wait);
> + mutex_init(&ac->cmd_lock);
> + ac->cmd_state = 0;
> +
> + return ac;
> +}
> +EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
> +
> +
Extra newline.
> +static int q6asm_probe(struct apr_device *adev)
> +{
> + struct q6asm *q6asm;
> +
> + q6asm = devm_kzalloc(&adev->dev, sizeof(*q6asm), GFP_KERNEL);
> + if (!q6asm)
> + return -ENOMEM;
> +
> + q6asm->dev = &adev->dev;
> + q6asm->adev = adev;
> + q6asm->mem_state = 0;
> + init_waitqueue_head(&q6asm->mem_wait);
> + mutex_init(&q6asm->session_lock);
> + dev_set_drvdata(&adev->dev, q6asm);
> +
> + q6asm->pcmdev = platform_device_register_data(&adev->dev,
> + "q6asm_dai", -1, NULL, 0);
> +
> + return 0;
> +}
> +
> +static int q6asm_remove(struct apr_device *adev)
> +{
> + struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
> +
> + platform_device_unregister(q6asm->pcmdev);
> +
> + return 0;
> +}
> +
> +static const struct apr_device_id q6asm_id[] = {
> + {"Q6ASM", APR_DOMAIN_ADSP, APR_SVC_ASM, APR_CLIENT_AUDIO},
> + {}
> +};
> +
> +static struct apr_driver qcom_q6asm_driver = {
> + .probe = q6asm_probe,
> + .remove = q6asm_remove,
> + .callback = q6asm_srvc_callback,
> + .id_table = q6asm_id,
> + .driver = {
> + .name = "qcom-q6asm",
> + },
Indentation
> +};
> +
> +module_apr_driver(qcom_q6asm_driver);
> +MODULE_DESCRIPTION("Q6 Audio Stream Manager driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
> new file mode 100644
> index 000000000000..7a8a9039fd89
> --- /dev/null
> +++ b/sound/soc/qcom/qdsp6/q6asm.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __Q6_ASM_H__
> +#define __Q6_ASM_H__
> +
> +#define MAX_SESSIONS 16
> +
> +typedef void (*app_cb) (uint32_t opcode, uint32_t token,
> + uint32_t *payload, void *priv);
This name of a type is too generic.
And make payload void *, unless the payload really really is an
unstructured uint32_t array.
Regards,
Bjorn