/*************************************************************************** * __________ __ ___. * 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 "mc13783-target.h" #include "adc.h" #include "button.h" static struct semaphore headphone_wakeup; static unsigned int headphone_thread_id; static int headphone_stack[200/sizeof(int)]; /* Not much 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) { int btn = BUTTON_NONE; if (data < 505) { if (data < 252) { if (data < 149) { if (data >= 64) { /* Play/Pause */ btn = BUTTON_RC_PLAY; } /* else headphone direct */ } else { /* DSP */ btn = BUTTON_RC_DSP; } } else { if (data < 370) { /* RW */ btn = BUTTON_RC_REW; } else { /* FF */ btn = BUTTON_RC_FF; } } } else { if (data < 870) { if (data < 675) { /* Vol + */ btn = BUTTON_RC_VOL_UP; } else { /* Vol - */ btn = BUTTON_RC_VOL_DOWN; } } #if 0 else { if (data < 951) { /* No buttons */ } else { /* Not inserted */ } } #endif } return btn; } static void headphone_thread(void) { int headphone_sleep_countdown = 0; int headphone_wait_timeout = TIMEOUT_BLOCK; 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, btn2; btn = adc_data_to_button(data); sleep(HZ/50); data = adc_read(ADC_HPREMOTE); btn2 = adc_data_to_button(data); if (btn != btn2) { /* 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); if (data >= 64 && data <= 951) { /* Should be a remote control - accelerate */ headphone_wait_timeout = HZ/20-HZ/50; 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 headphone_detect_event(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, 0); headphone_thread_id = create_thread(headphone_thread, headphone_stack, sizeof(headphone_stack), 0, headphone_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); /* Initially poll and then enable PMIC event */ headphone_detect_event(); mc13783_enable_event(MC13783_ONOFD2_EVENT, true); }