Merge branch 'for-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[linux-2.6-block.git] / drivers / staging / most / sound / sound.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * sound.c - Sound component for Mostcore
4  *
5  * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
6  */
7
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/module.h>
11 #include <linux/printk.h>
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <sound/core.h>
15 #include <sound/pcm.h>
16 #include <sound/pcm_params.h>
17 #include <linux/sched.h>
18 #include <linux/kthread.h>
19 #include <most/core.h>
20
21 #define DRIVER_NAME "sound"
22
23 static struct list_head dev_list;
24 static struct core_component comp;
25
26 /**
27  * struct channel - private structure to keep channel specific data
28  * @substream: stores the substream structure
29  * @iface: interface for which the channel belongs to
30  * @cfg: channel configuration
31  * @card: registered sound card
32  * @list: list for private use
33  * @id: channel index
34  * @period_pos: current period position (ring buffer)
35  * @buffer_pos: current buffer position (ring buffer)
36  * @is_stream_running: identifies whether a stream is running or not
37  * @opened: set when the stream is opened
38  * @playback_task: playback thread
39  * @playback_waitq: waitq used by playback thread
40  */
41 struct channel {
42         struct snd_pcm_substream *substream;
43         struct snd_pcm_hardware pcm_hardware;
44         struct most_interface *iface;
45         struct most_channel_config *cfg;
46         struct snd_card *card;
47         struct list_head list;
48         int id;
49         unsigned int period_pos;
50         unsigned int buffer_pos;
51         bool is_stream_running;
52
53         struct task_struct *playback_task;
54         wait_queue_head_t playback_waitq;
55
56         void (*copy_fn)(void *alsa, void *most, unsigned int bytes);
57 };
58
59 #define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
60                        SNDRV_PCM_INFO_MMAP_VALID | \
61                        SNDRV_PCM_INFO_BATCH | \
62                        SNDRV_PCM_INFO_INTERLEAVED | \
63                        SNDRV_PCM_INFO_BLOCK_TRANSFER)
64
65 #define swap16(val) ( \
66         (((u16)(val) << 8) & (u16)0xFF00) | \
67         (((u16)(val) >> 8) & (u16)0x00FF))
68
69 #define swap32(val) ( \
70         (((u32)(val) << 24) & (u32)0xFF000000) | \
71         (((u32)(val) <<  8) & (u32)0x00FF0000) | \
72         (((u32)(val) >>  8) & (u32)0x0000FF00) | \
73         (((u32)(val) >> 24) & (u32)0x000000FF))
74
75 static void swap_copy16(u16 *dest, const u16 *source, unsigned int bytes)
76 {
77         unsigned int i = 0;
78
79         while (i < (bytes / 2)) {
80                 dest[i] = swap16(source[i]);
81                 i++;
82         }
83 }
84
85 static void swap_copy24(u8 *dest, const u8 *source, unsigned int bytes)
86 {
87         unsigned int i = 0;
88
89         while (i < bytes - 2) {
90                 dest[i] = source[i + 2];
91                 dest[i + 1] = source[i + 1];
92                 dest[i + 2] = source[i];
93                 i += 3;
94         }
95 }
96
97 static void swap_copy32(u32 *dest, const u32 *source, unsigned int bytes)
98 {
99         unsigned int i = 0;
100
101         while (i < bytes / 4) {
102                 dest[i] = swap32(source[i]);
103                 i++;
104         }
105 }
106
107 static void alsa_to_most_memcpy(void *alsa, void *most, unsigned int bytes)
108 {
109         memcpy(most, alsa, bytes);
110 }
111
112 static void alsa_to_most_copy16(void *alsa, void *most, unsigned int bytes)
113 {
114         swap_copy16(most, alsa, bytes);
115 }
116
117 static void alsa_to_most_copy24(void *alsa, void *most, unsigned int bytes)
118 {
119         swap_copy24(most, alsa, bytes);
120 }
121
122 static void alsa_to_most_copy32(void *alsa, void *most, unsigned int bytes)
123 {
124         swap_copy32(most, alsa, bytes);
125 }
126
127 static void most_to_alsa_memcpy(void *alsa, void *most, unsigned int bytes)
128 {
129         memcpy(alsa, most, bytes);
130 }
131
132 static void most_to_alsa_copy16(void *alsa, void *most, unsigned int bytes)
133 {
134         swap_copy16(alsa, most, bytes);
135 }
136
137 static void most_to_alsa_copy24(void *alsa, void *most, unsigned int bytes)
138 {
139         swap_copy24(alsa, most, bytes);
140 }
141
142 static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes)
143 {
144         swap_copy32(alsa, most, bytes);
145 }
146
147 /**
148  * get_channel - get pointer to channel
149  * @iface: interface structure
150  * @channel_id: channel ID
151  *
152  * This traverses the channel list and returns the channel matching the
153  * ID and interface.
154  *
155  * Returns pointer to channel on success or NULL otherwise.
156  */
157 static struct channel *get_channel(struct most_interface *iface,
158                                    int channel_id)
159 {
160         struct channel *channel, *tmp;
161
162         list_for_each_entry_safe(channel, tmp, &dev_list, list) {
163                 if ((channel->iface == iface) && (channel->id == channel_id))
164                         return channel;
165         }
166
167         return NULL;
168 }
169
170 /**
171  * copy_data - implements data copying function
172  * @channel: channel
173  * @mbo: MBO from core
174  *
175  * Copy data from/to ring buffer to/from MBO and update the buffer position
176  */
177 static bool copy_data(struct channel *channel, struct mbo *mbo)
178 {
179         struct snd_pcm_runtime *const runtime = channel->substream->runtime;
180         unsigned int const frame_bytes = channel->cfg->subbuffer_size;
181         unsigned int const buffer_size = runtime->buffer_size;
182         unsigned int frames;
183         unsigned int fr0;
184
185         if (channel->cfg->direction & MOST_CH_RX)
186                 frames = mbo->processed_length / frame_bytes;
187         else
188                 frames = mbo->buffer_length / frame_bytes;
189         fr0 = min(buffer_size - channel->buffer_pos, frames);
190
191         channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes,
192                          mbo->virt_address,
193                          fr0 * frame_bytes);
194
195         if (frames > fr0) {
196                 /* wrap around at end of ring buffer */
197                 channel->copy_fn(runtime->dma_area,
198                                  mbo->virt_address + fr0 * frame_bytes,
199                                  (frames - fr0) * frame_bytes);
200         }
201
202         channel->buffer_pos += frames;
203         if (channel->buffer_pos >= buffer_size)
204                 channel->buffer_pos -= buffer_size;
205         channel->period_pos += frames;
206         if (channel->period_pos >= runtime->period_size) {
207                 channel->period_pos -= runtime->period_size;
208                 return true;
209         }
210
211         return false;
212 }
213
214 /**
215  * playback_thread - function implements the playback thread
216  * @data: private data
217  *
218  * Thread which does the playback functionality in a loop. It waits for a free
219  * MBO from mostcore for a particular channel and copy the data from ring buffer
220  * to MBO. Submit the MBO back to mostcore, after copying the data.
221  *
222  * Returns 0 on success or error code otherwise.
223  */
224 static int playback_thread(void *data)
225 {
226         struct channel *const channel = data;
227
228         while (!kthread_should_stop()) {
229                 struct mbo *mbo = NULL;
230                 bool period_elapsed = false;
231
232                 wait_event_interruptible(
233                         channel->playback_waitq,
234                         kthread_should_stop() ||
235                         (channel->is_stream_running &&
236                          (mbo = most_get_mbo(channel->iface, channel->id,
237                                              &comp))));
238                 if (!mbo)
239                         continue;
240
241                 if (channel->is_stream_running)
242                         period_elapsed = copy_data(channel, mbo);
243                 else
244                         memset(mbo->virt_address, 0, mbo->buffer_length);
245
246                 most_submit_mbo(mbo);
247                 if (period_elapsed)
248                         snd_pcm_period_elapsed(channel->substream);
249         }
250
251         return 0;
252 }
253
254 /**
255  * pcm_open - implements open callback function for PCM middle layer
256  * @substream: pointer to ALSA PCM substream
257  *
258  * This is called when a PCM substream is opened. At least, the function should
259  * initialize the runtime->hw record.
260  *
261  * Returns 0 on success or error code otherwise.
262  */
263 static int pcm_open(struct snd_pcm_substream *substream)
264 {
265         struct channel *channel = substream->private_data;
266         struct snd_pcm_runtime *runtime = substream->runtime;
267         struct most_channel_config *cfg = channel->cfg;
268
269         channel->substream = substream;
270
271         if (cfg->direction == MOST_CH_TX) {
272                 channel->playback_task = kthread_run(playback_thread, channel,
273                                                      "most_audio_playback");
274                 if (IS_ERR(channel->playback_task)) {
275                         pr_err("Couldn't start thread\n");
276                         return PTR_ERR(channel->playback_task);
277                 }
278         }
279
280         if (most_start_channel(channel->iface, channel->id, &comp)) {
281                 pr_err("most_start_channel() failed!\n");
282                 if (cfg->direction == MOST_CH_TX)
283                         kthread_stop(channel->playback_task);
284                 return -EBUSY;
285         }
286
287         runtime->hw = channel->pcm_hardware;
288         return 0;
289 }
290
291 /**
292  * pcm_close - implements close callback function for PCM middle layer
293  * @substream: sub-stream pointer
294  *
295  * Obviously, this is called when a PCM substream is closed. Any private
296  * instance for a PCM substream allocated in the open callback will be
297  * released here.
298  *
299  * Returns 0 on success or error code otherwise.
300  */
301 static int pcm_close(struct snd_pcm_substream *substream)
302 {
303         struct channel *channel = substream->private_data;
304
305         if (channel->cfg->direction == MOST_CH_TX)
306                 kthread_stop(channel->playback_task);
307         most_stop_channel(channel->iface, channel->id, &comp);
308
309         return 0;
310 }
311
312 /**
313  * pcm_hw_params - implements hw_params callback function for PCM middle layer
314  * @substream: sub-stream pointer
315  * @hw_params: contains the hardware parameters set by the application
316  *
317  * This is called when the hardware parameters is set by the application, that
318  * is, once when the buffer size, the period size, the format, etc. are defined
319  * for the PCM substream. Many hardware setups should be done is this callback,
320  * including the allocation of buffers.
321  *
322  * Returns 0 on success or error code otherwise.
323  */
324 static int pcm_hw_params(struct snd_pcm_substream *substream,
325                          struct snd_pcm_hw_params *hw_params)
326 {
327         struct channel *channel = substream->private_data;
328
329         if ((params_channels(hw_params) > channel->pcm_hardware.channels_max) ||
330             (params_channels(hw_params) < channel->pcm_hardware.channels_min)) {
331                 pr_err("Requested number of channels not supported.\n");
332                 return -EINVAL;
333         }
334         return snd_pcm_lib_alloc_vmalloc_buffer(substream,
335                                                 params_buffer_bytes(hw_params));
336 }
337
338 /**
339  * pcm_hw_free - implements hw_free callback function for PCM middle layer
340  * @substream: substream pointer
341  *
342  * This is called to release the resources allocated via hw_params.
343  * This function will be always called before the close callback is called.
344  *
345  * Returns 0 on success or error code otherwise.
346  */
347 static int pcm_hw_free(struct snd_pcm_substream *substream)
348 {
349         return snd_pcm_lib_free_vmalloc_buffer(substream);
350 }
351
352 /**
353  * pcm_prepare - implements prepare callback function for PCM middle layer
354  * @substream: substream pointer
355  *
356  * This callback is called when the PCM is "prepared". Format rate, sample rate,
357  * etc., can be set here. This callback can be called many times at each setup.
358  *
359  * Returns 0 on success or error code otherwise.
360  */
361 static int pcm_prepare(struct snd_pcm_substream *substream)
362 {
363         struct channel *channel = substream->private_data;
364         struct snd_pcm_runtime *runtime = substream->runtime;
365         struct most_channel_config *cfg = channel->cfg;
366         int width = snd_pcm_format_physical_width(runtime->format);
367
368         channel->copy_fn = NULL;
369
370         if (cfg->direction == MOST_CH_TX) {
371                 if (snd_pcm_format_big_endian(runtime->format) || width == 8)
372                         channel->copy_fn = alsa_to_most_memcpy;
373                 else if (width == 16)
374                         channel->copy_fn = alsa_to_most_copy16;
375                 else if (width == 24)
376                         channel->copy_fn = alsa_to_most_copy24;
377                 else if (width == 32)
378                         channel->copy_fn = alsa_to_most_copy32;
379         } else {
380                 if (snd_pcm_format_big_endian(runtime->format) || width == 8)
381                         channel->copy_fn = most_to_alsa_memcpy;
382                 else if (width == 16)
383                         channel->copy_fn = most_to_alsa_copy16;
384                 else if (width == 24)
385                         channel->copy_fn = most_to_alsa_copy24;
386                 else if (width == 32)
387                         channel->copy_fn = most_to_alsa_copy32;
388         }
389
390         if (!channel->copy_fn) {
391                 pr_err("unsupported format\n");
392                 return -EINVAL;
393         }
394
395         channel->period_pos = 0;
396         channel->buffer_pos = 0;
397
398         return 0;
399 }
400
401 /**
402  * pcm_trigger - implements trigger callback function for PCM middle layer
403  * @substream: substream pointer
404  * @cmd: action to perform
405  *
406  * This is called when the PCM is started, stopped or paused. The action will be
407  * specified in the second argument, SNDRV_PCM_TRIGGER_XXX
408  *
409  * Returns 0 on success or error code otherwise.
410  */
411 static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
412 {
413         struct channel *channel = substream->private_data;
414
415         switch (cmd) {
416         case SNDRV_PCM_TRIGGER_START:
417                 channel->is_stream_running = true;
418                 wake_up_interruptible(&channel->playback_waitq);
419                 return 0;
420
421         case SNDRV_PCM_TRIGGER_STOP:
422                 channel->is_stream_running = false;
423                 return 0;
424
425         default:
426                 pr_info("%s(), invalid\n", __func__);
427                 return -EINVAL;
428         }
429         return 0;
430 }
431
432 /**
433  * pcm_pointer - implements pointer callback function for PCM middle layer
434  * @substream: substream pointer
435  *
436  * This callback is called when the PCM middle layer inquires the current
437  * hardware position on the buffer. The position must be returned in frames,
438  * ranging from 0 to buffer_size-1.
439  */
440 static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
441 {
442         struct channel *channel = substream->private_data;
443
444         return channel->buffer_pos;
445 }
446
447 /**
448  * Initialization of struct snd_pcm_ops
449  */
450 static const struct snd_pcm_ops pcm_ops = {
451         .open       = pcm_open,
452         .close      = pcm_close,
453         .ioctl      = snd_pcm_lib_ioctl,
454         .hw_params  = pcm_hw_params,
455         .hw_free    = pcm_hw_free,
456         .prepare    = pcm_prepare,
457         .trigger    = pcm_trigger,
458         .pointer    = pcm_pointer,
459         .page       = snd_pcm_lib_get_vmalloc_page,
460 };
461
462 static int split_arg_list(char *buf, char **card_name, u16 *ch_num,
463                           char **sample_res)
464 {
465         char *num;
466         int ret;
467
468         *card_name = strsep(&buf, ".");
469         if (!*card_name) {
470                 pr_err("Missing sound card name\n");
471                 return -EIO;
472         }
473         num = strsep(&buf, "x");
474         if (!num)
475                 goto err;
476         ret = kstrtou16(num, 0, ch_num);
477         if (ret)
478                 goto err;
479         *sample_res = strsep(&buf, ".\n");
480         if (!*sample_res)
481                 goto err;
482         return 0;
483
484 err:
485         pr_err("Bad PCM format\n");
486         return -EIO;
487 }
488
489 static const struct sample_resolution_info {
490         const char *sample_res;
491         int bytes;
492         u64 formats;
493 } sinfo[] = {
494         { "8", 1, SNDRV_PCM_FMTBIT_S8 },
495         { "16", 2, SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE },
496         { "24", 3, SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE },
497         { "32", 4, SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE },
498 };
499
500 static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw,
501                                u16 ch_num, char *sample_res,
502                                struct most_channel_config *cfg)
503 {
504         int i;
505
506         for (i = 0; i < ARRAY_SIZE(sinfo); i++) {
507                 if (!strcmp(sample_res, sinfo[i].sample_res))
508                         goto found;
509         }
510         pr_err("Unsupported PCM format\n");
511         return -EIO;
512
513 found:
514         if (!ch_num) {
515                 pr_err("Bad number of channels\n");
516                 return -EINVAL;
517         }
518
519         if (cfg->subbuffer_size != ch_num * sinfo[i].bytes) {
520                 pr_err("Audio resolution doesn't fit subbuffer size\n");
521                 return -EINVAL;
522         }
523
524         pcm_hw->info = MOST_PCM_INFO;
525         pcm_hw->rates = SNDRV_PCM_RATE_48000;
526         pcm_hw->rate_min = 48000;
527         pcm_hw->rate_max = 48000;
528         pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size;
529         pcm_hw->period_bytes_min = cfg->buffer_size;
530         pcm_hw->period_bytes_max = cfg->buffer_size;
531         pcm_hw->periods_min = 1;
532         pcm_hw->periods_max = cfg->num_buffers;
533         pcm_hw->channels_min = ch_num;
534         pcm_hw->channels_max = ch_num;
535         pcm_hw->formats = sinfo[i].formats;
536         return 0;
537 }
538
539 /**
540  * audio_probe_channel - probe function of the driver module
541  * @iface: pointer to interface instance
542  * @channel_id: channel index/ID
543  * @cfg: pointer to actual channel configuration
544  * @arg_list: string that provides the name of the device to be created in /dev
545  *            plus the desired audio resolution
546  *
547  * Creates sound card, pcm device, sets pcm ops and registers sound card.
548  *
549  * Returns 0 on success or error code otherwise.
550  */
551 static int audio_probe_channel(struct most_interface *iface, int channel_id,
552                                struct most_channel_config *cfg,
553                                char *arg_list)
554 {
555         struct channel *channel;
556         struct snd_card *card;
557         struct snd_pcm *pcm;
558         int playback_count = 0;
559         int capture_count = 0;
560         int ret;
561         int direction;
562         char *card_name;
563         u16 ch_num;
564         char *sample_res;
565
566         if (!iface)
567                 return -EINVAL;
568
569         if (cfg->data_type != MOST_CH_SYNC) {
570                 pr_err("Incompatible channel type\n");
571                 return -EINVAL;
572         }
573
574         if (get_channel(iface, channel_id)) {
575                 pr_err("channel (%s:%d) is already linked\n",
576                        iface->description, channel_id);
577                 return -EINVAL;
578         }
579
580         if (cfg->direction == MOST_CH_TX) {
581                 playback_count = 1;
582                 direction = SNDRV_PCM_STREAM_PLAYBACK;
583         } else {
584                 capture_count = 1;
585                 direction = SNDRV_PCM_STREAM_CAPTURE;
586         }
587
588         ret = split_arg_list(arg_list, &card_name, &ch_num, &sample_res);
589         if (ret < 0)
590                 return ret;
591
592         ret = snd_card_new(&iface->dev, -1, card_name, THIS_MODULE,
593                            sizeof(*channel), &card);
594         if (ret < 0)
595                 return ret;
596
597         channel = card->private_data;
598         channel->card = card;
599         channel->cfg = cfg;
600         channel->iface = iface;
601         channel->id = channel_id;
602         init_waitqueue_head(&channel->playback_waitq);
603
604         ret = audio_set_hw_params(&channel->pcm_hardware, ch_num, sample_res,
605                                   cfg);
606         if (ret)
607                 goto err_free_card;
608
609         snprintf(card->driver, sizeof(card->driver), "%s", DRIVER_NAME);
610         snprintf(card->shortname, sizeof(card->shortname), "Microchip MOST:%d",
611                  card->number);
612         snprintf(card->longname, sizeof(card->longname), "%s at %s, ch %d",
613                  card->shortname, iface->description, channel_id);
614
615         ret = snd_pcm_new(card, card_name, 0, playback_count,
616                           capture_count, &pcm);
617         if (ret < 0)
618                 goto err_free_card;
619
620         pcm->private_data = channel;
621
622         snd_pcm_set_ops(pcm, direction, &pcm_ops);
623
624         ret = snd_card_register(card);
625         if (ret < 0)
626                 goto err_free_card;
627
628         list_add_tail(&channel->list, &dev_list);
629
630         return 0;
631
632 err_free_card:
633         snd_card_free(card);
634         return ret;
635 }
636
637 /**
638  * audio_disconnect_channel - function to disconnect a channel
639  * @iface: pointer to interface instance
640  * @channel_id: channel index
641  *
642  * This frees allocated memory and removes the sound card from ALSA
643  *
644  * Returns 0 on success or error code otherwise.
645  */
646 static int audio_disconnect_channel(struct most_interface *iface,
647                                     int channel_id)
648 {
649         struct channel *channel;
650
651         channel = get_channel(iface, channel_id);
652         if (!channel) {
653                 pr_err("sound_disconnect_channel(), invalid channel %d\n",
654                        channel_id);
655                 return -EINVAL;
656         }
657
658         list_del(&channel->list);
659         snd_card_free(channel->card);
660
661         return 0;
662 }
663
664 /**
665  * audio_rx_completion - completion handler for rx channels
666  * @mbo: pointer to buffer object that has completed
667  *
668  * This searches for the channel this MBO belongs to and copy the data from MBO
669  * to ring buffer
670  *
671  * Returns 0 on success or error code otherwise.
672  */
673 static int audio_rx_completion(struct mbo *mbo)
674 {
675         struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id);
676         bool period_elapsed = false;
677
678         if (!channel) {
679                 pr_err("sound_rx_completion(), invalid channel %d\n",
680                        mbo->hdm_channel_id);
681                 return -EINVAL;
682         }
683
684         if (channel->is_stream_running)
685                 period_elapsed = copy_data(channel, mbo);
686
687         most_put_mbo(mbo);
688
689         if (period_elapsed)
690                 snd_pcm_period_elapsed(channel->substream);
691
692         return 0;
693 }
694
695 /**
696  * audio_tx_completion - completion handler for tx channels
697  * @iface: pointer to interface instance
698  * @channel_id: channel index/ID
699  *
700  * This searches the channel that belongs to this combination of interface
701  * pointer and channel ID and wakes a process sitting in the wait queue of
702  * this channel.
703  *
704  * Returns 0 on success or error code otherwise.
705  */
706 static int audio_tx_completion(struct most_interface *iface, int channel_id)
707 {
708         struct channel *channel = get_channel(iface, channel_id);
709
710         if (!channel) {
711                 pr_err("sound_tx_completion(), invalid channel %d\n",
712                        channel_id);
713                 return -EINVAL;
714         }
715
716         wake_up_interruptible(&channel->playback_waitq);
717
718         return 0;
719 }
720
721 /**
722  * Initialization of the struct core_component
723  */
724 static struct core_component comp = {
725         .name = DRIVER_NAME,
726         .probe_channel = audio_probe_channel,
727         .disconnect_channel = audio_disconnect_channel,
728         .rx_completion = audio_rx_completion,
729         .tx_completion = audio_tx_completion,
730 };
731
732 static int __init audio_init(void)
733 {
734         pr_info("init()\n");
735
736         INIT_LIST_HEAD(&dev_list);
737
738         return most_register_component(&comp);
739 }
740
741 static void __exit audio_exit(void)
742 {
743         struct channel *channel, *tmp;
744
745         pr_info("exit()\n");
746
747         list_for_each_entry_safe(channel, tmp, &dev_list, list) {
748                 list_del(&channel->list);
749                 snd_card_free(channel->card);
750         }
751
752         most_deregister_component(&comp);
753 }
754
755 module_init(audio_init);
756 module_exit(audio_exit);
757
758 MODULE_LICENSE("GPL");
759 MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
760 MODULE_DESCRIPTION("Sound Component Module for Mostcore");