alsa compress driver

 https://elixir.bootlin.com/linux/v5.4.240/source/sound/soc/soc-compress.c#L856

platform driver 例子:https://elixir.bootlin.com/linux/v5.4.240/source/sound/soc/intel/atom/sst-mfld-platform-pcm.c#L525

static struct snd_soc_dai_driver sst_platform_dai[] = {
...
{
    .name = "compress-cpu-dai",
    .compress_new = snd_soc_new_compress,
    .ops = &sst_compr_dai_ops,
    .playback = {
        .stream_name = "Compress Playback",
        .channels_min = 1,
    },
},
...
};

static const struct snd_soc_dai_ops sst_media_dai_ops = {
    .startup = sst_media_open,
    .shutdown = sst_media_close,
    .prepare = sst_media_prepare,
    .hw_params = sst_media_hw_params,
    .hw_free = sst_media_hw_free,
    .mute_stream = sst_media_digital_mute,
};

static const struct snd_soc_component_driver sst_soc_platform_drv  = {
    .name        = DRV_NAME,
    .probe        = sst_soc_probe,
    .remove        = sst_soc_remove,
    .ops        = &sst_platform_ops,
    .compr_ops    = &sst_platform_compr_ops,
    .pcm_new    = sst_pcm_new,
};

const struct snd_compr_ops sst_platform_compr_ops = {

    .open = sst_platform_compr_open,
    .free = sst_platform_compr_free,
    .set_params = sst_platform_compr_set_params,
    .set_metadata = sst_platform_compr_set_metadata,
    .trigger = sst_platform_compr_trigger,
    .pointer = sst_platform_compr_pointer,
    .ack = sst_platform_compr_ack,
    .get_caps = sst_platform_compr_get_caps,
    .get_codec_caps = sst_platform_compr_get_codec_caps,
};

static int sst_platform_probe(struct platform_device *pdev)
{
    struct sst_data *drv;
    int ret;
    struct sst_platform_data *pdata;

    drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
    if (drv == NULL) {
        return -ENOMEM;
    }

    pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    if (pdata == NULL) {
        return -ENOMEM;
    }

    pdata->pdev_strm_map = dpcm_strm_map;
    pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
    drv->pdata = pdata;
    drv->pdev = pdev;
    mutex_init(&drv->lock);
    dev_set_drvdata(&pdev->dev, drv);

    ret = devm_snd_soc_register_component(&pdev->dev, &sst_soc_platform_drv,
                sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
    if (ret)
        dev_err(&pdev->dev, "registering cpu dais failed\n");

    return ret;
}

 

machine driver例子:https://elixir.bootlin.com/linux/v5.4.240/source/sound/soc/intel/boards/cht_bsw_nau8824.c#L191

SND_SOC_DAILINK_DEF(compress,
    DAILINK_COMP_ARRAY(COMP_CPU("compress-cpu-dai")));
static struct snd_soc_dai_link cht_dailink[] = {
    ...
    [MERR_DPCM_COMPR] = {
        .name = "Compressed Port",
        .stream_name = "Compress",
        SND_SOC_DAILINK_REG(compress, dummy, platform),
    },
    ...
};

/* SoC card */
static struct snd_soc_card snd_soc_card_cht = {
    .name = "chtnau8824",
    .owner = THIS_MODULE,
    .dai_link = cht_dailink,
    .num_links = ARRAY_SIZE(cht_dailink),
    .dapm_widgets = cht_dapm_widgets,
    .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
    .dapm_routes = cht_audio_map,
    .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
    .controls = cht_mc_controls,
    .num_controls = ARRAY_SIZE(cht_mc_controls),
};

static int snd_cht_mc_probe(struct platform_device *pdev)
{
    .....

    /* register the soc card */
    ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
    if (ret_val) {
        dev_err(&pdev->dev,
            "snd_soc_register_card failed %d\n", ret_val);
        return ret_val;
    }
    platform_set_drvdata(pdev, &snd_soc_card_cht);

    return ret_val;
}

 在platform driver的snd_soc_dai_driver定义了compress dai driver, 并将.compress_new=snd_soc_new_compress.

compress_new在注册声卡时会被call到。

static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
....
    for_each_card_rtds(card, rtd)
        soc_link_init(card, rtd);
....
}

static int soc_link_init(struct snd_soc_card *card,
             struct snd_soc_pcm_runtime *rtd)
{
...
    num = rtd->num;
    /* create compress_device if possible */
    ret = snd_soc_dai_compress_new(cpu_dai, rtd, num);
...
}

int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
                 struct snd_soc_pcm_runtime *rtd, int num)
{
    if (dai->driver->compress_new)
        return dai->driver->compress_new(rtd, num);
    return -ENOTSUPP;
}

snd_soc_new_compress创建一个新的struct snd_compr *compr结构,赋值compr->ops, 并且使用snd_compress_new创建compress device

/**
 * snd_soc_new_compress - create a new compress.
 *
 * @rtd: The runtime for which we will create compress
 * @num: the device index number (zero based - shared with normal PCMs)
 *
 * Return: 0 for success, else error.
 */
int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
{
    struct snd_soc_component *component;
    struct snd_soc_rtdcom_list *rtdcom;
    struct snd_soc_dai *codec_dai = rtd->codec_dai;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    struct snd_compr *compr;
    struct snd_pcm *be_pcm;
    char new_name[64];
    int ret = 0, direction = 0;
    int playback = 0, capture = 0;

    if (rtd->num_codecs > 1) {
        dev_err(rtd->card->dev,
            "Compress ASoC: Multicodec not supported\n");
        return -EINVAL;
    }

    if (!codec_dai) {
        dev_err(rtd->card->dev, "Missing codec\n");
        return -EINVAL;
    }

    /* check client and interface hw capabilities */
    if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
        snd_soc_dai_stream_valid(cpu_dai,   SNDRV_PCM_STREAM_PLAYBACK))
        playback = 1;
    if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
        snd_soc_dai_stream_valid(cpu_dai,   SNDRV_PCM_STREAM_CAPTURE))
        capture = 1;

    /*
     * Compress devices are unidirectional so only one of the directions
     * should be set, check for that (xor)
     */
    if (playback + capture != 1) {
        dev_err(rtd->card->dev,
            "Compress ASoC: Invalid direction for P %d, C %d\n",
            playback, capture);
        return -EINVAL;
    }

    if (playback)
        direction = SND_COMPRESS_PLAYBACK;
    else
        direction = SND_COMPRESS_CAPTURE;

    compr = devm_kzalloc(rtd->card->dev, sizeof(*compr), GFP_KERNEL);
    if (!compr)
        return -ENOMEM;

    compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops),
                  GFP_KERNEL);
    if (!compr->ops)
        return -ENOMEM;

    if (rtd->dai_link->dynamic) {
        snprintf(new_name, sizeof(new_name), "(%s)",
            rtd->dai_link->stream_name);

        ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
                rtd->dai_link->dpcm_playback,
                rtd->dai_link->dpcm_capture, &be_pcm);
        if (ret < 0) {
            dev_err(rtd->card->dev,
                "Compress ASoC: can't create compressed for %s: %d\n",
                rtd->dai_link->name, ret);
            return ret;
        }

        rtd->pcm = be_pcm;
        rtd->fe_compr = 1;
        if (rtd->dai_link->dpcm_playback)
            be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
        if (rtd->dai_link->dpcm_capture)
            be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
        memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops));
    } else {
        snprintf(new_name, sizeof(new_name), "%s %s-%d",
            rtd->dai_link->stream_name, codec_dai->name, num);

        memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
    }

    for_each_rtdcom(rtd, rtdcom) {
        component = rtdcom->component;

        if (!component->driver->compr_ops ||
            !component->driver->compr_ops->copy)
            continue;

        compr->ops->copy = soc_compr_copy;
        break;
    }

    mutex_init(&compr->lock);
    ret = snd_compress_new(rtd->card->snd_card, num, direction,
                new_name, compr);
    if (ret < 0) {
        component = rtd->codec_dai->component;
        dev_err(component->dev,
            "Compress ASoC: can't create compress for codec %s: %d\n",
            component->name, ret);
        return ret;
    }

    /* DAPM dai link stream work */
    INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);

    rtd->compr = compr;
    compr->private_data = rtd;

    dev_info(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n",
         codec_dai->name, cpu_dai->name);

    return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_new_compress);

 compr->ops:(如果component driver有定义copy函数则compr->ops->copy = soc_compr_copy)

/* ASoC Compress operations */
static struct snd_compr_ops soc_compr_ops = {
    .open        = soc_compr_open,
    .free        = soc_compr_free,
    .set_params    = soc_compr_set_params,
    .set_metadata   = soc_compr_set_metadata,
    .get_metadata    = soc_compr_get_metadata,
    .get_params    = soc_compr_get_params,
    .trigger    = soc_compr_trigger,
    .pointer    = soc_compr_pointer,
    .ack        = soc_compr_ack,
    .get_caps    = soc_compr_get_caps,
    .get_codec_caps = soc_compr_get_codec_caps
};

/* ASoC Dynamic Compress operations */
static struct snd_compr_ops soc_compr_dyn_ops = {
    .open        = soc_compr_open_fe,
    .free        = soc_compr_free_fe,
    .set_params    = soc_compr_set_params_fe,
    .get_params    = soc_compr_get_params,
    .set_metadata   = soc_compr_set_metadata,
    .get_metadata    = soc_compr_get_metadata,
    .trigger    = soc_compr_trigger_fe,
    .pointer    = soc_compr_pointer,
    .ack        = soc_compr_ack,
    .get_caps    = soc_compr_get_caps,
    .get_codec_caps = soc_compr_get_codec_caps
};

snd_compress_new与snd_pcm_new类似,call snd_device_new创建compress device(struct snd_device *dev), 并将snd_device加到card->devices的list中。

/*
 * snd_compress_new: create new compress device
 * @card: sound card pointer
 * @device: device number
 * @dirn: device direction, should be of type enum snd_compr_direction
 * @compr: compress device pointer
 */
int snd_compress_new(struct snd_card *card, int device,
            int dirn, const char *id, struct snd_compr *compr)
{
    static struct snd_device_ops ops = {
        .dev_free = snd_compress_dev_free,
        .dev_register = snd_compress_dev_register,
        .dev_disconnect = snd_compress_dev_disconnect,
    };
    int ret;

    compr->card = card;
    compr->device = device;
    compr->direction = dirn;

    snd_compress_set_id(compr, id);

    snd_device_initialize(&compr->dev, card);
    dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);

    ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
    if (ret == 0)
        snd_compress_proc_init(compr);

    return ret;
}
EXPORT_SYMBOL_GPL(snd_compress_new);
/**
 * snd_device_new - create an ALSA device component
 * @card: the card instance
 * @type: the device type, SNDRV_DEV_XXX
 * @device_data: the data pointer of this device
 * @ops: the operator table
 *
 * Creates a new device component for the given data pointer.
 * The device will be assigned to the card and managed together
 * by the card.
 *
 * The data pointer plays a role as the identifier, too, so the
 * pointer address must be unique and unchanged.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_device_new(struct snd_card *card, enum snd_device_type type,
           void *device_data, struct snd_device_ops *ops)
{
    struct snd_device *dev;
    struct list_head *p;

    if (snd_BUG_ON(!card || !device_data || !ops))
        return -ENXIO;
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    INIT_LIST_HEAD(&dev->list);
    dev->card = card;
    dev->type = type;
    dev->state = SNDRV_DEV_BUILD;
    dev->device_data = device_data;
    dev->ops = ops;

    /* insert the entry in an incrementally sorted list */
    list_for_each_prev(p, &card->devices) {
        struct snd_device *pdev = list_entry(p, struct snd_device, list);
        if ((unsigned int)pdev->type <= (unsigned int)type)
            break;
    }

    list_add(&dev->list, p);
    return 0;
}
EXPORT_SYMBOL(snd_device_new);

创建struct snd_device *dev时,其ops如下,在后续注册card会遍历card->devices调用ops.dev_register来将struct snd_compr注册进snd_minors[minor]中。

static struct snd_device_ops ops = {
        .dev_free = snd_compress_dev_free,
        .dev_register = snd_compress_dev_register,
        .dev_disconnect = snd_compress_dev_disconnect,
    };static int snd_compress_dev_register(struct snd_device *device)
{
    int ret = -EINVAL;
    struct snd_compr *compr;

    if (snd_BUG_ON(!device || !device->device_data))
        return -EBADFD;
    compr = device->device_data;

    pr_debug("reg device %s, direction %d\n", compr->name,
            compr->direction);
    /* register compressed device */
    ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
                  compr->card, compr->device,
                  &snd_compr_file_ops, compr, &compr->dev);
    if (ret < 0) {
        pr_err("snd_register_device failed %d\n", ret);
        return ret;
    }
    return ret;

}
/**
 * snd_register_device - Register the ALSA device file for the card
 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 * @card: the card instance
 * @dev: the device index
 * @f_ops: the file operations
 * @private_data: user pointer for f_ops->open()
 * @device: the device to register
 *
 * Registers an ALSA device file for the given card.
 * The operators have to be set in reg parameter.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_register_device(int type, struct snd_card *card, int dev,
            const struct file_operations *f_ops,
            void *private_data, struct device *device)
{
    int minor;
    int err = 0;
    struct snd_minor *preg;

    if (snd_BUG_ON(!device))
        return -EINVAL;

    preg = kmalloc(sizeof *preg, GFP_KERNEL);
    if (preg == NULL)
        return -ENOMEM;
    preg->type = type;
    preg->card = card ? card->number : -1;
    preg->device = dev;
    preg->f_ops = f_ops;
    preg->private_data = private_data;
    preg->card_ptr = card;
    mutex_lock(&sound_mutex);
    minor = snd_find_free_minor(type, card, dev);
    if (minor < 0) {
        err = minor;
        goto error;
    }

    preg->dev = device;
    device->devt = MKDEV(major, minor);
    err = device_add(device);
    if (err < 0)
        goto error;

    snd_minors[minor] = preg;
 error:
    mutex_unlock(&sound_mutex);
    if (err < 0)
        kfree(preg);
    return err;
}
EXPORT_SYMBOL(snd_register_device);

在snd_compress_dev_register内,snd_minors[minor]->f_ops:

static const struct file_operations snd_compr_file_ops = {
        .owner =    THIS_MODULE,
        .open =        snd_compr_open,
        .release =    snd_compr_free,
        .write =    snd_compr_write,
        .read =        snd_compr_read,
        .unlocked_ioctl = snd_compr_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl = snd_compr_ioctl_compat,
#endif
        .mmap =        snd_compr_mmap,
        .poll =        snd_compr_poll,
};

在user space 调用tinycompress lib open compress device (/dev/snd/comprCXDX)时, 会调用到f_ops.open()函数,即snd_compr_open()。

在snd_compr_open透过file的inode找到major和minor, 进而找到注册到snd_minors[minor]中对应的struct snd_compr *compr.将compr->ops赋值给stream->ops

(data->stream.ops = compr->ops;)

/*
 * a note on stream states used:
 * we use following states in the compressed core
 * SNDRV_PCM_STATE_OPEN: When stream has been opened.
 * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
 *    calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
 *    state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
 * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
 *    playback only). User after setting up stream writes the data buffer
 *    before starting the stream.
 * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
 *    decoding/encoding and rendering/capturing data.
 * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
 *    by calling SNDRV_COMPRESS_DRAIN.
 * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
 *    SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
 *    SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
 */
static int snd_compr_open(struct inode *inode, struct file *f)
{
    struct snd_compr *compr;
    struct snd_compr_file *data;
    struct snd_compr_runtime *runtime;
    enum snd_compr_direction dirn;
    int maj = imajor(inode);
    int ret;

    if ((f->f_flags & O_ACCMODE) == O_WRONLY)
        dirn = SND_COMPRESS_PLAYBACK;
    else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
        dirn = SND_COMPRESS_CAPTURE;
    else
        return -EINVAL;

    if (maj == snd_major)
        compr = snd_lookup_minor_data(iminor(inode),
                    SNDRV_DEVICE_TYPE_COMPRESS);
    else
        return -EBADFD;

    if (compr == NULL) {
        pr_err("no device data!!!\n");
        return -ENODEV;
    }

    if (dirn != compr->direction) {
        pr_err("this device doesn't support this direction\n");
        snd_card_unref(compr->card);
        return -EINVAL;
    }

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    if (!data) {
        snd_card_unref(compr->card);
        return -ENOMEM;
    }

    INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);

    data->stream.ops = compr->ops;
    data->stream.direction = dirn;
    data->stream.private_data = compr->private_data;
    data->stream.device = compr;
    runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
    if (!runtime) {
        kfree(data);
        snd_card_unref(compr->card);
        return -ENOMEM;
    }
    runtime->state = SNDRV_PCM_STATE_OPEN;
    init_waitqueue_head(&runtime->sleep);
    data->stream.runtime = runtime;
    f->private_data = (void *)data;
    mutex_lock(&compr->lock);
    ret = compr->ops->open(&data->stream);
    mutex_unlock(&compr->lock);
    if (ret) {
        kfree(runtime);
        kfree(data);
    }
    snd_card_unref(compr->card);
    return ret;
}

在snd_compr_open中找到struct snd_compr *compr后,调用compr->ops->open(), compr->ops即snd_soc_new_compress中赋值的soc_compr_ops.compr->open为soc_compr_open()

static int soc_compr_open(struct snd_compr_stream *cstream)
{
    struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    struct snd_soc_component *component;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    int ret;

    mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);

    if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) {
        ret = cpu_dai->driver->cops->startup(cstream, cpu_dai);
        if (ret < 0) {
            dev_err(cpu_dai->dev,
                "Compress ASoC: can't open interface %s: %d\n",
                cpu_dai->name, ret);
            goto out;
        }
    }

    ret = soc_compr_components_open(cstream, &component);
    if (ret < 0)
        goto machine_err;

    if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
        ret = rtd->dai_link->compr_ops->startup(cstream);
        if (ret < 0) {
            dev_err(rtd->dev,
                "Compress ASoC: %s startup failed: %d\n",
                rtd->dai_link->name, ret);
            goto machine_err;
        }
    }

    snd_soc_runtime_activate(rtd, cstream->direction);

    mutex_unlock(&rtd->card->pcm_mutex);

    return 0;

machine_err:
    soc_compr_components_free(cstream, component);

    if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown)
        cpu_dai->driver->cops->shutdown(cstream, cpu_dai);
out:
    mutex_unlock(&rtd->card->pcm_mutex);
    return ret;
}

在soc_compr_open中,会依次调用cpu_dai->driver->cops->startup, soc_compr_components_open(), dai_link->compr_ops->startup.

在soc_compr_components_open()中,调用component->driver->compr_ops->open()即platform driver的compr_ops->open().

static int soc_compr_components_open(struct snd_compr_stream *cstream,
                     struct snd_soc_component **last)
{
    struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    struct snd_soc_component *component;
    struct snd_soc_rtdcom_list *rtdcom;
    int ret;

    for_each_rtdcom(rtd, rtdcom) {
        component = rtdcom->component;

        if (!component->driver->compr_ops ||
            !component->driver->compr_ops->open)
            continue;

        ret = component->driver->compr_ops->open(cstream);
        if (ret < 0) {
            dev_err(component->dev,
                "Compress ASoC: can't open platform %s: %d\n",
                component->name, ret);

            *last = component;
            return ret;
        }
    }

    *last = NULL;
    return 0;
}

user space 通过tinycompress set param时会ioctl到compress_offload.c的snd_compr_ioctl

static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
    struct snd_compr_file *data = f->private_data;
    struct snd_compr_stream *stream;
    int retval = -ENOTTY;

    if (snd_BUG_ON(!data))
        return -EFAULT;

    stream = &data->stream;

    mutex_lock(&stream->device->lock);
    switch (_IOC_NR(cmd)) {
    case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
        retval = put_user(SNDRV_COMPRESS_VERSION,
                (int __user *)arg) ? -EFAULT : 0;
        break;
    case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
        retval = snd_compr_get_caps(stream, arg);
        break;
#ifndef COMPR_CODEC_CAPS_OVERFLOW
    case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
        retval = snd_compr_get_codec_caps(stream, arg);
        break;
#endif
    case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
        retval = snd_compr_set_params(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
        retval = snd_compr_get_params(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
        retval = snd_compr_set_metadata(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
        retval = snd_compr_get_metadata(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
        retval = snd_compr_tstamp(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_AVAIL):
        retval = snd_compr_ioctl_avail(stream, arg);
        break;
    case _IOC_NR(SNDRV_COMPRESS_PAUSE):
        retval = snd_compr_pause(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_RESUME):
        retval = snd_compr_resume(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_START):
        retval = snd_compr_start(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_STOP):
        retval = snd_compr_stop(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_DRAIN):
        retval = snd_compr_drain(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
        retval = snd_compr_partial_drain(stream);
        break;
    case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
        retval = snd_compr_next_track(stream);
        break;

    }
    mutex_unlock(&stream->device->lock);
    return retval;
}

snd_compr_set_params, 调用snd_compr_allocate_buffer分配buffer,并调用stream->ops->setparams

static int
snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
{
    struct snd_compr_params *params;
    int retval;

    if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
        /*
         * we should allow parameter change only when stream has been
         * opened not in other cases
         */
        params = memdup_user((void __user *)arg, sizeof(*params));
        if (IS_ERR(params))
            return PTR_ERR(params);

        retval = snd_compress_check_input(params);
        if (retval)
            goto out;

        retval = snd_compr_allocate_buffer(stream, params);
        if (retval) {
            retval = -ENOMEM;
            goto out;
        }

        retval = stream->ops->set_params(stream, params);
        if (retval)
            goto out;

        stream->metadata_set = false;
        stream->next_track = false;

        stream->runtime->state = SNDRV_PCM_STATE_SETUP;
    } else {
        return -EPERM;
    }
out:
    kfree(params);
    return retval;
}

如果stream->ops->copy没有define并且stream->runtime->dma_buffer_p为NULL时,则通过kmalloc申请buffer.

/* revisit this with snd_pcm_preallocate_xxx */
static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
        struct snd_compr_params *params)
{
    unsigned int buffer_size;
    void *buffer = NULL;

    buffer_size = params->buffer.fragment_size * params->buffer.fragments;
    if (stream->ops->copy) {
        buffer = NULL;
        /* if copy is defined the driver will be required to copy
         * the data from core
         */
    } else {
        if (stream->runtime->dma_buffer_p) {

            if (buffer_size > stream->runtime->dma_buffer_p->bytes)
                dev_err(&stream->device->dev,
                        "Not enough DMA buffer");
            else
                buffer = stream->runtime->dma_buffer_p->area;

        } else {
            buffer = kmalloc(buffer_size, GFP_KERNEL);
        }

        if (!buffer)
            return -ENOMEM;
    }
    stream->runtime->fragment_size = params->buffer.fragment_size;
    stream->runtime->fragments = params->buffer.fragments;
    stream->runtime->buffer = buffer;
    stream->runtime->buffer_size = buffer_size;
    return 0;
}

stream->ops->set_params即为compr->ops,即snd_soc_new_compress中赋值的soc_compr_ops.set_params(即soc_compr_set_params)

在soc_compr_set_params,会依次调用cpu_dai->driver->cops->set_params, soc_compr_components_set_params(), dai_link->compr_ops->set_params. 

static int soc_compr_set_params(struct snd_compr_stream *cstream,
                struct snd_compr_params *params)
{
    struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    int ret;

    mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);

    /*
     * First we call set_params for the CPU DAI, then the component
     * driver this should configure the SoC side. If the machine has
     * compressed ops then we call that as well. The expectation is
     * that these callbacks will configure everything for this compress
     * path, like configuring a PCM port for a CODEC.
     */
    if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) {
        ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai);
        if (ret < 0)
            goto err;
    }

    ret = soc_compr_components_set_params(cstream, params);
    if (ret < 0)
        goto err;

    if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
        ret = rtd->dai_link->compr_ops->set_params(cstream);
        if (ret < 0)
            goto err;
    }

    if (cstream->direction == SND_COMPRESS_PLAYBACK)
        snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
                      SND_SOC_DAPM_STREAM_START);
    else
        snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
                      SND_SOC_DAPM_STREAM_START);

    /* cancel any delayed stream shutdown that is pending */
    rtd->pop_wait = 0;
    mutex_unlock(&rtd->card->pcm_mutex);

    cancel_delayed_work_sync(&rtd->delayed_work);

    return 0;

err:
    mutex_unlock(&rtd->card->pcm_mutex);
    return ret;
}

在soc_compr_components_set_params()中,调用component->driver->compr_ops->set_params()即platform driver的compr_ops->set_params().

static int soc_compr_components_set_params(struct snd_compr_stream *cstream,
                       struct snd_compr_params *params)
{
    struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    struct snd_soc_component *component;
    struct snd_soc_rtdcom_list *rtdcom;
    int ret;

    for_each_rtdcom(rtd, rtdcom) {
        component = rtdcom->component;

        if (!component->driver->compr_ops ||
            !component->driver->compr_ops->set_params)
            continue;

        ret = component->driver->compr_ops->set_params(cstream, params);
        if (ret < 0)
            return ret;
    }

    return 0;
}

当user space 通过tinycompress write数据时,会调用到compress_offload.c的snd_compr_write:

static ssize_t snd_compr_write(struct file *f, const char __user *buf,
        size_t count, loff_t *offset)
{
    struct snd_compr_file *data = f->private_data;
    struct snd_compr_stream *stream;
    size_t avail;
    int retval;

    if (snd_BUG_ON(!data))
        return -EFAULT;

    stream = &data->stream;
    mutex_lock(&stream->device->lock);
    /* write is allowed when stream is running or has been steup */
    switch (stream->runtime->state) {
    case SNDRV_PCM_STATE_SETUP:
    case SNDRV_PCM_STATE_PREPARED:
    case SNDRV_PCM_STATE_RUNNING:
        break;
    default:
        mutex_unlock(&stream->device->lock);
        return -EBADFD;
    }

    avail = snd_compr_get_avail(stream);
    pr_debug("avail returned %ld\n", (unsigned long)avail);
    /* calculate how much we can write to buffer */
    if (avail > count)
        avail = count;

    if (stream->ops->copy) {
        char __user* cbuf = (char __user*)buf;
        retval = stream->ops->copy(stream, cbuf, avail);
    } else {
        retval = snd_compr_write_data(stream, buf, avail);
    }
    if (retval > 0)
        stream->runtime->total_bytes_available += retval;

    /* while initiating the stream, write should be called before START
     * call, so in setup move state */
    if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
        stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
        pr_debug("stream prepared, Houston we are good to go\n");
    }

    mutex_unlock(&stream->device->lock);
    return retval;
}

如果platform driver的compr ops有定义copy函数时,则stream->ops->copy即为compr->ops->copy,即为soc_compr_copy.

static int soc_compr_copy(struct snd_compr_stream *cstream,
              char __user *buf, size_t count)
{
    struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    struct snd_soc_component *component;
    struct snd_soc_rtdcom_list *rtdcom;
    int ret = 0;

    mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);

    for_each_rtdcom(rtd, rtdcom) {
        component = rtdcom->component;

        if (!component->driver->compr_ops ||
            !component->driver->compr_ops->copy)
            continue;

        ret = component->driver->compr_ops->copy(cstream, buf, count);
        break;
    }

    mutex_unlock(&rtd->card->pcm_mutex);
    return ret;
}

如果没有定义copy则调用snd_compr_write_data

static int snd_compr_write_data(struct snd_compr_stream *stream,
           const char __user *buf, size_t count)
{
    void *dstn;
    size_t copy;
    struct snd_compr_runtime *runtime = stream->runtime;
    /* 64-bit Modulus */
    u64 app_pointer = div64_u64(runtime->total_bytes_available,
                    runtime->buffer_size);
    app_pointer = runtime->total_bytes_available -
              (app_pointer * runtime->buffer_size);

    dstn = runtime->buffer + app_pointer;
    pr_debug("copying %ld at %lld\n",
            (unsigned long)count, app_pointer);
    if (count < runtime->buffer_size - app_pointer) {
        if (copy_from_user(dstn, buf, count))
            return -EFAULT;
    } else {
        copy = runtime->buffer_size - app_pointer;
        if (copy_from_user(dstn, buf, copy))
            return -EFAULT;
        if (copy_from_user(runtime->buffer, buf + copy, count - copy))
            return -EFAULT;
    }
    /* if DSP cares, let it know data has been written */
    if (stream->ops->ack)
        stream->ops->ack(stream, count);
    return count;
}

 

 

 
 

 

posted @ 2023-04-12 22:52  fellow_jing  阅读(197)  评论(0编辑  收藏  举报