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