/*  lcd.cpp
    Low level LCD driver for SED1520 122x32 graphic LCDs

    09.06.2001: tk, initial implementation.

    Copyright (c)2001 by Thomas Kindler, thomas.kindler@gmx.de

    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. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "sc12/hwapi.h"
#include "sc12/rtos.h"
#include "misc.h"
#include "sed1520.h"

// memory buffer for the LCD screen.
//
static unsigned char lcd_screen[4][122];

/**
 * Send the screen buffer to the actual display memory
 * \note
 *   I tried REP_OUTSB, but some displays are too
 *   slow for this transfer.
 *
 */
void LCD_Update()
{
  for (int y=0; y<4; y++) {
    outp(0x600, 0x00);
    outp(0x600, 0xB8 + y);
    outp(0x500, 0x00);
    outp(0x500, 0xB8 + y);
    for (int x=0; x<61; x++) {
      outp(0x602, lcd_screen[y][x   ]);
      outp(0x502, lcd_screen[y][x+61]);
    }
  }
}


/**
 * Set display brightness. This function MUST be called
 * during initialization, because it sets up the vertical
 * refresh timer for the LCD. (the brightness PWM signal
 * is derived from that clock)
 *
 * \param level  brigthness level 0..255
 *
 */
void LCD_SetBrightness(int level)
{
  long period = 2500000L/LCD_CLOCK;

  if (level <   0) level =   0;
  if (level > 255) level = 255;

  unsigned pwmOn, pwmOff;
  pwmOn  = (unsigned)(period*level/255);
  pwmOff = (unsigned)period - pwmOn;
  if (pwmOn  < LCD_MINPWMON )  pwmOn  = LCD_MINPWMON;
  if (pwmOff < LCD_MINPWMOFF)  pwmOff = LCD_MINPWMOFF;

  hal_init_timer(0, TIMER_CONT | TIMER_INT, pwmOn);
  hal_set_timer_duty(0, true, pwmOff);
  hal_start_timer(0);
}


/**
 * Get a pointer to the screen buffer
 */
char *LCD_GetScreenBuffer()
{
  return (char*)(&lcd_screen[0][0]);
}

/**
 * Clear the lcd_screen array.
 */
void LCD_Clear()
{
  memset(lcd_screen, 0x00, sizeof(lcd_screen));
}

/**
 * LCD_Init() must be called, before using any of the other
 * LCD functions. The display is initialized, and cleared.
 */
void LCD_Init()
{
  printf("initializing SED1520 LCD...\n");
  pfe_enable_bus(0xff, true);
  pfe_enable_pcs(5);
  pfe_enable_pcs(6);

  // enable refresh timer
  //
  pfe_enable_timer(TIMER_OUT0);
  LCD_SetBrightness(255);

  outp(0x500, 0xe2);  // reset
  outp(0x600, 0xe2);
  outp(0x500, 0xa4);  // static drive off
  outp(0x600, 0xa4);
  outp(0x500, 0xaf);  // display on
  outp(0x600, 0xaf);

  LCD_Clear();
  LCD_Update();
}


/**
 * Shut down the LCD controller.
 */
void LCD_Done()
{
  // enter power-save mode
  //
  outp(0x500, 0xa5);  // static drive on
  outp(0x600, 0xa5);
  outp(0x500, 0xae);  // display off
  outp(0x600, 0xae);

  // disable refresh timer
  //
  hal_stop_timer(0);

  // free pio pins
  //
  pfe_enable_pio(13, PIO_IPD);
  pfe_enable_pio( 2, PIO_IPU);
  pfe_enable_pio( 3, PIO_IPU); 
}

