Lines Matching +full:channel +full:-

1 // SPDX-License-Identifier: GPL-2.0
3 * sound.c - Sound component for Mostcore
28 * struct channel - private structure to keep channel specific data
30 * @pcm_hardware: low-level hardware description
31 * @iface: interface for which the channel belongs to
32 * @cfg: channel configuration
35 * @id: channel index
42 * @copy_fn: copy function for PCM-specific format and width
44 struct channel { struct
93 while (i < bytes - 2) { in swap_copy24()
152 * get_channel - get pointer to channel
154 * @channel_id: channel ID
156 * This traverses the channel list and returns the channel matching the
159 * Returns pointer to channel on success or NULL otherwise.
161 static struct channel *get_channel(struct most_interface *iface, in get_channel()
164 struct sound_adapter *adpt = iface->priv; in get_channel()
165 struct channel *channel; in get_channel() local
167 list_for_each_entry(channel, &adpt->dev_list, list) { in get_channel()
168 if ((channel->iface == iface) && (channel->id == channel_id)) in get_channel()
169 return channel; in get_channel()
175 * copy_data - implements data copying function
176 * @channel: channel
181 static bool copy_data(struct channel *channel, struct mbo *mbo) in copy_data() argument
183 struct snd_pcm_runtime *const runtime = channel->substream->runtime; in copy_data()
184 unsigned int const frame_bytes = channel->cfg->subbuffer_size; in copy_data()
185 unsigned int const buffer_size = runtime->buffer_size; in copy_data()
189 if (channel->cfg->direction & MOST_CH_RX) in copy_data()
190 frames = mbo->processed_length / frame_bytes; in copy_data()
192 frames = mbo->buffer_length / frame_bytes; in copy_data()
193 fr0 = min(buffer_size - channel->buffer_pos, frames); in copy_data()
195 channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes, in copy_data()
196 mbo->virt_address, in copy_data()
201 channel->copy_fn(runtime->dma_area, in copy_data()
202 mbo->virt_address + fr0 * frame_bytes, in copy_data()
203 (frames - fr0) * frame_bytes); in copy_data()
206 channel->buffer_pos += frames; in copy_data()
207 if (channel->buffer_pos >= buffer_size) in copy_data()
208 channel->buffer_pos -= buffer_size; in copy_data()
209 channel->period_pos += frames; in copy_data()
210 if (channel->period_pos >= runtime->period_size) { in copy_data()
211 channel->period_pos -= runtime->period_size; in copy_data()
218 * playback_thread - function implements the playback thread
222 * MBO from mostcore for a particular channel and copy the data from ring buffer
229 struct channel *const channel = data; in playback_thread() local
236 channel->playback_waitq, in playback_thread()
238 (channel->is_stream_running && in playback_thread()
239 (mbo = most_get_mbo(channel->iface, channel->id, in playback_thread()
244 if (channel->is_stream_running) in playback_thread()
245 period_elapsed = copy_data(channel, mbo); in playback_thread()
247 memset(mbo->virt_address, 0, mbo->buffer_length); in playback_thread()
251 snd_pcm_period_elapsed(channel->substream); in playback_thread()
257 * pcm_open - implements open callback function for PCM middle layer
261 * initialize the runtime->hw record.
267 struct channel *channel = substream->private_data; in pcm_open() local
268 struct snd_pcm_runtime *runtime = substream->runtime; in pcm_open()
269 struct most_channel_config *cfg = channel->cfg; in pcm_open()
272 channel->substream = substream; in pcm_open()
274 if (cfg->direction == MOST_CH_TX) { in pcm_open()
275 channel->playback_task = kthread_run(playback_thread, channel, in pcm_open()
277 if (IS_ERR(channel->playback_task)) { in pcm_open()
279 return PTR_ERR(channel->playback_task); in pcm_open()
283 ret = most_start_channel(channel->iface, channel->id, &comp); in pcm_open()
286 if (cfg->direction == MOST_CH_TX) in pcm_open()
287 kthread_stop(channel->playback_task); in pcm_open()
291 runtime->hw = channel->pcm_hardware; in pcm_open()
296 * pcm_close - implements close callback function for PCM middle layer
297 * @substream: sub-stream pointer
307 struct channel *channel = substream->private_data; in pcm_close() local
309 if (channel->cfg->direction == MOST_CH_TX) in pcm_close()
310 kthread_stop(channel->playback_task); in pcm_close()
311 most_stop_channel(channel->iface, channel->id, &comp); in pcm_close()
316 * pcm_prepare - implements prepare callback function for PCM middle layer
326 struct channel *channel = substream->private_data; in pcm_prepare() local
327 struct snd_pcm_runtime *runtime = substream->runtime; in pcm_prepare()
328 struct most_channel_config *cfg = channel->cfg; in pcm_prepare()
329 int width = snd_pcm_format_physical_width(runtime->format); in pcm_prepare()
331 channel->copy_fn = NULL; in pcm_prepare()
333 if (cfg->direction == MOST_CH_TX) { in pcm_prepare()
334 if (snd_pcm_format_big_endian(runtime->format) || width == 8) in pcm_prepare()
335 channel->copy_fn = alsa_to_most_memcpy; in pcm_prepare()
337 channel->copy_fn = alsa_to_most_copy16; in pcm_prepare()
339 channel->copy_fn = alsa_to_most_copy24; in pcm_prepare()
341 channel->copy_fn = alsa_to_most_copy32; in pcm_prepare()
343 if (snd_pcm_format_big_endian(runtime->format) || width == 8) in pcm_prepare()
344 channel->copy_fn = most_to_alsa_memcpy; in pcm_prepare()
346 channel->copy_fn = most_to_alsa_copy16; in pcm_prepare()
348 channel->copy_fn = most_to_alsa_copy24; in pcm_prepare()
350 channel->copy_fn = most_to_alsa_copy32; in pcm_prepare()
353 if (!channel->copy_fn) in pcm_prepare()
354 return -EINVAL; in pcm_prepare()
355 channel->period_pos = 0; in pcm_prepare()
356 channel->buffer_pos = 0; in pcm_prepare()
361 * pcm_trigger - implements trigger callback function for PCM middle layer
372 struct channel *channel = substream->private_data; in pcm_trigger() local
376 channel->is_stream_running = true; in pcm_trigger()
377 wake_up_interruptible(&channel->playback_waitq); in pcm_trigger()
381 channel->is_stream_running = false; in pcm_trigger()
385 return -EINVAL; in pcm_trigger()
391 * pcm_pointer - implements pointer callback function for PCM middle layer
396 * ranging from 0 to buffer_size-1.
400 struct channel *channel = substream->private_data; in pcm_pointer() local
402 return channel->buffer_pos; in pcm_pointer()
434 return -EINVAL; in split_arg_list()
459 return -EINVAL; in audio_set_hw_params()
464 return -EINVAL; in audio_set_hw_params()
467 if (cfg->subbuffer_size != ch_num * sinfo[i].bytes) { in audio_set_hw_params()
469 return -EINVAL; in audio_set_hw_params()
472 pcm_hw->info = MOST_PCM_INFO; in audio_set_hw_params()
473 pcm_hw->rates = SNDRV_PCM_RATE_48000; in audio_set_hw_params()
474 pcm_hw->rate_min = 48000; in audio_set_hw_params()
475 pcm_hw->rate_max = 48000; in audio_set_hw_params()
476 pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size; in audio_set_hw_params()
477 pcm_hw->period_bytes_min = cfg->buffer_size; in audio_set_hw_params()
478 pcm_hw->period_bytes_max = cfg->buffer_size; in audio_set_hw_params()
479 pcm_hw->periods_min = 1; in audio_set_hw_params()
480 pcm_hw->periods_max = cfg->num_buffers; in audio_set_hw_params()
481 pcm_hw->channels_min = ch_num; in audio_set_hw_params()
482 pcm_hw->channels_max = ch_num; in audio_set_hw_params()
483 pcm_hw->formats = sinfo[i].formats; in audio_set_hw_params()
489 struct channel *channel, *tmp; in release_adapter() local
491 list_for_each_entry_safe(channel, tmp, &adpt->dev_list, list) { in release_adapter()
492 list_del(&channel->list); in release_adapter()
493 kfree(channel); in release_adapter()
495 if (adpt->card) in release_adapter()
496 snd_card_free(adpt->card); in release_adapter()
497 list_del(&adpt->list); in release_adapter()
502 * audio_probe_channel - probe function of the driver module
504 * @channel_id: channel index/ID
505 * @cfg: pointer to actual channel configuration
517 struct channel *channel; in audio_probe_channel() local
528 if (cfg->data_type != MOST_CH_SYNC) { in audio_probe_channel()
529 pr_err("Incompatible channel type\n"); in audio_probe_channel()
530 return -EINVAL; in audio_probe_channel()
538 if (adpt->iface != iface) in audio_probe_channel()
540 if (adpt->registered) in audio_probe_channel()
541 return -ENOSPC; in audio_probe_channel()
542 adpt->pcm_dev_idx++; in audio_probe_channel()
547 return -ENOMEM; in audio_probe_channel()
549 adpt->iface = iface; in audio_probe_channel()
550 INIT_LIST_HEAD(&adpt->dev_list); in audio_probe_channel()
551 iface->priv = adpt; in audio_probe_channel()
552 list_add_tail(&adpt->list, &adpt_list); in audio_probe_channel()
553 ret = snd_card_new(iface->driver_dev, -1, "INIC", THIS_MODULE, in audio_probe_channel()
554 sizeof(*channel), &adpt->card); in audio_probe_channel()
557 snprintf(adpt->card->driver, sizeof(adpt->card->driver), in audio_probe_channel()
559 snprintf(adpt->card->shortname, sizeof(adpt->card->shortname), in audio_probe_channel()
561 snprintf(adpt->card->longname, sizeof(adpt->card->longname), in audio_probe_channel()
562 "%s at %s", adpt->card->shortname, iface->description); in audio_probe_channel()
565 pr_err("channel (%s:%d) is already linked\n", in audio_probe_channel()
566 iface->description, channel_id); in audio_probe_channel()
567 return -EEXIST; in audio_probe_channel()
570 if (cfg->direction == MOST_CH_TX) { in audio_probe_channel()
577 channel = kzalloc(sizeof(*channel), GFP_KERNEL); in audio_probe_channel()
578 if (!channel) { in audio_probe_channel()
579 ret = -ENOMEM; in audio_probe_channel()
582 channel->card = adpt->card; in audio_probe_channel()
583 channel->cfg = cfg; in audio_probe_channel()
584 channel->iface = iface; in audio_probe_channel()
585 channel->id = channel_id; in audio_probe_channel()
586 init_waitqueue_head(&channel->playback_waitq); in audio_probe_channel()
587 list_add_tail(&channel->list, &adpt->dev_list); in audio_probe_channel()
589 ret = audio_set_hw_params(&channel->pcm_hardware, ch_num, sample_res, in audio_probe_channel()
594 ret = snd_pcm_new(adpt->card, device_name, adpt->pcm_dev_idx, in audio_probe_channel()
600 pcm->private_data = channel; in audio_probe_channel()
601 strscpy(pcm->name, device_name, sizeof(pcm->name)); in audio_probe_channel()
617 if (!adpt->registered) in audio_create_sound_card()
620 return -ENODEV; in audio_create_sound_card()
622 ret = snd_card_register(adpt->card); in audio_create_sound_card()
627 adpt->registered = true; in audio_create_sound_card()
632 * audio_disconnect_channel - function to disconnect a channel
634 * @channel_id: channel index
643 struct channel *channel; in audio_disconnect_channel() local
644 struct sound_adapter *adpt = iface->priv; in audio_disconnect_channel()
646 channel = get_channel(iface, channel_id); in audio_disconnect_channel()
647 if (!channel) in audio_disconnect_channel()
648 return -EINVAL; in audio_disconnect_channel()
650 list_del(&channel->list); in audio_disconnect_channel()
652 kfree(channel); in audio_disconnect_channel()
653 if (list_empty(&adpt->dev_list)) in audio_disconnect_channel()
659 * audio_rx_completion - completion handler for rx channels
662 * This searches for the channel this MBO belongs to and copy the data from MBO
669 struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id); in audio_rx_completion() local
672 if (!channel) in audio_rx_completion()
673 return -EINVAL; in audio_rx_completion()
674 if (channel->is_stream_running) in audio_rx_completion()
675 period_elapsed = copy_data(channel, mbo); in audio_rx_completion()
678 snd_pcm_period_elapsed(channel->substream); in audio_rx_completion()
683 * audio_tx_completion - completion handler for tx channels
685 * @channel_id: channel index/ID
687 * This searches the channel that belongs to this combination of interface
688 * pointer and channel ID and wakes a process sitting in the wait queue of
689 * this channel.
695 struct channel *channel = get_channel(iface, channel_id); in audio_tx_completion() local
697 if (!channel) in audio_tx_completion()
698 return -EINVAL; in audio_tx_completion()
700 wake_up_interruptible(&channel->playback_waitq); in audio_tx_completion()