Re: h5100 h5400 h5500 automatic headphone detection patch

From: Koen Kooi <koen_at_dominion.kabel.utwente.nl>
Date: Wed, 30 Mar 2005 01:01:33 +0200

Op 30-mrt-05 om 0:54 heeft Jim Kaba het volgende geschreven:

> 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!?!?!?!?)
It won't be in 0.8.2, but it should be in the weekly snapshots after
that.

regards,

Koen

>
> 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-2246diff -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
> _______________________________________________
> H5400-port mailing list
> H5400-port_at_handhelds.org
> https://www.handhelds.org/mailman/listinfo/h5400-port
Received on Tue Mar 29 2005 - 18:04:27 EST

This archive was generated by hypermail 2.2.0 : Mon Jul 25 2005 - 17:20:11 EDT