Commit b7f0088d authored by Vaibhav Agarwal's avatar Vaibhav Agarwal Committed by Greg Kroah-Hartman

greybus: audio: Add GB Audio class protocol functionality in GB codec DAI ops

GB Audio class driver provides APIs to configure GB codec module.
This patch adds relevant operations in DAI ops callback functions to
configure codec module as per DAPM sequence triggered.
Signed-off-by: default avatarVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Signed-off-by: default avatarMark Greer <mgreer@animalcreek.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 6339d232
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <sound/msm-dynamic-dailink.h> #include <sound/msm-dynamic-dailink.h>
#include "audio_codec.h" #include "audio_codec.h"
#include "audio_apbridgea.h"
#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" #define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt"
#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" #define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data"
...@@ -19,28 +20,325 @@ ...@@ -19,28 +20,325 @@
static DEFINE_MUTEX(gb_codec_list_lock); static DEFINE_MUTEX(gb_codec_list_lock);
static LIST_HEAD(gb_codec_list); static LIST_HEAD(gb_codec_list);
/*
* codec DAI ops
*/
static int gbcodec_startup(struct snd_pcm_substream *substream, static int gbcodec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
return 0; int ret, found;
__u16 i2s_port, cportid;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb =
(struct gbaudio_codec_info *)dev_get_drvdata(dai->dev);
/* find the dai */
found = 0;
list_for_each_entry(gb_dai, &gb->dai_list, list) {
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
found = 1;
break;
}
}
if (!found) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
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);
dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid,
ret);
return ret;
} }
static void gbcodec_shutdown(struct snd_pcm_substream *substream, static void gbcodec_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
int ret, found;
__u16 i2s_port, cportid;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb =
(struct gbaudio_codec_info *)dev_get_drvdata(dai->dev);
/* find the dai */
found = 0;
list_for_each_entry(gb_dai, &gb->dai_list, list) {
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
found = 1;
break;
}
}
if (!found) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
return;
}
/* 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");
return;
}
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);
dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
gb_dai->connection->hd_cport_id, ret);
return;
} }
static int gbcodec_hw_params(struct snd_pcm_substream *substream, static int gbcodec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hwparams, struct snd_pcm_hw_params *hwparams,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
return 0; int ret, found;
uint8_t sig_bits, channels;
uint32_t format, rate;
uint16_t data_cport;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb =
(struct gbaudio_codec_info *)dev_get_drvdata(dai->dev);
/* find the dai */
found = 0;
list_for_each_entry(gb_dai, &gb->dai_list, list) {
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
found = 1;
break;
}
}
if (!found) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
return -EINVAL;
}
/*
* 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));
return -EINVAL;
}
channels = params_channels(hwparams);
if (params_rate(hwparams) != 48000) {
dev_err(dai->dev, "Invalid sampling rate:%d\n",
params_rate(hwparams));
return -EINVAL;
}
rate = GB_AUDIO_PCM_RATE_48000;
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
dev_err(dai->dev, "Invalid format:%d\n",
params_format(hwparams));
return -EINVAL;
}
format = GB_AUDIO_PCM_FMT_S16_LE;
data_cport = gb_dai->connection->intf_cport_id;
/* XXX check impact of sig_bit
* it should not change ideally
*/
dev_dbg(dai->dev, "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n",
data_cport, rate, channels, format, sig_bits);
ret = gb_audio_gb_set_pcm(gb->mgmt_connection, data_cport, format,
rate, channels, sig_bits);
if (ret) {
dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
return ret;
}
/*
* XXX need to check if
* set config is always required
* check for mclk_freq as well
*/
ret = gb_audio_apbridgea_set_config(gb_dai->connection, 0,
AUDIO_APBRIDGEA_PCM_FMT_16,
AUDIO_APBRIDGEA_PCM_RATE_48000,
6144000);
if (ret)
dev_err(dai->dev, "%d: Error during set_config\n", ret);
return ret;
} }
static int gbcodec_prepare(struct snd_pcm_substream *substream, static int gbcodec_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
return 0; int ret, found;
uint16_t data_cport;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb =
(struct gbaudio_codec_info *)dev_get_drvdata(dai->dev);
/* find the dai */
found = 0;
list_for_each_entry(gb_dai, &gb->dai_list, list) {
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
found = 1;
break;
}
}
if (!found) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
return -EINVAL;
}
/* deactivate rx/tx */
data_cport = gb_dai->connection->intf_cport_id;
switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
ret = gb_audio_gb_set_rx_data_size(gb->mgmt_connection,
data_cport, 192);
if (ret) {
dev_err(dai->dev,
"%d:Error during set_rx_data_size, cport:%d\n",
ret, data_cport);
return ret;
}
ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0,
192);
if (ret) {
dev_err(dai->dev,
"%d:Error during apbridgea_set_rx_data_size\n",
ret);
return ret;
}
ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport);
break;
case SNDRV_PCM_STREAM_PLAYBACK:
ret = gb_audio_gb_set_tx_data_size(gb->mgmt_connection,
data_cport, 192);
if (ret) {
dev_err(dai->dev,
"%d:Error during module set_tx_data_size, cport:%d\n",
ret, data_cport);
return ret;
}
ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0,
192);
if (ret) {
dev_err(dai->dev,
"%d:Error during apbridgea set_tx_data_size, cport\n",
ret);
return ret;
}
ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport);
break;
default:
dev_err(dai->dev, "Invalid stream type %d during prepare\n",
substream->stream);
return -EINVAL;
}
if (ret)
dev_err(dai->dev, "%d: Error during activate stream\n", ret);
return ret;
}
static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
int ret, found;
int tx, rx, start, stop;
struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb =
(struct gbaudio_codec_info *)dev_get_drvdata(dai->dev);
/* find the dai */
found = 0;
list_for_each_entry(gb_dai, &gb->dai_list, list) {
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
found = 1;
break;
}
}
if (!found) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
return -EINVAL;
}
tx = rx = start = stop = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
start = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
stop = 1;
break;
default:
dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd);
return -EINVAL;
}
switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
rx = 1;
break;
case SNDRV_PCM_STREAM_PLAYBACK:
tx = 1;
break;
default:
dev_err(dai->dev, "Invalid stream type:%d\n",
substream->stream);
return -EINVAL;
}
if (start && tx)
ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0, 0);
else if (start && rx)
ret = gb_audio_apbridgea_start_rx(gb_dai->connection, 0);
else if (stop && tx)
ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0);
else if (stop && rx)
ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0);
else
ret = -EINVAL;
if (ret)
dev_err(dai->dev, "%d:Error during %s stream\n", ret,
start ? "Start" : "Stop");
return ret;
} }
static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
...@@ -57,6 +355,7 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { ...@@ -57,6 +355,7 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = {
.startup = gbcodec_startup, .startup = gbcodec_startup,
.shutdown = gbcodec_shutdown, .shutdown = gbcodec_shutdown,
.hw_params = gbcodec_hw_params, .hw_params = gbcodec_hw_params,
.trigger = gbcodec_trigger,
.prepare = gbcodec_prepare, .prepare = gbcodec_prepare,
.set_fmt = gbcodec_set_dai_fmt, .set_fmt = gbcodec_set_dai_fmt,
.digital_mute = gbcodec_digital_mute, .digital_mute = gbcodec_digital_mute,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment