greybus: audio: Added jack support to audio module
authorVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Tue, 29 Mar 2016 11:02:36 +0000 (16:32 +0530)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 31 Mar 2016 17:19:57 +0000 (10:19 -0700)
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>
drivers/staging/greybus/audio_codec.c
drivers/staging/greybus/audio_codec.h
drivers/staging/greybus/audio_module.c
drivers/staging/greybus/audio_topology.c

index 803bab2..66a9548 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
+#include <uapi/linux/input.h>
 
 #include "audio_codec.h"
 #include "audio_apbridgea.h"
@@ -735,10 +736,80 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = {
        .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 ret;
        struct snd_soc_codec *codec;
+       struct snd_soc_jack *jack = NULL;
 
        if (!gbcodec) {
                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;
        }
 
+       ret = gbaudio_init_jack(module, codec);
+       if (ret) {
+               mutex_unlock(&gbcodec->lock);
+               return ret;
+       }
+
        if (module->dapm_widgets)
                snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
                                          module->num_dapm_widgets);
@@ -774,6 +851,15 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
                                                                &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);
        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_card *card = codec->card->snd_card;
+       struct snd_soc_jack *jack, *next_j;
 
        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);
        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);
 
        module->is_connected = 0;
index a2697dd..165b359 100644 (file)
@@ -10,6 +10,7 @@
 #define __LINUX_GBAUDIO_CODEC_H
 
 #include <sound/soc.h>
+#include <sound/jack.h>
 
 #include "greybus.h"
 #include "greybus_protocols.h"
@@ -57,6 +58,11 @@ enum gbcodec_reg_index {
 #define GBCODEC_APB1_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] = {
        GBCODEC_CTL_REG_DEFAULT,
        GBCODEC_MUTE_REG_DEFAULT,
@@ -139,6 +145,15 @@ struct gbaudio_module_info {
        int manager_id;
        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 */
        struct mutex lock;
        int is_connected;
index 94fdadc..9039aa6 100644 (file)
@@ -22,18 +22,150 @@ static LIST_HEAD(gb_codec_list);
  * 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;
-       struct gb_audio_streaming_event_request *req = op->request->payload;
+       int report, button_status;
+
+       dev_warn(module->dev, "Jack Event received: type: %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;
 
-       dev_warn(&connection->bundle->dev,
-                "Audio Event received: cport: %u, event: %u\n",
+       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);
 
        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)
 {
        struct gb_connection *connection = op->connection;
index 9e36fb2..79161c1 100644 (file)
@@ -669,6 +669,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
        case snd_soc_dapm_hp:
                *dw = (struct snd_soc_dapm_widget)
                        SND_SOC_DAPM_HP(w->name, gbcodec_event_hp);
+               module->num_jacks++;
                break;
        case snd_soc_dapm_mic:
                *dw = (struct snd_soc_dapm_widget)