mirror of https://gitee.com/openkylin/linux.git
greybus: audio: codec driver cleanup
audio codec driver is now moved to bundle driver approach. This resolved many race conditions related to audio mgmt & data connection init/exit sequence. Thus, a lot of helper functions can now be safely removed. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
127c1fbd55
commit
796fad441c
|
@ -18,30 +18,38 @@
|
||||||
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);
|
||||||
|
|
||||||
|
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
|
* 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)
|
||||||
{
|
{
|
||||||
int ret, found;
|
int ret;
|
||||||
__u16 i2s_port, cportid;
|
__u16 i2s_port, cportid;
|
||||||
|
|
||||||
struct gbaudio_dai *gb_dai;
|
struct gbaudio_dai *gb_dai;
|
||||||
struct gb_audio *audio = dev_get_drvdata(dai->dev);
|
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
/* find the dai */
|
/* find the dai */
|
||||||
found = 0;
|
mutex_lock(&gb->lock);
|
||||||
list_for_each_entry(gb_dai, &gb->dai_list, list) {
|
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
|
||||||
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
|
if (!gb_dai) {
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +62,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream,
|
||||||
ret);
|
ret);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
atomic_inc(&gb->users);
|
atomic_inc(&gb_dai->users);
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -62,28 +71,21 @@ static int gbcodec_startup(struct snd_pcm_substream *substream,
|
||||||
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;
|
int ret;
|
||||||
__u16 i2s_port, cportid;
|
__u16 i2s_port, cportid;
|
||||||
|
|
||||||
struct gbaudio_dai *gb_dai;
|
struct gbaudio_dai *gb_dai;
|
||||||
struct gb_audio *audio = dev_get_drvdata(dai->dev);
|
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
/* find the dai */
|
/* find the dai */
|
||||||
found = 0;
|
mutex_lock(&gb->lock);
|
||||||
list_for_each_entry(gb_dai, &gb->dai_list, list) {
|
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
|
||||||
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
|
if (!gb_dai) {
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
||||||
return;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_dec(&gb->users);
|
atomic_dec(&gb_dai->users);
|
||||||
|
|
||||||
/* deactivate rx/tx */
|
/* deactivate rx/tx */
|
||||||
cportid = gb_dai->connection->intf_cport_id;
|
cportid = gb_dai->connection->intf_cport_id;
|
||||||
|
@ -97,7 +99,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(dai->dev, "Invalid stream type during shutdown\n");
|
dev_err(dai->dev, "Invalid stream type during shutdown\n");
|
||||||
return;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -110,6 +112,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
|
dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
|
||||||
gb_dai->connection->hd_cport_id, ret);
|
gb_dai->connection->hd_cport_id, ret);
|
||||||
|
func_exit:
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -118,26 +122,20 @@ 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)
|
||||||
{
|
{
|
||||||
int ret, found;
|
int ret;
|
||||||
uint8_t sig_bits, channels;
|
uint8_t sig_bits, channels;
|
||||||
uint32_t format, rate;
|
uint32_t format, rate;
|
||||||
uint16_t data_cport;
|
uint16_t data_cport;
|
||||||
struct gbaudio_dai *gb_dai;
|
struct gbaudio_dai *gb_dai;
|
||||||
struct gb_audio *audio = dev_get_drvdata(dai->dev);
|
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
/* find the dai */
|
/* find the dai */
|
||||||
found = 0;
|
mutex_lock(&gb->lock);
|
||||||
list_for_each_entry(gb_dai, &gb->dai_list, list) {
|
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
|
||||||
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
|
if (!gb_dai) {
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -147,21 +145,24 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
|
||||||
if (params_channels(hwparams) != 2) {
|
if (params_channels(hwparams) != 2) {
|
||||||
dev_err(dai->dev, "Invalid channel count:%d\n",
|
dev_err(dai->dev, "Invalid channel count:%d\n",
|
||||||
params_channels(hwparams));
|
params_channels(hwparams));
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
channels = params_channels(hwparams);
|
channels = params_channels(hwparams);
|
||||||
|
|
||||||
if (params_rate(hwparams) != 48000) {
|
if (params_rate(hwparams) != 48000) {
|
||||||
dev_err(dai->dev, "Invalid sampling rate:%d\n",
|
dev_err(dai->dev, "Invalid sampling rate:%d\n",
|
||||||
params_rate(hwparams));
|
params_rate(hwparams));
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
rate = GB_AUDIO_PCM_RATE_48000;
|
rate = GB_AUDIO_PCM_RATE_48000;
|
||||||
|
|
||||||
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
|
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
|
||||||
dev_err(dai->dev, "Invalid format:%d\n",
|
dev_err(dai->dev, "Invalid format:%d\n",
|
||||||
params_format(hwparams));
|
params_format(hwparams));
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
format = GB_AUDIO_PCM_FMT_S16_LE;
|
format = GB_AUDIO_PCM_FMT_S16_LE;
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
|
||||||
rate, channels, sig_bits);
|
rate, channels, sig_bits);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
|
dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
|
||||||
return ret;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -190,31 +191,26 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
|
||||||
6144000);
|
6144000);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dai->dev, "%d: Error during set_config\n", ret);
|
dev_err(dai->dev, "%d: Error during set_config\n", ret);
|
||||||
|
func_exit:
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
return 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)
|
||||||
{
|
{
|
||||||
int ret, found;
|
int ret;
|
||||||
uint16_t data_cport;
|
uint16_t data_cport;
|
||||||
struct gbaudio_dai *gb_dai;
|
struct gbaudio_dai *gb_dai;
|
||||||
struct gb_audio *audio = dev_get_drvdata(dai->dev);
|
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
/* find the dai */
|
/* find the dai */
|
||||||
found = 0;
|
mutex_lock(&gb->lock);
|
||||||
list_for_each_entry(gb_dai, &gb->dai_list, list) {
|
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
|
||||||
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
|
if (!gb_dai) {
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deactivate rx/tx */
|
/* deactivate rx/tx */
|
||||||
|
@ -228,7 +224,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
|
||||||
dev_err(dai->dev,
|
dev_err(dai->dev,
|
||||||
"%d:Error during set_rx_data_size, cport:%d\n",
|
"%d:Error during set_rx_data_size, cport:%d\n",
|
||||||
ret, data_cport);
|
ret, data_cport);
|
||||||
return ret;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0,
|
ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0,
|
||||||
192);
|
192);
|
||||||
|
@ -236,7 +232,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
|
||||||
dev_err(dai->dev,
|
dev_err(dai->dev,
|
||||||
"%d:Error during apbridgea_set_rx_data_size\n",
|
"%d:Error during apbridgea_set_rx_data_size\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport);
|
ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport);
|
||||||
break;
|
break;
|
||||||
|
@ -247,7 +243,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
|
||||||
dev_err(dai->dev,
|
dev_err(dai->dev,
|
||||||
"%d:Error during module set_tx_data_size, cport:%d\n",
|
"%d:Error during module set_tx_data_size, cport:%d\n",
|
||||||
ret, data_cport);
|
ret, data_cport);
|
||||||
return ret;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0,
|
ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0,
|
||||||
192);
|
192);
|
||||||
|
@ -255,43 +251,40 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
|
||||||
dev_err(dai->dev,
|
dev_err(dai->dev,
|
||||||
"%d:Error during apbridgea set_tx_data_size, cport\n",
|
"%d:Error during apbridgea set_tx_data_size, cport\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport);
|
ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(dai->dev, "Invalid stream type %d during prepare\n",
|
dev_err(dai->dev, "Invalid stream type %d during prepare\n",
|
||||||
substream->stream);
|
substream->stream);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dai->dev, "%d: Error during activate stream\n", ret);
|
dev_err(dai->dev, "%d: Error during activate stream\n", ret);
|
||||||
|
|
||||||
|
func_exit:
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
|
static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
int ret, found;
|
int ret;
|
||||||
int tx, rx, start, stop;
|
int tx, rx, start, stop;
|
||||||
struct gbaudio_dai *gb_dai;
|
struct gbaudio_dai *gb_dai;
|
||||||
struct gb_audio *audio = dev_get_drvdata(dai->dev);
|
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
/* find the dai */
|
/* find the dai */
|
||||||
found = 0;
|
mutex_lock(&gb->lock);
|
||||||
list_for_each_entry(gb_dai, &gb->dai_list, list) {
|
gb_dai = gbaudio_find_dai(gb, -1, dai->name);
|
||||||
if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) {
|
if (!gb_dai) {
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = rx = start = stop = 0;
|
tx = rx = start = stop = 0;
|
||||||
|
@ -306,7 +299,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd);
|
dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (substream->stream) {
|
switch (substream->stream) {
|
||||||
|
@ -319,7 +313,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
default:
|
default:
|
||||||
dev_err(dai->dev, "Invalid stream type:%d\n",
|
dev_err(dai->dev, "Invalid stream type:%d\n",
|
||||||
substream->stream);
|
substream->stream);
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto func_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start && tx)
|
if (start && tx)
|
||||||
|
@ -341,6 +336,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
dev_err(dai->dev, "%d:Error during %s stream\n", ret,
|
dev_err(dai->dev, "%d:Error during %s stream\n", ret,
|
||||||
start ? "Start" : "Stop");
|
start ? "Start" : "Stop");
|
||||||
|
|
||||||
|
func_exit:
|
||||||
|
mutex_unlock(&gb->lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,8 +380,7 @@ static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
unsigned int value)
|
unsigned int value)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gbcodec = audio->gbcodec;
|
|
||||||
u8 *gbcodec_reg = gbcodec->reg;
|
u8 *gbcodec_reg = gbcodec->reg;
|
||||||
|
|
||||||
if (reg == SND_SOC_NOPM)
|
if (reg == SND_SOC_NOPM)
|
||||||
|
@ -404,8 +400,7 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec,
|
||||||
{
|
{
|
||||||
unsigned int val = 0;
|
unsigned int val = 0;
|
||||||
|
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gbcodec = audio->gbcodec;
|
|
||||||
u8 *gbcodec_reg = gbcodec->reg;
|
u8 *gbcodec_reg = gbcodec->reg;
|
||||||
|
|
||||||
if (reg == SND_SOC_NOPM)
|
if (reg == SND_SOC_NOPM)
|
||||||
|
@ -447,28 +442,15 @@ static struct snd_soc_dai_link gbaudio_dailink = {
|
||||||
.be_id = 34,
|
.be_id = 34,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void gbaudio_remove_dailinks(struct gbaudio_codec_info *gbcodec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < gbcodec->num_dai_links; i++) {
|
|
||||||
dev_dbg(gbcodec->dev, "Remove %s: DAI link\n",
|
|
||||||
gbcodec->dailink_name[i]);
|
|
||||||
devm_kfree(gbcodec->dev, gbcodec->dailink_name[i]);
|
|
||||||
gbcodec->dailink_name[i] = NULL;
|
|
||||||
}
|
|
||||||
gbcodec->num_dai_links = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec)
|
static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
char *dai_link_name;
|
char *dai_link_name;
|
||||||
struct snd_soc_dai_link *dai;
|
struct snd_soc_dai_link *dailink;
|
||||||
struct device *dev = gbcodec->dev;
|
struct device *dev = gbcodec->dev;
|
||||||
|
|
||||||
dai = &gbaudio_dailink;
|
dailink = &gbaudio_dailink;
|
||||||
dai->codec_name = gbcodec->name;
|
dailink->codec_name = gbcodec->name;
|
||||||
|
|
||||||
/* FIXME
|
/* FIXME
|
||||||
* allocate memory for DAI links based on count.
|
* allocate memory for DAI links based on count.
|
||||||
|
@ -481,98 +463,20 @@ static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec)
|
||||||
devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL);
|
devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL);
|
||||||
snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX",
|
snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX",
|
||||||
gbcodec->dev_id, i);
|
gbcodec->dev_id, i);
|
||||||
dai->name = dai_link_name;
|
dailink->name = dai_link_name;
|
||||||
dai->codec_dai_name = gbcodec->dais[i].name;
|
dailink->codec_dai_name = gbcodec->dais[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dai, 1);
|
ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dailink, 1);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(dev, "%d:Error while adding DAI link\n", ret);
|
dev_err(dev, "%d:Error while adding DAI link\n", ret);
|
||||||
goto err_dai_link;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err_dai_link:
|
|
||||||
gbcodec->num_dai_links = i;
|
|
||||||
gbaudio_remove_dailinks(gbcodec);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gb_snd management functions
|
* gb_snd management functions
|
||||||
*/
|
*/
|
||||||
static struct gbaudio_codec_info *gbaudio_find_codec(struct device *dev,
|
|
||||||
int dev_id)
|
|
||||||
{
|
|
||||||
struct gbaudio_codec_info *tmp, *ret;
|
|
||||||
|
|
||||||
mutex_lock(&gb_codec_list_lock);
|
|
||||||
list_for_each_entry_safe(ret, tmp, &gb_codec_list, list) {
|
|
||||||
dev_dbg(dev, "%d:device found\n", ret->dev_id);
|
|
||||||
if (ret->dev_id == dev_id) {
|
|
||||||
mutex_unlock(&gb_codec_list_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&gb_codec_list_lock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev,
|
|
||||||
int dev_id)
|
|
||||||
{
|
|
||||||
struct gbaudio_codec_info *gbcodec;
|
|
||||||
struct gb_audio *audio = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
gbcodec = gbaudio_find_codec(dev, dev_id);
|
|
||||||
if (gbcodec)
|
|
||||||
return gbcodec;
|
|
||||||
|
|
||||||
gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL);
|
|
||||||
if (!gbcodec)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
mutex_init(&gbcodec->lock);
|
|
||||||
INIT_LIST_HEAD(&gbcodec->dai_list);
|
|
||||||
INIT_LIST_HEAD(&gbcodec->widget_list);
|
|
||||||
INIT_LIST_HEAD(&gbcodec->codec_ctl_list);
|
|
||||||
INIT_LIST_HEAD(&gbcodec->widget_ctl_list);
|
|
||||||
gbcodec->dev_id = dev_id;
|
|
||||||
audio->gbcodec = gbcodec;
|
|
||||||
gbcodec->dev = dev;
|
|
||||||
snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name,
|
|
||||||
dev_name(dev));
|
|
||||||
|
|
||||||
mutex_lock(&gb_codec_list_lock);
|
|
||||||
list_add(&gbcodec->list, &gb_codec_list);
|
|
||||||
mutex_unlock(&gb_codec_list_lock);
|
|
||||||
dev_dbg(dev, "%d:%s Added to codec list\n", gbcodec->dev_id,
|
|
||||||
gbcodec->name);
|
|
||||||
|
|
||||||
return gbcodec;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gbaudio_free_codec(struct device *dev,
|
|
||||||
struct gbaudio_codec_info *gbcodec)
|
|
||||||
{
|
|
||||||
struct gb_audio *audio = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
mutex_lock(&gb_codec_list_lock);
|
|
||||||
if (!gbcodec->mgmt_connection &&
|
|
||||||
list_empty(&gbcodec->dai_list)) {
|
|
||||||
list_del(&gbcodec->list);
|
|
||||||
mutex_unlock(&gb_codec_list_lock);
|
|
||||||
audio->gbcodec = NULL;
|
|
||||||
devm_kfree(dev, gbcodec);
|
|
||||||
} else {
|
|
||||||
mutex_unlock(&gb_codec_list_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is the basic hook get things initialized and registered w/ gb
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* XXX
|
/* XXX
|
||||||
* since BE DAI path is not yet properly closed from above layer,
|
* since BE DAI path is not yet properly closed from above layer,
|
||||||
|
@ -593,7 +497,7 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb)
|
||||||
* In case of BE dailink, need to deactivate APBridge
|
* In case of BE dailink, need to deactivate APBridge
|
||||||
* manually
|
* manually
|
||||||
*/
|
*/
|
||||||
if (gbaudio_dailink.no_pcm && atomic_read(&gb->users)) {
|
if (atomic_read(&gb_dai->users)) {
|
||||||
connection = gb_dai->connection;
|
connection = gb_dai->connection;
|
||||||
/* PB active */
|
/* PB active */
|
||||||
ret = gb_audio_apbridgea_stop_tx(connection, 0);
|
ret = gb_audio_apbridgea_stop_tx(connection, 0);
|
||||||
|
@ -613,38 +517,34 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb)
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_info(dev, "%d:Failed during unregister cport\n",
|
dev_info(dev, "%d:Failed during unregister cport\n",
|
||||||
ret);
|
ret);
|
||||||
atomic_dec(&gb->users);
|
atomic_dec(&gb_dai->users);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gbaudio_codec_probe(struct gb_connection *connection)
|
static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
struct gbaudio_codec_info *gbcodec;
|
struct device *dev = gbcodec->dev;
|
||||||
|
struct gb_connection *connection = gbcodec->mgmt_connection;
|
||||||
|
/*
|
||||||
|
* FIXME: malloc for topology happens via audio_gb driver
|
||||||
|
* should be done within codec driver itself
|
||||||
|
*/
|
||||||
struct gb_audio_topology *topology;
|
struct gb_audio_topology *topology;
|
||||||
struct gb_audio_manager_module_descriptor desc;
|
|
||||||
struct device *dev = &connection->bundle->dev;
|
|
||||||
int dev_id = connection->intf->interface_id;
|
|
||||||
|
|
||||||
dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev));
|
|
||||||
/* get gbcodec data */
|
|
||||||
gbcodec = gbaudio_get_codec(dev, dev_id);
|
|
||||||
if (!gbcodec)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
gbcodec->mgmt_connection = connection;
|
|
||||||
|
|
||||||
ret = gb_connection_enable(connection);
|
ret = gb_connection_enable(connection);
|
||||||
if (ret)
|
if (ret) {
|
||||||
goto base_error;
|
dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbcodec->dev_id = connection->intf->interface_id;
|
||||||
/* fetch topology data */
|
/* fetch topology data */
|
||||||
ret = gb_audio_gb_get_topology(connection, &topology);
|
ret = gb_audio_gb_get_topology(connection, &topology);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(gbcodec->dev,
|
dev_err(dev, "%d:Error while fetching topology\n", ret);
|
||||||
"%d:Error while fetching topology\n", ret);
|
goto tplg_fetch_err;
|
||||||
goto err_connection_disable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* process topology data */
|
/* process topology data */
|
||||||
|
@ -652,7 +552,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "%d:Error while parsing topology data\n",
|
dev_err(dev, "%d:Error while parsing topology data\n",
|
||||||
ret);
|
ret);
|
||||||
goto topology_error;
|
goto tplg_parse_err;
|
||||||
}
|
}
|
||||||
gbcodec->topology = topology;
|
gbcodec->topology = topology;
|
||||||
|
|
||||||
|
@ -673,100 +573,39 @@ static int gbaudio_codec_probe(struct gb_connection *connection)
|
||||||
gbcodec->dais, 1);
|
gbcodec->dais, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "%d:Failed to register codec\n", ret);
|
dev_err(dev, "%d:Failed to register codec\n", ret);
|
||||||
goto parse_error;
|
goto codec_reg_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update DAI links in response to this codec */
|
/* update DAI links in response to this codec */
|
||||||
ret = gbaudio_add_dailinks(gbcodec);
|
ret = gbaudio_add_dailinks(gbcodec);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "%d: Failed to add DAI links\n", ret);
|
dev_err(dev, "%d: Failed to add DAI links\n", ret);
|
||||||
goto codec_reg_error;
|
goto add_dailink_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set registered flag */
|
return 0;
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
gbcodec->codec_registered = 1;
|
|
||||||
|
|
||||||
/* codec cleanup related */
|
add_dailink_err:
|
||||||
atomic_set(&gbcodec->users, 0);
|
|
||||||
|
|
||||||
/* inform above layer for uevent */
|
|
||||||
if (!gbcodec->set_uevent &&
|
|
||||||
(gbcodec->dai_added == gbcodec->num_dais)) {
|
|
||||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
|
||||||
/* prepare for the audio manager */
|
|
||||||
strlcpy(desc.name, gbcodec->name,
|
|
||||||
GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */
|
|
||||||
desc.slot = 1; /* todo */
|
|
||||||
desc.vid = 2; /* todo */
|
|
||||||
desc.pid = 3; /* todo */
|
|
||||||
desc.cport = gbcodec->dev_id;
|
|
||||||
desc.devices = 0x2; /* todo */
|
|
||||||
gbcodec->manager_id = gb_audio_manager_add(&desc);
|
|
||||||
gbcodec->set_uevent = 1;
|
|
||||||
}
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
codec_reg_error:
|
|
||||||
snd_soc_unregister_codec(dev);
|
snd_soc_unregister_codec(dev);
|
||||||
dev->driver = NULL;
|
codec_reg_err:
|
||||||
parse_error:
|
|
||||||
gbaudio_tplg_release(gbcodec);
|
gbaudio_tplg_release(gbcodec);
|
||||||
gbcodec->topology = NULL;
|
gbcodec->topology = NULL;
|
||||||
topology_error:
|
tplg_parse_err:
|
||||||
kfree(topology);
|
kfree(topology);
|
||||||
err_connection_disable:
|
tplg_fetch_err:
|
||||||
gb_connection_disable(connection);
|
gb_connection_disable(gbcodec->mgmt_connection);
|
||||||
base_error:
|
|
||||||
gbcodec->mgmt_connection = NULL;
|
|
||||||
gbaudio_free_codec(dev, gbcodec);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gbaudio_codec_remove(struct gb_connection *connection)
|
static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec)
|
||||||
{
|
{
|
||||||
struct gbaudio_codec_info *gbcodec;
|
gb_audio_cleanup(gbcodec);
|
||||||
struct device *dev = &connection->bundle->dev;
|
|
||||||
int dev_id = connection->intf->interface_id;
|
|
||||||
|
|
||||||
dev_dbg(dev, "Remove device:%d:%s\n", dev_id, dev_name(dev));
|
|
||||||
|
|
||||||
/* get gbcodec data */
|
|
||||||
gbcodec = gbaudio_find_codec(dev, dev_id);
|
|
||||||
if (!gbcodec)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* inform uevent to above layers */
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
if (gbcodec->set_uevent) {
|
|
||||||
/* notify the audio manager */
|
|
||||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 0);
|
|
||||||
gb_audio_manager_remove(gbcodec->manager_id);
|
|
||||||
gbcodec->set_uevent = 0;
|
|
||||||
}
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
|
|
||||||
if (atomic_read(&gbcodec->users)) {
|
|
||||||
dev_err(dev, "Cleanup Error: BE stream not yet closed\n");
|
|
||||||
gb_audio_cleanup(gbcodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink,
|
msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink,
|
||||||
1);
|
1);
|
||||||
gbaudio_remove_dailinks(gbcodec);
|
snd_soc_unregister_codec(gbcodec->dev);
|
||||||
|
|
||||||
snd_soc_unregister_codec(dev);
|
|
||||||
dev->driver = NULL;
|
|
||||||
gbaudio_tplg_release(gbcodec);
|
gbaudio_tplg_release(gbcodec);
|
||||||
kfree(gbcodec->topology);
|
kfree(gbcodec->topology);
|
||||||
gb_connection_disable(connection);
|
gb_connection_disable(gbcodec->mgmt_connection);
|
||||||
gbcodec->mgmt_connection = NULL;
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
gbcodec->codec_registered = 0;
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
gbaudio_free_codec(dev, gbcodec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gbaudio_codec_request_handler(struct gb_operation *op)
|
static int gbaudio_codec_request_handler(struct gb_operation *op)
|
||||||
|
@ -781,93 +620,6 @@ static int gbaudio_codec_request_handler(struct gb_operation *op)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gbaudio_dai_probe(struct gb_connection *connection)
|
|
||||||
{
|
|
||||||
struct gbaudio_dai *dai;
|
|
||||||
struct device *dev = &connection->bundle->dev;
|
|
||||||
int dev_id = connection->intf->interface_id;
|
|
||||||
struct gbaudio_codec_info *gbcodec;
|
|
||||||
struct gb_audio_manager_module_descriptor desc;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev));
|
|
||||||
|
|
||||||
/* get gbcodec data */
|
|
||||||
gbcodec = gbaudio_get_codec(dev, dev_id);
|
|
||||||
if (!gbcodec)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
ret = gb_connection_enable(connection);
|
|
||||||
if (ret)
|
|
||||||
goto err_free_codec;
|
|
||||||
|
|
||||||
/* add/update dai_list*/
|
|
||||||
dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection,
|
|
||||||
NULL);
|
|
||||||
if (!dai) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_connection_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update dai_added count */
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
gbcodec->dai_added++;
|
|
||||||
|
|
||||||
/* inform above layer for uevent */
|
|
||||||
if (!gbcodec->set_uevent && gbcodec->codec_registered &&
|
|
||||||
(gbcodec->dai_added == gbcodec->num_dais)) {
|
|
||||||
/* prepare for the audio manager */
|
|
||||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
|
||||||
strlcpy(desc.name, gbcodec->name,
|
|
||||||
GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */
|
|
||||||
desc.slot = 1; /* todo */
|
|
||||||
desc.vid = 2; /* todo */
|
|
||||||
desc.pid = 3; /* todo */
|
|
||||||
desc.cport = gbcodec->dev_id;
|
|
||||||
desc.devices = 0x2; /* todo */
|
|
||||||
gbcodec->manager_id = gb_audio_manager_add(&desc);
|
|
||||||
gbcodec->set_uevent = 1;
|
|
||||||
}
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_connection_disable:
|
|
||||||
gb_connection_disable(connection);
|
|
||||||
err_free_codec:
|
|
||||||
gbaudio_free_codec(dev, gbcodec);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gbaudio_dai_remove(struct gb_connection *connection)
|
|
||||||
{
|
|
||||||
struct device *dev = &connection->bundle->dev;
|
|
||||||
int dev_id = connection->intf->interface_id;
|
|
||||||
struct gbaudio_codec_info *gbcodec;
|
|
||||||
|
|
||||||
dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev));
|
|
||||||
|
|
||||||
/* get gbcodec data */
|
|
||||||
gbcodec = gbaudio_find_codec(dev, dev_id);
|
|
||||||
if (!gbcodec)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* inform uevent to above layers */
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
if (gbcodec->set_uevent) {
|
|
||||||
/* notify the audio manager */
|
|
||||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 0);
|
|
||||||
gb_audio_manager_remove(gbcodec->manager_id);
|
|
||||||
gbcodec->set_uevent = 0;
|
|
||||||
}
|
|
||||||
/* update dai_added count */
|
|
||||||
gbcodec->dai_added--;
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
|
|
||||||
gb_connection_disable(connection);
|
|
||||||
gbaudio_free_codec(dev, gbcodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gbaudio_dai_request_handler(struct gb_operation *op)
|
static int gbaudio_dai_request_handler(struct gb_operation *op)
|
||||||
{
|
{
|
||||||
struct gb_connection *connection = op->connection;
|
struct gb_connection *connection = op->connection;
|
||||||
|
@ -877,14 +629,14 @@ static int gbaudio_dai_request_handler(struct gb_operation *op)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gb_audio_add_mgmt_connection(struct gb_audio *audio,
|
static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec,
|
||||||
struct greybus_descriptor_cport *cport_desc,
|
struct greybus_descriptor_cport *cport_desc,
|
||||||
struct gb_bundle *bundle)
|
struct gb_bundle *bundle)
|
||||||
{
|
{
|
||||||
struct gb_connection *connection;
|
struct gb_connection *connection;
|
||||||
|
|
||||||
/* Management Cport */
|
/* Management Cport */
|
||||||
if (audio->mgmt_connection) {
|
if (gbcodec->mgmt_connection) {
|
||||||
dev_err(&bundle->dev,
|
dev_err(&bundle->dev,
|
||||||
"Can't have multiple Management connections\n");
|
"Can't have multiple Management connections\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -895,74 +647,99 @@ static int gb_audio_add_mgmt_connection(struct gb_audio *audio,
|
||||||
if (IS_ERR(connection))
|
if (IS_ERR(connection))
|
||||||
return PTR_ERR(connection);
|
return PTR_ERR(connection);
|
||||||
|
|
||||||
connection->private = audio;
|
connection->private = gbcodec;
|
||||||
audio->mgmt_connection = connection;
|
gbcodec->mgmt_connection = connection;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gb_audio_add_data_connection(struct gb_audio *audio,
|
static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec,
|
||||||
struct greybus_descriptor_cport *cport_desc,
|
struct greybus_descriptor_cport *cport_desc,
|
||||||
struct gb_bundle *bundle, int index)
|
struct gb_bundle *bundle)
|
||||||
{
|
{
|
||||||
struct gb_connection *connection;
|
struct gb_connection *connection;
|
||||||
|
struct gbaudio_dai *dai;
|
||||||
|
|
||||||
|
dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL);
|
||||||
|
if (!dai) {
|
||||||
|
dev_err(gbcodec->dev, "DAI Malloc failure\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
|
connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
|
||||||
gbaudio_dai_request_handler);
|
gbaudio_dai_request_handler);
|
||||||
if (IS_ERR(connection))
|
if (IS_ERR(connection)) {
|
||||||
|
devm_kfree(gbcodec->dev, dai);
|
||||||
return PTR_ERR(connection);
|
return PTR_ERR(connection);
|
||||||
|
}
|
||||||
|
|
||||||
connection->private = audio;
|
connection->private = gbcodec;
|
||||||
audio->data_connection[index] = connection;
|
atomic_set(&dai->users, 0);
|
||||||
|
dai->data_cport = connection->intf_cport_id;
|
||||||
|
dai->connection = connection;
|
||||||
|
list_add(&dai->list, &gbcodec->dai_list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* This is the basic hook get things initialized and registered w/ gb
|
||||||
|
*/
|
||||||
|
|
||||||
static int gb_audio_probe(struct gb_bundle *bundle,
|
static int gb_audio_probe(struct gb_bundle *bundle,
|
||||||
const struct greybus_bundle_id *id)
|
const struct greybus_bundle_id *id)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &bundle->dev;
|
||||||
|
struct gbaudio_codec_info *gbcodec;
|
||||||
struct greybus_descriptor_cport *cport_desc;
|
struct greybus_descriptor_cport *cport_desc;
|
||||||
struct gb_audio *audio;
|
struct gb_audio_manager_module_descriptor desc;
|
||||||
|
struct gbaudio_dai *dai, *_dai;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
int count = bundle->num_cports - 1;
|
|
||||||
|
|
||||||
/* There should be at least one Management and one Data cport */
|
/* There should be at least one Management and one Data cport */
|
||||||
if (bundle->num_cports < 2)
|
if (bundle->num_cports < 2)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&gb_codec_list_lock);
|
||||||
/*
|
/*
|
||||||
* There can be only one Management connection and any number of data
|
* There can be only one Management connection and any number of data
|
||||||
* connections.
|
* connections.
|
||||||
*/
|
*/
|
||||||
audio = kzalloc(sizeof(*audio) +
|
gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL);
|
||||||
count * sizeof(*audio->data_connection), GFP_KERNEL);
|
if (!gbcodec) {
|
||||||
if (!audio)
|
mutex_unlock(&gb_codec_list_lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
audio->num_data_connections = count;
|
gbcodec->num_data_connections = bundle->num_cports - 1;
|
||||||
greybus_set_drvdata(bundle, audio);
|
mutex_init(&gbcodec->lock);
|
||||||
|
INIT_LIST_HEAD(&gbcodec->dai_list);
|
||||||
|
INIT_LIST_HEAD(&gbcodec->widget_list);
|
||||||
|
INIT_LIST_HEAD(&gbcodec->codec_ctl_list);
|
||||||
|
INIT_LIST_HEAD(&gbcodec->widget_ctl_list);
|
||||||
|
gbcodec->dev = dev;
|
||||||
|
snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name,
|
||||||
|
dev_name(dev));
|
||||||
|
greybus_set_drvdata(bundle, gbcodec);
|
||||||
|
|
||||||
/* Create all connections */
|
/* Create all connections */
|
||||||
for (count = 0, i = 0; i < bundle->num_cports; i++) {
|
for (i = 0; i < bundle->num_cports; i++) {
|
||||||
cport_desc = &bundle->cport_desc[i];
|
cport_desc = &bundle->cport_desc[i];
|
||||||
|
|
||||||
switch (cport_desc->protocol_id) {
|
switch (cport_desc->protocol_id) {
|
||||||
case GREYBUS_PROTOCOL_AUDIO_MGMT:
|
case GREYBUS_PROTOCOL_AUDIO_MGMT:
|
||||||
ret = gb_audio_add_mgmt_connection(audio, cport_desc,
|
ret = gb_audio_add_mgmt_connection(gbcodec, cport_desc,
|
||||||
bundle);
|
bundle);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto destroy_connections;
|
goto destroy_connections;
|
||||||
break;
|
break;
|
||||||
case GREYBUS_PROTOCOL_AUDIO_DATA:
|
case GREYBUS_PROTOCOL_AUDIO_DATA:
|
||||||
ret = gb_audio_add_data_connection(audio, cport_desc,
|
ret = gb_audio_add_data_connection(gbcodec, cport_desc,
|
||||||
bundle, count);
|
bundle);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto destroy_connections;
|
goto destroy_connections;
|
||||||
|
|
||||||
count++;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&bundle->dev, "Unsupported protocol: 0x%02x\n",
|
dev_err(dev, "Unsupported protocol: 0x%02x\n",
|
||||||
cport_desc->protocol_id);
|
cport_desc->protocol_id);
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto destroy_connections;
|
goto destroy_connections;
|
||||||
|
@ -970,57 +747,88 @@ static int gb_audio_probe(struct gb_bundle *bundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There must be a management cport */
|
/* There must be a management cport */
|
||||||
if (!audio->mgmt_connection) {
|
if (!gbcodec->mgmt_connection) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
dev_err(&bundle->dev, "Missing management connection\n");
|
dev_err(dev, "Missing management connection\n");
|
||||||
goto destroy_connections;
|
goto destroy_connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize management connection */
|
/* Initialize management connection */
|
||||||
ret = gbaudio_codec_probe(audio->mgmt_connection);
|
ret = gbaudio_register_codec(gbcodec);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto destroy_connections;
|
goto destroy_connections;
|
||||||
|
|
||||||
/* Initialize data connections */
|
/* Initialize data connections */
|
||||||
for (i = 0; i < audio->num_data_connections; i++) {
|
list_for_each_entry(dai, &gbcodec->dai_list, list) {
|
||||||
ret = gbaudio_dai_probe(audio->data_connection[i]);
|
ret = gb_connection_enable(dai->connection);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto remove_dai;
|
goto remove_dai;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* inform above layer for uevent */
|
||||||
|
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
||||||
|
/* prepare for the audio manager */
|
||||||
|
strlcpy(desc.name, gbcodec->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
|
||||||
|
desc.slot = 1; /* todo */
|
||||||
|
desc.vid = 2; /* todo */
|
||||||
|
desc.pid = 3; /* todo */
|
||||||
|
desc.cport = gbcodec->dev_id;
|
||||||
|
desc.devices = 0x2; /* todo */
|
||||||
|
gbcodec->manager_id = gb_audio_manager_add(&desc);
|
||||||
|
|
||||||
|
list_add(&gbcodec->list, &gb_codec_list);
|
||||||
|
dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name);
|
||||||
|
mutex_unlock(&gb_codec_list_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
remove_dai:
|
remove_dai:
|
||||||
while (i--)
|
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list)
|
||||||
gbaudio_dai_remove(audio->data_connection[i]);
|
gb_connection_disable(dai->connection);
|
||||||
|
|
||||||
gbaudio_codec_remove(audio->mgmt_connection);
|
gbaudio_unregister_codec(gbcodec);
|
||||||
destroy_connections:
|
destroy_connections:
|
||||||
while (count--)
|
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
|
||||||
gb_connection_destroy(audio->data_connection[count]);
|
gb_connection_destroy(dai->connection);
|
||||||
|
list_del(&dai->list);
|
||||||
|
devm_kfree(dev, dai);
|
||||||
|
}
|
||||||
|
|
||||||
if (audio->mgmt_connection)
|
if (gbcodec->mgmt_connection)
|
||||||
gb_connection_destroy(audio->mgmt_connection);
|
gb_connection_destroy(gbcodec->mgmt_connection);
|
||||||
|
|
||||||
kfree(audio);
|
devm_kfree(dev, gbcodec);
|
||||||
|
mutex_unlock(&gb_codec_list_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gb_audio_disconnect(struct gb_bundle *bundle)
|
static void gb_audio_disconnect(struct gb_bundle *bundle)
|
||||||
{
|
{
|
||||||
struct gb_audio *audio = greybus_get_drvdata(bundle);
|
struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle);
|
||||||
int i;
|
struct gbaudio_dai *dai, *_dai;
|
||||||
|
|
||||||
for (i = audio->num_data_connections - 1; i >= 0; i--) {
|
mutex_lock(&gb_codec_list_lock);
|
||||||
gbaudio_dai_remove(audio->data_connection[i]);
|
list_del(&gbcodec->list);
|
||||||
gb_connection_destroy(audio->data_connection[i]);
|
/* inform uevent to above layers */
|
||||||
|
gb_audio_manager_remove(gbcodec->manager_id);
|
||||||
|
|
||||||
|
mutex_lock(&gbcodec->lock);
|
||||||
|
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list)
|
||||||
|
gb_connection_disable(dai->connection);
|
||||||
|
gbaudio_unregister_codec(gbcodec);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
|
||||||
|
gb_connection_destroy(dai->connection);
|
||||||
|
list_del(&dai->list);
|
||||||
|
devm_kfree(gbcodec->dev, dai);
|
||||||
}
|
}
|
||||||
|
gb_connection_destroy(gbcodec->mgmt_connection);
|
||||||
|
gbcodec->mgmt_connection = NULL;
|
||||||
|
mutex_unlock(&gbcodec->lock);
|
||||||
|
|
||||||
gbaudio_codec_remove(audio->mgmt_connection);
|
devm_kfree(&bundle->dev, gbcodec);
|
||||||
gb_connection_destroy(audio->mgmt_connection);
|
mutex_unlock(&gb_codec_list_lock);
|
||||||
|
|
||||||
kfree(audio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct greybus_bundle_id gb_audio_id_table[] = {
|
static const struct greybus_bundle_id gb_audio_id_table[] = {
|
||||||
|
|
|
@ -84,6 +84,8 @@ struct gbaudio_control {
|
||||||
struct gbaudio_dai {
|
struct gbaudio_dai {
|
||||||
__le16 data_cport;
|
__le16 data_cport;
|
||||||
char name[NAME_SIZE];
|
char name[NAME_SIZE];
|
||||||
|
/* DAI users */
|
||||||
|
atomic_t users;
|
||||||
struct gb_connection *connection;
|
struct gb_connection *connection;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
@ -116,8 +118,9 @@ struct gbaudio_codec_info {
|
||||||
char *dailink_name[MAX_DAIS];
|
char *dailink_name[MAX_DAIS];
|
||||||
int num_dai_links;
|
int num_dai_links;
|
||||||
|
|
||||||
/* topology related */
|
|
||||||
struct gb_connection *mgmt_connection;
|
struct gb_connection *mgmt_connection;
|
||||||
|
size_t num_data_connections;
|
||||||
|
/* topology related */
|
||||||
int num_dais;
|
int num_dais;
|
||||||
int num_kcontrols;
|
int num_kcontrols;
|
||||||
int num_dapm_widgets;
|
int num_dapm_widgets;
|
||||||
|
@ -131,9 +134,6 @@ struct gbaudio_codec_info {
|
||||||
struct snd_soc_dapm_route *routes;
|
struct snd_soc_dapm_route *routes;
|
||||||
struct snd_soc_dai_driver *dais;
|
struct snd_soc_dai_driver *dais;
|
||||||
|
|
||||||
/* codec users */
|
|
||||||
atomic_t users;
|
|
||||||
|
|
||||||
/* lists */
|
/* lists */
|
||||||
struct list_head dai_list;
|
struct list_head dai_list;
|
||||||
struct list_head widget_list;
|
struct list_head widget_list;
|
||||||
|
@ -142,17 +142,8 @@ struct gbaudio_codec_info {
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gb_audio {
|
struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec,
|
||||||
struct gb_connection *mgmt_connection;
|
int data_cport, const char *name);
|
||||||
size_t num_data_connections;
|
|
||||||
struct gbaudio_codec_info *gbcodec;
|
|
||||||
struct gb_connection *data_connection[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec,
|
|
||||||
int data_cport,
|
|
||||||
struct gb_connection *connection,
|
|
||||||
const char *name);
|
|
||||||
int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
|
int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
|
||||||
struct gb_audio_topology *tplg_data);
|
struct gb_audio_topology *tplg_data);
|
||||||
void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec);
|
void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec);
|
||||||
|
|
|
@ -92,8 +92,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
|
||||||
struct gbaudio_ctl_pvt *data;
|
struct gbaudio_ctl_pvt *data;
|
||||||
struct gb_audio_ctl_elem_info *info;
|
struct gb_audio_ctl_elem_info *info;
|
||||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gbcodec = audio->gbcodec;
|
|
||||||
|
|
||||||
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
||||||
info = (struct gb_audio_ctl_elem_info *)data->info;
|
info = (struct gb_audio_ctl_elem_info *)data->info;
|
||||||
|
@ -139,8 +138,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
|
||||||
struct gbaudio_ctl_pvt *data;
|
struct gbaudio_ctl_pvt *data;
|
||||||
struct gb_audio_ctl_elem_value gbvalue;
|
struct gb_audio_ctl_elem_value gbvalue;
|
||||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
||||||
info = (struct gb_audio_ctl_elem_info *)data->info;
|
info = (struct gb_audio_ctl_elem_info *)data->info;
|
||||||
|
@ -187,8 +185,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
|
||||||
struct gbaudio_ctl_pvt *data;
|
struct gbaudio_ctl_pvt *data;
|
||||||
struct gb_audio_ctl_elem_value gbvalue;
|
struct gb_audio_ctl_elem_value gbvalue;
|
||||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
||||||
info = (struct gb_audio_ctl_elem_info *)data->info;
|
info = (struct gb_audio_ctl_elem_info *)data->info;
|
||||||
|
@ -282,8 +279,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
|
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
|
||||||
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
||||||
struct snd_soc_codec *codec = widget->codec;
|
struct snd_soc_codec *codec = widget->codec;
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
||||||
info = (struct gb_audio_ctl_elem_info *)data->info;
|
info = (struct gb_audio_ctl_elem_info *)data->info;
|
||||||
|
@ -317,8 +313,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
|
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
|
||||||
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
|
||||||
struct snd_soc_codec *codec = widget->codec;
|
struct snd_soc_codec *codec = widget->codec;
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gb = audio->gbcodec;
|
|
||||||
|
|
||||||
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
|
||||||
info = (struct gb_audio_ctl_elem_info *)data->info;
|
info = (struct gb_audio_ctl_elem_info *)data->info;
|
||||||
|
@ -524,8 +519,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
|
||||||
int wid;
|
int wid;
|
||||||
int ret;
|
int ret;
|
||||||
struct snd_soc_codec *codec = w->codec;
|
struct snd_soc_codec *codec = w->codec;
|
||||||
struct gb_audio *audio = snd_soc_codec_get_drvdata(codec);
|
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
|
||||||
struct gbaudio_codec_info *gbcodec = audio->gbcodec;
|
|
||||||
|
|
||||||
dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
|
dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
|
||||||
|
|
||||||
|
@ -819,9 +813,9 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec,
|
||||||
int i, ret;
|
int i, ret;
|
||||||
struct snd_soc_dai_driver *gb_dais;
|
struct snd_soc_dai_driver *gb_dais;
|
||||||
struct gb_audio_dai *curr;
|
struct gb_audio_dai *curr;
|
||||||
struct gbaudio_dai *dai, *_dai;
|
|
||||||
size_t size;
|
size_t size;
|
||||||
char dai_name[NAME_SIZE];
|
char dai_name[NAME_SIZE];
|
||||||
|
struct gbaudio_dai *dai;
|
||||||
|
|
||||||
size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais;
|
size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais;
|
||||||
gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
|
gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
|
||||||
|
@ -839,10 +833,10 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec,
|
||||||
/* append dev_id to dai_name */
|
/* append dev_id to dai_name */
|
||||||
snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name,
|
snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name,
|
||||||
gbcodec->dev_id);
|
gbcodec->dev_id);
|
||||||
dai = gbaudio_add_dai(gbcodec, curr->data_cport, NULL,
|
dai = gbaudio_find_dai(gbcodec, curr->data_cport, NULL);
|
||||||
dai_name);
|
|
||||||
if (!dai)
|
if (!dai)
|
||||||
goto error;
|
goto error;
|
||||||
|
strlcpy(dai->name, dai_name, NAME_SIZE);
|
||||||
dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name);
|
dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name);
|
||||||
gb_dais[i].name = dai->name;
|
gb_dais[i].name = dai->name;
|
||||||
curr++;
|
curr++;
|
||||||
|
@ -852,10 +846,6 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
|
|
||||||
list_del(&dai->list);
|
|
||||||
devm_kfree(gbcodec->dev, dai);
|
|
||||||
}
|
|
||||||
devm_kfree(gbcodec->dev, gb_dais);
|
devm_kfree(gbcodec->dev, gb_dais);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -948,68 +938,6 @@ static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb,
|
|
||||||
int data_cport,
|
|
||||||
struct gb_connection *connection,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
struct gbaudio_dai *dai;
|
|
||||||
|
|
||||||
mutex_lock(&gb->lock);
|
|
||||||
dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL);
|
|
||||||
if (!dai) {
|
|
||||||
dev_err(gb->dev, "%s:DAI Malloc failure\n", name);
|
|
||||||
mutex_unlock(&gb->lock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
dai->data_cport = data_cport;
|
|
||||||
dai->connection = connection;
|
|
||||||
|
|
||||||
/* update name */
|
|
||||||
if (name)
|
|
||||||
strlcpy(dai->name, name, NAME_SIZE);
|
|
||||||
list_add(&dai->list, &gb->dai_list);
|
|
||||||
dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name);
|
|
||||||
mutex_unlock(&gb->lock);
|
|
||||||
|
|
||||||
return dai;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec,
|
|
||||||
int data_cport,
|
|
||||||
struct gb_connection *connection,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
struct gbaudio_dai *dai, *_dai;
|
|
||||||
|
|
||||||
/* FIXME need to take care for multiple DAIs */
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
if (list_empty(&gbcodec->dai_list)) {
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
return gbaudio_allocate_dai(gbcodec, data_cport, connection,
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
|
|
||||||
if (dai->data_cport == data_cport) {
|
|
||||||
if (connection)
|
|
||||||
dai->connection = connection;
|
|
||||||
|
|
||||||
if (name)
|
|
||||||
strlcpy(dai->name, name, NAME_SIZE);
|
|
||||||
dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n",
|
|
||||||
data_cport, dai->name);
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
return dai;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_err(gbcodec->dev, "%s:DAI not found\n", name);
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
|
int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
|
||||||
struct gb_audio_topology *tplg_data)
|
struct gb_audio_topology *tplg_data)
|
||||||
{
|
{
|
||||||
|
@ -1074,7 +1002,6 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
|
||||||
|
|
||||||
void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec)
|
void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec)
|
||||||
{
|
{
|
||||||
struct gbaudio_dai *dai, *_dai;
|
|
||||||
struct gbaudio_control *control, *_control;
|
struct gbaudio_control *control, *_control;
|
||||||
struct gbaudio_widget *widget, *_widget;
|
struct gbaudio_widget *widget, *_widget;
|
||||||
|
|
||||||
|
@ -1109,12 +1036,4 @@ void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec)
|
||||||
/* release routes */
|
/* release routes */
|
||||||
if (gbcodec->routes)
|
if (gbcodec->routes)
|
||||||
devm_kfree(gbcodec->dev, gbcodec->routes);
|
devm_kfree(gbcodec->dev, gbcodec->routes);
|
||||||
|
|
||||||
/* release DAIs */
|
|
||||||
mutex_lock(&gbcodec->lock);
|
|
||||||
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
|
|
||||||
list_del(&dai->list);
|
|
||||||
devm_kfree(gbcodec->dev, dai);
|
|
||||||
}
|
|
||||||
mutex_unlock(&gbcodec->lock);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue