Hi,
Attached are two patches that enable automatic heaphone detection for the
OSS sound drivers on the h5xxx ipaqs.
These patches are made against today's (March 29) CVS HEAD of the 2.4
kernel.
These patches are based HEAVILY on Luca Bedboi's ALSA patches and
complete the missing functionality. They should be usable for ALSA
*almost* as is, except for the names of the occasional structure and
perhaps the actual call to switch between SOUND_MASK_LINE and
SOUND_MASK_MIC as the SOUND_MIXER_WRITE_RECSRC argument.
Patch1 includes minor chages to ak4535.c to remove some argument checks
that interfered with the autodetection. (I think this code was not
strictly necessary anyway, as the code that follows works correclty without
the checks.)
Patch2 includes the changes, which are localized to h5400-audio.c They
pretty much follow Luca's code. When I get the headphone insert/removal
interrupt, I check the value of the gpio line to see if the headphones are
currently inserted or not and then set the mixer to line/mic accordingly.
I also do an autodetection at "startup" so that the configuration will be
set even if the headset was inserted/removed prior to the application
starting.
(Note: The headphone status bit in the ak4535 control registers did not
seem to work for me. It would be 1 when the mic power was enabled and 0
otherswise, regardless of any headphones actually being inserted. So I
don't use that register at all.)
Note: This automatic detection does not disable manual switching in
software betwen line and mic -- so it is possible for the headphone to be
plugged in and then for the application software to switch back to internal
mic. I'm not sure whether this is a feature or not. I made a quick
attempt to disable this, but I couldn't get the code to run without
unresolved symbols, which I couldn't seem to clear up without making more
radical changes to ak4535.c ak4535.h h5400-audio.c, etc --- so I punted.
It would have ended up being 3 lines of code in ak4535.c (e.g. in the
mixer_ioctl() func, don't let the user switch to internal mic if the
headphone is currently inserted...)
Enjoy -- I hope this makes it into the repository soon, (possibly in time
for 0.8.2!?!?!?!?)
Jim
-- ---------------------------------------------------------------------- James T. Kaba Sarnoff Corporation There are 10 kinds of people in the world: jkaba_at_sarnoff.com those who understand binary, and those who don't. 609-734-2246
diff -Naur linux_head_march29/kernel/drivers/sound/ak4535.c linux_jtk/kernel/drivers/sound/ak4535.c
--- linux_head_march29/kernel/drivers/sound/ak4535.c 2005-02-28 14:04:17.000000000 -0500
+++ linux_jtk/kernel/drivers/sound/ak4535.c 2005-03-29 16:59:51.000000000 -0500
@@ -383,12 +383,8 @@
if (ret)
goto out;
- // cannot both be enabled at once so disable prev setting first
- if (akm->line_connected)
- val &= ~SOUND_MASK_LINE;
- else if (akm->mic_connected)
- val &= ~SOUND_MASK_MIC;
-
+ // mic and speaker are switched together
+ // internal/external states are logically treated as mutually exclusive
if (val & SOUND_MASK_LINE) {
akm->line_connected = 1;
akm->mic_connected = 0;
diff -Naur linux_head_march29/kernel/drivers/sound/h5400-audio.c linux_jtk/kernel/drivers/sound/h5400-audio.c
--- linux_head_march29/kernel/drivers/sound/h5400-audio.c 2005-03-28 17:14:28.000000000 -0500
+++ linux_jtk/kernel/drivers/sound/h5400-audio.c 2005-03-29 17:53:26.000000000 -0500
@@ -34,6 +34,8 @@
#include <asm/dma.h>
#include <asm/arch-sa1100/h3600_hal.h>
#include <asm/arch/h5400-asic.h>
+#include <asm/arch/h5400-gpio.h>
+#include <asm/arch/irqs.h>
#include "ak4535.h"
#include "pxa-i2s.h"
@@ -188,6 +190,79 @@
i2c_command(ak4535, I2C_AK4535_CONFIGURE, &cfg);
}
+/* Check state of GPIO_NR_H5400_HEADPHONE_DETECT to see whether headphones are inserted. */
+/* The ak4535 Status word (control register 0F) does not seem to work correctly in all cases. */
+static int headphones_detected(void) {
+ return GET_H5400_GPIO(HEADPHONE_DETECT) == 0 ? 0 : 1;
+}
+
+extern int ak4535_command(struct i2c_client *clnt, int cmd, void *arg);
+
+/* Headphone plug tasklet issued by headphone handler */
+void autodetect_headphone_plug(void *unused){
+ struct i2c_client *clnt = ak4535;
+ long arg = 0;
+ mm_segment_t fs;
+ int ret;
+
+ if (clnt == NULL) {
+ return;
+ }
+
+ if (headphones_detected())
+ arg = SOUND_MASK_LINE;
+ else
+ arg = SOUND_MASK_MIC;
+
+ // disable userspace<-->kernelspace memory mapping so that we can call i2C with arg from kernel memory
+ fs = get_fs();
+ set_fs( get_ds() );
+ ret = i2c_command(ak4535, SOUND_MIXER_WRITE_RECSRC, (long *) &arg);
+ set_fs( fs );
+}
+
+/* Wait queue for headphone plug event */
+static struct tq_struct hp_task = {
+ routine: autodetect_headphone_plug,
+ data: NULL
+};
+static struct tq_struct hp_backup_task = {
+ routine: autodetect_headphone_plug,
+ data: NULL
+};
+
+/* Headphone AutoDetect Interrupt Handler */
+static void
+h5400_headphone_handler( int irq, void *dev_id, struct pt_regs *regs )
+{
+ // This routine gets called whenever an external headset is plugged into or removed from the iPAQ.
+ // Note -- The interrupt is "bouncy," so this may get called multiple times on plug insertion or removal.
+ // There is a very, very remote possibility of misdetecting the state, almost nill the way this is coded.
+ if ( schedule_task(&hp_task) == 0)
+ if ( schedule_task(&hp_backup_task) == 0)
+ printk(KERN_WARNING "HEADPHONE PLUG AUTODETECT: insertion/removal may have been misdetected!\n");
+}
+
+static void shutdown_headphone_autodetect(void)
+{
+ free_irq(IRQ_GPIO(GPIO_NR_H5400_HEADPHONE_DETECT), NULL);
+}
+
+static void init_headphone_autodetect(void)
+{
+ int result;
+ set_GPIO_IRQ_edge(GPIO_NR_H5400_HEADPHONE_DETECT, GPIO_BOTH_EDGES);
+ result = request_irq( IRQ_GPIO(GPIO_NR_H5400_HEADPHONE_DETECT),
+ h5400_headphone_handler,
+ SA_INTERRUPT,
+ "headphone plug",
+ NULL);
+ if(result){
+ printk(KERN_INFO "%d: Headphone Plug irq callback failed: %d\n",GPIO_NR_H5400_HEADPHONE_DETECT,result);
+ shutdown_headphone_autodetect();
+ }
+}
+
static void h5400_audio_init(void *dummy)
{
h3600_audio_power(0);
@@ -213,6 +288,8 @@
printk("h5400_audio_shutdown\n");
/* disable the audio power and all signals leading to the audio chip */
+ shutdown_headphone_autodetect();
+
h3600_audio_mute(1);
i2c_command(ak4535, I2C_AK4535_CLOSE, 0);
@@ -323,6 +400,9 @@
if (err == 0)
i2c_inc_use_client(ak4535);
+ init_headphone_autodetect();
+ autodetect_headphone_plug(NULL);
+
#ifdef DEBUG_PROC
create_proc_debug(ak4535);
#endif
Received on Tue Mar 29 2005 - 17:58:05 EST
This archive was generated by hypermail 2.2.0 : Mon Jul 25 2005 - 17:20:11 EDT