/*
 * (C) Copyright 2007-2009, NXP Semiconductors
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <malloc.h>
#include <zlib.h>
#include <configs/STB225v1.h>
#include <bmp_layout.h>
#include <video_fb.h>

#include "phStbKBufMgr_Generated.h"

#if defined (CONFIG_SPLASH_SCREEN)

extern int video_display_bitmap (ulong, int, int);
extern int gunzip (void *, int, unsigned char *, unsigned long *);

/************************************/

#include "stb225_splash_data.c"

/************************************/

/* Define the maximum size of the display */
#define STB225_VIDEO_WIDTH                  (720)

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_NTSC) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
#define STB225_VIDEO_HEIGHT                 (480)
#else
#define STB225_VIDEO_HEIGHT                 (576)
#endif

#define STB225_BYTES_PER_PIXEL              (4)
#define STB225_VIDEO_BUFFER_SIZE            (STB225_VIDEO_WIDTH * STB225_VIDEO_HEIGHT * STB225_BYTES_PER_PIXEL)
#define STB225_VIDEO_DESCRIPTOR_BUFFER_SIZE (512)

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P)
#define PROGRESSIVE_X_OFFSET (6*STB225_BYTES_PER_PIXEL)
#define PROGRESSIVE_Y_OFFSET (-8)
#elif (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
#define PROGRESSIVE_X_OFFSET (5*STB225_BYTES_PER_PIXEL)
#define PROGRESSIVE_Y_OFFSET (0)
#endif

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
#define PROGRESSIVE_BUFFER_WIDTH (STB225_VIDEO_WIDTH + PROGRESSIVE_X_OFFSET)
#define PROGRESSIVE_BUFFER_HEIGHT (STB225_VIDEO_HEIGHT + PROGRESSIVE_Y_OFFSET)
#endif

#define KSEG0           0x80000000

#define ALIGNMENT       (64 * 1024)

#if defined (STB225_SCART_OUTPUT_MODE)
#define I2C_SCART_ADDR  0x22
#endif

#if defined (STB225_HDMI_OUTPUT_MODE)
#define I2C_HDMI_ADDR   0xE0
#endif

/************************************/

static GraphicDevice stb225_GD;

static unsigned int* displayBuffer = NULL;

typedef struct
{
    unsigned long address;
    unsigned int data;
}
reg_data_pair_t;

/************************************/

#include "stb225_ntsc.c"
#include "stb225_pal.c"
#include "stb225_480p.c"
#include "stb225_576p.c"

static const unsigned int OSDScalerCoeff[] =
{
    0x00000000, 0x00000000, 0x00010000, 0x00000000,
    0x00000000, 0x00000000, 0xF8000000, 0x00000800,
    0x00000000, 0x00000000, 0xF0000000, 0x00001000,
    0x00000000, 0x00000000, 0xE8000000, 0x00001800,
    0x00000000, 0x00000000, 0xE0000000, 0x00002000,
    0x00000000, 0x00000000, 0xD8000000, 0x00002800,
    0x00000000, 0x00000000, 0xD0000000, 0x00003000,
    0x00000000, 0x00000000, 0xC8000000, 0x00003800,
    0x00000000, 0x00000000, 0xC0000000, 0x00004000,
    0x00000000, 0x00000000, 0xB8000000, 0x00004800,
    0x00000000, 0x00000000, 0xB0000000, 0x00005000,
    0x00000000, 0x00000000, 0xA8000000, 0x00005800,
    0x00000000, 0x00000000, 0xA0000000, 0x00006000,
    0x00000000, 0x00000000, 0x98000000, 0x00006800,
    0x00000000, 0x00000000, 0x90000000, 0x00007000,
    0x00000000, 0x00000000, 0x88000000, 0x00007800,
    0x00000000, 0x00000000, 0x80000000, 0x00008000,
    0x00000000, 0x00000000, 0x78000000, 0x00008800,
    0x00000000, 0x00000000, 0x70000000, 0x00009000,
    0x00000000, 0x00000000, 0x68000000, 0x00009800,
    0x00000000, 0x00000000, 0x60000000, 0x0000A000,
    0x00000000, 0x00000000, 0x58000000, 0x0000A800,
    0x00000000, 0x00000000, 0x50000000, 0x0000B000,
    0x00000000, 0x00000000, 0x48000000, 0x0000B800,
    0x00000000, 0x00000000, 0x40000000, 0x0000C000,
    0x00000000, 0x00000000, 0x38000000, 0x0000C800,
    0x00000000, 0x00000000, 0x30000000, 0x0000D000,
    0x00000000, 0x00000000, 0x28000000, 0x0000D800,
    0x00000000, 0x00000000, 0x20000000, 0x0000E000,
    0x00000000, 0x00000000, 0x18000000, 0x0000E800,
    0x00000000, 0x00000000, 0x10000000, 0x0000F000,
    0x00000000, 0x00000000, 0x08000000, 0x0000F800,
    0x00000000, 0x80008000, 0x00000400, 0x80007c00,
    0x00000800, 0x80007800, 0x00000c00, 0x80007400,
    0x00001000, 0x80007000, 0x00001400, 0x80006c00,
    0x00001800, 0x80006800, 0x00001c00, 0x80006400,
    0x00002000, 0x80006000, 0x00002400, 0x80005c00,
    0x00002800, 0x80005800, 0x00002c00, 0x80005400,
    0x00003000, 0x80005000, 0x00003400, 0x80004c00,
    0x00003800, 0x80004800, 0x00003c00, 0x80004400,
    0x00004000, 0x80004000, 0x00004400, 0x80003c00,
    0x00004800, 0x80003800, 0x00004c00, 0x80003400,
    0x00005000, 0x80003000, 0x00005400, 0x80002c00,
    0x00005800, 0x80002800, 0x00005c00, 0x80002400,
    0x00006000, 0x80002000, 0x00006400, 0x80001c00,
    0x00006800, 0x80001800, 0x00006c00, 0x80001400,
    0x00007000, 0x80001000, 0x00007400, 0x80000c00,
    0x00007800, 0x80000800, 0x00007c00, 0x80000400
};

#if defined (STB225_SCART_OUTPUT_MODE)
static const unsigned char pScartSetup[] =
{
#if defined (SCART_ENCODER_CVBS)
    0x00,0x70,0x04,0x1f,0x27,0x09,0x11,0x04,0x00,0x00,0x08,0x00,0x2A    /* eg STB236: tv cvbs, vcr cvbs */
#else
    0x00,0x70,0x04,0x19,0x27,0x0B,0x13,0x04,0x00,0x00,0x08,0x00,0x2A
#endif
};
#endif

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
static const unsigned char coeff_hor_init[] =
{
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x01u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xf8u,
    0x00u, 0x08u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xf0u,
    0x00u, 0x10u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xe8u,
    0x00u, 0x18u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xe0u,
    0x00u, 0x20u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xd8u,
    0x00u, 0x28u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xd0u,
    0x00u, 0x30u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xc8u,
    0x00u, 0x38u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xc0u,
    0x00u, 0x40u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xb8u,
    0x00u, 0x48u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xb0u,
    0x00u, 0x50u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xa8u,
    0x00u, 0x58u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0xa0u,
    0x00u, 0x60u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x98u,
    0x00u, 0x68u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x90u,
    0x00u, 0x70u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x88u,
    0x00u, 0x78u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x80u,
    0x00u, 0x80u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x78u,
    0x00u, 0x88u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x70u,
    0x00u, 0x90u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x68u,
    0x00u, 0x98u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x60u,
    0x00u, 0xa0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x58u,
    0x00u, 0xa8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x50u,
    0x00u, 0xb0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x48u,
    0x00u, 0xb8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x40u,
    0x00u, 0xc0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x38u,
    0x00u, 0xc8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x30u,
    0x00u, 0xd0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x28u,
    0x00u, 0xd8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x20u,
    0x00u, 0xe0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x18u,
    0x00u, 0xe8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x10u,
    0x00u, 0xf0u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x00u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x08u,
    0x00u, 0xf8u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
};

static const unsigned char coeff_ver_2taps_init[] =
{
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
    0x01u, 0x00u, 0x00u, 0x00u,   0x00u, 0x00u, 0x00u, 0x00u,
};

static unsigned char *pY_buffer = NULL;
static unsigned char *pUV_buffer = NULL;

#define coef_a_x (47)
#define coef_b_x (157)
#define coef_c_x (16)
#define coef_a_y (-26)
#define coef_b_y (-86)
#define coef_c_y (112)
#define coef_a_z (112)
#define coef_b_z (-102)
#define coef_c_z (-10)
#define coef_cst_x (16)
#define coef_cst_y (128)
#define coef_cst_z (128)

/* RGB to YUV conversion routine - uses the same parameters as the HW conversion */
static void RGB2YUV(uint32_t value, uint8_t *pY, uint8_t *pU, uint8_t *pV)
{
    int32_t blue;
    int32_t green;
    int32_t red;

    red   = (int32_t)(((value >> 16) & 0xFF));
    green = (int32_t)(((value >> 8) & 0xFF));
    blue  = (int32_t)((value & 0xFF));

    if (pY)
    {
        *pY = (uint8_t)((((uint32_t)((coef_a_x*red) + (coef_b_x*green) + (coef_c_x*blue))) >> 8) + coef_cst_x);
    }

    if (pU)
    {
        *pU = (uint8_t)((((uint32_t)((coef_a_y*red) + (coef_b_y*green) + (coef_c_y*blue))) >> 8) + coef_cst_y);
    }

    if (pV)
    {
        *pV = (uint8_t)((((uint32_t)((coef_a_z*red) + (coef_b_z*green) + (coef_c_z*blue))) >> 8) + coef_cst_z);
    }
}

/* Function used to copy the OSD display to the SD YUV output buffers */
static void stb225_copy_OSD_to_Video(void)
{
    int row;
    int col;
    unsigned char *pYRow = pY_buffer + (PROGRESSIVE_Y_OFFSET * PROGRESSIVE_BUFFER_WIDTH) + PROGRESSIVE_X_OFFSET;
    unsigned char *pUVRow = pUV_buffer + (PROGRESSIVE_Y_OFFSET * PROGRESSIVE_BUFFER_WIDTH) + PROGRESSIVE_X_OFFSET;

    for(row=0; row<STB225_VIDEO_HEIGHT; row++)
    {
        if (pYRow >= pY_buffer)
        {
            unsigned char *pY = pYRow;
            unsigned char *pUV = pUVRow;
            unsigned char *pV = NULL;
            unsigned char *pU = pUV;
            for(col=0; col<STB225_VIDEO_WIDTH; col++)
            {
                RGB2YUV(displayBuffer[(row*STB225_VIDEO_WIDTH)+col], pY, pU, pV);
                if ((col % 2) == 0)
                {
                    pU = pUV;
                    pV = NULL;
                }
                else
                {
                    pV = pUV;
                    pU = NULL;
                }
                pY++;
                pUV++;
            }
        }
        /* Increment Y and UV pointers by the same value ... 422 format! */
        pYRow += PROGRESSIVE_BUFFER_WIDTH;
        pUVRow += PROGRESSIVE_BUFFER_WIDTH;
    }
}
#endif

static void stb225_reg_write (unsigned long addr, unsigned int value)
{
    *((volatile unsigned int *) addr) = value;
}

static void stb225_setupRegisters (const reg_data_pair_t *pData, int numEntries)
{
    int i;

    for (i = 0; i < numEntries; i++)
    {
        stb225_reg_write (pData[i].address, pData[i].data);
    }
}

#if defined (STB225_HDMI_OUTPUT_MODE) || defined (STB225_SCART_OUTPUT_MODE)
static void stb225_sendI2CMessage (unsigned char address, int which, const unsigned char *message, int length)
{
    #define I2C_OK                  0
    #define I2C_BUSY                1
    #define I2C_ERROR               2
    #define I2C_ARBITRATION_LOST    3

    #define I2C_CONTROL     ((volatile unsigned int *) (base + 0x000))
    #define I2C_DATA        ((volatile unsigned int *) (base + 0x004))
    #define I2C_STATUS      ((volatile unsigned int *) (base + 0x008))
    #define I2C_STOP        ((volatile unsigned int *) (base + 0x010))
    #define I2C_INTSTATUS   ((volatile unsigned int *) (base + 0xfe0))
    #define I2C_INTENABLE   ((volatile unsigned int *) (base + 0xfe4))
    #define I2C_INTCLEAR    ((volatile unsigned int *) (base + 0xfe8))

    unsigned long base = (which == 0) ? 0xb7e0d000 : 0xb7e0e000;
    int dataPosition;
    int drvStatus;

    *I2C_INTENABLE = 0x01;
    *I2C_INTCLEAR  = 0x01;
    dataPosition = 0;

    *I2C_CONTROL = 0x64;    /* generate Start */

    drvStatus = I2C_BUSY;
    while(drvStatus == I2C_BUSY)
    {
        int Status;
        Status = *I2C_INTSTATUS;

        if (Status & 0x01)
        {
            Status = *I2C_STATUS;
            switch (Status)
            {
                case 0x08:  // (re)start condition
                case 0x10:  *I2C_DATA = address;
                            *I2C_CONTROL = 0x44;    // clear SI bit to send address
                            *I2C_INTCLEAR = 0x01;
                            break;
                case 0x18:  *I2C_DATA = message[dataPosition++];  // send next data byte
                            *I2C_CONTROL = 0x44;
                            *I2C_INTCLEAR = 0x01;
                            break;
                case 0x20:  // no Ack received
                            *I2C_CONTROL = 0x54;    // Stop condition
                            *I2C_INTCLEAR = 0x01;
                            drvStatus = I2C_ERROR;
                            break;
                case 0x28:  // ack received
                            if (dataPosition < length) {
                                *I2C_DATA = message[dataPosition++];
                                *I2C_CONTROL = 0x44;  // release interrupt
                            }
                            else {
                                *I2C_CONTROL = 0x54;
                                *I2C_STOP = 0x01;
                                drvStatus = I2C_OK;
                            }
                            *I2C_INTCLEAR = 0x01;
                            break;
                case 0x30: // no ACK for data byte
                            *I2C_CONTROL = 0x54; // stop condition
                            *I2C_INTCLEAR = 0x01;
                            drvStatus = I2C_ERROR;
                            break;
                case 0x38: // arbitration lost -> not addressed as slave
                            *I2C_CONTROL = 0x64; // send start again
                            *I2C_INTCLEAR = 0x01;
                            drvStatus = I2C_ARBITRATION_LOST;
                            break;
                case 0xF8:
                case 0x00:
                            break;
                default:    // undefined error
                            *I2C_CONTROL = 0x54; // send stop
                            *I2C_INTCLEAR = 0x01;
                            drvStatus = I2C_ERROR;
                            break;
            }
        }
    }
    *I2C_INTENABLE = 0x00;

    #undef I2C_CONTROL
    #undef I2C_DATA
    #undef I2C_STATUS
    #undef I2C_STOP
    #undef I2C_INTSTATUS
    #undef I2C_INTENABLE
    #undef I2C_INTCLEAR
}
#endif

#if defined (STB225_HDMI_OUTPUT_MODE)
static void stb225_setup_hdmi (void)
{
    int i;

    for (i = 0; i < sizeof(pHDMISetup); i += 2)
    {
        stb225_sendI2CMessage (I2C_HDMI_ADDR, 0, &pHDMISetup[i], 2);
    }
}
#endif

static void stb225_setup_video (void)
{
#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
    int i;
    unsigned char *tapsVert = NULL;
    unsigned char *tapsHorz = NULL;

    /* Set up the horizontal and vertical tap filters */
    tapsVert = malloc(sizeof(coeff_ver_2taps_init));
    tapsHorz = malloc(sizeof(coeff_hor_init));
    memcpy(tapsVert, coeff_ver_2taps_init, sizeof(coeff_ver_2taps_init));
    memcpy(tapsHorz, coeff_hor_init, sizeof(coeff_hor_init));
    stb225_reg_write(0xb7e110F4, ((unsigned int)tapsVert) & 0x0fffffff);
    stb225_reg_write(0xb7e11104, ((unsigned int)tapsVert) & 0x0fffffff);
    stb225_reg_write(0xb7e110EC, ((unsigned int)tapsHorz) & 0x0fffffff);
    stb225_reg_write(0xb7e110FC, ((unsigned int)tapsHorz) & 0x0fffffff);

    /* Set up the pointers to the display memory */
    stb225_reg_write(0xb7e11004, (unsigned int)(pY_buffer));
    stb225_reg_write(0xb7e11008, (unsigned int)(pY_buffer + PROGRESSIVE_BUFFER_WIDTH));
    stb225_reg_write(0xb7e1100C, (unsigned int)(pUV_buffer));
    stb225_reg_write(0xb7e11010, (unsigned int)(pUV_buffer + PROGRESSIVE_BUFFER_WIDTH));

    /* Set up the display memory width */
    stb225_reg_write(0xb7e1103C, (unsigned int)(PROGRESSIVE_BUFFER_WIDTH * 2));

    /* TODO: Work out why do the registers have to be set multiple times in order to work?!?!? */
    /*       If you don't do this then the SD output is not initialised correctly!             */
    for(i=0; i< 4; i++)
    {
#endif
        stb225_setupRegisters (clock,      sizeof(clock)      / sizeof(reg_data_pair_t));
        stb225_setupRegisters (config,     sizeof(config)     / sizeof(reg_data_pair_t));
        stb225_setupRegisters (denc,       sizeof(denc)       / sizeof(reg_data_pair_t));
        stb225_setupRegisters (intlcr,     sizeof(intlcr)     / sizeof(reg_data_pair_t));
        stb225_setupRegisters (mixer,      sizeof(mixer)      / sizeof(reg_data_pair_t));
        stb225_setupRegisters (osd_0,      sizeof(osd_0)      / sizeof(reg_data_pair_t));
        stb225_setupRegisters (sync_hd,    sizeof(sync_hd)    / sizeof(reg_data_pair_t));
        stb225_setupRegisters (cdmmu,      sizeof(cdmmu)      / sizeof(reg_data_pair_t));

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
        stb225_setupRegisters (sync_sd,    sizeof(sync_sd)    / sizeof(reg_data_pair_t));
        stb225_setupRegisters (vo,         sizeof(vo)         / sizeof(reg_data_pair_t));
        stb225_setupRegisters (display_sd, sizeof(display_sd) / sizeof(reg_data_pair_t));
    }
#endif

#if defined (STB225_HDMI_OUTPUT_MODE)
    stb225_setupRegisters (dvo,        sizeof(dvo)        / sizeof(reg_data_pair_t));
#endif

#if defined (STB225_SCART_OUTPUT_MODE)
    /* Setup SCART switch */
#if defined (SCART_CONNECTED_ON_I2C0)
    stb225_sendI2CMessage (I2C_SCART_ADDR, 0, pScartSetup, sizeof(pScartSetup));    /* eg STB236 */
#else
    stb225_sendI2CMessage (I2C_SCART_ADDR, 1, pScartSetup, sizeof(pScartSetup));
#endif
#endif
}

static int stb225_get_bmp_dimensions (unsigned int addr, unsigned int source_data_len, unsigned int* pWidth, unsigned int* pHeight)
{
    bmp_image_t *bmp=(bmp_image_t *)addr;
#ifdef CONFIG_VIDEO_BMP_GZIP
    unsigned char *dst = NULL;
    ulong len;
#endif /* CONFIG_VIDEO_BMP_GZIP */

    if (!((bmp->header.signature[0]=='B') &&
          (bmp->header.signature[1]=='M'))) {

#ifdef CONFIG_VIDEO_BMP_GZIP
        /*
         * Decompress bmp image
         */
        len = source_data_len;
        dst = malloc(CFG_VIDEO_LOGO_MAX_SIZE);
        if (dst == NULL) {
            printf("Error: malloc in gunzip failed!\n");
            return(1);
        }
        if (gunzip(dst, CFG_VIDEO_LOGO_MAX_SIZE, (uchar *)addr, &len) != 0) {
            printf("There is no valid bmp file at the given address\n");
            return(1);
        }
        if (len == CFG_VIDEO_LOGO_MAX_SIZE) {
            printf("Image could be truncated (increase CFG_VIDEO_LOGO_MAX_SIZE)!\n");
        }

        /*
         * Set addr to decompressed image
         */
        bmp = (bmp_image_t *)dst;

        /*
         * Check for bmp mark 'BM'
         */
        if (!((bmp->header.signature[0] == 'B') &&
              (bmp->header.signature[1] == 'M'))) {
            printf("There is no valid bmp file at the given address\n");
            free(dst);
            return(1);
        }

#else /* CONFIG_VIDEO_BMP_GZIP */
        printf("There is no valid bmp file at the given address\n");
        return(1);
#endif /* CONFIG_VIDEO_BMP_GZIP */
    }
    *pWidth = (unsigned int)le32_to_cpu(bmp->header.width);
    *pHeight = (unsigned int)le32_to_cpu(bmp->header.height);

#ifdef CONFIG_VIDEO_BMP_GZIP
    if (dst) {
        free(dst);
    }
#endif /* CONFIG_VIDEO_BMP_GZIP */

    return(0);
}

/* Implmentation of video system hardware initialisation function */
void *video_hw_init (void)
{
    DECLARE_GLOBAL_DATA_PTR;

    unsigned int *descriptor;
    unsigned long displayBufferAddr;

    /* Allocate memory for the OSD descriptor */
    descriptor = malloc (STB225_VIDEO_DESCRIPTOR_BUFFER_SIZE);
    if (descriptor == NULL)
    {
        return NULL;
    }

    /* Get the dimensions of the splash screen image */
    stb225_get_bmp_dimensions((unsigned int)stb225_splash_data_gz, sizeof(stb225_splash_data_gz), &stb225_GD.winSizeX, &stb225_GD.winSizeY);

    stb225_GD.gdfIndex = GDF_32BIT_X888RGB;
    stb225_GD.gdfBytesPP = 4;
    stb225_GD.frameAdrs = (unsigned int)malloc (stb225_GD.winSizeX * stb225_GD.winSizeY * sizeof(unsigned int));
    if (stb225_GD.frameAdrs == 0)
    {
        return NULL;
    }

    /* Set up the display memory for the splash screen - */
    /* Use the Video Y buffers as they will not be overwritten for a long time! */
    displayBufferAddr = CFG_SDRAM_BASE + (phStbKBufMgr_start_FSY & (sdram_size() - 1));
    displayBufferAddr &= ~(ALIGNMENT - 1);

    /* u-boot uses all memory above its initial stack pointer + any stack usage below... */
    if ((displayBufferAddr & 0x1FFFFFFF) > ((gd->addr_sp - (2 * 1024 * 1024)) & 0x1FFFFFFF))
    {
        printf ("Splash: framebuffer (0x%08x) too close to u-boot memory (0x%08x), aborting\n",
                displayBufferAddr, gd->addr_sp);
        return NULL;
    }

#if 0
    /* Temp debug... */
    printf ("Splash: framebuffer %d MBytes below addr_sp\n",
            ((gd->addr_sp & 0x1FFFFFFF) - (displayBufferAddr & 0x1FFFFFFF)) / (1024 * 1024));
#endif

    displayBuffer = (unsigned int *) displayBufferAddr;

    stb225_reg_write (0xb7e08004, 0x77ffffff);  /* release resets ?? */
    stb225_reg_write (0xb7e08014, 0x0000007f);  /* release resets some more ?? */

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
    /* Allocate the YUV buffers used to hold the SD downscale path image */
    pY_buffer = malloc(PROGRESSIVE_BUFFER_WIDTH*PROGRESSIVE_BUFFER_HEIGHT);
    if (pY_buffer == NULL)
    {
        return NULL;
    }
    pUV_buffer = malloc(PROGRESSIVE_BUFFER_WIDTH*PROGRESSIVE_BUFFER_HEIGHT);
    if (pUV_buffer == NULL)
    {
        return NULL;
    }
#endif

    /* Set up analog output & DVO */
    stb225_setup_video();

    /* Enable i2s output clock. Must be done before HDMI setup, but
     * hopefully quite safe to do here even if HDMI is not enabled.
     */
    stb225_reg_write (0xb7e290c4, 0x0);

#if defined (STB225_HDMI_OUTPUT_MODE)
    /* Set up HDMI output, which must happen after DVO setup */
    stb225_setup_hdmi();
#endif

    /* Initialise the OSD descriptor */
    descriptor[0] = 0xa18020FC;

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_NTSC)
    descriptor[1] = 0x702902d0;
#elif (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
    descriptor[1] = 0x705702d0;
#elif (STB225_DISPLAY_MODE == DISPLAY_MODE_576P)
    descriptor[1] = 0x702e02d0;
#else
    descriptor[1] = 0x702c02d0;
#endif
    descriptor[2] = (STB225_VIDEO_HEIGHT << 17);
    descriptor[3] = 0x00000000;
    descriptor[4] = (unsigned int) ((unsigned int) displayBuffer - (unsigned int) descriptor);
    descriptor[5] = (unsigned int) ((unsigned int) displayBuffer - (unsigned int) descriptor);
#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
    descriptor[6] = 0x807ffcff;
#else
    descriptor[6] = 0x807f3dff;
#endif
    descriptor[7] = (unsigned int) ((unsigned int) OSDScalerCoeff - (unsigned int) descriptor);

    flush_cache ((unsigned int) descriptor, STB225_VIDEO_DESCRIPTOR_BUFFER_SIZE);

    /*
       OSD Anchor address
    */
    stb225_reg_write (0xb7e1200c, (unsigned int) (((unsigned long) descriptor) & ~KSEG0));
    stb225_reg_write (0xb7e12000, 0x00000001);

    return (void*)&stb225_GD;
}

/* Implmentation of video system colour look up table function */
void video_set_lut (unsigned int index,unsigned char r,unsigned char g,unsigned char b)
{
}

static void stb225_scale_to_fit (unsigned int *pSrc, int srcWidth,  int srcHeight,
                                 unsigned int *pDest, int destWidth, int destHeight)
{
    int row;
    int col;

    if (pSrc && pDest)
    {
        /* Check to see if the image matches the display */
        if ((srcWidth == destWidth) &&
            (srcHeight == destHeight))
        {
            /* Got a match - copy the data ... adding the alpha! */
            for(row=0; row<destHeight; row++)
            {
                for(col=0; col<destWidth; col++)
                {
                    *pDest = *pSrc | 0xFF000000;
                    pDest++;
                    pSrc++;
                }
            }
        }
        else
        {
            /* Source and destination do not match - do some scaling ... and add the alpha! */
            for(row=0; row<destHeight; row++)
            {
                int srcRow;
                unsigned int *srcPtr;
                /* Work out the source row that contribute to the destination row pixel */
                srcRow = (row * srcHeight) / destHeight;
                if (srcRow > (srcHeight-1))
                {
                    srcRow = srcHeight-1;
                }
                srcPtr = pSrc + (srcWidth*srcRow);
                for(col=0; col<destWidth; col++)
                {
                    *pDest = (*(srcPtr+((srcWidth*col)/destWidth))) | 0xFF000000;
                    pDest++;
                }
            }
        }

#if (STB225_DISPLAY_MODE == DISPLAY_MODE_576P) || (STB225_DISPLAY_MODE == DISPLAY_MODE_480P)
        stb225_copy_OSD_to_Video();
#endif
    }
}

int do_splash_noargs (void)
{
    /* Decode the splash screen bitmap */
    video_display_bitmap((unsigned int)stb225_splash_data_gz, 0, 0);

    /* Scale the image to fit the display */
    stb225_scale_to_fit((unsigned int*)stb225_GD.frameAdrs, stb225_GD.winSizeX, stb225_GD.winSizeY,
                        displayBuffer, STB225_VIDEO_WIDTH, STB225_VIDEO_HEIGHT);
    return 0;
}

int do_splash (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    return do_splash_noargs();
}

/***************************************************/

U_BOOT_CMD(
    splash, CFG_MAXARGS, 1, do_splash,
    "splash  - init splash screen\n",
    "\n"
);

/***************************************************/
#endif

