mirror of https://gitee.com/openkylin/linux.git
greybus: audio: Added jack support to audio module
Register jack with ASoC sound card in case audio module populates it via codec FW. Currently, only a single jack with 4 buttons can be registered for each module. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
6da549ec85
commit
64a7e2cceb
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
|
#include <uapi/linux/input.h>
|
||||||
|
|
||||||
#include "audio_codec.h"
|
#include "audio_codec.h"
|
||||||
#include "audio_apbridgea.h"
|
#include "audio_apbridgea.h"
|
||||||
|
@ -735,10 +736,80 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = {
|
||||||
.digital_mute = gbcodec_digital_mute,
|
.digital_mute = gbcodec_digital_mute,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int gbaudio_init_jack(struct gbaudio_module_info *module,
|
||||||
|
struct snd_soc_codec *codec)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!module->num_jacks)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* register jack(s) in case any */
|
||||||
|
if (module->num_jacks > 1) {
|
||||||
|
dev_err(module->dev, "Currently supports max=1 jack\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack",
|
||||||
|
module->dev_id);
|
||||||
|
ret = snd_soc_jack_new(codec, module->jack_name, GBCODEC_JACK_MASK,
|
||||||
|
&module->headset_jack);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to create new jack\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack",
|
||||||
|
module->dev_id);
|
||||||
|
ret = snd_soc_jack_new(codec, module->button_name,
|
||||||
|
GBCODEC_JACK_BUTTON_MASK, &module->button_jack);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to create button jack\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0,
|
||||||
|
KEY_MEDIA);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to set BTN_0\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1,
|
||||||
|
KEY_VOICECOMMAND);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to set BTN_1\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2,
|
||||||
|
KEY_VOLUMEUP);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to set BTN_2\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3,
|
||||||
|
KEY_VOLUMEDOWN);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(module->dev, "Failed to set BTN_0\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME
|
||||||
|
* verify if this is really required
|
||||||
|
set_bit(INPUT_PROP_NO_DUMMY_RELEASE,
|
||||||
|
module->button_jack.jack->input_dev->propbit);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int gbaudio_register_module(struct gbaudio_module_info *module)
|
int gbaudio_register_module(struct gbaudio_module_info *module)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct snd_soc_codec *codec;
|
struct snd_soc_codec *codec;
|
||||||
|
struct snd_soc_jack *jack = NULL;
|
||||||
|
|
||||||
if (!gbcodec) {
|
if (!gbcodec) {
|
||||||
dev_err(module->dev, "GB Codec not yet probed\n");
|
dev_err(module->dev, "GB Codec not yet probed\n");
|
||||||
|
@ -756,6 +827,12 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = gbaudio_init_jack(module, codec);
|
||||||
|
if (ret) {
|
||||||
|
mutex_unlock(&gbcodec->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (module->dapm_widgets)
|
if (module->dapm_widgets)
|
||||||
snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
|
snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
|
||||||
module->num_dapm_widgets);
|
module->num_dapm_widgets);
|
||||||
|
@ -774,6 +851,15 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
|
||||||
&codec->dapm);
|
&codec->dapm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_JACK
|
||||||
|
/* register jack devices for this module from codec->jack_list */
|
||||||
|
list_for_each_entry(jack, &codec->jack_list, list) {
|
||||||
|
if ((jack == &module->headset_jack)
|
||||||
|
|| (jack == &module->button_jack))
|
||||||
|
snd_device_register(codec->card->snd_card, jack->jack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
list_add(&module->list, &gbcodec->module_list);
|
list_add(&module->list, &gbcodec->module_list);
|
||||||
dev_dbg(codec->dev, "Registered %s module\n", module->name);
|
dev_dbg(codec->dev, "Registered %s module\n", module->name);
|
||||||
|
|
||||||
|
@ -851,6 +937,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = gbcodec->codec;
|
struct snd_soc_codec *codec = gbcodec->codec;
|
||||||
struct snd_card *card = codec->card->snd_card;
|
struct snd_card *card = codec->card->snd_card;
|
||||||
|
struct snd_soc_jack *jack, *next_j;
|
||||||
|
|
||||||
dev_dbg(codec->dev, "Unregister %s module\n", module->name);
|
dev_dbg(codec->dev, "Unregister %s module\n", module->name);
|
||||||
|
|
||||||
|
@ -862,6 +949,17 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
|
||||||
dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
|
dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
|
||||||
mutex_lock(&module->lock);
|
mutex_lock(&module->lock);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_JACK
|
||||||
|
/* free jack devices for this module from codec->jack_list */
|
||||||
|
list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) {
|
||||||
|
if ((jack == &module->headset_jack)
|
||||||
|
|| (jack == &module->button_jack)) {
|
||||||
|
snd_device_free(codec->card->snd_card, jack->jack);
|
||||||
|
list_del(&jack->list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
gbaudio_codec_cleanup(module);
|
gbaudio_codec_cleanup(module);
|
||||||
|
|
||||||
module->is_connected = 0;
|
module->is_connected = 0;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#define __LINUX_GBAUDIO_CODEC_H
|
#define __LINUX_GBAUDIO_CODEC_H
|
||||||
|
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
|
#include <sound/jack.h>
|
||||||
|
|
||||||
#include "greybus.h"
|
#include "greybus.h"
|
||||||
#include "greybus_protocols.h"
|
#include "greybus_protocols.h"
|
||||||
|
@ -57,6 +58,11 @@ enum gbcodec_reg_index {
|
||||||
#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00
|
#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00
|
||||||
#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00
|
#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00
|
||||||
|
|
||||||
|
#define GBCODEC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
|
||||||
|
SND_JACK_LINEIN | SND_JACK_UNSUPPORTED)
|
||||||
|
#define GBCODEC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
|
||||||
|
SND_JACK_BTN_2 | SND_JACK_BTN_3)
|
||||||
|
|
||||||
static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = {
|
static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = {
|
||||||
GBCODEC_CTL_REG_DEFAULT,
|
GBCODEC_CTL_REG_DEFAULT,
|
||||||
GBCODEC_MUTE_REG_DEFAULT,
|
GBCODEC_MUTE_REG_DEFAULT,
|
||||||
|
@ -139,6 +145,15 @@ struct gbaudio_module_info {
|
||||||
int manager_id;
|
int manager_id;
|
||||||
char name[NAME_SIZE];
|
char name[NAME_SIZE];
|
||||||
|
|
||||||
|
/* jack related */
|
||||||
|
char jack_name[NAME_SIZE];
|
||||||
|
char button_name[NAME_SIZE];
|
||||||
|
int num_jacks;
|
||||||
|
int jack_type;
|
||||||
|
int button_status;
|
||||||
|
struct snd_soc_jack headset_jack;
|
||||||
|
struct snd_soc_jack button_jack;
|
||||||
|
|
||||||
/* used by codec_ops */
|
/* used by codec_ops */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
int is_connected;
|
int is_connected;
|
||||||
|
|
|
@ -22,18 +22,150 @@ static LIST_HEAD(gb_codec_list);
|
||||||
* gb_snd management functions
|
* gb_snd management functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gbaudio_codec_request_handler(struct gb_operation *op)
|
static int gbaudio_request_jack(struct gbaudio_module_info *module,
|
||||||
|
struct gb_audio_jack_event_request *req)
|
||||||
{
|
{
|
||||||
struct gb_connection *connection = op->connection;
|
int report, button_status;
|
||||||
struct gb_audio_streaming_event_request *req = op->request->payload;
|
|
||||||
|
|
||||||
dev_warn(&connection->bundle->dev,
|
dev_warn(module->dev, "Jack Event received: type: %u, event: %u\n",
|
||||||
"Audio Event received: cport: %u, event: %u\n",
|
req->widget_type, req->event);
|
||||||
|
|
||||||
|
mutex_lock(&module->lock);
|
||||||
|
if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) {
|
||||||
|
module->jack_type = 0;
|
||||||
|
button_status = module->button_status;
|
||||||
|
module->button_status = 0;
|
||||||
|
mutex_unlock(&module->lock);
|
||||||
|
if (button_status)
|
||||||
|
snd_soc_jack_report(&module->button_jack, 0,
|
||||||
|
GBCODEC_JACK_BUTTON_MASK);
|
||||||
|
snd_soc_jack_report(&module->headset_jack, 0,
|
||||||
|
GBCODEC_JACK_MASK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
report &= ~GBCODEC_JACK_MASK;
|
||||||
|
/* currently supports Headphone, Headset & Lineout only */
|
||||||
|
if (req->widget_type && GB_AUDIO_WIDGET_TYPE_HP)
|
||||||
|
report |= SND_JACK_HEADPHONE & GBCODEC_JACK_MASK;
|
||||||
|
|
||||||
|
if (req->widget_type && GB_AUDIO_WIDGET_TYPE_MIC)
|
||||||
|
report = SND_JACK_MICROPHONE & GBCODEC_JACK_MASK;
|
||||||
|
|
||||||
|
if (req->widget_type && GB_AUDIO_WIDGET_TYPE_LINE)
|
||||||
|
report = (report & GBCODEC_JACK_MASK) |
|
||||||
|
SND_JACK_LINEOUT | SND_JACK_LINEIN;
|
||||||
|
|
||||||
|
if (module->jack_type)
|
||||||
|
dev_warn(module->dev, "Modifying jack from %d to %d\n",
|
||||||
|
module->jack_type, report);
|
||||||
|
|
||||||
|
module->jack_type = report;
|
||||||
|
mutex_unlock(&module->lock);
|
||||||
|
snd_soc_jack_report(&module->headset_jack, report, GBCODEC_JACK_MASK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gbaudio_request_button(struct gbaudio_module_info *module,
|
||||||
|
struct gb_audio_button_event_request *req)
|
||||||
|
{
|
||||||
|
int soc_button_id, report;
|
||||||
|
|
||||||
|
dev_warn(module->dev, "Button Event received: id: %u, event: %u\n",
|
||||||
|
req->button_id, req->event);
|
||||||
|
|
||||||
|
/* currently supports 4 buttons only */
|
||||||
|
mutex_lock(&module->lock);
|
||||||
|
if (!module->jack_type) {
|
||||||
|
dev_err(module->dev, "Jack not present. Bogus event!!\n");
|
||||||
|
mutex_unlock(&module->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
report = module->button_status & GBCODEC_JACK_BUTTON_MASK;
|
||||||
|
|
||||||
|
switch (req->button_id) {
|
||||||
|
case 1:
|
||||||
|
soc_button_id = SND_JACK_BTN_0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
soc_button_id = SND_JACK_BTN_1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
soc_button_id = SND_JACK_BTN_2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
soc_button_id = SND_JACK_BTN_3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(module->dev, "Invalid button request received\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS)
|
||||||
|
report = report | soc_button_id;
|
||||||
|
else
|
||||||
|
report = report & ~soc_button_id;
|
||||||
|
|
||||||
|
module->button_status = report;
|
||||||
|
|
||||||
|
mutex_unlock(&module->lock);
|
||||||
|
|
||||||
|
snd_soc_jack_report(&module->button_jack, report,
|
||||||
|
GBCODEC_JACK_BUTTON_MASK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gbaudio_request_stream(struct gbaudio_module_info *module,
|
||||||
|
struct gb_audio_streaming_event_request *req)
|
||||||
|
{
|
||||||
|
dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n",
|
||||||
req->data_cport, req->event);
|
req->data_cport, req->event);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int gbaudio_codec_request_handler(struct gb_operation *op)
|
||||||
|
{
|
||||||
|
struct gb_connection *connection = op->connection;
|
||||||
|
struct gbaudio_module_info *module =
|
||||||
|
greybus_get_drvdata(connection->bundle);
|
||||||
|
struct gb_operation_msg_hdr *header = op->request->header;
|
||||||
|
struct gb_audio_streaming_event_request *stream_req;
|
||||||
|
struct gb_audio_jack_event_request *jack_req;
|
||||||
|
struct gb_audio_button_event_request *button_req;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (header->type) {
|
||||||
|
case GB_AUDIO_TYPE_STREAMING_EVENT:
|
||||||
|
stream_req = op->request->payload;
|
||||||
|
ret = gbaudio_request_stream(module, stream_req);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_AUDIO_TYPE_JACK_EVENT:
|
||||||
|
jack_req = op->request->payload;
|
||||||
|
ret = gbaudio_request_jack(module, jack_req);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_AUDIO_TYPE_BUTTON_EVENT:
|
||||||
|
button_req = op->request->payload;
|
||||||
|
ret = gbaudio_request_button(module, button_req);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dev_err(&connection->bundle->dev,
|
||||||
|
"Invalid Audio Event received\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int gbaudio_data_connection_request_handler(struct gb_operation *op)
|
static int gbaudio_data_connection_request_handler(struct gb_operation *op)
|
||||||
{
|
{
|
||||||
struct gb_connection *connection = op->connection;
|
struct gb_connection *connection = op->connection;
|
||||||
|
|
|
@ -669,6 +669,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
|
||||||
case snd_soc_dapm_hp:
|
case snd_soc_dapm_hp:
|
||||||
*dw = (struct snd_soc_dapm_widget)
|
*dw = (struct snd_soc_dapm_widget)
|
||||||
SND_SOC_DAPM_HP(w->name, gbcodec_event_hp);
|
SND_SOC_DAPM_HP(w->name, gbcodec_event_hp);
|
||||||
|
module->num_jacks++;
|
||||||
break;
|
break;
|
||||||
case snd_soc_dapm_mic:
|
case snd_soc_dapm_mic:
|
||||||
*dw = (struct snd_soc_dapm_widget)
|
*dw = (struct snd_soc_dapm_widget)
|
||||||
|
|
Loading…
Reference in New Issue