xref: /openbmc/linux/drivers/most/most_snd.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
113b41b57SChristian Gromm // SPDX-License-Identifier: GPL-2.0
213b41b57SChristian Gromm /*
313b41b57SChristian Gromm  * sound.c - Sound component for Mostcore
413b41b57SChristian Gromm  *
513b41b57SChristian Gromm  * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
613b41b57SChristian Gromm  */
713b41b57SChristian Gromm 
813b41b57SChristian Gromm #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
913b41b57SChristian Gromm 
1013b41b57SChristian Gromm #include <linux/module.h>
1113b41b57SChristian Gromm #include <linux/printk.h>
1213b41b57SChristian Gromm #include <linux/kernel.h>
1313b41b57SChristian Gromm #include <linux/slab.h>
1413b41b57SChristian Gromm #include <linux/init.h>
1513b41b57SChristian Gromm #include <sound/core.h>
1613b41b57SChristian Gromm #include <sound/pcm.h>
1713b41b57SChristian Gromm #include <sound/pcm_params.h>
1813b41b57SChristian Gromm #include <linux/sched.h>
1913b41b57SChristian Gromm #include <linux/kthread.h>
2013b41b57SChristian Gromm #include <linux/most.h>
2113b41b57SChristian Gromm 
2213b41b57SChristian Gromm #define DRIVER_NAME "sound"
2313b41b57SChristian Gromm #define STRING_SIZE	80
2413b41b57SChristian Gromm 
2513b41b57SChristian Gromm static struct most_component comp;
2613b41b57SChristian Gromm 
2713b41b57SChristian Gromm /**
2813b41b57SChristian Gromm  * struct channel - private structure to keep channel specific data
2913b41b57SChristian Gromm  * @substream: stores the substream structure
30*fba3993eSRandy Dunlap  * @pcm_hardware: low-level hardware description
3113b41b57SChristian Gromm  * @iface: interface for which the channel belongs to
3213b41b57SChristian Gromm  * @cfg: channel configuration
3313b41b57SChristian Gromm  * @card: registered sound card
3413b41b57SChristian Gromm  * @list: list for private use
3513b41b57SChristian Gromm  * @id: channel index
3613b41b57SChristian Gromm  * @period_pos: current period position (ring buffer)
3713b41b57SChristian Gromm  * @buffer_pos: current buffer position (ring buffer)
3813b41b57SChristian Gromm  * @is_stream_running: identifies whether a stream is running or not
3913b41b57SChristian Gromm  * @opened: set when the stream is opened
4013b41b57SChristian Gromm  * @playback_task: playback thread
4113b41b57SChristian Gromm  * @playback_waitq: waitq used by playback thread
42*fba3993eSRandy Dunlap  * @copy_fn: copy function for PCM-specific format and width
4313b41b57SChristian Gromm  */
4413b41b57SChristian Gromm struct channel {
4513b41b57SChristian Gromm 	struct snd_pcm_substream *substream;
4613b41b57SChristian Gromm 	struct snd_pcm_hardware pcm_hardware;
4713b41b57SChristian Gromm 	struct most_interface *iface;
4813b41b57SChristian Gromm 	struct most_channel_config *cfg;
4913b41b57SChristian Gromm 	struct snd_card *card;
5013b41b57SChristian Gromm 	struct list_head list;
5113b41b57SChristian Gromm 	int id;
5213b41b57SChristian Gromm 	unsigned int period_pos;
5313b41b57SChristian Gromm 	unsigned int buffer_pos;
5413b41b57SChristian Gromm 	bool is_stream_running;
5513b41b57SChristian Gromm 	struct task_struct *playback_task;
5613b41b57SChristian Gromm 	wait_queue_head_t playback_waitq;
5713b41b57SChristian Gromm 	void (*copy_fn)(void *alsa, void *most, unsigned int bytes);
5813b41b57SChristian Gromm };
5913b41b57SChristian Gromm 
6013b41b57SChristian Gromm struct sound_adapter {
6113b41b57SChristian Gromm 	struct list_head dev_list;
6213b41b57SChristian Gromm 	struct most_interface *iface;
6313b41b57SChristian Gromm 	struct snd_card *card;
6413b41b57SChristian Gromm 	struct list_head list;
6513b41b57SChristian Gromm 	bool registered;
6613b41b57SChristian Gromm 	int pcm_dev_idx;
6713b41b57SChristian Gromm };
6813b41b57SChristian Gromm 
6913b41b57SChristian Gromm static struct list_head adpt_list;
7013b41b57SChristian Gromm 
7113b41b57SChristian Gromm #define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
7213b41b57SChristian Gromm 		       SNDRV_PCM_INFO_MMAP_VALID | \
7313b41b57SChristian Gromm 		       SNDRV_PCM_INFO_BATCH | \
7413b41b57SChristian Gromm 		       SNDRV_PCM_INFO_INTERLEAVED | \
7513b41b57SChristian Gromm 		       SNDRV_PCM_INFO_BLOCK_TRANSFER)
7613b41b57SChristian Gromm 
swap_copy16(u16 * dest,const u16 * source,unsigned int bytes)7713b41b57SChristian Gromm static void swap_copy16(u16 *dest, const u16 *source, unsigned int bytes)
7813b41b57SChristian Gromm {
7913b41b57SChristian Gromm 	unsigned int i = 0;
8013b41b57SChristian Gromm 
8113b41b57SChristian Gromm 	while (i < (bytes / 2)) {
8213b41b57SChristian Gromm 		dest[i] = swab16(source[i]);
8313b41b57SChristian Gromm 		i++;
8413b41b57SChristian Gromm 	}
8513b41b57SChristian Gromm }
8613b41b57SChristian Gromm 
swap_copy24(u8 * dest,const u8 * source,unsigned int bytes)8713b41b57SChristian Gromm static void swap_copy24(u8 *dest, const u8 *source, unsigned int bytes)
8813b41b57SChristian Gromm {
8913b41b57SChristian Gromm 	unsigned int i = 0;
9013b41b57SChristian Gromm 
9113b41b57SChristian Gromm 	if (bytes < 2)
9213b41b57SChristian Gromm 		return;
9313b41b57SChristian Gromm 	while (i < bytes - 2) {
9413b41b57SChristian Gromm 		dest[i] = source[i + 2];
9513b41b57SChristian Gromm 		dest[i + 1] = source[i + 1];
9613b41b57SChristian Gromm 		dest[i + 2] = source[i];
9713b41b57SChristian Gromm 		i += 3;
9813b41b57SChristian Gromm 	}
9913b41b57SChristian Gromm }
10013b41b57SChristian Gromm 
swap_copy32(u32 * dest,const u32 * source,unsigned int bytes)10113b41b57SChristian Gromm static void swap_copy32(u32 *dest, const u32 *source, unsigned int bytes)
10213b41b57SChristian Gromm {
10313b41b57SChristian Gromm 	unsigned int i = 0;
10413b41b57SChristian Gromm 
10513b41b57SChristian Gromm 	while (i < bytes / 4) {
10613b41b57SChristian Gromm 		dest[i] = swab32(source[i]);
10713b41b57SChristian Gromm 		i++;
10813b41b57SChristian Gromm 	}
10913b41b57SChristian Gromm }
11013b41b57SChristian Gromm 
alsa_to_most_memcpy(void * alsa,void * most,unsigned int bytes)11113b41b57SChristian Gromm static void alsa_to_most_memcpy(void *alsa, void *most, unsigned int bytes)
11213b41b57SChristian Gromm {
11313b41b57SChristian Gromm 	memcpy(most, alsa, bytes);
11413b41b57SChristian Gromm }
11513b41b57SChristian Gromm 
alsa_to_most_copy16(void * alsa,void * most,unsigned int bytes)11613b41b57SChristian Gromm static void alsa_to_most_copy16(void *alsa, void *most, unsigned int bytes)
11713b41b57SChristian Gromm {
11813b41b57SChristian Gromm 	swap_copy16(most, alsa, bytes);
11913b41b57SChristian Gromm }
12013b41b57SChristian Gromm 
alsa_to_most_copy24(void * alsa,void * most,unsigned int bytes)12113b41b57SChristian Gromm static void alsa_to_most_copy24(void *alsa, void *most, unsigned int bytes)
12213b41b57SChristian Gromm {
12313b41b57SChristian Gromm 	swap_copy24(most, alsa, bytes);
12413b41b57SChristian Gromm }
12513b41b57SChristian Gromm 
alsa_to_most_copy32(void * alsa,void * most,unsigned int bytes)12613b41b57SChristian Gromm static void alsa_to_most_copy32(void *alsa, void *most, unsigned int bytes)
12713b41b57SChristian Gromm {
12813b41b57SChristian Gromm 	swap_copy32(most, alsa, bytes);
12913b41b57SChristian Gromm }
13013b41b57SChristian Gromm 
most_to_alsa_memcpy(void * alsa,void * most,unsigned int bytes)13113b41b57SChristian Gromm static void most_to_alsa_memcpy(void *alsa, void *most, unsigned int bytes)
13213b41b57SChristian Gromm {
13313b41b57SChristian Gromm 	memcpy(alsa, most, bytes);
13413b41b57SChristian Gromm }
13513b41b57SChristian Gromm 
most_to_alsa_copy16(void * alsa,void * most,unsigned int bytes)13613b41b57SChristian Gromm static void most_to_alsa_copy16(void *alsa, void *most, unsigned int bytes)
13713b41b57SChristian Gromm {
13813b41b57SChristian Gromm 	swap_copy16(alsa, most, bytes);
13913b41b57SChristian Gromm }
14013b41b57SChristian Gromm 
most_to_alsa_copy24(void * alsa,void * most,unsigned int bytes)14113b41b57SChristian Gromm static void most_to_alsa_copy24(void *alsa, void *most, unsigned int bytes)
14213b41b57SChristian Gromm {
14313b41b57SChristian Gromm 	swap_copy24(alsa, most, bytes);
14413b41b57SChristian Gromm }
14513b41b57SChristian Gromm 
most_to_alsa_copy32(void * alsa,void * most,unsigned int bytes)14613b41b57SChristian Gromm static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes)
14713b41b57SChristian Gromm {
14813b41b57SChristian Gromm 	swap_copy32(alsa, most, bytes);
14913b41b57SChristian Gromm }
15013b41b57SChristian Gromm 
15113b41b57SChristian Gromm /**
15213b41b57SChristian Gromm  * get_channel - get pointer to channel
15313b41b57SChristian Gromm  * @iface: interface structure
15413b41b57SChristian Gromm  * @channel_id: channel ID
15513b41b57SChristian Gromm  *
15613b41b57SChristian Gromm  * This traverses the channel list and returns the channel matching the
15713b41b57SChristian Gromm  * ID and interface.
15813b41b57SChristian Gromm  *
15913b41b57SChristian Gromm  * Returns pointer to channel on success or NULL otherwise.
16013b41b57SChristian Gromm  */
get_channel(struct most_interface * iface,int channel_id)16113b41b57SChristian Gromm static struct channel *get_channel(struct most_interface *iface,
16213b41b57SChristian Gromm 				   int channel_id)
16313b41b57SChristian Gromm {
16413b41b57SChristian Gromm 	struct sound_adapter *adpt = iface->priv;
16513b41b57SChristian Gromm 	struct channel *channel;
16613b41b57SChristian Gromm 
16713b41b57SChristian Gromm 	list_for_each_entry(channel, &adpt->dev_list, list) {
16813b41b57SChristian Gromm 		if ((channel->iface == iface) && (channel->id == channel_id))
16913b41b57SChristian Gromm 			return channel;
17013b41b57SChristian Gromm 	}
17113b41b57SChristian Gromm 	return NULL;
17213b41b57SChristian Gromm }
17313b41b57SChristian Gromm 
17413b41b57SChristian Gromm /**
17513b41b57SChristian Gromm  * copy_data - implements data copying function
17613b41b57SChristian Gromm  * @channel: channel
17713b41b57SChristian Gromm  * @mbo: MBO from core
17813b41b57SChristian Gromm  *
17913b41b57SChristian Gromm  * Copy data from/to ring buffer to/from MBO and update the buffer position
18013b41b57SChristian Gromm  */
copy_data(struct channel * channel,struct mbo * mbo)18113b41b57SChristian Gromm static bool copy_data(struct channel *channel, struct mbo *mbo)
18213b41b57SChristian Gromm {
18313b41b57SChristian Gromm 	struct snd_pcm_runtime *const runtime = channel->substream->runtime;
18413b41b57SChristian Gromm 	unsigned int const frame_bytes = channel->cfg->subbuffer_size;
18513b41b57SChristian Gromm 	unsigned int const buffer_size = runtime->buffer_size;
18613b41b57SChristian Gromm 	unsigned int frames;
18713b41b57SChristian Gromm 	unsigned int fr0;
18813b41b57SChristian Gromm 
18913b41b57SChristian Gromm 	if (channel->cfg->direction & MOST_CH_RX)
19013b41b57SChristian Gromm 		frames = mbo->processed_length / frame_bytes;
19113b41b57SChristian Gromm 	else
19213b41b57SChristian Gromm 		frames = mbo->buffer_length / frame_bytes;
19313b41b57SChristian Gromm 	fr0 = min(buffer_size - channel->buffer_pos, frames);
19413b41b57SChristian Gromm 
19513b41b57SChristian Gromm 	channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes,
19613b41b57SChristian Gromm 			 mbo->virt_address,
19713b41b57SChristian Gromm 			 fr0 * frame_bytes);
19813b41b57SChristian Gromm 
19913b41b57SChristian Gromm 	if (frames > fr0) {
20013b41b57SChristian Gromm 		/* wrap around at end of ring buffer */
20113b41b57SChristian Gromm 		channel->copy_fn(runtime->dma_area,
20213b41b57SChristian Gromm 				 mbo->virt_address + fr0 * frame_bytes,
20313b41b57SChristian Gromm 				 (frames - fr0) * frame_bytes);
20413b41b57SChristian Gromm 	}
20513b41b57SChristian Gromm 
20613b41b57SChristian Gromm 	channel->buffer_pos += frames;
20713b41b57SChristian Gromm 	if (channel->buffer_pos >= buffer_size)
20813b41b57SChristian Gromm 		channel->buffer_pos -= buffer_size;
20913b41b57SChristian Gromm 	channel->period_pos += frames;
21013b41b57SChristian Gromm 	if (channel->period_pos >= runtime->period_size) {
21113b41b57SChristian Gromm 		channel->period_pos -= runtime->period_size;
21213b41b57SChristian Gromm 		return true;
21313b41b57SChristian Gromm 	}
21413b41b57SChristian Gromm 	return false;
21513b41b57SChristian Gromm }
21613b41b57SChristian Gromm 
21713b41b57SChristian Gromm /**
21813b41b57SChristian Gromm  * playback_thread - function implements the playback thread
21913b41b57SChristian Gromm  * @data: private data
22013b41b57SChristian Gromm  *
22113b41b57SChristian Gromm  * Thread which does the playback functionality in a loop. It waits for a free
22213b41b57SChristian Gromm  * MBO from mostcore for a particular channel and copy the data from ring buffer
22313b41b57SChristian Gromm  * to MBO. Submit the MBO back to mostcore, after copying the data.
22413b41b57SChristian Gromm  *
22513b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
22613b41b57SChristian Gromm  */
playback_thread(void * data)22713b41b57SChristian Gromm static int playback_thread(void *data)
22813b41b57SChristian Gromm {
22913b41b57SChristian Gromm 	struct channel *const channel = data;
23013b41b57SChristian Gromm 
23113b41b57SChristian Gromm 	while (!kthread_should_stop()) {
23213b41b57SChristian Gromm 		struct mbo *mbo = NULL;
23313b41b57SChristian Gromm 		bool period_elapsed = false;
23413b41b57SChristian Gromm 
23513b41b57SChristian Gromm 		wait_event_interruptible(
23613b41b57SChristian Gromm 			channel->playback_waitq,
23713b41b57SChristian Gromm 			kthread_should_stop() ||
23813b41b57SChristian Gromm 			(channel->is_stream_running &&
23913b41b57SChristian Gromm 			 (mbo = most_get_mbo(channel->iface, channel->id,
24013b41b57SChristian Gromm 					     &comp))));
24113b41b57SChristian Gromm 		if (!mbo)
24213b41b57SChristian Gromm 			continue;
24313b41b57SChristian Gromm 
24413b41b57SChristian Gromm 		if (channel->is_stream_running)
24513b41b57SChristian Gromm 			period_elapsed = copy_data(channel, mbo);
24613b41b57SChristian Gromm 		else
24713b41b57SChristian Gromm 			memset(mbo->virt_address, 0, mbo->buffer_length);
24813b41b57SChristian Gromm 
24913b41b57SChristian Gromm 		most_submit_mbo(mbo);
25013b41b57SChristian Gromm 		if (period_elapsed)
25113b41b57SChristian Gromm 			snd_pcm_period_elapsed(channel->substream);
25213b41b57SChristian Gromm 	}
25313b41b57SChristian Gromm 	return 0;
25413b41b57SChristian Gromm }
25513b41b57SChristian Gromm 
25613b41b57SChristian Gromm /**
25713b41b57SChristian Gromm  * pcm_open - implements open callback function for PCM middle layer
25813b41b57SChristian Gromm  * @substream: pointer to ALSA PCM substream
25913b41b57SChristian Gromm  *
26013b41b57SChristian Gromm  * This is called when a PCM substream is opened. At least, the function should
26113b41b57SChristian Gromm  * initialize the runtime->hw record.
26213b41b57SChristian Gromm  *
26313b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
26413b41b57SChristian Gromm  */
pcm_open(struct snd_pcm_substream * substream)26513b41b57SChristian Gromm static int pcm_open(struct snd_pcm_substream *substream)
26613b41b57SChristian Gromm {
26713b41b57SChristian Gromm 	struct channel *channel = substream->private_data;
26813b41b57SChristian Gromm 	struct snd_pcm_runtime *runtime = substream->runtime;
26913b41b57SChristian Gromm 	struct most_channel_config *cfg = channel->cfg;
27013b41b57SChristian Gromm 	int ret;
27113b41b57SChristian Gromm 
27213b41b57SChristian Gromm 	channel->substream = substream;
27313b41b57SChristian Gromm 
27413b41b57SChristian Gromm 	if (cfg->direction == MOST_CH_TX) {
27513b41b57SChristian Gromm 		channel->playback_task = kthread_run(playback_thread, channel,
27613b41b57SChristian Gromm 						     "most_audio_playback");
27713b41b57SChristian Gromm 		if (IS_ERR(channel->playback_task)) {
27813b41b57SChristian Gromm 			pr_err("Couldn't start thread\n");
27913b41b57SChristian Gromm 			return PTR_ERR(channel->playback_task);
28013b41b57SChristian Gromm 		}
28113b41b57SChristian Gromm 	}
28213b41b57SChristian Gromm 
28313b41b57SChristian Gromm 	ret = most_start_channel(channel->iface, channel->id, &comp);
28413b41b57SChristian Gromm 	if (ret) {
28513b41b57SChristian Gromm 		pr_err("most_start_channel() failed!\n");
28613b41b57SChristian Gromm 		if (cfg->direction == MOST_CH_TX)
28713b41b57SChristian Gromm 			kthread_stop(channel->playback_task);
28813b41b57SChristian Gromm 		return ret;
28913b41b57SChristian Gromm 	}
29013b41b57SChristian Gromm 
29113b41b57SChristian Gromm 	runtime->hw = channel->pcm_hardware;
29213b41b57SChristian Gromm 	return 0;
29313b41b57SChristian Gromm }
29413b41b57SChristian Gromm 
29513b41b57SChristian Gromm /**
29613b41b57SChristian Gromm  * pcm_close - implements close callback function for PCM middle layer
29713b41b57SChristian Gromm  * @substream: sub-stream pointer
29813b41b57SChristian Gromm  *
29913b41b57SChristian Gromm  * Obviously, this is called when a PCM substream is closed. Any private
30013b41b57SChristian Gromm  * instance for a PCM substream allocated in the open callback will be
30113b41b57SChristian Gromm  * released here.
30213b41b57SChristian Gromm  *
30313b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
30413b41b57SChristian Gromm  */
pcm_close(struct snd_pcm_substream * substream)30513b41b57SChristian Gromm static int pcm_close(struct snd_pcm_substream *substream)
30613b41b57SChristian Gromm {
30713b41b57SChristian Gromm 	struct channel *channel = substream->private_data;
30813b41b57SChristian Gromm 
30913b41b57SChristian Gromm 	if (channel->cfg->direction == MOST_CH_TX)
31013b41b57SChristian Gromm 		kthread_stop(channel->playback_task);
31113b41b57SChristian Gromm 	most_stop_channel(channel->iface, channel->id, &comp);
31213b41b57SChristian Gromm 	return 0;
31313b41b57SChristian Gromm }
31413b41b57SChristian Gromm 
31513b41b57SChristian Gromm /**
31613b41b57SChristian Gromm  * pcm_prepare - implements prepare callback function for PCM middle layer
31713b41b57SChristian Gromm  * @substream: substream pointer
31813b41b57SChristian Gromm  *
31913b41b57SChristian Gromm  * This callback is called when the PCM is "prepared". Format rate, sample rate,
32013b41b57SChristian Gromm  * etc., can be set here. This callback can be called many times at each setup.
32113b41b57SChristian Gromm  *
32213b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
32313b41b57SChristian Gromm  */
pcm_prepare(struct snd_pcm_substream * substream)32413b41b57SChristian Gromm static int pcm_prepare(struct snd_pcm_substream *substream)
32513b41b57SChristian Gromm {
32613b41b57SChristian Gromm 	struct channel *channel = substream->private_data;
32713b41b57SChristian Gromm 	struct snd_pcm_runtime *runtime = substream->runtime;
32813b41b57SChristian Gromm 	struct most_channel_config *cfg = channel->cfg;
32913b41b57SChristian Gromm 	int width = snd_pcm_format_physical_width(runtime->format);
33013b41b57SChristian Gromm 
33113b41b57SChristian Gromm 	channel->copy_fn = NULL;
33213b41b57SChristian Gromm 
33313b41b57SChristian Gromm 	if (cfg->direction == MOST_CH_TX) {
33413b41b57SChristian Gromm 		if (snd_pcm_format_big_endian(runtime->format) || width == 8)
33513b41b57SChristian Gromm 			channel->copy_fn = alsa_to_most_memcpy;
33613b41b57SChristian Gromm 		else if (width == 16)
33713b41b57SChristian Gromm 			channel->copy_fn = alsa_to_most_copy16;
33813b41b57SChristian Gromm 		else if (width == 24)
33913b41b57SChristian Gromm 			channel->copy_fn = alsa_to_most_copy24;
34013b41b57SChristian Gromm 		else if (width == 32)
34113b41b57SChristian Gromm 			channel->copy_fn = alsa_to_most_copy32;
34213b41b57SChristian Gromm 	} else {
34313b41b57SChristian Gromm 		if (snd_pcm_format_big_endian(runtime->format) || width == 8)
34413b41b57SChristian Gromm 			channel->copy_fn = most_to_alsa_memcpy;
34513b41b57SChristian Gromm 		else if (width == 16)
34613b41b57SChristian Gromm 			channel->copy_fn = most_to_alsa_copy16;
34713b41b57SChristian Gromm 		else if (width == 24)
34813b41b57SChristian Gromm 			channel->copy_fn = most_to_alsa_copy24;
34913b41b57SChristian Gromm 		else if (width == 32)
35013b41b57SChristian Gromm 			channel->copy_fn = most_to_alsa_copy32;
35113b41b57SChristian Gromm 	}
35213b41b57SChristian Gromm 
35313b41b57SChristian Gromm 	if (!channel->copy_fn)
35413b41b57SChristian Gromm 		return -EINVAL;
35513b41b57SChristian Gromm 	channel->period_pos = 0;
35613b41b57SChristian Gromm 	channel->buffer_pos = 0;
35713b41b57SChristian Gromm 	return 0;
35813b41b57SChristian Gromm }
35913b41b57SChristian Gromm 
36013b41b57SChristian Gromm /**
36113b41b57SChristian Gromm  * pcm_trigger - implements trigger callback function for PCM middle layer
36213b41b57SChristian Gromm  * @substream: substream pointer
36313b41b57SChristian Gromm  * @cmd: action to perform
36413b41b57SChristian Gromm  *
36513b41b57SChristian Gromm  * This is called when the PCM is started, stopped or paused. The action will be
36613b41b57SChristian Gromm  * specified in the second argument, SNDRV_PCM_TRIGGER_XXX
36713b41b57SChristian Gromm  *
36813b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
36913b41b57SChristian Gromm  */
pcm_trigger(struct snd_pcm_substream * substream,int cmd)37013b41b57SChristian Gromm static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
37113b41b57SChristian Gromm {
37213b41b57SChristian Gromm 	struct channel *channel = substream->private_data;
37313b41b57SChristian Gromm 
37413b41b57SChristian Gromm 	switch (cmd) {
37513b41b57SChristian Gromm 	case SNDRV_PCM_TRIGGER_START:
37613b41b57SChristian Gromm 		channel->is_stream_running = true;
37713b41b57SChristian Gromm 		wake_up_interruptible(&channel->playback_waitq);
37813b41b57SChristian Gromm 		return 0;
37913b41b57SChristian Gromm 
38013b41b57SChristian Gromm 	case SNDRV_PCM_TRIGGER_STOP:
38113b41b57SChristian Gromm 		channel->is_stream_running = false;
38213b41b57SChristian Gromm 		return 0;
38313b41b57SChristian Gromm 
38413b41b57SChristian Gromm 	default:
38513b41b57SChristian Gromm 		return -EINVAL;
38613b41b57SChristian Gromm 	}
38713b41b57SChristian Gromm 	return 0;
38813b41b57SChristian Gromm }
38913b41b57SChristian Gromm 
39013b41b57SChristian Gromm /**
39113b41b57SChristian Gromm  * pcm_pointer - implements pointer callback function for PCM middle layer
39213b41b57SChristian Gromm  * @substream: substream pointer
39313b41b57SChristian Gromm  *
39413b41b57SChristian Gromm  * This callback is called when the PCM middle layer inquires the current
39513b41b57SChristian Gromm  * hardware position on the buffer. The position must be returned in frames,
39613b41b57SChristian Gromm  * ranging from 0 to buffer_size-1.
39713b41b57SChristian Gromm  */
pcm_pointer(struct snd_pcm_substream * substream)39813b41b57SChristian Gromm static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
39913b41b57SChristian Gromm {
40013b41b57SChristian Gromm 	struct channel *channel = substream->private_data;
40113b41b57SChristian Gromm 
40213b41b57SChristian Gromm 	return channel->buffer_pos;
40313b41b57SChristian Gromm }
40413b41b57SChristian Gromm 
405*fba3993eSRandy Dunlap /*
40613b41b57SChristian Gromm  * Initialization of struct snd_pcm_ops
40713b41b57SChristian Gromm  */
40813b41b57SChristian Gromm static const struct snd_pcm_ops pcm_ops = {
40913b41b57SChristian Gromm 	.open       = pcm_open,
41013b41b57SChristian Gromm 	.close      = pcm_close,
41113b41b57SChristian Gromm 	.prepare    = pcm_prepare,
41213b41b57SChristian Gromm 	.trigger    = pcm_trigger,
41313b41b57SChristian Gromm 	.pointer    = pcm_pointer,
41413b41b57SChristian Gromm };
41513b41b57SChristian Gromm 
split_arg_list(char * buf,u16 * ch_num,char ** sample_res)41613b41b57SChristian Gromm static int split_arg_list(char *buf, u16 *ch_num, char **sample_res)
41713b41b57SChristian Gromm {
41813b41b57SChristian Gromm 	char *num;
41913b41b57SChristian Gromm 	int ret;
42013b41b57SChristian Gromm 
42113b41b57SChristian Gromm 	num = strsep(&buf, "x");
42213b41b57SChristian Gromm 	if (!num)
42313b41b57SChristian Gromm 		goto err;
42413b41b57SChristian Gromm 	ret = kstrtou16(num, 0, ch_num);
42513b41b57SChristian Gromm 	if (ret)
42613b41b57SChristian Gromm 		goto err;
42713b41b57SChristian Gromm 	*sample_res = strsep(&buf, ".\n");
42813b41b57SChristian Gromm 	if (!*sample_res)
42913b41b57SChristian Gromm 		goto err;
43013b41b57SChristian Gromm 	return 0;
43113b41b57SChristian Gromm 
43213b41b57SChristian Gromm err:
43313b41b57SChristian Gromm 	pr_err("Bad PCM format\n");
43413b41b57SChristian Gromm 	return -EINVAL;
43513b41b57SChristian Gromm }
43613b41b57SChristian Gromm 
43713b41b57SChristian Gromm static const struct sample_resolution_info {
43813b41b57SChristian Gromm 	const char *sample_res;
43913b41b57SChristian Gromm 	int bytes;
44013b41b57SChristian Gromm 	u64 formats;
44113b41b57SChristian Gromm } sinfo[] = {
44213b41b57SChristian Gromm 	{ "8", 1, SNDRV_PCM_FMTBIT_S8 },
44313b41b57SChristian Gromm 	{ "16", 2, SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE },
44413b41b57SChristian Gromm 	{ "24", 3, SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE },
44513b41b57SChristian Gromm 	{ "32", 4, SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE },
44613b41b57SChristian Gromm };
44713b41b57SChristian Gromm 
audio_set_hw_params(struct snd_pcm_hardware * pcm_hw,u16 ch_num,char * sample_res,struct most_channel_config * cfg)44813b41b57SChristian Gromm static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw,
44913b41b57SChristian Gromm 			       u16 ch_num, char *sample_res,
45013b41b57SChristian Gromm 			       struct most_channel_config *cfg)
45113b41b57SChristian Gromm {
45213b41b57SChristian Gromm 	int i;
45313b41b57SChristian Gromm 
45413b41b57SChristian Gromm 	for (i = 0; i < ARRAY_SIZE(sinfo); i++) {
45513b41b57SChristian Gromm 		if (!strcmp(sample_res, sinfo[i].sample_res))
45613b41b57SChristian Gromm 			goto found;
45713b41b57SChristian Gromm 	}
45813b41b57SChristian Gromm 	pr_err("Unsupported PCM format\n");
45913b41b57SChristian Gromm 	return -EINVAL;
46013b41b57SChristian Gromm 
46113b41b57SChristian Gromm found:
46213b41b57SChristian Gromm 	if (!ch_num) {
46313b41b57SChristian Gromm 		pr_err("Bad number of channels\n");
46413b41b57SChristian Gromm 		return -EINVAL;
46513b41b57SChristian Gromm 	}
46613b41b57SChristian Gromm 
46713b41b57SChristian Gromm 	if (cfg->subbuffer_size != ch_num * sinfo[i].bytes) {
46813b41b57SChristian Gromm 		pr_err("Audio resolution doesn't fit subbuffer size\n");
46913b41b57SChristian Gromm 		return -EINVAL;
47013b41b57SChristian Gromm 	}
47113b41b57SChristian Gromm 
47213b41b57SChristian Gromm 	pcm_hw->info = MOST_PCM_INFO;
47313b41b57SChristian Gromm 	pcm_hw->rates = SNDRV_PCM_RATE_48000;
47413b41b57SChristian Gromm 	pcm_hw->rate_min = 48000;
47513b41b57SChristian Gromm 	pcm_hw->rate_max = 48000;
47613b41b57SChristian Gromm 	pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size;
47713b41b57SChristian Gromm 	pcm_hw->period_bytes_min = cfg->buffer_size;
47813b41b57SChristian Gromm 	pcm_hw->period_bytes_max = cfg->buffer_size;
47913b41b57SChristian Gromm 	pcm_hw->periods_min = 1;
48013b41b57SChristian Gromm 	pcm_hw->periods_max = cfg->num_buffers;
48113b41b57SChristian Gromm 	pcm_hw->channels_min = ch_num;
48213b41b57SChristian Gromm 	pcm_hw->channels_max = ch_num;
48313b41b57SChristian Gromm 	pcm_hw->formats = sinfo[i].formats;
48413b41b57SChristian Gromm 	return 0;
48513b41b57SChristian Gromm }
48613b41b57SChristian Gromm 
release_adapter(struct sound_adapter * adpt)48713b41b57SChristian Gromm static void release_adapter(struct sound_adapter *adpt)
48813b41b57SChristian Gromm {
48913b41b57SChristian Gromm 	struct channel *channel, *tmp;
49013b41b57SChristian Gromm 
49113b41b57SChristian Gromm 	list_for_each_entry_safe(channel, tmp, &adpt->dev_list, list) {
49213b41b57SChristian Gromm 		list_del(&channel->list);
49313b41b57SChristian Gromm 		kfree(channel);
49413b41b57SChristian Gromm 	}
49513b41b57SChristian Gromm 	if (adpt->card)
49613b41b57SChristian Gromm 		snd_card_free(adpt->card);
49713b41b57SChristian Gromm 	list_del(&adpt->list);
49813b41b57SChristian Gromm 	kfree(adpt);
49913b41b57SChristian Gromm }
50013b41b57SChristian Gromm 
50113b41b57SChristian Gromm /**
50213b41b57SChristian Gromm  * audio_probe_channel - probe function of the driver module
50313b41b57SChristian Gromm  * @iface: pointer to interface instance
50413b41b57SChristian Gromm  * @channel_id: channel index/ID
50513b41b57SChristian Gromm  * @cfg: pointer to actual channel configuration
506*fba3993eSRandy Dunlap  * @device_name: name of the device to be created in /dev
507*fba3993eSRandy Dunlap  * @arg_list: string that provides the desired audio resolution
50813b41b57SChristian Gromm  *
50913b41b57SChristian Gromm  * Creates sound card, pcm device, sets pcm ops and registers sound card.
51013b41b57SChristian Gromm  *
51113b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
51213b41b57SChristian Gromm  */
audio_probe_channel(struct most_interface * iface,int channel_id,struct most_channel_config * cfg,char * device_name,char * arg_list)51313b41b57SChristian Gromm static int audio_probe_channel(struct most_interface *iface, int channel_id,
51413b41b57SChristian Gromm 			       struct most_channel_config *cfg,
51513b41b57SChristian Gromm 			       char *device_name, char *arg_list)
51613b41b57SChristian Gromm {
51713b41b57SChristian Gromm 	struct channel *channel;
51813b41b57SChristian Gromm 	struct sound_adapter *adpt;
51913b41b57SChristian Gromm 	struct snd_pcm *pcm;
52013b41b57SChristian Gromm 	int playback_count = 0;
52113b41b57SChristian Gromm 	int capture_count = 0;
52213b41b57SChristian Gromm 	int ret;
52313b41b57SChristian Gromm 	int direction;
52413b41b57SChristian Gromm 	u16 ch_num;
52513b41b57SChristian Gromm 	char *sample_res;
52613b41b57SChristian Gromm 	char arg_list_cpy[STRING_SIZE];
52713b41b57SChristian Gromm 
52813b41b57SChristian Gromm 	if (cfg->data_type != MOST_CH_SYNC) {
52913b41b57SChristian Gromm 		pr_err("Incompatible channel type\n");
53013b41b57SChristian Gromm 		return -EINVAL;
53113b41b57SChristian Gromm 	}
53213b41b57SChristian Gromm 	strscpy(arg_list_cpy, arg_list, STRING_SIZE);
53313b41b57SChristian Gromm 	ret = split_arg_list(arg_list_cpy, &ch_num, &sample_res);
53413b41b57SChristian Gromm 	if (ret < 0)
53513b41b57SChristian Gromm 		return ret;
53613b41b57SChristian Gromm 
53713b41b57SChristian Gromm 	list_for_each_entry(adpt, &adpt_list, list) {
53813b41b57SChristian Gromm 		if (adpt->iface != iface)
53913b41b57SChristian Gromm 			continue;
54013b41b57SChristian Gromm 		if (adpt->registered)
54113b41b57SChristian Gromm 			return -ENOSPC;
54213b41b57SChristian Gromm 		adpt->pcm_dev_idx++;
54313b41b57SChristian Gromm 		goto skip_adpt_alloc;
54413b41b57SChristian Gromm 	}
54513b41b57SChristian Gromm 	adpt = kzalloc(sizeof(*adpt), GFP_KERNEL);
54613b41b57SChristian Gromm 	if (!adpt)
54713b41b57SChristian Gromm 		return -ENOMEM;
54813b41b57SChristian Gromm 
54913b41b57SChristian Gromm 	adpt->iface = iface;
55013b41b57SChristian Gromm 	INIT_LIST_HEAD(&adpt->dev_list);
55113b41b57SChristian Gromm 	iface->priv = adpt;
55213b41b57SChristian Gromm 	list_add_tail(&adpt->list, &adpt_list);
55313b41b57SChristian Gromm 	ret = snd_card_new(iface->driver_dev, -1, "INIC", THIS_MODULE,
55413b41b57SChristian Gromm 			   sizeof(*channel), &adpt->card);
55513b41b57SChristian Gromm 	if (ret < 0)
55613b41b57SChristian Gromm 		goto err_free_adpt;
55713b41b57SChristian Gromm 	snprintf(adpt->card->driver, sizeof(adpt->card->driver),
55813b41b57SChristian Gromm 		 "%s", DRIVER_NAME);
55913b41b57SChristian Gromm 	snprintf(adpt->card->shortname, sizeof(adpt->card->shortname),
56013b41b57SChristian Gromm 		 "Microchip INIC");
56113b41b57SChristian Gromm 	snprintf(adpt->card->longname, sizeof(adpt->card->longname),
56213b41b57SChristian Gromm 		 "%s at %s", adpt->card->shortname, iface->description);
56313b41b57SChristian Gromm skip_adpt_alloc:
56413b41b57SChristian Gromm 	if (get_channel(iface, channel_id)) {
56513b41b57SChristian Gromm 		pr_err("channel (%s:%d) is already linked\n",
56613b41b57SChristian Gromm 		       iface->description, channel_id);
56713b41b57SChristian Gromm 		return -EEXIST;
56813b41b57SChristian Gromm 	}
56913b41b57SChristian Gromm 
57013b41b57SChristian Gromm 	if (cfg->direction == MOST_CH_TX) {
57113b41b57SChristian Gromm 		playback_count = 1;
57213b41b57SChristian Gromm 		direction = SNDRV_PCM_STREAM_PLAYBACK;
57313b41b57SChristian Gromm 	} else {
57413b41b57SChristian Gromm 		capture_count = 1;
57513b41b57SChristian Gromm 		direction = SNDRV_PCM_STREAM_CAPTURE;
57613b41b57SChristian Gromm 	}
57713b41b57SChristian Gromm 	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
57813b41b57SChristian Gromm 	if (!channel) {
57913b41b57SChristian Gromm 		ret = -ENOMEM;
58013b41b57SChristian Gromm 		goto err_free_adpt;
58113b41b57SChristian Gromm 	}
58213b41b57SChristian Gromm 	channel->card = adpt->card;
58313b41b57SChristian Gromm 	channel->cfg = cfg;
58413b41b57SChristian Gromm 	channel->iface = iface;
58513b41b57SChristian Gromm 	channel->id = channel_id;
58613b41b57SChristian Gromm 	init_waitqueue_head(&channel->playback_waitq);
58713b41b57SChristian Gromm 	list_add_tail(&channel->list, &adpt->dev_list);
58813b41b57SChristian Gromm 
58913b41b57SChristian Gromm 	ret = audio_set_hw_params(&channel->pcm_hardware, ch_num, sample_res,
59013b41b57SChristian Gromm 				  cfg);
59113b41b57SChristian Gromm 	if (ret)
59213b41b57SChristian Gromm 		goto err_free_adpt;
59313b41b57SChristian Gromm 
59413b41b57SChristian Gromm 	ret = snd_pcm_new(adpt->card, device_name, adpt->pcm_dev_idx,
59513b41b57SChristian Gromm 			  playback_count, capture_count, &pcm);
59613b41b57SChristian Gromm 
59713b41b57SChristian Gromm 	if (ret < 0)
59813b41b57SChristian Gromm 		goto err_free_adpt;
59913b41b57SChristian Gromm 
60013b41b57SChristian Gromm 	pcm->private_data = channel;
60113b41b57SChristian Gromm 	strscpy(pcm->name, device_name, sizeof(pcm->name));
60213b41b57SChristian Gromm 	snd_pcm_set_ops(pcm, direction, &pcm_ops);
60313b41b57SChristian Gromm 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
60413b41b57SChristian Gromm 	return 0;
60513b41b57SChristian Gromm 
60613b41b57SChristian Gromm err_free_adpt:
60713b41b57SChristian Gromm 	release_adapter(adpt);
60813b41b57SChristian Gromm 	return ret;
60913b41b57SChristian Gromm }
61013b41b57SChristian Gromm 
audio_create_sound_card(void)61113b41b57SChristian Gromm static int audio_create_sound_card(void)
61213b41b57SChristian Gromm {
61313b41b57SChristian Gromm 	int ret;
61413b41b57SChristian Gromm 	struct sound_adapter *adpt;
61513b41b57SChristian Gromm 
61613b41b57SChristian Gromm 	list_for_each_entry(adpt, &adpt_list, list) {
61713b41b57SChristian Gromm 		if (!adpt->registered)
61813b41b57SChristian Gromm 			goto adpt_alloc;
61913b41b57SChristian Gromm 	}
62013b41b57SChristian Gromm 	return -ENODEV;
62113b41b57SChristian Gromm adpt_alloc:
62213b41b57SChristian Gromm 	ret = snd_card_register(adpt->card);
62313b41b57SChristian Gromm 	if (ret < 0) {
62413b41b57SChristian Gromm 		release_adapter(adpt);
62513b41b57SChristian Gromm 		return ret;
62613b41b57SChristian Gromm 	}
62713b41b57SChristian Gromm 	adpt->registered = true;
62813b41b57SChristian Gromm 	return 0;
62913b41b57SChristian Gromm }
63013b41b57SChristian Gromm 
63113b41b57SChristian Gromm /**
63213b41b57SChristian Gromm  * audio_disconnect_channel - function to disconnect a channel
63313b41b57SChristian Gromm  * @iface: pointer to interface instance
63413b41b57SChristian Gromm  * @channel_id: channel index
63513b41b57SChristian Gromm  *
63613b41b57SChristian Gromm  * This frees allocated memory and removes the sound card from ALSA
63713b41b57SChristian Gromm  *
63813b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
63913b41b57SChristian Gromm  */
audio_disconnect_channel(struct most_interface * iface,int channel_id)64013b41b57SChristian Gromm static int audio_disconnect_channel(struct most_interface *iface,
64113b41b57SChristian Gromm 				    int channel_id)
64213b41b57SChristian Gromm {
64313b41b57SChristian Gromm 	struct channel *channel;
64413b41b57SChristian Gromm 	struct sound_adapter *adpt = iface->priv;
64513b41b57SChristian Gromm 
64613b41b57SChristian Gromm 	channel = get_channel(iface, channel_id);
64713b41b57SChristian Gromm 	if (!channel)
64813b41b57SChristian Gromm 		return -EINVAL;
64913b41b57SChristian Gromm 
65013b41b57SChristian Gromm 	list_del(&channel->list);
65113b41b57SChristian Gromm 
65213b41b57SChristian Gromm 	kfree(channel);
65313b41b57SChristian Gromm 	if (list_empty(&adpt->dev_list))
65413b41b57SChristian Gromm 		release_adapter(adpt);
65513b41b57SChristian Gromm 	return 0;
65613b41b57SChristian Gromm }
65713b41b57SChristian Gromm 
65813b41b57SChristian Gromm /**
65913b41b57SChristian Gromm  * audio_rx_completion - completion handler for rx channels
66013b41b57SChristian Gromm  * @mbo: pointer to buffer object that has completed
66113b41b57SChristian Gromm  *
66213b41b57SChristian Gromm  * This searches for the channel this MBO belongs to and copy the data from MBO
66313b41b57SChristian Gromm  * to ring buffer
66413b41b57SChristian Gromm  *
66513b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
66613b41b57SChristian Gromm  */
audio_rx_completion(struct mbo * mbo)66713b41b57SChristian Gromm static int audio_rx_completion(struct mbo *mbo)
66813b41b57SChristian Gromm {
66913b41b57SChristian Gromm 	struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id);
67013b41b57SChristian Gromm 	bool period_elapsed = false;
67113b41b57SChristian Gromm 
67213b41b57SChristian Gromm 	if (!channel)
67313b41b57SChristian Gromm 		return -EINVAL;
67413b41b57SChristian Gromm 	if (channel->is_stream_running)
67513b41b57SChristian Gromm 		period_elapsed = copy_data(channel, mbo);
67613b41b57SChristian Gromm 	most_put_mbo(mbo);
67713b41b57SChristian Gromm 	if (period_elapsed)
67813b41b57SChristian Gromm 		snd_pcm_period_elapsed(channel->substream);
67913b41b57SChristian Gromm 	return 0;
68013b41b57SChristian Gromm }
68113b41b57SChristian Gromm 
68213b41b57SChristian Gromm /**
68313b41b57SChristian Gromm  * audio_tx_completion - completion handler for tx channels
68413b41b57SChristian Gromm  * @iface: pointer to interface instance
68513b41b57SChristian Gromm  * @channel_id: channel index/ID
68613b41b57SChristian Gromm  *
68713b41b57SChristian Gromm  * This searches the channel that belongs to this combination of interface
68813b41b57SChristian Gromm  * pointer and channel ID and wakes a process sitting in the wait queue of
68913b41b57SChristian Gromm  * this channel.
69013b41b57SChristian Gromm  *
69113b41b57SChristian Gromm  * Returns 0 on success or error code otherwise.
69213b41b57SChristian Gromm  */
audio_tx_completion(struct most_interface * iface,int channel_id)69313b41b57SChristian Gromm static int audio_tx_completion(struct most_interface *iface, int channel_id)
69413b41b57SChristian Gromm {
69513b41b57SChristian Gromm 	struct channel *channel = get_channel(iface, channel_id);
69613b41b57SChristian Gromm 
69713b41b57SChristian Gromm 	if (!channel)
69813b41b57SChristian Gromm 		return -EINVAL;
69913b41b57SChristian Gromm 
70013b41b57SChristian Gromm 	wake_up_interruptible(&channel->playback_waitq);
70113b41b57SChristian Gromm 	return 0;
70213b41b57SChristian Gromm }
70313b41b57SChristian Gromm 
704*fba3993eSRandy Dunlap /*
70513b41b57SChristian Gromm  * Initialization of the struct most_component
70613b41b57SChristian Gromm  */
70713b41b57SChristian Gromm static struct most_component comp = {
70813b41b57SChristian Gromm 	.mod = THIS_MODULE,
70913b41b57SChristian Gromm 	.name = DRIVER_NAME,
71013b41b57SChristian Gromm 	.probe_channel = audio_probe_channel,
71113b41b57SChristian Gromm 	.disconnect_channel = audio_disconnect_channel,
71213b41b57SChristian Gromm 	.rx_completion = audio_rx_completion,
71313b41b57SChristian Gromm 	.tx_completion = audio_tx_completion,
71413b41b57SChristian Gromm 	.cfg_complete = audio_create_sound_card,
71513b41b57SChristian Gromm };
71613b41b57SChristian Gromm 
audio_init(void)71713b41b57SChristian Gromm static int __init audio_init(void)
71813b41b57SChristian Gromm {
71913b41b57SChristian Gromm 	int ret;
72013b41b57SChristian Gromm 
72113b41b57SChristian Gromm 	INIT_LIST_HEAD(&adpt_list);
72213b41b57SChristian Gromm 
72313b41b57SChristian Gromm 	ret = most_register_component(&comp);
72413b41b57SChristian Gromm 	if (ret) {
72513b41b57SChristian Gromm 		pr_err("Failed to register %s\n", comp.name);
72613b41b57SChristian Gromm 		return ret;
72713b41b57SChristian Gromm 	}
72813b41b57SChristian Gromm 	ret = most_register_configfs_subsys(&comp);
72913b41b57SChristian Gromm 	if (ret) {
73013b41b57SChristian Gromm 		pr_err("Failed to register %s configfs subsys\n", comp.name);
73113b41b57SChristian Gromm 		most_deregister_component(&comp);
73213b41b57SChristian Gromm 	}
73313b41b57SChristian Gromm 	return ret;
73413b41b57SChristian Gromm }
73513b41b57SChristian Gromm 
audio_exit(void)73613b41b57SChristian Gromm static void __exit audio_exit(void)
73713b41b57SChristian Gromm {
73813b41b57SChristian Gromm 	most_deregister_configfs_subsys(&comp);
73913b41b57SChristian Gromm 	most_deregister_component(&comp);
74013b41b57SChristian Gromm }
74113b41b57SChristian Gromm 
74213b41b57SChristian Gromm module_init(audio_init);
74313b41b57SChristian Gromm module_exit(audio_exit);
74413b41b57SChristian Gromm 
74513b41b57SChristian Gromm MODULE_LICENSE("GPL");
74613b41b57SChristian Gromm MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
74713b41b57SChristian Gromm MODULE_DESCRIPTION("Sound Component Module for Mostcore");
748