/*
 * UPIWIN - Micro Pi Windowing Framework Kernel
 * Copyright (C) 2019 Amy Bowersox/Erbosoft Metaverse Design Solutions
 *    
 * 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 program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *-------------------------------------------------------------------------
 */
#include <stdint.h>
#include <bcm2835.h>
#include "config.h"
#include "log.h"
#include "scode.h"
#include "gpio.h"

/* GPIO lines used by various peripheral devices */
#define GLINE_BUTTON1   17
#define GLINE_BUTTON2   22
#define GLINE_BUTTON3   23
#define GLINE_BUTTON4   27
#define GLINE_BACKLIGHT 18

#define PWM_BACKLIGHT   0   /* PWM channel used for backlight */

static void do_cleanup(void)
{
  /* close down the backlight lines */
  bcm2835_pwm_set_mode(PWM_BACKLIGHT, 1, 0);
  bcm2835_gpio_set_pud(GLINE_BACKLIGHT, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BACKLIGHT, BCM2835_GPIO_FSEL_INPT);

  /* close down the button lines */
  bcm2835_gpio_set_pud(GLINE_BUTTON1, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BUTTON1, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON2, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BUTTON2, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON3, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BUTTON3, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON4, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BUTTON4, BCM2835_GPIO_FSEL_INPT);

  if (!bcm2835_close())
    Log(LWARN, "Closing BCM2835 library failed");
}

HRESULT Gpio_setup(void)
{
  HRESULT hr;
  
  if (!bcm2835_init())
  {
    Log(LFATAL, "Error initializing BCM2835 library");
    return E_FAIL;
  }

  /* configure the buttons */
  bcm2835_gpio_set_pud(GLINE_BUTTON1, BCM2835_GPIO_PUD_UP);
  bcm2835_gpio_fsel(GLINE_BUTTON1, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON2, BCM2835_GPIO_PUD_UP);
  bcm2835_gpio_fsel(GLINE_BUTTON2, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON3, BCM2835_GPIO_PUD_UP);
  bcm2835_gpio_fsel(GLINE_BUTTON3, BCM2835_GPIO_FSEL_INPT);
  bcm2835_gpio_set_pud(GLINE_BUTTON4, BCM2835_GPIO_PUD_UP);
  bcm2835_gpio_fsel(GLINE_BUTTON4, BCM2835_GPIO_FSEL_INPT);

  /* configure the PWM for the backlight */
  bcm2835_gpio_set_pud(GLINE_BACKLIGHT, BCM2835_GPIO_PUD_OFF);
  bcm2835_gpio_fsel(GLINE_BACKLIGHT, BCM2835_GPIO_FSEL_ALT5);
  bcm2835_pwm_set_clock(BCM2835_PWM_CLOCK_DIVIDER_2);
  bcm2835_pwm_set_mode(PWM_BACKLIGHT, 1, 1);
  bcm2835_pwm_set_range(PWM_BACKLIGHT, GSB_BACKLIGHT_MAX + 1);
  bcm2835_pwm_set_data(PWM_BACKLIGHT, GSB_BACKLIGHT_DEFAULT);

  hr = Config_exitfunc(do_cleanup);
  if (FAILED(hr))
    do_cleanup();
  return hr;
}

UINT32 Gpio_read_buttons(void)
{
  UINT32 rc = 0;

  if (bcm2835_gpio_lev(GLINE_BUTTON1) == LOW)
    rc |= GRB_STATE_BUTTON1;
  if (bcm2835_gpio_lev(GLINE_BUTTON2) == LOW)
    rc |= GRB_STATE_BUTTON2;
  if (bcm2835_gpio_lev(GLINE_BUTTON3) == LOW)
    rc |= GRB_STATE_BUTTON3;
  if (bcm2835_gpio_lev(GLINE_BUTTON4) == LOW)
    rc |= GRB_STATE_BUTTON4;
  return rc;
}

void Gpio_set_backlight(UINT32 level)
{
  if (level > GSB_BACKLIGHT_MAX)
    level = GSB_BACKLIGHT_MAX;
  bcm2835_pwm_set_data(PWM_BACKLIGHT, level);
}
