Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * sound.c - Sound component for Mostcore
0004  *
0005  * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/module.h>
0011 #include <linux/printk.h>
0012 #include <linux/kernel.h>
0013 #include <linux/slab.h>
0014 #include <linux/init.h>
0015 #include <sound/core.h>
0016 #include <sound/pcm.h>
0017 #include <sound/pcm_params.h>
0018 #include <linux/sched.h>
0019 #include <linux/kthread.h>
0020 #include <linux/most.h>
0021 
0022 #define DRIVER_NAME "sound"
0023 #define STRING_SIZE 80
0024 
0025 static struct most_component comp;
0026 
0027 /**
0028  * struct channel - private structure to keep channel specific data
0029  * @substream: stores the substream structure
0030  * @iface: interface for which the channel belongs to
0031  * @cfg: channel configuration
0032  * @card: registered sound card
0033  * @list: list for private use
0034  * @id: channel index
0035  * @period_pos: current period position (ring buffer)
0036  * @buffer_pos: current buffer position (ring buffer)
0037  * @is_stream_running: identifies whether a stream is running or not
0038  * @opened: set when the stream is opened
0039  * @playback_task: playback thread
0040  * @playback_waitq: waitq used by playback thread
0041  */
0042 struct channel {
0043     struct snd_pcm_substream *substream;
0044     struct snd_pcm_hardware pcm_hardware;
0045     struct most_interface *iface;
0046     struct most_channel_config *cfg;
0047     struct snd_card *card;
0048     struct list_head list;
0049     int id;
0050     unsigned int period_pos;
0051     unsigned int buffer_pos;
0052     bool is_stream_running;
0053     struct task_struct *playback_task;
0054     wait_queue_head_t playback_waitq;
0055     void (*copy_fn)(void *alsa, void *most, unsigned int bytes);
0056 };
0057 
0058 struct sound_adapter {
0059     struct list_head dev_list;
0060     struct most_interface *iface;
0061     struct snd_card *card;
0062     struct list_head list;
0063     bool registered;
0064     int pcm_dev_idx;
0065 };
0066 
0067 static struct list_head adpt_list;
0068 
0069 #define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
0070                SNDRV_PCM_INFO_MMAP_VALID | \
0071                SNDRV_PCM_INFO_BATCH | \
0072                SNDRV_PCM_INFO_INTERLEAVED | \
0073                SNDRV_PCM_INFO_BLOCK_TRANSFER)
0074 
0075 static void swap_copy16(u16 *dest, const u16 *source, unsigned int bytes)
0076 {
0077     unsigned int i = 0;
0078 
0079     while (i < (bytes / 2)) {
0080         dest[i] = swab16(source[i]);
0081         i++;
0082     }
0083 }
0084 
0085 static void swap_copy24(u8 *dest, const u8 *source, unsigned int bytes)
0086 {
0087     unsigned int i = 0;
0088 
0089     if (bytes < 2)
0090         return;
0091     while (i < bytes - 2) {
0092         dest[i] = source[i + 2];
0093         dest[i + 1] = source[i + 1];
0094         dest[i + 2] = source[i];
0095         i += 3;
0096     }
0097 }
0098 
0099 static void swap_copy32(u32 *dest, const u32 *source, unsigned int bytes)
0100 {
0101     unsigned int i = 0;
0102 
0103     while (i < bytes / 4) {
0104         dest[i] = swab32(source[i]);
0105         i++;
0106     }
0107 }
0108 
0109 static void alsa_to_most_memcpy(void *alsa, void *most, unsigned int bytes)
0110 {
0111     memcpy(most, alsa, bytes);
0112 }
0113 
0114 static void alsa_to_most_copy16(void *alsa, void *most, unsigned int bytes)
0115 {
0116     swap_copy16(most, alsa, bytes);
0117 }
0118 
0119 static void alsa_to_most_copy24(void *alsa, void *most, unsigned int bytes)
0120 {
0121     swap_copy24(most, alsa, bytes);
0122 }
0123 
0124 static void alsa_to_most_copy32(void *alsa, void *most, unsigned int bytes)
0125 {
0126     swap_copy32(most, alsa, bytes);
0127 }
0128 
0129 static void most_to_alsa_memcpy(void *alsa, void *most, unsigned int bytes)
0130 {
0131     memcpy(alsa, most, bytes);
0132 }
0133 
0134 static void most_to_alsa_copy16(void *alsa, void *most, unsigned int bytes)
0135 {
0136     swap_copy16(alsa, most, bytes);
0137 }
0138 
0139 static void most_to_alsa_copy24(void *alsa, void *most, unsigned int bytes)
0140 {
0141     swap_copy24(alsa, most, bytes);
0142 }
0143 
0144 static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes)
0145 {
0146     swap_copy32(alsa, most, bytes);
0147 }
0148 
0149 /**
0150  * get_channel - get pointer to channel
0151  * @iface: interface structure
0152  * @channel_id: channel ID
0153  *
0154  * This traverses the channel list and returns the channel matching the
0155  * ID and interface.
0156  *
0157  * Returns pointer to channel on success or NULL otherwise.
0158  */
0159 static struct channel *get_channel(struct most_interface *iface,
0160                    int channel_id)
0161 {
0162     struct sound_adapter *adpt = iface->priv;
0163     struct channel *channel;
0164 
0165     list_for_each_entry(channel, &adpt->dev_list, list) {
0166         if ((channel->iface == iface) && (channel->id == channel_id))
0167             return channel;
0168     }
0169     return NULL;
0170 }
0171 
0172 /**
0173  * copy_data - implements data copying function
0174  * @channel: channel
0175  * @mbo: MBO from core
0176  *
0177  * Copy data from/to ring buffer to/from MBO and update the buffer position
0178  */
0179 static bool copy_data(struct channel *channel, struct mbo *mbo)
0180 {
0181     struct snd_pcm_runtime *const runtime = channel->substream->runtime;
0182     unsigned int const frame_bytes = channel->cfg->subbuffer_size;
0183     unsigned int const buffer_size = runtime->buffer_size;
0184     unsigned int frames;
0185     unsigned int fr0;
0186 
0187     if (channel->cfg->direction & MOST_CH_RX)
0188         frames = mbo->processed_length / frame_bytes;
0189     else
0190         frames = mbo->buffer_length / frame_bytes;
0191     fr0 = min(buffer_size - channel->buffer_pos, frames);
0192 
0193     channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes,
0194              mbo->virt_address,
0195              fr0 * frame_bytes);
0196 
0197     if (frames > fr0) {
0198         /* wrap around at end of ring buffer */
0199         channel->copy_fn(runtime->dma_area,
0200                  mbo->virt_address + fr0 * frame_bytes,
0201                  (frames - fr0) * frame_bytes);
0202     }
0203 
0204     channel->buffer_pos += frames;
0205     if (channel->buffer_pos >= buffer_size)
0206         channel->buffer_pos -= buffer_size;
0207     channel->period_pos += frames;
0208     if (channel->period_pos >= runtime->period_size) {
0209         channel->period_pos -= runtime->period_size;
0210         return true;
0211     }
0212     return false;
0213 }
0214 
0215 /**
0216  * playback_thread - function implements the playback thread
0217  * @data: private data
0218  *
0219  * Thread which does the playback functionality in a loop. It waits for a free
0220  * MBO from mostcore for a particular channel and copy the data from ring buffer
0221  * to MBO. Submit the MBO back to mostcore, after copying the data.
0222  *
0223  * Returns 0 on success or error code otherwise.
0224  */
0225 static int playback_thread(void *data)
0226 {
0227     struct channel *const channel = data;
0228 
0229     while (!kthread_should_stop()) {
0230         struct mbo *mbo = NULL;
0231         bool period_elapsed = false;
0232 
0233         wait_event_interruptible(
0234             channel->playback_waitq,
0235             kthread_should_stop() ||
0236             (channel->is_stream_running &&
0237              (mbo = most_get_mbo(channel->iface, channel->id,
0238                          &comp))));
0239         if (!mbo)
0240             continue;
0241 
0242         if (channel->is_stream_running)
0243             period_elapsed = copy_data(channel, mbo);
0244         else
0245             memset(mbo->virt_address, 0, mbo->buffer_length);
0246 
0247         most_submit_mbo(mbo);
0248         if (period_elapsed)
0249             snd_pcm_period_elapsed(channel->substream);
0250     }
0251     return 0;
0252 }
0253 
0254 /**
0255  * pcm_open - implements open callback function for PCM middle layer
0256  * @substream: pointer to ALSA PCM substream
0257  *
0258  * This is called when a PCM substream is opened. At least, the function should
0259  * initialize the runtime->hw record.
0260  *
0261  * Returns 0 on success or error code otherwise.
0262  */
0263 static int pcm_open(struct snd_pcm_substream *substream)
0264 {
0265     struct channel *channel = substream->private_data;
0266     struct snd_pcm_runtime *runtime = substream->runtime;
0267     struct most_channel_config *cfg = channel->cfg;
0268     int ret;
0269 
0270     channel->substream = substream;
0271 
0272     if (cfg->direction == MOST_CH_TX) {
0273         channel->playback_task = kthread_run(playback_thread, channel,
0274                              "most_audio_playback");
0275         if (IS_ERR(channel->playback_task)) {
0276             pr_err("Couldn't start thread\n");
0277             return PTR_ERR(channel->playback_task);
0278         }
0279     }
0280 
0281     ret = most_start_channel(channel->iface, channel->id, &comp);
0282     if (ret) {
0283         pr_err("most_start_channel() failed!\n");
0284         if (cfg->direction == MOST_CH_TX)
0285             kthread_stop(channel->playback_task);
0286         return ret;
0287     }
0288 
0289     runtime->hw = channel->pcm_hardware;
0290     return 0;
0291 }
0292 
0293 /**
0294  * pcm_close - implements close callback function for PCM middle layer
0295  * @substream: sub-stream pointer
0296  *
0297  * Obviously, this is called when a PCM substream is closed. Any private
0298  * instance for a PCM substream allocated in the open callback will be
0299  * released here.
0300  *
0301  * Returns 0 on success or error code otherwise.
0302  */
0303 static int pcm_close(struct snd_pcm_substream *substream)
0304 {
0305     struct channel *channel = substream->private_data;
0306 
0307     if (channel->cfg->direction == MOST_CH_TX)
0308         kthread_stop(channel->playback_task);
0309     most_stop_channel(channel->iface, channel->id, &comp);
0310     return 0;
0311 }
0312 
0313 /**
0314  * pcm_prepare - implements prepare callback function for PCM middle layer
0315  * @substream: substream pointer
0316  *
0317  * This callback is called when the PCM is "prepared". Format rate, sample rate,
0318  * etc., can be set here. This callback can be called many times at each setup.
0319  *
0320  * Returns 0 on success or error code otherwise.
0321  */
0322 static int pcm_prepare(struct snd_pcm_substream *substream)
0323 {
0324     struct channel *channel = substream->private_data;
0325     struct snd_pcm_runtime *runtime = substream->runtime;
0326     struct most_channel_config *cfg = channel->cfg;
0327     int width = snd_pcm_format_physical_width(runtime->format);
0328 
0329     channel->copy_fn = NULL;
0330 
0331     if (cfg->direction == MOST_CH_TX) {
0332         if (snd_pcm_format_big_endian(runtime->format) || width == 8)
0333             channel->copy_fn = alsa_to_most_memcpy;
0334         else if (width == 16)
0335             channel->copy_fn = alsa_to_most_copy16;
0336         else if (width == 24)
0337             channel->copy_fn = alsa_to_most_copy24;
0338         else if (width == 32)
0339             channel->copy_fn = alsa_to_most_copy32;
0340     } else {
0341         if (snd_pcm_format_big_endian(runtime->format) || width == 8)
0342             channel->copy_fn = most_to_alsa_memcpy;
0343         else if (width == 16)
0344             channel->copy_fn = most_to_alsa_copy16;
0345         else if (width == 24)
0346             channel->copy_fn = most_to_alsa_copy24;
0347         else if (width == 32)
0348             channel->copy_fn = most_to_alsa_copy32;
0349     }
0350 
0351     if (!channel->copy_fn)
0352         return -EINVAL;
0353     channel->period_pos = 0;
0354     channel->buffer_pos = 0;
0355     return 0;
0356 }
0357 
0358 /**
0359  * pcm_trigger - implements trigger callback function for PCM middle layer
0360  * @substream: substream pointer
0361  * @cmd: action to perform
0362  *
0363  * This is called when the PCM is started, stopped or paused. The action will be
0364  * specified in the second argument, SNDRV_PCM_TRIGGER_XXX
0365  *
0366  * Returns 0 on success or error code otherwise.
0367  */
0368 static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
0369 {
0370     struct channel *channel = substream->private_data;
0371 
0372     switch (cmd) {
0373     case SNDRV_PCM_TRIGGER_START:
0374         channel->is_stream_running = true;
0375         wake_up_interruptible(&channel->playback_waitq);
0376         return 0;
0377 
0378     case SNDRV_PCM_TRIGGER_STOP:
0379         channel->is_stream_running = false;
0380         return 0;
0381 
0382     default:
0383         return -EINVAL;
0384     }
0385     return 0;
0386 }
0387 
0388 /**
0389  * pcm_pointer - implements pointer callback function for PCM middle layer
0390  * @substream: substream pointer
0391  *
0392  * This callback is called when the PCM middle layer inquires the current
0393  * hardware position on the buffer. The position must be returned in frames,
0394  * ranging from 0 to buffer_size-1.
0395  */
0396 static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
0397 {
0398     struct channel *channel = substream->private_data;
0399 
0400     return channel->buffer_pos;
0401 }
0402 
0403 /**
0404  * Initialization of struct snd_pcm_ops
0405  */
0406 static const struct snd_pcm_ops pcm_ops = {
0407     .open       = pcm_open,
0408     .close      = pcm_close,
0409     .prepare    = pcm_prepare,
0410     .trigger    = pcm_trigger,
0411     .pointer    = pcm_pointer,
0412 };
0413 
0414 static int split_arg_list(char *buf, u16 *ch_num, char **sample_res)
0415 {
0416     char *num;
0417     int ret;
0418 
0419     num = strsep(&buf, "x");
0420     if (!num)
0421         goto err;
0422     ret = kstrtou16(num, 0, ch_num);
0423     if (ret)
0424         goto err;
0425     *sample_res = strsep(&buf, ".\n");
0426     if (!*sample_res)
0427         goto err;
0428     return 0;
0429 
0430 err:
0431     pr_err("Bad PCM format\n");
0432     return -EINVAL;
0433 }
0434 
0435 static const struct sample_resolution_info {
0436     const char *sample_res;
0437     int bytes;
0438     u64 formats;
0439 } sinfo[] = {
0440     { "8", 1, SNDRV_PCM_FMTBIT_S8 },
0441     { "16", 2, SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE },
0442     { "24", 3, SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE },
0443     { "32", 4, SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE },
0444 };
0445 
0446 static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw,
0447                    u16 ch_num, char *sample_res,
0448                    struct most_channel_config *cfg)
0449 {
0450     int i;
0451 
0452     for (i = 0; i < ARRAY_SIZE(sinfo); i++) {
0453         if (!strcmp(sample_res, sinfo[i].sample_res))
0454             goto found;
0455     }
0456     pr_err("Unsupported PCM format\n");
0457     return -EINVAL;
0458 
0459 found:
0460     if (!ch_num) {
0461         pr_err("Bad number of channels\n");
0462         return -EINVAL;
0463     }
0464 
0465     if (cfg->subbuffer_size != ch_num * sinfo[i].bytes) {
0466         pr_err("Audio resolution doesn't fit subbuffer size\n");
0467         return -EINVAL;
0468     }
0469 
0470     pcm_hw->info = MOST_PCM_INFO;
0471     pcm_hw->rates = SNDRV_PCM_RATE_48000;
0472     pcm_hw->rate_min = 48000;
0473     pcm_hw->rate_max = 48000;
0474     pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size;
0475     pcm_hw->period_bytes_min = cfg->buffer_size;
0476     pcm_hw->period_bytes_max = cfg->buffer_size;
0477     pcm_hw->periods_min = 1;
0478     pcm_hw->periods_max = cfg->num_buffers;
0479     pcm_hw->channels_min = ch_num;
0480     pcm_hw->channels_max = ch_num;
0481     pcm_hw->formats = sinfo[i].formats;
0482     return 0;
0483 }
0484 
0485 static void release_adapter(struct sound_adapter *adpt)
0486 {
0487     struct channel *channel, *tmp;
0488 
0489     list_for_each_entry_safe(channel, tmp, &adpt->dev_list, list) {
0490         list_del(&channel->list);
0491         kfree(channel);
0492     }
0493     if (adpt->card)
0494         snd_card_free(adpt->card);
0495     list_del(&adpt->list);
0496     kfree(adpt);
0497 }
0498 
0499 /**
0500  * audio_probe_channel - probe function of the driver module
0501  * @iface: pointer to interface instance
0502  * @channel_id: channel index/ID
0503  * @cfg: pointer to actual channel configuration
0504  * @arg_list: string that provides the name of the device to be created in /dev
0505  *        plus the desired audio resolution
0506  *
0507  * Creates sound card, pcm device, sets pcm ops and registers sound card.
0508  *
0509  * Returns 0 on success or error code otherwise.
0510  */
0511 static int audio_probe_channel(struct most_interface *iface, int channel_id,
0512                    struct most_channel_config *cfg,
0513                    char *device_name, char *arg_list)
0514 {
0515     struct channel *channel;
0516     struct sound_adapter *adpt;
0517     struct snd_pcm *pcm;
0518     int playback_count = 0;
0519     int capture_count = 0;
0520     int ret;
0521     int direction;
0522     u16 ch_num;
0523     char *sample_res;
0524     char arg_list_cpy[STRING_SIZE];
0525 
0526     if (cfg->data_type != MOST_CH_SYNC) {
0527         pr_err("Incompatible channel type\n");
0528         return -EINVAL;
0529     }
0530     strscpy(arg_list_cpy, arg_list, STRING_SIZE);
0531     ret = split_arg_list(arg_list_cpy, &ch_num, &sample_res);
0532     if (ret < 0)
0533         return ret;
0534 
0535     list_for_each_entry(adpt, &adpt_list, list) {
0536         if (adpt->iface != iface)
0537             continue;
0538         if (adpt->registered)
0539             return -ENOSPC;
0540         adpt->pcm_dev_idx++;
0541         goto skip_adpt_alloc;
0542     }
0543     adpt = kzalloc(sizeof(*adpt), GFP_KERNEL);
0544     if (!adpt)
0545         return -ENOMEM;
0546 
0547     adpt->iface = iface;
0548     INIT_LIST_HEAD(&adpt->dev_list);
0549     iface->priv = adpt;
0550     list_add_tail(&adpt->list, &adpt_list);
0551     ret = snd_card_new(iface->driver_dev, -1, "INIC", THIS_MODULE,
0552                sizeof(*channel), &adpt->card);
0553     if (ret < 0)
0554         goto err_free_adpt;
0555     snprintf(adpt->card->driver, sizeof(adpt->card->driver),
0556          "%s", DRIVER_NAME);
0557     snprintf(adpt->card->shortname, sizeof(adpt->card->shortname),
0558          "Microchip INIC");
0559     snprintf(adpt->card->longname, sizeof(adpt->card->longname),
0560          "%s at %s", adpt->card->shortname, iface->description);
0561 skip_adpt_alloc:
0562     if (get_channel(iface, channel_id)) {
0563         pr_err("channel (%s:%d) is already linked\n",
0564                iface->description, channel_id);
0565         return -EEXIST;
0566     }
0567 
0568     if (cfg->direction == MOST_CH_TX) {
0569         playback_count = 1;
0570         direction = SNDRV_PCM_STREAM_PLAYBACK;
0571     } else {
0572         capture_count = 1;
0573         direction = SNDRV_PCM_STREAM_CAPTURE;
0574     }
0575     channel = kzalloc(sizeof(*channel), GFP_KERNEL);
0576     if (!channel) {
0577         ret = -ENOMEM;
0578         goto err_free_adpt;
0579     }
0580     channel->card = adpt->card;
0581     channel->cfg = cfg;
0582     channel->iface = iface;
0583     channel->id = channel_id;
0584     init_waitqueue_head(&channel->playback_waitq);
0585     list_add_tail(&channel->list, &adpt->dev_list);
0586 
0587     ret = audio_set_hw_params(&channel->pcm_hardware, ch_num, sample_res,
0588                   cfg);
0589     if (ret)
0590         goto err_free_adpt;
0591 
0592     ret = snd_pcm_new(adpt->card, device_name, adpt->pcm_dev_idx,
0593               playback_count, capture_count, &pcm);
0594 
0595     if (ret < 0)
0596         goto err_free_adpt;
0597 
0598     pcm->private_data = channel;
0599     strscpy(pcm->name, device_name, sizeof(pcm->name));
0600     snd_pcm_set_ops(pcm, direction, &pcm_ops);
0601     snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
0602     return 0;
0603 
0604 err_free_adpt:
0605     release_adapter(adpt);
0606     return ret;
0607 }
0608 
0609 static int audio_create_sound_card(void)
0610 {
0611     int ret;
0612     struct sound_adapter *adpt;
0613 
0614     list_for_each_entry(adpt, &adpt_list, list) {
0615         if (!adpt->registered)
0616             goto adpt_alloc;
0617     }
0618     return -ENODEV;
0619 adpt_alloc:
0620     ret = snd_card_register(adpt->card);
0621     if (ret < 0) {
0622         release_adapter(adpt);
0623         return ret;
0624     }
0625     adpt->registered = true;
0626     return 0;
0627 }
0628 
0629 /**
0630  * audio_disconnect_channel - function to disconnect a channel
0631  * @iface: pointer to interface instance
0632  * @channel_id: channel index
0633  *
0634  * This frees allocated memory and removes the sound card from ALSA
0635  *
0636  * Returns 0 on success or error code otherwise.
0637  */
0638 static int audio_disconnect_channel(struct most_interface *iface,
0639                     int channel_id)
0640 {
0641     struct channel *channel;
0642     struct sound_adapter *adpt = iface->priv;
0643 
0644     channel = get_channel(iface, channel_id);
0645     if (!channel)
0646         return -EINVAL;
0647 
0648     list_del(&channel->list);
0649 
0650     kfree(channel);
0651     if (list_empty(&adpt->dev_list))
0652         release_adapter(adpt);
0653     return 0;
0654 }
0655 
0656 /**
0657  * audio_rx_completion - completion handler for rx channels
0658  * @mbo: pointer to buffer object that has completed
0659  *
0660  * This searches for the channel this MBO belongs to and copy the data from MBO
0661  * to ring buffer
0662  *
0663  * Returns 0 on success or error code otherwise.
0664  */
0665 static int audio_rx_completion(struct mbo *mbo)
0666 {
0667     struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id);
0668     bool period_elapsed = false;
0669 
0670     if (!channel)
0671         return -EINVAL;
0672     if (channel->is_stream_running)
0673         period_elapsed = copy_data(channel, mbo);
0674     most_put_mbo(mbo);
0675     if (period_elapsed)
0676         snd_pcm_period_elapsed(channel->substream);
0677     return 0;
0678 }
0679 
0680 /**
0681  * audio_tx_completion - completion handler for tx channels
0682  * @iface: pointer to interface instance
0683  * @channel_id: channel index/ID
0684  *
0685  * This searches the channel that belongs to this combination of interface
0686  * pointer and channel ID and wakes a process sitting in the wait queue of
0687  * this channel.
0688  *
0689  * Returns 0 on success or error code otherwise.
0690  */
0691 static int audio_tx_completion(struct most_interface *iface, int channel_id)
0692 {
0693     struct channel *channel = get_channel(iface, channel_id);
0694 
0695     if (!channel)
0696         return -EINVAL;
0697 
0698     wake_up_interruptible(&channel->playback_waitq);
0699     return 0;
0700 }
0701 
0702 /**
0703  * Initialization of the struct most_component
0704  */
0705 static struct most_component comp = {
0706     .mod = THIS_MODULE,
0707     .name = DRIVER_NAME,
0708     .probe_channel = audio_probe_channel,
0709     .disconnect_channel = audio_disconnect_channel,
0710     .rx_completion = audio_rx_completion,
0711     .tx_completion = audio_tx_completion,
0712     .cfg_complete = audio_create_sound_card,
0713 };
0714 
0715 static int __init audio_init(void)
0716 {
0717     int ret;
0718 
0719     INIT_LIST_HEAD(&adpt_list);
0720 
0721     ret = most_register_component(&comp);
0722     if (ret) {
0723         pr_err("Failed to register %s\n", comp.name);
0724         return ret;
0725     }
0726     ret = most_register_configfs_subsys(&comp);
0727     if (ret) {
0728         pr_err("Failed to register %s configfs subsys\n", comp.name);
0729         most_deregister_component(&comp);
0730     }
0731     return ret;
0732 }
0733 
0734 static void __exit audio_exit(void)
0735 {
0736     most_deregister_configfs_subsys(&comp);
0737     most_deregister_component(&comp);
0738 }
0739 
0740 module_init(audio_init);
0741 module_exit(audio_exit);
0742 
0743 MODULE_LICENSE("GPL");
0744 MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
0745 MODULE_DESCRIPTION("Sound Component Module for Mostcore");