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

greybus: audio: Use greybus connection device for codec registration

Use GB Audio mgmt, data protocol ids to register codec module with
GB protocol. And in response to mgmt->connection_init(), register
GB codec driver with ASoC.

Now, using msm8994  machine to register DAI link dynamically on
codec insertion.

ToDos:
- snd_soc_register_codec() uses driver->name to identify device id.
  However, for GB device, .driver{} is not yet populated by GB core.
  Thus, defining dummy structure within codec driver. This should
  come from GB core itself.
  Even existing .driver{} may cause problem in case of multiple
  modules inserted or inserted at a different slot.
- Fix logic for gbcodec->dais & gbcodec->dailinks. Current
  implementation contains some hard coded data with assumption of
  count=1.
- Evaluate definition of 'gbaudio_dailink.be_id' in case of multiple
  DAI links.
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 86a685dd
...@@ -5,10 +5,20 @@ ...@@ -5,10 +5,20 @@
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
#include <linux/kernel.h>
#include <linux/module.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_codec.h"
#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt"
#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data"
static DEFINE_MUTEX(gb_codec_list_lock);
static LIST_HEAD(gb_codec_list);
static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, static int gbcodec_event_spk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event) struct snd_kcontrol *k, int event)
{ {
...@@ -190,10 +200,7 @@ static struct snd_soc_dai_driver gbcodec_dai = { ...@@ -190,10 +200,7 @@ static struct snd_soc_dai_driver gbcodec_dai = {
static int gbcodec_probe(struct snd_soc_codec *codec) static int gbcodec_probe(struct snd_soc_codec *codec)
{ {
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); /* Empty function for now */
gbcodec->codec = codec;
return 0; return 0;
} }
...@@ -263,53 +270,412 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { ...@@ -263,53 +270,412 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = {
.num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes),
}; };
static int gbaudio_codec_probe(struct platform_device *pdev) /*
* GB codec DAI link related
*/
static struct snd_soc_dai_link gbaudio_dailink = {
.name = "PRI_MI2S_RX",
.stream_name = "Primary MI2S Playback",
.platform_name = "msm-pcm-routing",
.cpu_dai_name = "msm-dai-q6-mi2s.0",
.no_pcm = 1,
.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)
{
int ret, i;
char *dai_link_name;
struct snd_soc_dai_link *dai;
struct device *dev = gbcodec->dev;
dai = &gbaudio_dailink;
dai->codec_name = gbcodec->name;
/* FIXME
* allocate memory for DAI links based on count.
* currently num_dai_links=1, so using static struct
*/
gbcodec->num_dai_links = 1;
for (i = 0; i < gbcodec->num_dai_links; i++) {
gbcodec->dailink_name[i] = dai_link_name =
devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL);
snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX",
gbcodec->dev_id, i);
dai->name = dai_link_name;
dai->codec_dai_name = gbcodec->dais[i].name;
}
ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dai, 1);
if (ret) {
dev_err(dev, "%d:Error while adding DAI link\n", ret);
goto err_dai_link;
}
return ret;
err_dai_link:
gbcodec->num_dai_links = i;
gbaudio_remove_dailinks(gbcodec);
return ret;
}
/*
* 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;
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);
gbcodec->dev_id = dev_id;
dev_set_drvdata(dev, gbcodec);
gbcodec->dev = dev;
strlcpy(gbcodec->name, dev_name(dev), NAME_SIZE);
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)
{
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);
dev_set_drvdata(dev, NULL);
devm_kfree(dev, gbcodec);
} else {
mutex_unlock(&gb_codec_list_lock);
}
}
/*
* This is the basic hook get things initialized and registered w/ gb
*/
/*
* GB codec module driver ops
*/
struct device_driver gb_codec_driver = {
.name = "1-8",
.owner = THIS_MODULE,
};
static int gbaudio_codec_probe(struct gb_connection *connection)
{ {
int ret; int ret;
struct gbaudio_codec_info *gbcodec; struct gbaudio_codec_info *gbcodec;
char dai_name[NAME_SIZE]; struct device *dev = &connection->bundle->dev;
int dev_id = connection->bundle->id;
gbcodec = devm_kzalloc(&pdev->dev, sizeof(struct gbaudio_codec_info), dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev));
GFP_KERNEL); /* get gbcodec data */
gbcodec = gbaudio_get_codec(dev, dev_id);
if (!gbcodec) if (!gbcodec)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, gbcodec);
snprintf(dai_name, NAME_SIZE, "%s.%d", "gbcodec_pcm", pdev->id); gbcodec->mgmt_connection = connection;
gbcodec_dai.name = dai_name;
/* update DAI info */
gbcodec->dais = &gbcodec_dai;
/* FIXME */
dev->driver = &gb_codec_driver;
/* register codec */
ret = snd_soc_register_codec(dev, &soc_codec_dev_gbcodec,
gbcodec->dais, 1);
if (ret) {
dev_err(dev, "%d:Failed to register codec\n", ret);
goto base_error;
}
/* update DAI links in response to this codec */
ret = gbaudio_add_dailinks(gbcodec);
if (ret) {
dev_err(dev, "%d: Failed to add DAI links\n", ret);
goto codec_reg_error;
}
/* set registered flag */
mutex_lock(&gbcodec->lock);
gbcodec->codec_registered = 1;
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbcodec, mutex_unlock(&gbcodec->lock);
&gbcodec_dai, 1);
if (!ret)
gbcodec->registered = 1;
return ret; return ret;
codec_reg_error:
snd_soc_unregister_codec(dev);
base_error:
dev->driver = NULL;
gbcodec->mgmt_connection = NULL;
return ret;
} }
static const struct of_device_id gbcodec_of_match[] = { static void gbaudio_codec_remove(struct gb_connection *connection)
{ .compatible = "greybus,codec", }, {
{}, struct gbaudio_codec_info *gbcodec;
struct device *dev = &connection->bundle->dev;
int dev_id = connection->bundle->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;
msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink,
1);
gbaudio_remove_dailinks(gbcodec);
snd_soc_unregister_codec(dev);
dev->driver = NULL;
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_report_event_recv(u8 type, struct gb_operation *op)
{
struct gb_connection *connection = op->connection;
struct gb_audio_streaming_event_request *req = op->request->payload;
dev_warn(&connection->bundle->dev,
"Audio Event received: cport: %u, event: %u\n",
req->data_cport, req->event);
return 0;
}
static struct gb_protocol gb_audio_mgmt_protocol = {
.name = GB_AUDIO_MGMT_DRIVER_NAME,
.id = GREYBUS_PROTOCOL_AUDIO_MGMT,
.major = 0,
.minor = 1,
.connection_init = gbaudio_codec_probe,
.connection_exit = gbaudio_codec_remove,
.request_recv = gbaudio_codec_report_event_recv,
}; };
static int gbaudio_codec_remove(struct platform_device *pdev) 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;
}
static int gbaudio_dai_probe(struct gb_connection *connection)
{ {
snd_soc_unregister_codec(&pdev->dev); struct gbaudio_dai *dai;
struct device *dev = &connection->bundle->dev;
int dev_id = connection->bundle->id;
struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev);
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;
/* add/update dai_list*/
dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection,
NULL);
if (!dai)
return -ENOMEM;
/* update dai_added count */
mutex_lock(&gbcodec->lock);
gbcodec->dai_added++;
mutex_unlock(&gbcodec->lock);
return 0; return 0;
} }
static struct platform_driver gbaudio_codec_driver = { static void gbaudio_dai_remove(struct gb_connection *connection)
.driver = { {
.name = "gbaudio-codec", struct device *dev = &connection->bundle->dev;
.owner = THIS_MODULE, int dev_id = connection->bundle->id;
.of_match_table = gbcodec_of_match, struct gbaudio_codec_info *gbcodec;
},
.probe = gbaudio_codec_probe, dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev));
.remove = gbaudio_codec_remove,
/* get gbcodec data */
gbcodec = gbaudio_find_codec(dev, dev_id);
if (!gbcodec)
return;
/* inform uevent to above layers */
mutex_lock(&gbcodec->lock);
/* update dai_added count */
gbcodec->dai_added--;
mutex_unlock(&gbcodec->lock);
gbaudio_free_codec(dev, gbcodec);
}
static int gbaudio_dai_report_event_recv(u8 type, struct gb_operation *op)
{
struct gb_connection *connection = op->connection;
dev_warn(&connection->bundle->dev, "Audio Event received\n");
return 0;
}
static struct gb_protocol gb_audio_data_protocol = {
.name = GB_AUDIO_DATA_DRIVER_NAME,
.id = GREYBUS_PROTOCOL_AUDIO_DATA,
.major = 0,
.minor = 1,
.connection_init = gbaudio_dai_probe,
.connection_exit = gbaudio_dai_remove,
.request_recv = gbaudio_dai_report_event_recv,
}; };
module_platform_driver(gbaudio_codec_driver);
MODULE_DESCRIPTION("Greybus Audio virtual codec driver"); /*
* This is the basic hook get things initialized and registered w/ gb
*/
static int __init gb_audio_protocol_init(void)
{
int err;
err = gb_protocol_register(&gb_audio_mgmt_protocol);
if (err) {
pr_err("Can't register i2s mgmt protocol driver: %d\n", -err);
return err;
}
err = gb_protocol_register(&gb_audio_data_protocol);
if (err) {
pr_err("Can't register Audio protocol driver: %d\n", -err);
goto err_unregister_audio_mgmt;
}
return 0;
err_unregister_audio_mgmt:
gb_protocol_deregister(&gb_audio_mgmt_protocol);
return err;
}
module_init(gb_audio_protocol_init);
static void __exit gb_audio_protocol_exit(void)
{
gb_protocol_deregister(&gb_audio_data_protocol);
gb_protocol_deregister(&gb_audio_mgmt_protocol);
}
module_exit(gb_audio_protocol_exit);
MODULE_DESCRIPTION("Greybus Audio codec driver");
MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>"); MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:gbaudio-codec"); MODULE_ALIAS("platform:gbaudio-codec");
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "greybus_protocols.h" #include "greybus_protocols.h"
#define NAME_SIZE 32 #define NAME_SIZE 32
#define MAX_DAIS 2 /* APB1, APB2 */
enum { enum {
APB1_PCM = 0, APB1_PCM = 0,
...@@ -67,24 +68,54 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { ...@@ -67,24 +68,54 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = {
GBCODEC_APB2_MUX_REG_DEFAULT, GBCODEC_APB2_MUX_REG_DEFAULT,
}; };
struct gbaudio_dai {
__le16 data_cport;
char name[NAME_SIZE];
struct gb_connection *connection;
struct list_head list;
};
struct gbaudio_codec_info { struct gbaudio_codec_info {
/* module info */
int dev_id; /* check if it should be bundle_id/hd_cport_id */
int vid;
int pid;
int slot;
int type;
int dai_added;
int codec_registered;
char vstr[NAME_SIZE];
char pstr[NAME_SIZE];
struct list_head list;
char name[NAME_SIZE];
/* soc related data */
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct device *dev;
bool usable;
u8 reg[GBCODEC_REG_COUNT]; u8 reg[GBCODEC_REG_COUNT];
int registered;
/* dai_link related */
char card_name[NAME_SIZE];
char *dailink_name[MAX_DAIS];
int num_dai_links;
/* topology related */
struct gb_connection *mgmt_connection;
int num_dais;
int num_kcontrols; int num_kcontrols;
int num_dapm_widgets; int num_dapm_widgets;
int num_dapm_routes; int num_dapm_routes;
struct snd_kcontrol_new *kctls; struct snd_kcontrol_new *kctls;
struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_widget *widgets;
struct snd_soc_dapm_route *routes; struct snd_soc_dapm_route *routes;
struct snd_soc_dai_driver *dais;
/* lists */
struct list_head dai_list;
struct mutex lock; struct mutex lock;
}; };
extern int gb_audio_gb_get_topology(struct gb_connection *connection, /* protocol related */
struct gb_audio_topology **topology);
extern int gb_audio_gb_get_control(struct gb_connection *connection, extern int gb_audio_gb_get_control(struct gb_connection *connection,
uint8_t control_id, uint8_t index, uint8_t control_id, uint8_t index,
struct gb_audio_ctl_elem_value *value); struct gb_audio_ctl_elem_value *value);
......
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