2220a4b695
Fix stuff that was bugging me about the way I did it at first. While messing around I found RDS code wasn't masking its GPIO ISR as it should, which might lead to two different interrupts messing with the static data. Change-Id: I54626809ea3039a842af0cc9e3e42853326c4193
173 lines
5.7 KiB
C
173 lines
5.7 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 2009 by Michael Sevakis
|
|
*
|
|
* Driver to handle headphone jack events
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "kernel.h"
|
|
#include "thread.h"
|
|
#include "mc13783.h"
|
|
#include "adc.h"
|
|
#include "button.h"
|
|
|
|
static struct semaphore headphone_wakeup;
|
|
static unsigned int headphone_thread_id;
|
|
static unsigned int headphone_stack[176/sizeof(int)]; /* Little stack needed */
|
|
static const char * const headphone_thread_name = "headphone";
|
|
static bool headphones_detect = false;
|
|
|
|
/* Convert ADC reading into a button value. */
|
|
static int adc_data_to_button(unsigned int data)
|
|
{
|
|
/* _______370_______
|
|
* ___149___ ___675___
|
|
* ___64__ __252__ __505__ __870__
|
|
* x PLAY DSP REW FF VOL+ VOL- x
|
|
*
|
|
* Child nodes are at 2*n and 2*n+1 per usual bintree array representation
|
|
*/
|
|
static const unsigned int button_tree[16] =
|
|
{
|
|
[ 0] = 0,
|
|
[ 1] = 370,
|
|
[ 2] = 149,
|
|
[ 3] = 675,
|
|
[ 4] = 64,
|
|
[ 5] = 252,
|
|
[ 6] = 505,
|
|
[ 7] = 870,
|
|
[ 8] = BUTTON_NONE,
|
|
[ 9] = BUTTON_RC_PLAY,
|
|
[10] = BUTTON_RC_DSP,
|
|
[11] = BUTTON_RC_REW,
|
|
[12] = BUTTON_RC_FF,
|
|
[13] = BUTTON_RC_VOL_UP,
|
|
[14] = BUTTON_RC_VOL_DOWN,
|
|
[15] = BUTTON_NONE,
|
|
};
|
|
|
|
int i, button;
|
|
|
|
asm volatile (
|
|
"ldr %0, [%2, #1*4] \n" /* button = button_tree[1] */
|
|
"mov %1, #1 \n" /* i = 1 */
|
|
"cmp %3, %0 \n" /* C=1 if data > button */
|
|
"adc %1, %1, %1 \n" /* i = 2*n + C */
|
|
"ldr %0, [%2, %1, lsl #2] \n" /* button = button_tree[i] */
|
|
"cmp %3, %0 \n" /* C=1 if data > button */
|
|
"adc %1, %1, %1 \n" /* i = 2*n + C */
|
|
"ldr %0, [%2, %1, lsl #2] \n" /* button = button_tree[i] */
|
|
"cmp %3, %0 \n" /* C=1 if data > button */
|
|
"adc %1, %1, %1 \n" /* i = 2*n + C */
|
|
"ldr %0, [%2, %1, lsl #2] \n" /* button = button_tree[i] */
|
|
: "=&r"(button), "=&r"(i)
|
|
: "r"(button_tree), "r"(data));
|
|
|
|
return button;
|
|
}
|
|
|
|
static void NORETURN_ATTR headphone_thread(void)
|
|
{
|
|
int headphone_sleep_countdown = 0;
|
|
int headphone_wait_timeout = TIMEOUT_BLOCK;
|
|
int last_btn = BUTTON_NONE;
|
|
|
|
while (1)
|
|
{
|
|
int rc = semaphore_wait(&headphone_wakeup, headphone_wait_timeout);
|
|
unsigned int data = adc_read(ADC_HPREMOTE);
|
|
|
|
if (rc == OBJ_WAIT_TIMEDOUT)
|
|
{
|
|
if (headphone_sleep_countdown <= 0)
|
|
{
|
|
/* Polling ADC */
|
|
int btn = adc_data_to_button(data);
|
|
if (btn != last_btn)
|
|
{
|
|
last_btn = btn;
|
|
/* If the buttons dont agree twice in a row, then it's
|
|
* none (from meg-fx remote reader). */
|
|
btn = BUTTON_NONE;
|
|
}
|
|
|
|
button_headphone_set(btn);
|
|
continue;
|
|
}
|
|
|
|
if (--headphone_sleep_countdown == 0)
|
|
{
|
|
/* Nothing has changed and remote is not present -
|
|
* go to sleep. */
|
|
headphone_wait_timeout = TIMEOUT_BLOCK;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
headphones_detect = data <= 951; /* Max remote value */
|
|
|
|
/* Cancel any buttons if jack readings are unstable. */
|
|
button_headphone_set(BUTTON_NONE);
|
|
last_btn = BUTTON_NONE;
|
|
|
|
if (data >= 64 && data <= 951)
|
|
{
|
|
/* Should be a remote control - accelerate */
|
|
headphone_wait_timeout = HZ/25;
|
|
headphone_sleep_countdown = 0;
|
|
}
|
|
else if (rc == OBJ_WAIT_SUCCEEDED)
|
|
{
|
|
/* Got signaled - something is being plugged/unplugged. Set
|
|
* countdown until we just give up and go to sleep (~10s). */
|
|
headphone_wait_timeout = HZ/2;
|
|
headphone_sleep_countdown = 10*2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* HP plugged/unplugged event - called from PMIC ISR */
|
|
void MC13783_EVENT_CB_ONOFD2(void)
|
|
{
|
|
/* Trigger the thread immediately. */
|
|
semaphore_release(&headphone_wakeup);
|
|
}
|
|
|
|
/* Tell if anything is in the jack. */
|
|
bool headphones_inserted(void)
|
|
{
|
|
return headphones_detect;
|
|
}
|
|
|
|
void INIT_ATTR headphone_init(void)
|
|
{
|
|
/* A thread is required to monitor the remote ADC and jack state. */
|
|
semaphore_init(&headphone_wakeup, 1, 1);
|
|
headphone_thread_id = create_thread(headphone_thread,
|
|
headphone_stack,
|
|
sizeof(headphone_stack),
|
|
0, headphone_thread_name
|
|
IF_PRIO(, PRIORITY_REALTIME)
|
|
IF_COP(, CPU));
|
|
|
|
/* Enable PMIC event */
|
|
mc13783_enable_event(MC13783_INT_ID_ONOFD2, true);
|
|
}
|