/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2009 Bertrik Sikken
 * Copyright (C) 2008 François Dinel
 * Copyright ©   2008-2009 Rafaël Carré
 *
 * 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 "button.h"
#include "as3525v2.h"
#ifndef BOOTLOADER
#include "backlight.h"
#endif

#if   defined(SANSA_CLIP)
# define OUT_PIN GPIOC_PIN
# define OUT_DIR GPIOC_DIR
# define INITIAL 0
# define IN_PIN  GPIOB_PIN
# define IN_DIR  GPIOB_DIR
#elif defined(SANSA_CLIPV2)
# define OUT_PIN GPIOD_PIN
# define OUT_DIR GPIOD_DIR
# define INITIAL 1
# define IN_PIN  GPIOD_PIN
# define IN_DIR  GPIOD_DIR
#endif

static const int rows[3] = {
#if   defined(SANSA_CLIP)
    4, 5, 6
#elif defined(SANSA_CLIPV2)
    3, 4, 5
#endif
};

void button_init_device(void)
{
    GPIOA_DIR &= ~((1<<7) | (1<<3));
    CCU_IO &= ~(3<<2);
    IN_DIR &= ~((1<<2) | (1<<1) | (1<<0));

    for (int i = 0; i < 3; i++) {
        OUT_PIN(rows[i]) = INITIAL << rows[i];
        OUT_DIR |= 1 << rows[i];
    }

    /* get initial readings */
    button_read_device();
    button_read_device();
    button_read_device();
}

int button_read_device(void)
{
    static int row, buttons;
    static unsigned power_counter;

    if(button_hold())
    {
        power_counter = HZ;
        return 0;
    }

    /* direct GPIO connections */
    /* read power, but not if hold button was just released, since
     * you basically always hit power due to the slider mechanism after
     * releasing hold (wait 1 sec) */
    if (power_counter)
        power_counter--;

    if (GPIOA_PIN(7) && !power_counter)
        buttons |= BUTTON_POWER;
    else
        buttons &= ~BUTTON_POWER;

    static const int matrix [3][3] = {
        { 0 /*unused*/, BUTTON_VOL_UP,   BUTTON_UP },
        { BUTTON_LEFT,  BUTTON_SELECT,   BUTTON_RIGHT },
        { BUTTON_DOWN,  BUTTON_VOL_DOWN, BUTTON_HOME },
    };

    for (int i = 0; i<3; i++)
        if (IN_PIN(i) ^ (INITIAL << i))
            buttons |=  matrix[row][i];
        else
            buttons &= ~matrix[row][i];

    /* prepare next row */
    OUT_PIN(rows[row]) = INITIAL << rows[row];
    row++;
    row %= 3;
    OUT_PIN(rows[row]) = (!INITIAL) << rows[row];

    return buttons;
}

bool button_hold(void)
{
#ifdef SANSA_CLIPV2
    GPIOA_DIR |= 1<<7;
    GPIOA_PIN(7) = 1<<7;
    udelay(2);
#endif

    bool hold_button = (GPIOA_PIN(3) != 0);

#ifdef SANSA_CLIPV2
    GPIOA_PIN(7) = 0;
    GPIOA_DIR &= ~(1<<7);
    udelay(4);
#endif

#ifndef BOOTLOADER
    static bool hold_button_old = false;
    /* light handling */
    if (hold_button != hold_button_old)
    {
        hold_button_old = hold_button;
        backlight_hold_changed(hold_button);
    }
#endif /* BOOTLOADER */

    return hold_button;
}
