/*
* Greybus audio driver
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/msm-dynamic-dailink.h>
#include "audio_codec.h"
#include "audio_apbridgea.h"
#include "audio_manager.h"
static DEFINE_MUTEX(gb_codec_list_lock);
static LIST_HEAD(gb_codec_list);
struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec,
int data_cport, const char *name)
{
struct gbaudio_dai *dai;
list_for_each_entry(dai, &gbcodec->dai_list, list) {
if (name && !strncmp(dai->name, name, NAME_SIZE))
return dai;
if ((data_cport != -1) && (dai->data_cport == data_cport))
return dai;
}
return NULL;
}
/*
* codec DAI ops
*/
static int gbcodec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret;
__u16 i2s_port, cportid;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */
mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
if (!gb_dai) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
mutex_unlock(&gb->lock);
return -EINVAL;
}
/* register cport */
i2s_port = 0; /* fixed for now */
cportid = gb_dai->connection->hd_cport_id;
ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port,
cportid,
AUDIO_APBRIDGEA_DIRECTION_TX);
dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid,
ret);
if (!ret)
atomic_inc(&gb_dai->users);
mutex_unlock(&gb->lock);
return ret;
}
static void gbcodec_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret;
__u16 i2s_port, cportid;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
/* find the dai */
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
if (!gb_dai) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
goto func_exit;
}
atomic_dec(&gb_dai->users);
if (!atomic_read(&gb->is_connected)) {
if (!atomic_read(&gb_dai->users))
wake_up_interruptible(&gb_dai->wait_queue);
return;
}
mutex_lock(&gb->lock);
/* deactivate rx/tx */
cportid = gb_dai->connection->intf_cport_id;
switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
ret = gb_audio_gb_deactivate_rx(gb->mgmt_connection, cportid);
break;
case SNDRV_PCM_STREAM_PLAYBACK:
ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid);
break;
default:
dev_err(dai->dev, "Invalid stream type during shutdown\n");
goto func_exit;
}
if (ret)
dev_err(dai->dev, "%d:Error during deactivate\n", ret);
/* un register cport */
i2s_port = 0; /* fixed for now */
ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port,
gb_dai->connection->hd_cport_id,
AUDIO_APBRIDGEA_DIRECTION_TX);
dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
gb_dai->connection->hd_cport_id, ret);
func_exit:
mutex_unlock(&gb->lock);
/*
if (!atomic_read(&gb_dai->users))
wake_up_interruptible(&gb_dai->wait_queue);
*/
return;
}
static int gbcodec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hwparams,
struct snd_soc_dai *dai)
{
int ret;
uint8_t sig_bits, channels;
uint32_t format, rate;
uint16_t data_cport;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */
mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
if (!gb_dai) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
ret = -EINVAL;
goto func_exit;
}
/*
* assuming, currently only 48000 Hz, 16BIT_LE, stereo
* is supported, validate params before configuring codec
*/
if (params_channels(hwparams) != 2) {
dev_err(dai->dev, "Invalid channel count:%d\n",
params_channels(hwparams));
ret = -EINVAL;
goto