aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Cross <xobs@kosagi.com>2014-02-26 14:26:16 +0800
committerSean Cross <xobs@kosagi.com>2014-10-15 21:01:10 +0800
commit1d0d3cd462dc40b6c3be5bff20fac0ad0fb35856 (patch)
treedc7d7491e41de73b269cafc0c546c669a60568ae
parentb844253e42141b613d50c55aaeb13ba64220fa59 (diff)
downloadnovena-linux-1d0d3cd462dc40b6c3be5bff20fac0ad0fb35856.tar.gz
novena-linux-1d0d3cd462dc40b6c3be5bff20fac0ad0fb35856.tar.bz2
novena-linux-1d0d3cd462dc40b6c3be5bff20fac0ad0fb35856.zip
sound: soc: jack: Add jack kcontrol
This allows SoC jacks to work with headphone plugging.
-rw-r--r--include/sound/jack.h4
-rw-r--r--sound/core/Kconfig1
-rw-r--r--sound/core/ctljack.c12
-rw-r--r--sound/core/jack.c76
4 files changed, 88 insertions, 5 deletions
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 58916573db5..cbe896e6f28 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -26,6 +26,7 @@
#include <sound/core.h>
struct input_dev;
+struct snd_kcontrol;
/**
* Jack types which can be reported. These values are used as a
@@ -58,11 +59,12 @@ enum snd_jack_types {
struct snd_jack {
struct input_dev *input_dev;
+ struct snd_kcontrol *kctl[SND_JACK_SWITCH_TYPES]; /* control for each key */
int registered;
int type;
const char *id;
char name[100];
- unsigned int key[6]; /* Keep in sync with definitions above */
+ unsigned int key[SND_JACK_SWITCH_TYPES];
void *private_data;
void (*private_free)(struct snd_jack *);
};
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 313f22e9d92..67df60877af 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -23,6 +23,7 @@ config SND_COMPRESS_OFFLOAD
# to avoid having to force INPUT on.
config SND_JACK
bool
+ select SND_KCTL_JACK
config SND_SEQUENCER
tristate "Sequencer support"
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
index e4b38fbe51d..d0370c7933e 100644
--- a/sound/core/ctljack.c
+++ b/sound/core/ctljack.c
@@ -14,7 +14,17 @@
#include <sound/core.h>
#include <sound/control.h>
-#define jack_detect_kctl_info snd_ctl_boolean_mono_info
+#define jack_detect_kctl_info jack_ctl_integer_info
+
+int jack_ctl_integer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 0x10000U;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ return 0;
+}
static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 8658578eb58..8e456a8974c 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/jack.h>
+#include <sound/control.h>
#include <sound/core.h>
static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
@@ -54,25 +55,61 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
static int snd_jack_dev_free(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
+ int i;
if (jack->private_free)
jack->private_free(jack);
snd_jack_dev_disconnect(device);
+ /* Free available KControls*/
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; ++i)
+ if (jack->kctl[i])
+ snd_ctl_remove(device->card, jack->kctl[i]);
+
kfree(jack->id);
kfree(jack);
return 0;
}
+const char * jack_get_name_by_key(const char *name, int key)
+{
+ char *jack_name;
+ size_t jack_name_size;
+ const char *key_name;
+
+ switch(key) {
+ case SW_HEADPHONE_INSERT: key_name = "Headphone"; break;
+ case SW_MICROPHONE_INSERT: key_name = "Mic"; break;
+ case SW_LINEOUT_INSERT: key_name = "Line Out"; break;
+ case SW_JACK_PHYSICAL_INSERT: key_name = "Mechanical"; break;
+ case SW_VIDEOOUT_INSERT: key_name = "Video Out"; break;
+ case SW_LINEIN_INSERT: key_name = "Line In"; break;
+ default: key_name = "Unknown";
+ }
+
+ /* Avoid duplicate name in KControl */
+ if (strcmp(name, key_name) != 0) {
+ /* allocate necessary memory space only */
+ jack_name_size = strlen(name) + strlen(key_name) + 4;
+ jack_name = kmalloc(jack_name_size, GFP_KERNEL);
+
+ snprintf(jack_name, jack_name_size, "%s (%s)", name, key_name);
+ } else {
+ jack_name = (char *)name;
+ }
+
+ return jack_name;
+}
+
static int snd_jack_dev_register(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
struct snd_card *card = device->card;
int err, i;
- snprintf(jack->name, sizeof(jack->name), "%s %s",
+ snprintf(jack->name, sizeof(jack->name), "%s %s Jack",
card->shortname, jack->id);
jack->input_dev->name = jack->name;
@@ -93,6 +130,18 @@ static int snd_jack_dev_register(struct snd_device *device)
input_set_capability(jack->input_dev, EV_KEY, jack->key[i]);
}
+ /* We don't need to free the control, it's freed by snd_ctl_add itself
+ if an error occur */
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; ++i)
+ if (jack->kctl[i]) {
+ err = snd_ctl_add(card, jack->kctl[i]);
+ if (err < 0) {
+ pr_notice("%s: ALSA Jack Control not available for '%s'\n", __func__,
+ jack_get_name_by_key(jack->id, jack_switch_types[i]));
+ jack->kctl[i] = NULL;
+ }
+ }
+
err = input_register_device(jack->input_dev);
if (err == 0)
jack->registered = 1;
@@ -117,6 +166,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jjack)
{
struct snd_jack *jack;
+ struct snd_kcontrol *kctl;
int err;
int i;
static struct snd_device_ops ops = {
@@ -142,10 +192,20 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
jack->type = type;
for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
- if (type & (1 << i))
+ if (type & (1 << i)) {
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
+ /* card is the private_data */
+ kctl = snd_kctl_jack_new(jack_get_name_by_key(id, jack_switch_types[i]), 0, card);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto fail_kctl;
+ }
+
+ jack->kctl[i] = kctl;
+ }
+
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
goto fail_input;
@@ -154,6 +214,11 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
return 0;
+fail_kctl:
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; ++i)
+ if (jack->kctl[i])
+ snd_ctl_remove(card, jack->kctl[i]);
+
fail_input:
input_free_device(jack->input_dev);
kfree(jack->id);
@@ -245,10 +310,15 @@ void snd_jack_report(struct snd_jack *jack, int status)
for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
int testbit = 1 << i;
- if (jack->type & testbit)
+ if (jack->type & testbit) {
input_report_switch(jack->input_dev,
jack_switch_types[i],
status & testbit);
+
+ /* Update ALSA KControl interface */
+ snd_kctl_jack_report((struct snd_card *)jack->kctl[i]->private_data,
+ jack->kctl[i], status & testbit);
+ }
}
input_sync(jack->input_dev);