Re: ALSA mixer volume control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Henrique,

Here are the rest of the changes.

One comment/question for these: I created a function
volume_alsa_notify_change() to be called when that the volume has been
changed outside of alsa callbacks (to in turn notify software mixers
to change their sliders, etc).  Since I'm not completely familiar with
the driver, I'm not sure I placed it everywhere it is needed.  In
particular, I wonder about putting a call in
hotkey_compare_and_issue_event.  I put one in there initially, but it
didn't do anything on my laptop.

Any comments on this or the previous patch are welcome.

Thanks,
Lorne
=== modified file 'drivers/misc/thinkpad_acpi.c'
--- old/drivers/misc/thinkpad_acpi.c	2009-01-10 17:26:33 +0000
+++ new/drivers/misc/thinkpad_acpi.c	2009-01-30 23:42:50 +0000
@@ -80,6 +80,11 @@
 
 #include <linux/pci_ids.h>
 
+/* ALSA */
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+/* end ALSA */
 
 /* ThinkPad CMOS commands */
 #define TP_CMOS_VOLUME_DOWN	0
@@ -290,6 +295,8 @@
 	unsigned int led;
 };
 
+static void volume_alsa_notify_change(void);
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -2402,6 +2409,8 @@
 			} else {
 				unk_ev = 1;
 			}
+			if(scancode == 0x14 || scancode == 0x15 || scancode == 0x16)
+				volume_alsa_notify_change();
 			break;
 		case 2:
 			/* Wakeup reason */
@@ -5059,15 +5068,187 @@
  * Volume subdriver
  */
 
+#define VOLUME_ALSA_DRIVER_NAME "Thinkpad Vol"
+#define VOLUME_ALSA_SHORT_NAME "Speaker"
+#define VOLUME_ALSA_LONG_NAME "Thinkpad Speaker Volume"
+
 static int volume_offset = 0x30;
 static struct mutex volume_write_mutex;
+static int volume_cmos_set_level_mute(u8 new_level, u8 new_mute);
+
+static int volume_alsa_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *volume_alsa_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+
+static struct snd_card * volume_alsa_card;
+static struct snd_ctl_elem_id * volume_alsa_mixer_vol_id;
+static struct snd_ctl_elem_id * volume_alsa_mixer_mute_id;
+
+static void volume_alsa_notify_change(void){
+	if(volume_alsa_mixer_vol_id)
+		snd_ctl_notify(volume_alsa_card, SNDRV_CTL_EVENT_MASK_VALUE,
+					   volume_alsa_mixer_vol_id);
+	if(volume_alsa_mixer_mute_id)
+		snd_ctl_notify(volume_alsa_card, SNDRV_CTL_EVENT_MASK_VALUE,
+					   volume_alsa_mixer_mute_id);
+	return;
+}
+
+/* ALSA Callbacks */
+static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 15;
+	return 0;
+}
+
+static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol,
+							   struct snd_ctl_elem_value *ucontrol)
+{
+	u8 level;
+	if (!acpi_ec_read(volume_offset, &level))
+		return -EIO;
+
+	ucontrol->value.integer.value[0] = level & 0xf;
+	return 0;
+}
+
+static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol,
+							   struct snd_ctl_elem_value *ucontrol)
+{
+	u8 level;
+	if (!acpi_ec_read(volume_offset, &level))
+		return -EIO;
+
+	/* ALSA control is anti-mute.  ie., 1=>mute off */
+	ucontrol->value.integer.value[0] = (level & 0x40) ? 0 : 1;
+	return 0;
+}
+
+static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
+							   struct snd_ctl_elem_value *ucontrol)
+{
+	u8 cur_level;
+	u8 new_level;
+	u8 mute;
+	int changed = 0;
+	if (!acpi_ec_read(volume_offset, &cur_level))
+		return -EIO;
+
+	mute = cur_level & 0x40;
+	cur_level = cur_level & 0xf;
+	new_level = (u8)(ucontrol->value.integer.value[0]);
+	if (cur_level != new_level) {
+		mutex_lock(&volume_write_mutex);
+		if(volume_cmos_set_level_mute(new_level, mute) ||
+		   !acpi_ec_write(volume_offset, new_level + mute)) {
+			mutex_unlock(&volume_write_mutex);
+			return -EIO;
+		}
+		mutex_unlock(&volume_write_mutex);
+		changed = 1;
+	}
+	return changed;
+}
+
+static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
+							   struct snd_ctl_elem_value *ucontrol)
+{
+	u8 cur_mute, new_mute, level;
+	int changed = 0;
+	if (!acpi_ec_read(volume_offset, &cur_mute))
+		return -EIO;
+
+	level = cur_mute & 0xf;
+	cur_mute = cur_mute & 0x40;
+	/* ALSA control is anti-mute.  ie., 1=>mute off */
+	new_mute = (ucontrol->value.integer.value[0]) ? 0 : 0x40;
+	if (cur_mute != new_mute) {
+		mutex_lock(&volume_write_mutex);
+		if(volume_cmos_set_level_mute(level, new_mute) ||
+		   !acpi_ec_write(volume_offset, level + new_mute))	{
+			mutex_unlock(&volume_write_mutex);
+			return -EIO;
+		}
+		mutex_unlock(&volume_write_mutex);
+		changed = 1;
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Playback Volume",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = volume_alsa_vol_info,
+	.get = volume_alsa_vol_get,
+	.put = volume_alsa_vol_put
+};
+
+static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Playback Switch",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_ctl_boolean_mono_info, /* Use builtin ALSA def */
+	.get = volume_alsa_mute_get,
+	.put = volume_alsa_mute_put
+};
+/* end ALSA Callbacks */
 
 static int __init volume_init(struct ibm_init_struct *iibm)
 {
+
+	int dev = tpacpi_pdev->id;
+	struct snd_kcontrol * vol_ctl;
+	struct snd_kcontrol * mute_ctl;
+
 	vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n");
 
+	volume_alsa_mixer_vol_id = NULL;
+	volume_alsa_mixer_mute_id = NULL;
 	mutex_init(&volume_write_mutex);
-
+	
+	/* ALSA initializations. */
+	volume_alsa_card = snd_card_new(volume_alsa_index[dev],
+									volume_alsa_id[dev],THIS_MODULE,0);
+	if(!volume_alsa_card){
+		vdbg_printk(TPACPI_DBG_INIT, "Failed to create ALSA card\n");
+		return 0;
+	}
+	
+	strcpy(volume_alsa_card->driver, VOLUME_ALSA_DRIVER_NAME);
+	strcpy(volume_alsa_card->shortname, VOLUME_ALSA_SHORT_NAME);
+	strcpy(volume_alsa_card->longname, VOLUME_ALSA_LONG_NAME);
+	strcpy(volume_alsa_card->mixername, VOLUME_ALSA_DRIVER_NAME);
+
+	/* Create Controls */
+	vol_ctl = snd_ctl_new1(&volume_alsa_control_vol,NULL);
+	if(snd_ctl_add(volume_alsa_card,vol_ctl)){
+		vdbg_printk(TPACPI_DBG_INIT, "Failed to create ALSA volume mixer\n");
+		snd_card_free(volume_alsa_card);
+		return 0;
+	}
+
+	mute_ctl = snd_ctl_new1(&volume_alsa_control_mute,NULL);
+	if(snd_ctl_add(volume_alsa_card,mute_ctl)){
+		vdbg_printk(TPACPI_DBG_INIT, "Failed to create ALSA mute mixer\n");
+		snd_card_free(volume_alsa_card);
+		return 0;
+	}
+
+	snd_card_set_dev(volume_alsa_card, &tpacpi_pdev->dev);
+
+	if(snd_card_register(volume_alsa_card)){
+		vdbg_printk(TPACPI_DBG_INIT, "Failed to register ALSA card\n");
+		snd_card_free(volume_alsa_card);
+	}
+
+	volume_alsa_mixer_vol_id = &vol_ctl->id;
+	volume_alsa_mixer_mute_id = &mute_ctl->id;
 	return 0;
 }
 
@@ -5177,13 +5358,21 @@
 	}
 	mutex_unlock(&volume_write_mutex);
 
+	volume_alsa_notify_change();
+
 	return 0;
 }
 
+static void volume_exit(void)
+{
+	snd_card_free(volume_alsa_card);
+}
+
 static struct ibm_struct volume_driver_data = {
 	.name = "volume",
 	.read = volume_read,
 	.write = volume_write,
+	.exit = volume_exit
 };
 
 /*************************************************************************

------------------------------------------------------------------------------
This SF.net email is sponsored by:
SourcForge Community
SourceForge wants to tell your story.
http://p.sf.net/sfu/sf-spreadtheword
_______________________________________________
ibm-acpi-devel mailing list
ibm-acpi-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/ibm-acpi-devel

[Index of Archives]     [Linux ACPI]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Photo]     [Yosemite Photos]     [Yosemite Advice]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux